diff options
Diffstat (limited to 'etoolkit/__main__.py')
| -rw-r--r-- | etoolkit/__main__.py | 79 |
1 files changed, 72 insertions, 7 deletions
diff --git a/etoolkit/__main__.py b/etoolkit/__main__.py index 8c4245d..603bb2e 100644 --- a/etoolkit/__main__.py +++ b/etoolkit/__main__.py | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | # etoolkit | 1 | # etoolkit |
| 2 | # Copyright (C) 2021 Simeon Simeonov | 2 | # Copyright (C) 2021-2022 Simeon Simeonov |
| 3 | 3 | ||
| 4 | # This program is free software: you can redistribute it and/or modify | 4 | # This program is free software: you can redistribute it and/or modify |
| 5 | # it under the terms of the GNU General Public License as published by | 5 | # it under the terms of the GNU General Public License as published by |
| @@ -24,6 +24,7 @@ python -m etoolkit -p | |||
| 24 | import argparse | 24 | import argparse |
| 25 | import errno | 25 | import errno |
| 26 | import getpass | 26 | import getpass |
| 27 | import io | ||
| 27 | import json | 28 | import json |
| 28 | import logging | 29 | import logging |
| 29 | import os | 30 | import os |
| @@ -52,11 +53,29 @@ def decrypt_value(args: argparse.Namespace, config: dict): | |||
| 52 | :type config: dict | 53 | :type config: dict |
| 53 | """ | 54 | """ |
| 54 | password_hash = None | 55 | password_hash = None |
| 56 | pipe_input = None | ||
| 57 | if not os.isatty(sys.stdin.fileno()): | ||
| 58 | pipe_input = sys.stdin.read().strip() | ||
| 55 | if 'general' in config: | 59 | if 'general' in config: |
| 56 | password_hash = config['general'].get('MASTER_PASSWORD_HASH') | 60 | password_hash = config['general'].get('MASTER_PASSWORD_HASH') |
| 57 | password = etoolkit.EtoolkitInstance.confirm_password_prompt( | 61 | |
| 58 | password_hash, False | 62 | if ( |
| 59 | ) | 63 | args.master_password_prompt |
| 64 | or os.environ.get('ETOOLKIT_MASTER_PASSWORD') is None | ||
| 65 | ): | ||
| 66 | password = etoolkit.EtoolkitInstance.confirm_password_prompt( | ||
| 67 | password_hash, False | ||
| 68 | ) | ||
| 69 | else: | ||
| 70 | password = os.environ.get('ETOOLKIT_MASTER_PASSWORD') | ||
| 71 | |||
| 72 | if pipe_input: | ||
| 73 | # the input came from stdin. No need to prompt | ||
| 74 | print( | ||
| 75 | 'Decrypted value: ' | ||
| 76 | f'{etoolkit.EtoolkitInstance.decrypt(password, pipe_input)}' | ||
| 77 | ) | ||
| 78 | return | ||
| 60 | while True: | 79 | while True: |
| 61 | try: | 80 | try: |
| 62 | value = input('Value: ') | 81 | value = input('Value: ') |
| @@ -69,6 +88,7 @@ def decrypt_value(args: argparse.Namespace, config: dict): | |||
| 69 | except KeyboardInterrupt: | 88 | except KeyboardInterrupt: |
| 70 | print(os.linesep) | 89 | print(os.linesep) |
| 71 | break | 90 | break |
| 91 | return | ||
| 72 | 92 | ||
| 73 | 93 | ||
| 74 | def encrypt_value(args: argparse.Namespace, config: dict): | 94 | def encrypt_value(args: argparse.Namespace, config: dict): |
| @@ -86,9 +106,29 @@ def encrypt_value(args: argparse.Namespace, config: dict): | |||
| 86 | :type config: dict | 106 | :type config: dict |
| 87 | """ | 107 | """ |
| 88 | password_hash = None | 108 | password_hash = None |
| 109 | pipe_input = None | ||
| 110 | if not os.isatty(sys.stdin.fileno()): | ||
| 111 | pipe_input = sys.stdin.read().strip() | ||
| 89 | if 'general' in config: | 112 | if 'general' in config: |
| 90 | password_hash = config['general'].get('MASTER_PASSWORD_HASH') | 113 | password_hash = config['general'].get('MASTER_PASSWORD_HASH') |
| 91 | password = etoolkit.EtoolkitInstance.confirm_password_prompt(password_hash) | 114 | |
| 115 | if ( | ||
| 116 | args.master_password_prompt | ||
| 117 | or os.environ.get('ETOOLKIT_MASTER_PASSWORD') is None | ||
| 118 | ): | ||
| 119 | password = etoolkit.EtoolkitInstance.confirm_password_prompt( | ||
| 120 | password_hash | ||
| 121 | ) | ||
| 122 | else: | ||
| 123 | password = os.environ.get('ETOOLKIT_MASTER_PASSWORD') | ||
| 124 | |||
| 125 | if pipe_input: | ||
| 126 | # the input came from stdin. No need to prompt | ||
| 127 | print( | ||
| 128 | 'Encrypted value: ' | ||
| 129 | f'{etoolkit.EtoolkitInstance.encrypt(password, pipe_input)}' | ||
| 130 | ) | ||
| 131 | return | ||
| 92 | while True: | 132 | while True: |
| 93 | try: | 133 | try: |
| 94 | if args.echo: | 134 | if args.echo: |
| @@ -104,6 +144,7 @@ def encrypt_value(args: argparse.Namespace, config: dict): | |||
| 104 | except KeyboardInterrupt: | 144 | except KeyboardInterrupt: |
| 105 | print(os.linesep) | 145 | print(os.linesep) |
| 106 | break | 146 | break |
| 147 | return | ||
| 107 | 148 | ||
| 108 | 149 | ||
| 109 | def main(inargs=None): | 150 | def main(inargs=None): |
| @@ -192,6 +233,16 @@ def main(inargs=None): | |||
| 192 | ), | 233 | ), |
| 193 | ) | 234 | ) |
| 194 | parser.add_argument( | 235 | parser.add_argument( |
| 236 | '-P', | ||
| 237 | '--master-password-prompt', | ||
| 238 | dest='master_password_prompt', | ||
| 239 | action='store_true', | ||
| 240 | help=( | ||
| 241 | 'Force prompt for the master password even if the env. variable ' | ||
| 242 | '"ETOOLKIT_MASTER_PASSWORD" is set' | ||
| 243 | ), | ||
| 244 | ) | ||
| 245 | parser.add_argument( | ||
| 195 | '-q', | 246 | '-q', |
| 196 | '--no-output', | 247 | '--no-output', |
| 197 | dest='dump_output', | 248 | dest='dump_output', |
| @@ -217,12 +268,26 @@ def main(inargs=None): | |||
| 217 | ) | 268 | ) |
| 218 | args = parser.parse_args(inargs) | 269 | args = parser.parse_args(inargs) |
| 219 | try: | 270 | try: |
| 220 | with open(args.config_file, 'r', encoding='utf-8') as fp: | 271 | with io.open(args.config_file, 'r', encoding='utf-8') as fp: |
| 221 | config_dict = json.load(fp) | 272 | config_dict = json.load(fp) |
| 273 | except FileNotFoundError as e: | ||
| 274 | # do not raise exception if config-file is missing for: | ||
| 275 | # - decrypting value | ||
| 276 | # - encrypting value | ||
| 277 | # - password hash generation | ||
| 278 | if args.password_hash or args.decrypt_value or args.encrypt_value: | ||
| 279 | logger.warning( | ||
| 280 | "Configuration file %s is missing, although not required " | ||
| 281 | "by the provided parameters", | ||
| 282 | args.config_file, | ||
| 283 | ) | ||
| 284 | config_dict = {} | ||
| 285 | else: | ||
| 286 | logger.error("Configuration file %s is missing", args.config_file) | ||
| 287 | raise SystemExit(errno.EIO) from e | ||
| 222 | except Exception as e: | 288 | except Exception as e: |
| 223 | logger.error("Unable to parse %r: %s", args.config_file, e) | 289 | logger.error("Unable to parse %r: %s", args.config_file, e) |
| 224 | raise SystemExit(errno.EIO) from e | 290 | raise SystemExit(errno.EIO) from e |
| 225 | |||
| 226 | try: | 291 | try: |
| 227 | if args.decrypt_value: | 292 | if args.decrypt_value: |
| 228 | decrypt_value(args, config_dict) | 293 | decrypt_value(args, config_dict) |
