diff --git a/.pylintrc b/.pylintrc index b27682a5..5bd9dbc2 100644 --- a/.pylintrc +++ b/.pylintrc @@ -533,5 +533,5 @@ valid-metaclass-classmethod-first-arg=cls # Exceptions that will emit a warning when being caught. Defaults to # "BaseException, Exception". -overgeneral-exceptions=BaseException, - Exception +overgeneral-exceptions=builtins.BaseException, + builtins.Exception diff --git a/docs/ahriman.1 b/docs/ahriman.1 index b640dad3..77986186 100644 --- a/docs/ahriman.1 +++ b/docs/ahriman.1 @@ -1,4 +1,4 @@ -.TH AHRIMAN "1" "2023\-01\-27" "ahriman" "Generated Python Manual" +.TH AHRIMAN "1" "2023\-02\-05" "ahriman" "Generated Python Manual" .SH NAME ahriman .SH SYNOPSIS @@ -590,10 +590,15 @@ clear directory with built packages (default: False) clear directory with pacman local database cache (default: False) .SH COMMAND \fI\,'ahriman service\-config'\/\fR -usage: ahriman service\-config [\-h] +usage: ahriman service\-config [\-h] [\-\-secure | \-\-no\-secure] dump configuration for the specified architecture +.SH OPTIONS \fI\,'ahriman service\-config'\/\fR +.TP +\fB\-\-secure\fR, \fB\-\-no\-secure\fR +hide passwords and secrets from output (default: True) + .SH COMMAND \fI\,'ahriman service\-config\-validate'\/\fR usage: ahriman service\-config\-validate [\-h] [\-e] diff --git a/docs/ahriman.core.database.migrations.rst b/docs/ahriman.core.database.migrations.rst index 2a1644d3..c35d10f7 100644 --- a/docs/ahriman.core.database.migrations.rst +++ b/docs/ahriman.core.database.migrations.rst @@ -52,6 +52,14 @@ ahriman.core.database.migrations.m005\_make\_opt\_depends module :no-undoc-members: :show-inheritance: +ahriman.core.database.migrations.m006\_packages\_architecture\_required module +------------------------------------------------------------------------------ + +.. automodule:: ahriman.core.database.migrations.m006_packages_architecture_required + :members: + :no-undoc-members: + :show-inheritance: + Module contents --------------- diff --git a/docs/completions/bash/_ahriman b/docs/completions/bash/_ahriman index 2d81c99f..248c0fa6 100644 --- a/docs/completions/bash/_ahriman +++ b/docs/completions/bash/_ahriman @@ -48,9 +48,9 @@ _shtab_ahriman_update_option_strings=('-h' '--help' '--dry-run' '-e' '--exit-cod _shtab_ahriman_service_clean_option_strings=('-h' '--help' '--cache' '--no-cache' '--chroot' '--no-chroot' '--manual' '--no-manual' '--packages' '--no-packages' '--pacman' '--no-pacman') _shtab_ahriman_clean_option_strings=('-h' '--help' '--cache' '--no-cache' '--chroot' '--no-chroot' '--manual' '--no-manual' '--packages' '--no-packages' '--pacman' '--no-pacman') _shtab_ahriman_repo_clean_option_strings=('-h' '--help' '--cache' '--no-cache' '--chroot' '--no-chroot' '--manual' '--no-manual' '--packages' '--no-packages' '--pacman' '--no-pacman') -_shtab_ahriman_service_config_option_strings=('-h' '--help') -_shtab_ahriman_config_option_strings=('-h' '--help') -_shtab_ahriman_repo_config_option_strings=('-h' '--help') +_shtab_ahriman_service_config_option_strings=('-h' '--help' '--secure' '--no-secure') +_shtab_ahriman_config_option_strings=('-h' '--help' '--secure' '--no-secure') +_shtab_ahriman_repo_config_option_strings=('-h' '--help' '--secure' '--no-secure') _shtab_ahriman_service_config_validate_option_strings=('-h' '--help' '-e' '--exit-code') _shtab_ahriman_config_validate_option_strings=('-h' '--help' '-e' '--exit-code') _shtab_ahriman_repo_config_validate_option_strings=('-h' '--help' '-e' '--exit-code') @@ -362,10 +362,16 @@ _shtab_ahriman_repo_clean___pacman_nargs=0 _shtab_ahriman_repo_clean___no_pacman_nargs=0 _shtab_ahriman_service_config__h_nargs=0 _shtab_ahriman_service_config___help_nargs=0 +_shtab_ahriman_service_config___secure_nargs=0 +_shtab_ahriman_service_config___no_secure_nargs=0 _shtab_ahriman_config__h_nargs=0 _shtab_ahriman_config___help_nargs=0 +_shtab_ahriman_config___secure_nargs=0 +_shtab_ahriman_config___no_secure_nargs=0 _shtab_ahriman_repo_config__h_nargs=0 _shtab_ahriman_repo_config___help_nargs=0 +_shtab_ahriman_repo_config___secure_nargs=0 +_shtab_ahriman_repo_config___no_secure_nargs=0 _shtab_ahriman_service_config_validate__h_nargs=0 _shtab_ahriman_service_config_validate___help_nargs=0 _shtab_ahriman_service_config_validate__e_nargs=0 diff --git a/docs/completions/zsh/_ahriman b/docs/completions/zsh/_ahriman index a167a6b6..5ec67945 100644 --- a/docs/completions/zsh/_ahriman +++ b/docs/completions/zsh/_ahriman @@ -122,6 +122,7 @@ _shtab_ahriman_clean_options=( _shtab_ahriman_config_options=( "(- : *)"{-h,--help}"[show this help message and exit]" + {--secure,--no-secure}"[hide passwords and secrets from output (default\: \%(default)s)]:secure:" ) _shtab_ahriman_config_validate_options=( @@ -293,6 +294,7 @@ _shtab_ahriman_repo_clean_options=( _shtab_ahriman_repo_config_options=( "(- : *)"{-h,--help}"[show this help message and exit]" + {--secure,--no-secure}"[hide passwords and secrets from output (default\: \%(default)s)]:secure:" ) _shtab_ahriman_repo_config_validate_options=( @@ -423,6 +425,7 @@ _shtab_ahriman_service_clean_options=( _shtab_ahriman_service_config_options=( "(- : *)"{-h,--help}"[show this help message and exit]" + {--secure,--no-secure}"[hide passwords and secrets from output (default\: \%(default)s)]:secure:" ) _shtab_ahriman_service_config_validate_options=( diff --git a/src/ahriman/application/ahriman.py b/src/ahriman/application/ahriman.py index e5a76960..5516584f 100644 --- a/src/ahriman/application/ahriman.py +++ b/src/ahriman/application/ahriman.py @@ -750,6 +750,8 @@ def _set_service_config_parser(root: SubParserAction) -> argparse.ArgumentParser parser = root.add_parser("service-config", aliases=["config", "repo-config"], help="dump configuration", description="dump configuration for the specified architecture", formatter_class=_formatter) + parser.add_argument("--secure", help="hide passwords and secrets from output", + action=argparse.BooleanOptionalAction, default=True) parser.set_defaults(handler=handlers.Dump, lock=None, report=False, quiet=True, unsafe=True) return parser diff --git a/src/ahriman/application/handlers/dump.py b/src/ahriman/application/handlers/dump.py index 9aefd582..a3aa5c72 100644 --- a/src/ahriman/application/handlers/dump.py +++ b/src/ahriman/application/handlers/dump.py @@ -48,4 +48,4 @@ class Dump(Handler): """ dump = configuration.dump() for section, values in sorted(dump.items()): - ConfigurationPrinter(section, values).print(verbose=False, separator=" = ") + ConfigurationPrinter(section, values).print(verbose=not args.secure, separator=" = ") diff --git a/src/ahriman/core/database/migrations/m000_initial.py b/src/ahriman/core/database/migrations/m000_initial.py index f2c655a1..c609ebf7 100644 --- a/src/ahriman/core/database/migrations/m000_initial.py +++ b/src/ahriman/core/database/migrations/m000_initial.py @@ -114,19 +114,19 @@ def migrate_package_statuses(connection: Connection, paths: RepositoryPaths) -> values (:package_base, :version, :aur_url) """, - dict(package_base=metadata.base, version=metadata.version, aur_url="")) + {"package_base": metadata.base, "version": metadata.version, "aur_url": ""}) connection.execute( """ insert into package_statuses (package_base, status, last_updated) values (:package_base, :status, :last_updated)""", - dict(package_base=metadata.base, status=last_status.status.value, last_updated=last_status.timestamp)) + {"package_base": metadata.base, "status": last_status.status.value, "last_updated": last_status.timestamp}) def insert_packages(metadata: Package) -> None: package_list = [] for name, description in metadata.packages.items(): - package_list.append(dict(package=name, package_base=metadata.base, **description.view())) + package_list.append({"package": name, "package_base": metadata.base, **description.view()}) connection.executemany( """ insert into packages diff --git a/src/ahriman/core/database/migrations/m001_package_source.py b/src/ahriman/core/database/migrations/m001_package_source.py index c0589d41..89caae0b 100644 --- a/src/ahriman/core/database/migrations/m001_package_source.py +++ b/src/ahriman/core/database/migrations/m001_package_source.py @@ -80,11 +80,11 @@ def migrate_package_remotes(connection: Connection, paths: RepositoryPaths) -> N web_url = :web_url, source = :source where package_base = :package_base """, - dict( - package_base=base, - branch=remote.branch, git_url=remote.git_url, path=remote.path, - web_url=remote.web_url, source=remote.source - ) + { + "package_base": base, + "branch": remote.branch, "git_url": remote.git_url, "path": remote.path, + "web_url": remote.web_url, "source": remote.source + } ) packages = PackageOperations._packages_get_select_package_bases(connection) diff --git a/src/ahriman/core/database/operations/logs_operations.py b/src/ahriman/core/database/operations/logs_operations.py index eb9f72bb..2d3f317c 100644 --- a/src/ahriman/core/database/operations/logs_operations.py +++ b/src/ahriman/core/database/operations/logs_operations.py @@ -71,12 +71,12 @@ class LogsOperations(Operations): values (:package_base, :process_id, :created, :record) """, - dict( - package_base=log_record_id.package_base, - process_id=log_record_id.process_id, - created=created, - record=record - ) + { + "package_base": log_record_id.package_base, + "process_id": log_record_id.process_id, + "created": created, + "record": record, + } ) return self.with_connection(run, commit=True) diff --git a/src/ahriman/core/database/operations/package_operations.py b/src/ahriman/core/database/operations/package_operations.py index 789816c9..e54d3192 100644 --- a/src/ahriman/core/database/operations/package_operations.py +++ b/src/ahriman/core/database/operations/package_operations.py @@ -82,15 +82,15 @@ class PackageOperations(Operations): on conflict (package_base) do update set version = :version, branch = :branch, git_url = :git_url, path = :path, web_url = :web_url, source = :source """, - dict( - package_base=package.base, - version=package.version, - branch=package.remote.branch if package.remote is not None else None, - git_url=package.remote.git_url if package.remote is not None else None, - path=package.remote.path if package.remote is not None else None, - web_url=package.remote.web_url if package.remote is not None else None, - source=package.remote.source.value if package.remote is not None else None, - ) + { + "package_base": package.base, + "version": package.version, + "branch": package.remote.branch if package.remote is not None else None, + "git_url": package.remote.git_url if package.remote is not None else None, + "path": package.remote.path if package.remote is not None else None, + "web_url": package.remote.web_url if package.remote is not None else None, + "source": package.remote.source.value if package.remote is not None else None, + } ) @staticmethod @@ -106,7 +106,7 @@ class PackageOperations(Operations): for name, description in package.packages.items(): if description.architecture is None: continue # architecture is required - package_list.append(dict(package=name, package_base=package.base, **description.view())) + package_list.append({"package": name, "package_base": package.base, **description.view()}) connection.executemany( """ insert into packages @@ -145,7 +145,7 @@ class PackageOperations(Operations): on conflict (package_base) do update set status = :status, last_updated = :last_updated """, - dict(package_base=package_base, status=status.status.value, last_updated=status.timestamp)) + {"package_base": package_base, "status": status.status.value, "last_updated": status.timestamp}) @staticmethod def _packages_get_select_package_bases(connection: Connection) -> Dict[str, Package]: diff --git a/src/ahriman/core/formatters/configuration_printer.py b/src/ahriman/core/formatters/configuration_printer.py index b547be32..903125ed 100644 --- a/src/ahriman/core/formatters/configuration_printer.py +++ b/src/ahriman/core/formatters/configuration_printer.py @@ -28,9 +28,18 @@ class ConfigurationPrinter(StringPrinter): print content of the configuration section Attributes: + HIDE_KEYS(List[str]): (class attribute) hide values for mentioned keys. This list must be used in order to hide + passwords from outputs values(Dict[str, str]): configuration values dictionary """ + HIDE_KEYS = [ + "api_key", # telegram key + "client_secret", # oauth secret + "password", # generic password (github, email, web server, etc) + "secret_key", # aws secret key + ] + def __init__(self, section: str, values: Dict[str, str]) -> None: """ default constructor @@ -50,6 +59,6 @@ class ConfigurationPrinter(StringPrinter): List[Property]: list of content properties """ return [ - Property(key, value, is_required=True) + Property(key, value, is_required=key not in self.HIDE_KEYS) for key, value in sorted(self.values.items()) ] diff --git a/tests/ahriman/application/handlers/test_handler_dump.py b/tests/ahriman/application/handlers/test_handler_dump.py index dcec01e6..5a0b65cd 100644 --- a/tests/ahriman/application/handlers/test_handler_dump.py +++ b/tests/ahriman/application/handlers/test_handler_dump.py @@ -6,10 +6,25 @@ from ahriman.application.handlers import Dump from ahriman.core.configuration import Configuration +def _default_args(args: argparse.Namespace) -> argparse.Namespace: + """ + default arguments for these test cases + + Args: + args(argparse.Namespace): command line arguments fixture + + Returns: + argparse.Namespace: generated arguments for these test cases + """ + args.secure = True + return args + + def test_run(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None: """ must run command """ + args = _default_args(args) print_mock = mocker.patch("ahriman.core.formatters.Printer.print") application_mock = mocker.patch("ahriman.core.configuration.Configuration.dump", return_value=configuration.dump()) diff --git a/tests/ahriman/core/formatters/test_configuration_printer.py b/tests/ahriman/core/formatters/test_configuration_printer.py index f420de2a..70e2dd38 100644 --- a/tests/ahriman/core/formatters/test_configuration_printer.py +++ b/tests/ahriman/core/formatters/test_configuration_printer.py @@ -10,10 +10,13 @@ def test_properties(configuration_printer: ConfigurationPrinter) -> None: def test_properties_required(configuration_printer: ConfigurationPrinter) -> None: """ - must return all properties as required + must return all safe properties as required """ assert all(prop.is_required for prop in configuration_printer.properties()) + configuration_printer.values = {"password": "pa55w0rd"} + assert all(not prop.is_required for prop in configuration_printer.properties()) + def test_title(configuration_printer: ConfigurationPrinter) -> None: """