From 10435114ec0206e3558734634d906cffbd49e783 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Tue, 16 Feb 2021 15:36:01 +0100 Subject: [PATCH] updated remarks and docs --- core/admin/mailu/manage.py | 25 ++++++++++++--------- core/admin/mailu/models.py | 12 +++++----- core/admin/mailu/schemas.py | 3 ++- docs/cli.rst | 45 ++++++++++++++++++++++++------------- 4 files changed, 51 insertions(+), 34 deletions(-) diff --git a/core/admin/mailu/manage.py b/core/admin/mailu/manage.py index 05eae010..756400ad 100644 --- a/core/admin/mailu/manage.py +++ b/core/admin/mailu/manage.py @@ -337,16 +337,19 @@ def config_import(verbose=0, secrets=False, quiet=False, color=False, update=Fal """ # verbose - # 0 : show number of changes - # 1 : also show changes - # 2 : also show secrets - # 3 : also show input data - # 4 : also show sql queries - # 5 : also show tracebacks + # 0 : only show number of changes + # 1 : also show detailed changes + # 2 : also show input data + # 3 : also show sql queries (also needs -s, as sql may contain secrets) + # 4 : also show tracebacks (also needs -s, as tracebacks may contain secrets) if quiet: verbose = -1 + if verbose > 2 and not secrets: + print('[Warning] Verbosity level capped to 2. Specify --secrets to log sql and tracebacks.') + verbose = 2 + color_cfg = { 'color': color or sys.stdout.isatty(), 'lexer': 'python', @@ -376,7 +379,7 @@ def config_import(verbose=0, secrets=False, quiet=False, color=False, update=Fal fmt = f' - {{:<{max([len(loc) for loc, msg in res])}}} : {{}}' res = [fmt.format(loc, msg) for loc, msg in res] num = f'error{["s",""][len(res)==1]}' - res.insert(0, f'[ValidationError] {len(res)} {num} occured during input validation') + res.insert(0, f'[ValidationError] {len(res)} {num} occurred during input validation') return '\n'.join(res) @@ -484,7 +487,7 @@ def config_import(verbose=0, secrets=False, quiet=False, color=False, update=Fal if backref is not None: log('Modified', item, '{target!r} {key}: {before!r} -> {after!r}'.format(**backref)) return - # verbose? + # show input data? if not verbose >= 2: return # hide secrets in data @@ -532,7 +535,7 @@ def config_import(verbose=0, secrets=False, quiet=False, color=False, update=Fal except ValidationError as exc: raise click.ClickException(format_errors(exc.messages)) from exc except Exception as exc: - if verbose >= 5: + if verbose >= 3: raise # (yaml.scanner.ScannerError, UnicodeDecodeError, ...) raise click.ClickException( @@ -584,7 +587,7 @@ def config_export(full=False, secrets=False, color=False, dns=False, output=None if only: for spec in only: if spec.split('.', 1)[0] not in MailuSchema.Meta.order: - raise click.ClickException(f'[ERROR] Unknown section: {spec}') + raise click.ClickException(f'[ValidationError] Unknown section: {spec}') else: only = MailuSchema.Meta.order @@ -606,7 +609,7 @@ def config_export(full=False, secrets=False, color=False, dns=False, output=None print(colorize(schema.dumps(models.MailuConfig()), **color_cfg), file=output) except ValueError as exc: if spec := get_fieldspec(exc): - raise click.ClickException(f'[ERROR] Invalid filter: {spec}') from exc + raise click.ClickException(f'[ValidationError] Invalid filter: {spec}') from exc raise diff --git a/core/admin/mailu/models.py b/core/admin/mailu/models.py index 5799e282..4c119984 100644 --- a/core/admin/mailu/models.py +++ b/core/admin/mailu/models.py @@ -33,7 +33,7 @@ class IdnaDomain(db.TypeDecorator): """ Stores a Unicode string in it's IDNA representation (ASCII only) """ - # TODO: String(80) is too small? + # TODO: use db.String(255)? impl = db.String(80) def process_bind_param(self, value, dialect): @@ -50,7 +50,7 @@ class IdnaEmail(db.TypeDecorator): """ Stores a Unicode string in it's IDNA representation (ASCII only) """ - # TODO: String(255) is too small? + # TODO: use db.String(254)? impl = db.String(255) def process_bind_param(self, value, dialect): @@ -314,7 +314,7 @@ class Relay(Base): __tablename__ = 'relay' name = db.Column(IdnaDomain, primary_key=True, nullable=False) - # TODO: String(80) is too small? + # TODO: use db.String(266)? transport(8):(1)[nexthop(255)](2) smtp = db.Column(db.String(80), nullable=True) @@ -322,9 +322,7 @@ class Email(object): """ Abstraction for an email address (localpart and domain). """ - # TODO: validate max. total length of address (<=254) - - # TODO: String(80) is too large (64)? + # TODO: use db.String(64)? localpart = db.Column(db.String(80), nullable=False) @declarative.declared_attr @@ -634,7 +632,7 @@ class Token(Base): user = db.relationship(User, backref=db.backref('tokens', cascade='all, delete-orphan')) password = db.Column(db.String(255), nullable=False) - # TODO: String(255) is too large? (43 should be sufficient) + # TODO: use db.String(32)? ip = db.Column(db.String(255)) def check_password(self, password): diff --git a/core/admin/mailu/schemas.py b/core/admin/mailu/schemas.py index b9b8e393..7d0393f0 100644 --- a/core/admin/mailu/schemas.py +++ b/core/admin/mailu/schemas.py @@ -36,7 +36,7 @@ from . import models, dkim ma = Marshmallow() -# TODO: how and where to mark keys as "required" while unserializing in api? +# TODO: how and where to mark keys as "required" while deserializing in api? # - when modifying, nothing is required (only the primary key, but this key is in the uri) # - the primary key from post data must not differ from the key in the uri # - when creating all fields without default or auto-increment are required @@ -705,6 +705,7 @@ class BaseSchema(ma.SQLAlchemyAutoSchema): # add attributes required for validation from db # TODO: this will cause validation errors if value from database does not validate + # but there should not be an invalid value in the database for attr_name, field_obj in self.load_fields.items(): if field_obj.required and attr_name not in data: data[attr_name] = getattr(instance, attr_name) diff --git a/docs/cli.rst b/docs/cli.rst index 497cdfc5..6d48c576 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -97,7 +97,7 @@ where mail-config.yml looks like: without ``--delete-object`` option config-update will only add/update new values but will *not* remove any entries missing in provided YAML input. Users ------ +^^^^^ following are additional parameters that could be defined for users: @@ -116,7 +116,7 @@ following are additional parameters that could be defined for users: * spam_threshold Alias ------ +^^^^^ additional fields: @@ -125,11 +125,11 @@ additional fields: config-export ------------- -The purpose of this command is to export domain-, relay-, alias- and user-configuration in YAML or JSON format. +The purpose of this command is to export the complete configuration in YAML or JSON format. .. code-block:: bash - # docker-compose exec admin flask mailu config-export --help + $ docker-compose exec admin flask mailu config-export --help Usage: flask mailu config-export [OPTIONS] [FILTER]... @@ -152,18 +152,18 @@ filters to export only some objects or attributes (try: ``user`` or ``domain.nam .. code-block:: bash - docker-compose exec admin flask mailu config-export -o mail-config.yml + $ docker-compose exec admin flask mailu config-export -o mail-config.yml - docker-compose exec admin flask mailu config-export --dns domain.dns_mx domain.dns_spf + $ docker-compose exec admin flask mailu config-export --dns domain.dns_mx domain.dns_spf config-import ------------- -The purpose of this command is for importing domain-, relay-, alias- and user-configuration in bulk and synchronizing DB entries with an external YAML/JOSN source. +This command imports configuration data from an external YAML or JSON source. .. code-block:: bash - # docker-compose exec admin flask mailu config-import --help + $ docker-compose exec admin flask mailu config-import --help Usage: flask mailu config-import [OPTIONS] [FILENAME|-] @@ -211,13 +211,28 @@ mail-config.yml contains the configuration and looks like this: config-update shows the number of created/modified/deleted objects after import. To suppress all messages except error messages use ``--quiet``. -By adding the ``--verbose`` switch (one or more times) the import gets more detailed and shows exactyl what attributes changed. -In all messages plain-text secrets (dkim-keys, passwords) are hidden by default. Use ``--secrets`` to show secrets. -If you want to test what would be done when importing use ``--dry-run``. -By default config-update replaces the whole configuration. You can use ``--update`` to change the existing configuration instead. -When updating you can add new and change existing objects. -To delete an object use ``-key: value`` (To delete the domain example.com ``-name: example.com`` for example). -To reset an attribute to default use ``-key: null`` (To reset enable_imap ``-enable_imap: null`` for example). +By adding the ``--verbose`` switch (up to two times) the import gets more detailed and shows exactly what attributes changed. +In all log messages plain-text secrets (dkim-keys, passwords) are hidden by default. Use ``--secrets`` to log secrets. +If you want to test what would be done when importing without committing any changes, use ``--dry-run``. + +By default config-update replaces the whole configuration. ``--update`` allows to modify the existing configuration instead. +New elements will be added and existing elements will be modified. +It is possible to delete a single element or prune all elements from lists and associative arrays using a special notation: + ++-----------------------------+------------------+--------------------------+ +| Delete what? | notation | example | ++=============================+==================+==========================+ +| specific array object | ``- -key: id`` | ``- -name: example.com`` | ++-----------------------------+------------------+--------------------------+ +| specific list item | ``- -id`` | ``- -user1@example.com`` | ++-----------------------------+------------------+--------------------------+ +| all remaining array objects | ``- -key: null`` | ``- -email: null`` | ++-----------------------------+------------------+--------------------------+ +| all remaining list items | ``- -prune-`` | ``- -prune-`` | ++-----------------------------+------------------+--------------------------+ + +The ``-key: null`` notation can also be used to reset an attribute to its default. +To reset *spam_threshold* to it's default *80* use ``-spam_threshold: null``. This is a complete YAML template with all additional parameters that can be defined: