diff --git a/core/admin/mailu/internal/views/postfix.py b/core/admin/mailu/internal/views/postfix.py index 8188270c..62b400d3 100644 --- a/core/admin/mailu/internal/views/postfix.py +++ b/core/admin/mailu/internal/views/postfix.py @@ -143,8 +143,9 @@ def postfix_sender_login(sender): if localpart is None: return flask.jsonify(",".join(wildcard_senders)) if wildcard_senders else flask.abort(404) localpart = localpart[:next((i for i, ch in enumerate(localpart) if ch in flask.current_app.config.get('RECIPIENT_DELIMITER')), None)] - destinations = models.Email.resolve_destination(localpart, domain_name, True) or [] - destinations.extend(wildcard_senders) + destinations = set(models.Email.resolve_destination(localpart, domain_name, True) or []) + destinations.update(wildcard_senders) + destinations.update(i[0] for i in models.User.query.filter_by(allow_spoofing=True).with_entities(models.User.email).all()) if destinations: return flask.jsonify(",".join(idna_encode(destinations))) return flask.abort(404) diff --git a/core/admin/mailu/models.py b/core/admin/mailu/models.py index 20eb6d85..b33a0776 100644 --- a/core/admin/mailu/models.py +++ b/core/admin/mailu/models.py @@ -505,6 +505,7 @@ class User(Base, Email): # Features enable_imap = db.Column(db.Boolean, nullable=False, default=True) enable_pop = db.Column(db.Boolean, nullable=False, default=True) + allow_spoofing = db.Column(db.Boolean, nullable=False, default=False) # Filters forward_enabled = db.Column(db.Boolean, nullable=False, default=False) diff --git a/core/admin/mailu/ui/forms.py b/core/admin/mailu/ui/forms.py index ec19bb0b..959f46b2 100644 --- a/core/admin/mailu/ui/forms.py +++ b/core/admin/mailu/ui/forms.py @@ -94,6 +94,7 @@ class UserForm(flask_wtf.FlaskForm): quota_bytes = fields_.IntegerSliderField(_('Quota'), default=10**9) enable_imap = fields.BooleanField(_('Allow IMAP access'), default=True) enable_pop = fields.BooleanField(_('Allow POP3 access'), default=True) + allow_spoofing = fields.BooleanField(_('Allow the user to spoof the sender (send email as anyone)'), default=False) displayed_name = fields.StringField(_('Displayed name')) comment = fields.StringField(_('Comment')) enabled = fields.BooleanField(_('Enabled'), default=True) diff --git a/core/admin/mailu/ui/templates/macros.html b/core/admin/mailu/ui/templates/macros.html index 90084246..31efd0e4 100644 --- a/core/admin/mailu/ui/templates/macros.html +++ b/core/admin/mailu/ui/templates/macros.html @@ -3,7 +3,7 @@ {%- for fieldname, errors in form.errors.items() %} {%- if bootstrap_is_hidden_field(form[fieldname]) %} {%- for error in errors %} -

{{error}}

+

{{error}}

{%- endfor %} {%- endif %} {%- endfor %} @@ -13,7 +13,7 @@ {%- macro form_field_errors(field) %} {%- if field.errors %} {%- for error in field.errors %} -

{{ error }}

+

{{ error }}

{%- endfor %} {%- endif %} {%- endmacro %} @@ -23,7 +23,7 @@
{%- for field in fields %} -
+
{%- if field.__class__.__name__ == 'list' %} {%- for subfield in field %} {{ form_individual_field(subfield, prepend=prepend, append=append, label=label, **kwargs) }} @@ -38,12 +38,13 @@ {%- endmacro %} {%- macro form_individual_field(field, prepend='', append='', label=True, class_="") %} + {%- set fieldclass=" ".join(["form-control"] + ([class_] if class_ else []) + (["is-invalid"] if field.errors else [])) %} {%- if field.type == "BooleanField" %} {{ field(**kwargs) }}  {{ field.label if label else '' }} {%- else %} {{ field.label if label else '' }}{{ form_field_errors(field) }} {%- if prepend %}
{%- elif append %}
{%- endif %} - {{ prepend|safe }}{{ field(class_=("form-control " + class_) if class_ else "form-control", **kwargs) }}{{ append|safe }} + {{ prepend|safe }}{{ field(class_=fieldclass, **kwargs) }}{{ append|safe }} {%- if prepend or append %}
{%- endif %} {%- endif %} {%- endmacro %} diff --git a/core/admin/mailu/ui/templates/user/create.html b/core/admin/mailu/ui/templates/user/create.html index 5369621c..495e788b 100644 --- a/core/admin/mailu/ui/templates/user/create.html +++ b/core/admin/mailu/ui/templates/user/create.html @@ -25,6 +25,7 @@ prepend=' GB') }} {{ macros.form_field(form.enable_imap) }} {{ macros.form_field(form.enable_pop) }} + {{ macros.form_field(form.allow_spoofing) }} {%- endcall %} {{ macros.form_field(form.submit) }} diff --git a/core/admin/mailu/ui/templates/user/list.html b/core/admin/mailu/ui/templates/user/list.html index 14626212..1c845062 100644 --- a/core/admin/mailu/ui/templates/user/list.html +++ b/core/admin/mailu/ui/templates/user/list.html @@ -39,9 +39,10 @@   {{ user }} - - {% if user.enable_imap %}imap{% endif %} - {% if user.enable_pop %}pop3{% endif %} + + {% if user.enable_imap %}imap{% endif %} + {% if user.enable_pop %}pop3{% endif %} + {% if user.allow_spoofing %}allow-spoofing{% endif %} {{ user.quota_bytes_used | filesizeformat }} / {{ (user.quota_bytes | filesizeformat) if user.quota_bytes else '∞' }} {{ user.comment or '-' }} diff --git a/core/admin/migrations/versions/7ac252f2bbbf_.py b/core/admin/migrations/versions/7ac252f2bbbf_.py new file mode 100644 index 00000000..039ab8b6 --- /dev/null +++ b/core/admin/migrations/versions/7ac252f2bbbf_.py @@ -0,0 +1,22 @@ +""" Add user.allow_spoofing + +Revision ID: 7ac252f2bbbf +Revises: 8f9ea78776f4 +Create Date: 2022-11-20 08:57:16.879152 + +""" + +# revision identifiers, used by Alembic. +revision = '7ac252f2bbbf' +down_revision = 'f4f0f89e0047' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + op.add_column('user', sa.Column('allow_spoofing', sa.Boolean(), nullable=False, server_default=sa.sql.expression.false())) + + +def downgrade(): + op.drop_column('user', 'allow_spoofing') diff --git a/docs/webadministration.rst b/docs/webadministration.rst index fde4a271..7e2a5728 100644 --- a/docs/webadministration.rst +++ b/docs/webadministration.rst @@ -325,7 +325,7 @@ This page also shows an overview of the following settings of an user: * Email. The email address of the user. -* Features. Shows if IMAP or POP3 access is enabled. +* Features. Shows if IMAP or POP3 access is enabled and whether the user should be allowed to spoof emails. * Storage quota. Shows how much assigned storage has been consumed. @@ -361,6 +361,8 @@ For adding a new user the following options can be configured. * Allow POP3 access. When ticked, allows email retrieval via the POP3 protocol. +* Allow the user to spoof the sender. When ticked, allows the user to send email as anyone. + Aliases ``````` diff --git a/towncrier/newsfragments/2372.feature b/towncrier/newsfragments/2372.feature new file mode 100644 index 00000000..ec2c5bef --- /dev/null +++ b/towncrier/newsfragments/2372.feature @@ -0,0 +1 @@ +Create a GUI for WILDCARD_SENDERS