From a362f4ad3b01c1a1fe70d424083c6aa8e586e849 Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Wed, 26 Jan 2022 15:50:41 +0100 Subject: [PATCH] Revert "Redesign of email settings (#2426)" This reverts commit e3c7cd7c6d239a1cf775ec30825f61b670cbb829. --- doc/admin/config.rst | 22 +- src/pretix/api/serializers/event.py | 1 + src/pretix/base/models/event.py | 4 +- src/pretix/base/models/organizer.py | 4 +- src/pretix/base/services/mail.py | 3 +- src/pretix/base/services/notifications.py | 2 +- src/pretix/base/settings.py | 2 +- src/pretix/control/forms/__init__.py | 45 ++- src/pretix/control/forms/event.py | 5 +- src/pretix/control/forms/mailsetup.py | 129 -------- src/pretix/control/forms/organizer.py | 7 +- .../pretixcontrol/email/email_setup.txt | 14 - .../templates/pretixcontrol/email_setup.html | 127 -------- .../pretixcontrol/email_setup_simple.html | 79 ----- .../pretixcontrol/email_setup_smtp.html | 42 --- .../templates/pretixcontrol/event/mail.html | 58 ++-- .../pretixcontrol/organizers/mail.html | 49 +-- src/pretix/control/urls.py | 3 - src/pretix/control/views/event.py | 39 +-- src/pretix/control/views/mailsetup.py | 278 ------------------ src/pretix/control/views/organizer.py | 33 ++- src/pretix/settings.py | 8 +- .../static/pretixcontrol/scss/_forms.scss | 4 - src/setup.py | 1 - src/tests/control/test_events.py | 214 +------------- src/tests/control/test_organizer.py | 204 +------------ src/tests/control/test_permissions.py | 6 - 27 files changed, 160 insertions(+), 1223 deletions(-) delete mode 100644 src/pretix/control/forms/mailsetup.py delete mode 100644 src/pretix/control/templates/pretixcontrol/email/email_setup.txt delete mode 100644 src/pretix/control/templates/pretixcontrol/email_setup.html delete mode 100644 src/pretix/control/templates/pretixcontrol/email_setup_simple.html delete mode 100644 src/pretix/control/templates/pretixcontrol/email_setup_smtp.html delete mode 100644 src/pretix/control/views/mailsetup.py diff --git a/doc/admin/config.rst b/doc/admin/config.rst index 04f2772bef..d508fb952e 100644 --- a/doc/admin/config.rst +++ b/doc/admin/config.rst @@ -220,30 +220,12 @@ Example:: ``user``, ``password`` The SMTP user data to use for the connection. Empty by default. -``tls``, ``ssl`` - Use STARTTLS or SSL for the SMTP connection. Off by default. - ``from`` The email address to set as ``From`` header in outgoing emails by the system. Default: ``pretix@localhost`` -``from_notifications`` - The email address to set as ``From`` header in admin notification emails by the system. - Defaults to the value of ``from``. - -``from_organizers`` - The email address to set as ``From`` header in outgoing emails by the system sent on behalf of organizers. - Defaults to the value of ``from``. - -``custom_sender_verification_required`` - If this is on (the default), organizers need to verify email addresses they want to use as senders in their event. - -``custom_sender_spf_string`` - If this is set to a valid SPF string, pretix will show a warning if organizers use a sender address from a domain - that does not include this value. - -``custom_smtp_allow_private_networks`` - If this is off (the default), custom SMTP servers cannot be private network addresses. +``tls``, ``ssl`` + Use STARTTLS or SSL for the SMTP connection. Off by default. ``admins`` Comma-separated list of email addresses that should receive a report about every error code 500 thrown by pretix. diff --git a/src/pretix/api/serializers/event.py b/src/pretix/api/serializers/event.py index bfc0668fa6..855681c63e 100644 --- a/src/pretix/api/serializers/event.py +++ b/src/pretix/api/serializers/event.py @@ -713,6 +713,7 @@ class EventSettingsSerializer(SettingsSerializer): 'ticket_download_require_validated_email', 'ticket_secret_length', 'mail_prefix', + 'mail_from', 'mail_from_name', 'mail_attach_ical', 'mail_attach_tickets', diff --git a/src/pretix/base/models/event.py b/src/pretix/base/models/event.py index c721dccdae..13d43a137c 100644 --- a/src/pretix/base/models/event.py +++ b/src/pretix/base/models/event.py @@ -665,13 +665,13 @@ class Event(EventMixin, LoggedModel): return locking.LockManager(self) - def get_mail_backend(self, timeout=None): + def get_mail_backend(self, timeout=None, force_custom=False): """ Returns an email server connection, either by using the system-wide connection or by returning a custom one based on the event's settings. """ - if self.settings.smtp_use_custom: + if self.settings.smtp_use_custom or force_custom: return get_connection(backend=settings.EMAIL_CUSTOM_SMTP_BACKEND, host=self.settings.smtp_host, port=self.settings.smtp_port, diff --git a/src/pretix/base/models/organizer.py b/src/pretix/base/models/organizer.py index 96633995c5..7a842c7daa 100644 --- a/src/pretix/base/models/organizer.py +++ b/src/pretix/base/models/organizer.py @@ -191,12 +191,12 @@ class Organizer(LoggedModel): e.delete() self.teams.all().delete() - def get_mail_backend(self, timeout=None): + def get_mail_backend(self, timeout=None, force_custom=False): """ Returns an email server connection, either by using the system-wide connection or by returning a custom one based on the organizer's settings. """ - if self.settings.smtp_use_custom: + if self.settings.smtp_use_custom or force_custom: return get_connection(backend=settings.EMAIL_CUSTOM_SMTP_BACKEND, host=self.settings.smtp_host, port=self.settings.smtp_port, diff --git a/src/pretix/base/services/mail.py b/src/pretix/base/services/mail.py index ae87fdf7c7..6bc8a77291 100644 --- a/src/pretix/base/services/mail.py +++ b/src/pretix/base/services/mail.py @@ -217,8 +217,7 @@ def mail(email: Union[str, Sequence[str]], subject: str, template: Union[str, La for bcc_mail in settings_holder.settings.mail_bcc.split(','): bcc.append(bcc_mail.strip()) - if settings_holder.settings.mail_from not in (settings.DEFAULT_FROM_EMAIL, settings.MAIL_FROM_ORGANIZERS) \ - and settings_holder.settings.contact_mail and not headers.get('Reply-To'): + if settings_holder.settings.mail_from == settings.DEFAULT_FROM_EMAIL and settings_holder.settings.contact_mail and not headers.get('Reply-To'): headers['Reply-To'] = settings_holder.settings.contact_mail prefix = settings_holder.settings.get('mail_prefix') diff --git a/src/pretix/base/services/notifications.py b/src/pretix/base/services/notifications.py index b88a518a57..15dfd66644 100644 --- a/src/pretix/base/services/notifications.py +++ b/src/pretix/base/services/notifications.py @@ -148,7 +148,7 @@ def send_notification_mail(notification: Notification, user: User): ), 'body': body_plain, 'html': body_html, - 'sender': settings.MAIL_FROM_NOTIFICATIONS, + 'sender': settings.MAIL_FROM, 'headers': {}, 'user': user.pk }) diff --git a/src/pretix/base/settings.py b/src/pretix/base/settings.py index 914d76b174..1281004e97 100644 --- a/src/pretix/base/settings.py +++ b/src/pretix/base/settings.py @@ -1589,7 +1589,7 @@ DEFAULTS = { 'type': str }, 'mail_from': { - 'default': settings.MAIL_FROM_ORGANIZERS, + 'default': settings.MAIL_FROM, 'type': str, 'form_class': forms.EmailField, 'serializer_class': serializers.EmailField, diff --git a/src/pretix/control/forms/__init__.py b/src/pretix/control/forms/__init__.py index 1fb86d232e..d3345b606e 100644 --- a/src/pretix/control/forms/__init__.py +++ b/src/pretix/control/forms/__init__.py @@ -48,7 +48,7 @@ from django.utils.timezone import now from django.utils.translation import gettext_lazy as _ from django_scopes.forms import SafeModelMultipleChoiceField -from ...base.forms import I18nModelForm +from ...base.forms import I18nModelForm, SecretKeySettingsField # Import for backwards compatibility with okd import paths from ...base.forms.widgets import ( # noqa @@ -373,6 +373,49 @@ class FontSelect(forms.RadioSelect): option_template_name = 'pretixcontrol/font_option.html' +class SMTPSettingsMixin(forms.Form): + smtp_use_custom = forms.BooleanField( + label=_("Use custom SMTP server"), + help_text=_("All mail related to your event will be sent over the smtp server specified by you."), + required=False + ) + smtp_host = forms.CharField( + label=_("Hostname"), + required=False, + widget=forms.TextInput(attrs={'placeholder': 'mail.example.org'}) + ) + smtp_port = forms.IntegerField( + label=_("Port"), + required=False, + widget=forms.TextInput(attrs={'placeholder': 'e.g. 587, 465, 25, ...'}) + ) + smtp_username = forms.CharField( + label=_("Username"), + widget=forms.TextInput(attrs={'placeholder': 'myuser@example.org'}), + required=False + ) + smtp_password = SecretKeySettingsField( + label=_("Password"), + required=False, + ) + smtp_use_tls = forms.BooleanField( + label=_("Use STARTTLS"), + help_text=_("Commonly enabled on port 587."), + required=False + ) + smtp_use_ssl = forms.BooleanField( + label=_("Use SSL"), + help_text=_("Commonly enabled on port 465."), + required=False + ) + + def clean(self): + data = super().clean() + if data.get('smtp_use_tls') and data.get('smtp_use_ssl'): + raise ValidationError(_('You can activate either SSL or STARTTLS security, but not both at the same time.')) + return data + + class ItemMultipleChoiceField(SafeModelMultipleChoiceField): def label_from_instance(self, obj): return str(obj) if obj.active else mark_safe(f'{escape(obj)}') diff --git a/src/pretix/control/forms/event.py b/src/pretix/control/forms/event.py index 35745896ac..ea0580c3a9 100644 --- a/src/pretix/control/forms/event.py +++ b/src/pretix/control/forms/event.py @@ -64,7 +64,7 @@ from pretix.base.settings import ( PERSON_NAME_SCHEMES, PERSON_NAME_TITLE_GROUPS, validate_event_settings, ) from pretix.control.forms import ( - MultipleLanguagesWidget, SlugWidget, SplitDateTimeField, + MultipleLanguagesWidget, SlugWidget, SMTPSettingsMixin, SplitDateTimeField, SplitDateTimePickerWidget, ) from pretix.control.forms.widgets import Select2 @@ -865,9 +865,10 @@ def contains_web_channel_validate(val): raise ValidationError(_("The online shop must be selected to receive these emails.")) -class MailSettingsForm(SettingsForm): +class MailSettingsForm(SMTPSettingsMixin, SettingsForm): auto_fields = [ 'mail_prefix', + 'mail_from', 'mail_from_name', 'mail_attach_ical', 'mail_attach_tickets', diff --git a/src/pretix/control/forms/mailsetup.py b/src/pretix/control/forms/mailsetup.py deleted file mode 100644 index d3c0e91c17..0000000000 --- a/src/pretix/control/forms/mailsetup.py +++ /dev/null @@ -1,129 +0,0 @@ -# -# This file is part of pretix (Community Edition). -# -# Copyright (C) 2014-2020 Raphael Michel and contributors -# Copyright (C) 2020-2021 rami.io GmbH and contributors -# -# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General -# Public License as published by the Free Software Foundation in version 3 of the License. -# -# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are -# applicable granting you additional permissions and placing additional restrictions on your usage of this software. -# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive -# this file, see . -# -# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied -# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more -# details. -# -# You should have received a copy of the GNU Affero General Public License along with this program. If not, see -# . -# -import ipaddress -import socket - -from django import forms -from django.conf import settings -from django.core.exceptions import ValidationError -from django.utils.translation import gettext_lazy as _ - -from pretix.base.forms import SecretKeySettingsField, SettingsForm - - -class SMTPMailForm(SettingsForm): - mail_from = forms.EmailField( - label=_("Sender address"), - help_text=_("Sender address for outgoing emails"), - required=True, - ) - smtp_host = forms.CharField( - label=_("Hostname"), - required=True, - widget=forms.TextInput(attrs={'placeholder': 'mail.example.org'}) - ) - smtp_port = forms.IntegerField( - label=_("Port"), - required=True, - widget=forms.TextInput(attrs={'placeholder': 'e.g. 587, 465, 25, ...'}) - ) - smtp_username = forms.CharField( - label=_("Username"), - widget=forms.TextInput(attrs={'placeholder': 'myuser@example.org'}), - required=False - ) - smtp_password = SecretKeySettingsField( - label=_("Password"), - required=False, - ) - smtp_use_tls = forms.BooleanField( - label=_("Use STARTTLS"), - help_text=_("Commonly enabled on port 587."), - required=False - ) - smtp_use_ssl = forms.BooleanField( - label=_("Use SSL"), - help_text=_("Commonly enabled on port 465."), - required=False - ) - - def clean(self): - data = super().clean() - if data.get('smtp_use_tls') and data.get('smtp_use_ssl'): - raise ValidationError(_('You can activate either SSL or STARTTLS security, but not both at the same time.')) - for k, v in self.fields.items(): - val = data.get(k) - if v._required and not val: - self.add_error(k, _('This field is required.')) - return data - - def clean_smtp_host(self): - v = self.cleaned_data['smtp_host'] - if not settings.MAIL_CUSTOM_SMTP_ALLOW_PRIVATE_NETWORKS: - try: - if ipaddress.ip_address(v).is_private: - raise ValidationError(_('You are not allowed to use this mail server, please choose one with a ' - 'public IP address instead.')) - except ValueError: - try: - if ipaddress.ip_address(socket.gethostbyname(v)).is_private: - raise ValidationError(_('You are not allowed to use this mail server, please choose one with a ' - 'public IP address instead.')) - except OSError: - raise ValidationError(_('We were unable to resolve this hostname.')) - return v - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - if self.obj.settings.mail_from in (settings.MAIL_FROM, settings.MAIL_FROM_ORGANIZERS): - self.initial.pop('mail_from') - - for k, v in self.fields.items(): - v._required = v.required - v.required = False - v.widget.is_required = False - - -class SimpleMailForm(SettingsForm): - mail_from = forms.EmailField( - label=_("Sender address"), - help_text=_("Sender address for outgoing emails"), - required=True, - ) - - def clean(self): - cleaned_data = super().clean() - for k, v in self.fields.items(): - val = cleaned_data.get(k) - if v._required and not val: - self.add_error(k, _('This field is required.')) - return cleaned_data - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - if self.obj.settings.mail_from in (settings.MAIL_FROM, settings.MAIL_FROM_ORGANIZERS): - self.initial.pop('mail_from') - - for k, v in self.fields.items(): - v._required = v.required - v.required = False - v.widget.is_required = False diff --git a/src/pretix/control/forms/organizer.py b/src/pretix/control/forms/organizer.py index 7631c64bc1..18cc8c4761 100644 --- a/src/pretix/control/forms/organizer.py +++ b/src/pretix/control/forms/organizer.py @@ -60,7 +60,9 @@ from pretix.base.models import ( MembershipType, Organizer, Team, ) from pretix.base.settings import PERSON_NAME_SCHEMES, PERSON_NAME_TITLE_GROUPS -from pretix.control.forms import ExtFileField, SplitDateTimeField +from pretix.control.forms import ( + ExtFileField, SMTPSettingsMixin, SplitDateTimeField, +) from pretix.control.forms.event import ( SafeEventMultipleChoiceField, multimail_validate, ) @@ -356,8 +358,9 @@ class OrganizerSettingsForm(SettingsForm): ] -class MailSettingsForm(SettingsForm): +class MailSettingsForm(SMTPSettingsMixin, SettingsForm): auto_fields = [ + 'mail_from', 'mail_from_name', ] diff --git a/src/pretix/control/templates/pretixcontrol/email/email_setup.txt b/src/pretix/control/templates/pretixcontrol/email/email_setup.txt deleted file mode 100644 index d3780c3bba..0000000000 --- a/src/pretix/control/templates/pretixcontrol/email/email_setup.txt +++ /dev/null @@ -1,14 +0,0 @@ -{% load i18n %}{% blocktrans with code=code instance=instance %}Hello, - -someone requested to use {{ address }} as a sender address on {{ instance }}. -This will allow them to send emails that are shown to originate from this email address. -If that was you, please enter the following confirmation code: - -{{ code }} - -If this was not requested by you, you can safely ignore this email. - -Best regards, - -Your {{ instance }} team -{% endblocktrans %} \ No newline at end of file diff --git a/src/pretix/control/templates/pretixcontrol/email_setup.html b/src/pretix/control/templates/pretixcontrol/email_setup.html deleted file mode 100644 index 4971f1be1b..0000000000 --- a/src/pretix/control/templates/pretixcontrol/email_setup.html +++ /dev/null @@ -1,127 +0,0 @@ -{% extends basetpl %} -{% load i18n %} -{% load bootstrap3 %} -{% load hierarkey_form %} -{% load static %} -{% block title %}{% trans "Organizer" %}{% endblock %} -{% block content %} -

{% trans "E-mail sending" %}

-
- {% csrf_token %} -
-
-
-
-

- - -

-
-
- -
-
-
-
-

- - -

-
-
- -
-
-
-
-

- - -

-
-
- -
- {% if request.event %} -
-
-
-

- - -

-
-
- -
- {% endif %} -
- -
- -
-
-{% endblock %} diff --git a/src/pretix/control/templates/pretixcontrol/email_setup_simple.html b/src/pretix/control/templates/pretixcontrol/email_setup_simple.html deleted file mode 100644 index 05ec59d8ed..0000000000 --- a/src/pretix/control/templates/pretixcontrol/email_setup_simple.html +++ /dev/null @@ -1,79 +0,0 @@ -{% extends basetpl %} -{% load i18n %} -{% load bootstrap3 %} -{% load hierarkey_form %} -{% load static %} -{% block title %}{% trans "Organizer" %}{% endblock %} -{% block content %} -

{% trans "E-mail sending" %}

-
- {% csrf_token %} - {% for k, v in request.POST.items %} - - {% endfor %} - -
-
-

- {% trans "Use system email server with a custom sender address" %} -

-
-
- {% if spf_warning %} -
-

- {{ spf_warning }} -

- {% if spf_record %} -

- {% trans "This is the SPF record we found on your domain:" %} -

-
{{ spf_record }}
-

- {% trans "To fix this, include the following part before the last word:" %} -

-
{{ spf_key }}
- {% else %} -

- {% trans "Your new SPF record could look like this:" %} -

-
v=spf1 a mx {{ spf_key }} ~all
- {% endif %} -

- {% trans "Please keep in mind that updates to DNS might require multiple hours to take effect." %} -

-
- {% elif spf_key %} -
- {% blocktrans trimmed %} - We found an SPF record on your domain that includes this system. Great! - {% endblocktrans %} -
- {% endif %} - {% if verification %} -

{% trans "Verification" %}

-

- {% blocktrans trimmed with recp=recp %} - We've sent an email to {{ recp }} with a confirmation code to verify that this email address - is owned by you. Please enter the verification code below: - {% endblocktrans %} -

-
- -
- -
-
- {% endif %} -
-
- -
- -
-
-{% endblock %} diff --git a/src/pretix/control/templates/pretixcontrol/email_setup_smtp.html b/src/pretix/control/templates/pretixcontrol/email_setup_smtp.html deleted file mode 100644 index 9709493b8b..0000000000 --- a/src/pretix/control/templates/pretixcontrol/email_setup_smtp.html +++ /dev/null @@ -1,42 +0,0 @@ -{% extends basetpl %} -{% load i18n %} -{% load bootstrap3 %} -{% load hierarkey_form %} -{% load static %} -{% block title %}{% trans "Organizer" %}{% endblock %} -{% block content %} -

{% trans "E-mail sending" %}

-
- {% csrf_token %} - {% for k, v in request.POST.items %} - - {% endfor %} - -
-
-

- {% trans "Use a custom SMTP server" %} -

-
-
-
- {% blocktrans trimmed %} - A test connection to your SMTP server was successful. You can now save your new settings - to put them in use. - {% endblocktrans %} -
- {% if known_host_problem %} -
- {{ known_host_problem }} -
- {% endif %} -
-
- -
- -
-
-{% endblock %} diff --git a/src/pretix/control/templates/pretixcontrol/event/mail.html b/src/pretix/control/templates/pretixcontrol/event/mail.html index c7dd99a773..06c41fe554 100644 --- a/src/pretix/control/templates/pretixcontrol/event/mail.html +++ b/src/pretix/control/templates/pretixcontrol/event/mail.html @@ -12,49 +12,16 @@
{% trans "General" %} - {% bootstrap_field form.mail_prefix layout="control" %} - {% bootstrap_field form.mail_attach_tickets layout="control" %} - {% bootstrap_field form.mail_attach_ical layout="control" %} {% url "control:organizer.settings.mail" organizer=request.organizer.slug as org_url %} - {% propagated request.event org_url "mail_from" "smtp_use_custom" "smtp_host" "smtp_port" "smtp_username" "smtp_password" "smtp_use_tls" "smtp_use_ssl" %} -
- -
- {% if request.event.settings.smtp_use_custom %} - {% trans "Custom SMTP server" %}: {{ request.event.settings.smtp_host }} - {% else %} - {% trans "System-provided email server" %} - {% endif %} -    - - - {% trans "Edit" %} - -
-
-
- -
- {{ request.event.settings.mail_from }} -    - - - {% trans "Edit" %} - -
-
- {% endpropagated %} - {% propagated request.event org_url "mail_from_name" "mail_text_signature" "mail_bcc" %} + {% propagated request.event org_url "mail_from" "mail_from_name" "mail_text_signature" "mail_bcc" %} + {% bootstrap_field form.mail_from layout="control" %} {% bootstrap_field form.mail_from_name layout="control" %} {% bootstrap_field form.mail_text_signature layout="control" %} {% bootstrap_field form.mail_bcc layout="control" %} {% endpropagated %} + {% bootstrap_field form.mail_prefix layout="control" %} + {% bootstrap_field form.mail_attach_tickets layout="control" %} + {% bootstrap_field form.mail_attach_ical layout="control" %} {% bootstrap_field form.mail_sales_channel_placed_paid layout="control" %}
@@ -118,11 +85,26 @@

{% trans "Attachments" %}

{% bootstrap_field form.mail_attachment_new_order layout="control" %}
+
+ {% trans "SMTP settings" %} + {% propagated request.event org_url "smtp_use_custom" "smtp_host" "smtp_port" "smtp_username" "smtp_password" "smtp_use_tls" "smtp_use_ssl" %} + {% bootstrap_field form.smtp_use_custom layout="control" %} + {% bootstrap_field form.smtp_host layout="control" %} + {% bootstrap_field form.smtp_port layout="control" %} + {% bootstrap_field form.smtp_username layout="control" %} + {% bootstrap_field form.smtp_password layout="control" %} + {% bootstrap_field form.smtp_use_tls layout="control" %} + {% bootstrap_field form.smtp_use_ssl layout="control" %} + {% endpropagated %} +
+
{% endblock %} diff --git a/src/pretix/control/templates/pretixcontrol/organizers/mail.html b/src/pretix/control/templates/pretixcontrol/organizers/mail.html index 1119d46f10..3b1968a37b 100644 --- a/src/pretix/control/templates/pretixcontrol/organizers/mail.html +++ b/src/pretix/control/templates/pretixcontrol/organizers/mail.html @@ -11,45 +11,13 @@

{% trans "E-mail settings" %}

+ mail-preview-url="{% url "control:organizer.settings.mail.preview" organizer=request.organizer.slug %}"> {% csrf_token %} {% bootstrap_form_errors form %}
{% trans "General" %} -
- -
- {% if request.organizer.settings.smtp_use_custom %} - {% trans "Custom SMTP server" %}: {{ request.organizer.settings.smtp_host }} - {% else %} - {% trans "System-provided email server" %} - {% endif %} -    - - - {% trans "Edit" %} - -
-
-
- -
- {{ request.organizer.settings.mail_from }} -    - - - {% trans "Edit" %} - -
-
- + {% bootstrap_field form.mail_from layout="control" %} {% bootstrap_field form.mail_from_name layout="control" %} {% bootstrap_field form.mail_text_signature layout="control" %} {% bootstrap_field form.mail_bcc layout="control" %} @@ -67,11 +35,24 @@ {% include "pretixcontrol/event/mail_settings_fragment.html" with pid="reset" title=title_reset items="mail_text_customer_reset" %}
+
+ {% trans "SMTP settings" %} + {% bootstrap_field form.smtp_use_custom layout="control" %} + {% bootstrap_field form.smtp_host layout="control" %} + {% bootstrap_field form.smtp_port layout="control" %} + {% bootstrap_field form.smtp_username layout="control" %} + {% bootstrap_field form.smtp_password layout="control" %} + {% bootstrap_field form.smtp_use_tls layout="control" %} + {% bootstrap_field form.smtp_use_ssl layout="control" %} +
+
{% endblock %} diff --git a/src/pretix/control/urls.py b/src/pretix/control/urls.py index ed1ac7698c..8042f121c1 100644 --- a/src/pretix/control/urls.py +++ b/src/pretix/control/urls.py @@ -112,8 +112,6 @@ urlpatterns = [ re_path(r'^organizer/(?P[^/]+)/edit$', organizer.OrganizerUpdate.as_view(), name='organizer.edit'), re_path(r'^organizer/(?P[^/]+)/settings/email$', organizer.OrganizerMailSettings.as_view(), name='organizer.settings.mail'), - re_path(r'^organizer/(?P[^/]+)/settings/email/setup$', - organizer.MailSettingsSetup.as_view(), name='organizer.settings.mail.setup'), re_path(r'^organizer/(?P[^/]+)/settings/email/preview$', organizer.MailSettingsPreview.as_view(), name='organizer.settings.mail.preview'), re_path(r'^organizer/(?P[^/]+)/delete$', organizer.OrganizerDelete.as_view(), name='organizer.delete'), @@ -216,7 +214,6 @@ urlpatterns = [ re_path(r'^settings/tickets/preview/(?P[^/]+)$', event.TicketSettingsPreview.as_view(), name='event.settings.tickets.preview'), re_path(r'^settings/email$', event.MailSettings.as_view(), name='event.settings.mail'), - re_path(r'^settings/email/setup$', event.MailSettingsSetup.as_view(), name='event.settings.mail.setup'), re_path(r'^settings/email/preview$', event.MailSettingsPreview.as_view(), name='event.settings.mail.preview'), re_path(r'^settings/email/layoutpreview$', event.MailSettingsRendererPreview.as_view(), name='event.settings.mail.preview.layout'), diff --git a/src/pretix/control/views/event.py b/src/pretix/control/views/event.py index 51fee18af4..6eecc9d5bb 100644 --- a/src/pretix/control/views/event.py +++ b/src/pretix/control/views/event.py @@ -65,7 +65,9 @@ from i18nfield.utils import I18nJSONEncoder from pytz import timezone from pretix.base.channels import get_all_sales_channels -from pretix.base.email import get_available_placeholders +from pretix.base.email import ( + get_available_placeholders, test_custom_smtp_backend, +) from pretix.base.models import Event, LogEntry, Order, TaxRule, Voucher from pretix.base.models.event import EventMetaValue from pretix.base.services import tickets @@ -81,7 +83,6 @@ from pretix.control.forms.event import ( TicketSettingsForm, WidgetCodeForm, ) from pretix.control.permissions import EventPermissionRequiredMixin -from pretix.control.views.mailsetup import MailSettingsSetupView from pretix.control.views.user import RecentAuthenticationRequiredMixin from pretix.helpers.database import rolledback_transaction from pretix.multidomain.urlreverse import get_event_domain @@ -638,29 +639,29 @@ class MailSettings(EventSettingsViewMixin, EventSettingsFormView): k: form.cleaned_data.get(k) for k in form.changed_data } ) - messages.success(self.request, _('Your changes have been saved.')) + + if request.POST.get('test', '0').strip() == '1': + backend = self.request.event.get_mail_backend(force_custom=True, timeout=10) + try: + test_custom_smtp_backend(backend, self.request.event.settings.mail_from) + except Exception as e: + messages.warning(self.request, _('An error occurred while contacting the SMTP server: %s') % str(e)) + else: + if form.cleaned_data.get('smtp_use_custom'): + messages.success(self.request, _('Your changes have been saved and the connection attempt to ' + 'your SMTP server was successful.')) + else: + messages.success(self.request, _('We\'ve been able to contact the SMTP server you configured. ' + 'Remember to check the "use custom SMTP server" checkbox, ' + 'otherwise your SMTP server will not be used.')) + else: + messages.success(self.request, _('Your changes have been saved.')) return redirect(self.get_success_url()) else: messages.error(self.request, _('We could not save your changes. See below for details.')) return self.get(request) -class MailSettingsSetup(EventPermissionRequiredMixin, MailSettingsSetupView): - permission = 'can_change_event_settings' - basetpl = 'pretixcontrol/event/base.html' - - def get_success_url(self) -> str: - return reverse('control:event.settings.mail', kwargs={ - 'organizer': self.request.event.organizer.slug, - 'event': self.request.event.slug - }) - - def log_action(self, data): - self.request.event.log_action( - 'pretix.event.settings', user=self.request.user, data=data - ) - - class MailSettingsPreview(EventPermissionRequiredMixin, View): permission = 'can_change_event_settings' diff --git a/src/pretix/control/views/mailsetup.py b/src/pretix/control/views/mailsetup.py deleted file mode 100644 index d436294fc2..0000000000 --- a/src/pretix/control/views/mailsetup.py +++ /dev/null @@ -1,278 +0,0 @@ -# -# This file is part of pretix (Community Edition). -# -# Copyright (C) 2014-2020 Raphael Michel and contributors -# Copyright (C) 2020-2021 rami.io GmbH and contributors -# -# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General -# Public License as published by the Free Software Foundation in version 3 of the License. -# -# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are -# applicable granting you additional permissions and placing additional restrictions on your usage of this software. -# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive -# this file, see . -# -# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied -# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more -# details. -# -# You should have received a copy of the GNU Affero General Public License along with this program. If not, see -# . -# -import logging - -import dns.resolver -from django.conf import settings -from django.contrib import messages -from django.core.mail import get_connection -from django.shortcuts import redirect -from django.utils.crypto import get_random_string -from django.utils.functional import cached_property -from django.utils.translation import gettext_lazy as _ -from django.views.generic import TemplateView - -from pretix.base import email -from pretix.base.models import Event -from pretix.base.services.mail import mail -from pretix.control.forms.filter import OrganizerFilterForm -from pretix.control.forms.mailsetup import SimpleMailForm, SMTPMailForm - -logger = logging.getLogger(__name__) - - -def get_spf_record(hostname): - try: - for resp in dns.resolver.resolve(hostname, 'TXT'): - data = b''.join(resp.strings).decode() - if data.lower().strip().startswith('v=spf1 '): # RFC7208, section 4.5 - return data - except: - logger.exception("Could not fetch SPF record") - - -def _check_spf_record(not_found_lookup_parts, spf_record, depth): - if depth > 10: # prevent infinite loops - return - - parts = spf_record.lower().split(" ") # RFC 7208, section 4.6.1 - - for p in parts: - try: - not_found_lookup_parts.remove(p) - except KeyError: - pass - - if not not_found_lookup_parts: # save some DNS requests if we already found everything - return - - for p in parts: - if p.startswith('include:') or p.startswith('+include:'): - _, hostname = p.split(':') - rec_record = get_spf_record(hostname) - if rec_record: - _check_spf_record(not_found_lookup_parts, rec_record, depth + 1) - - -def check_spf_record(lookup, spf_record): - """ - Check that all parts of lookup appear somewhere in the given SPF record, resolving - include: directives recursively - """ - not_found_lookup_parts = set(lookup.split(" ")) - _check_spf_record(not_found_lookup_parts, spf_record, 0) - return not not_found_lookup_parts - - -class MailSettingsSetupView(TemplateView): - template_name = 'pretixcontrol/email_setup.html' - basetpl = None - - @cached_property - def object(self): - return getattr(self.request, 'event', self.request.organizer) - - @cached_property - def smtp_form(self): - return SMTPMailForm( - obj=self.object, - prefix='smtp', - data=self.request.POST if (self.request.method == "POST" and self.request.POST.get("mode") == "smtp") else None, - ) - - @cached_property - def simple_form(self): - return SimpleMailForm( - obj=self.object, - prefix='simple', - data=self.request.POST if (self.request.method == "POST" and self.request.POST.get("mode") == "simple") else None, - ) - - def get_context_data(self, **kwargs): - ctx = super().get_context_data(**kwargs) - ctx['basetpl'] = self.basetpl - ctx['object'] = self.object - ctx['smtp_form'] = self.smtp_form - ctx['simple_form'] = self.simple_form - ctx['default_sender_address'] = settings.MAIL_FROM_ORGANIZERS - if 'mode' in self.request.POST: - ctx['mode'] = self.request.POST.get('mode') - elif self.object.settings.smtp_use_custom: - ctx['mode'] = 'smtp' - elif self.object.settings.mail_from not in (settings.MAIL_FROM_ORGANIZERS, settings.MAIL_FROM): - ctx['mode'] = 'simple' - else: - ctx['mode'] = 'system' - return ctx - - @cached_property - def filter_form(self): - return OrganizerFilterForm(data=self.request.GET, request=self.request) - - def post(self, request, *args, **kwargs): - if request.POST.get('mode') == 'system': - if isinstance(self.object, Event) and 'mail_from' in self.object.organizer.settings._cache(): - self.object.settings.mail_from = settings.MAIL_FROM_ORGANIZERS - else: - del self.object.settings.mail_from - self.object.settings.smtp_use_custom = False - del self.object.settings.smtp_host - del self.object.settings.smtp_port - del self.object.settings.smtp_username - del self.object.settings.smtp_password - del self.object.settings.smtp_use_tls - del self.object.settings.smtp_use_ssl - messages.success(request, _('Your changes have been saved.')) - return redirect(self.get_success_url()) - - elif request.POST.get('mode') == 'reset': - del self.object.settings.mail_from - del self.object.settings.smtp_use_custom - del self.object.settings.smtp_host - del self.object.settings.smtp_port - del self.object.settings.smtp_username - del self.object.settings.smtp_password - del self.object.settings.smtp_use_tls - del self.object.settings.smtp_use_ssl - messages.success(request, _('Your changes have been saved.')) - return redirect(self.get_success_url()) - - elif request.POST.get('mode') == 'simple': - if not self.simple_form.is_valid(): - return super().get(request, *args, **kwargs) - - session_key = f'sender_mail_verification_code_{self.request.path}_{self.simple_form.cleaned_data.get("mail_from")}' - allow_save = ( - (not settings.MAIL_CUSTOM_SENDER_VERIFICATION_REQUIRED or - ('verification' in self.request.POST and self.request.POST.get('verification', '') == self.request.session.get(session_key, None))) and - (not settings.MAIL_CUSTOM_SENDER_SPF_STRING or self.request.POST.get('state') == 'save') - ) - - if allow_save: - for k, v in self.simple_form.cleaned_data.items(): - self.object.settings.set(k, v) - self.log_action(self.simple_form.cleaned_data) - if session_key in request.session: - del request.session[session_key] - messages.success(request, _('Your changes have been saved.')) - return redirect(self.get_success_url()) - - spf_warning = None - spf_record = None - if settings.MAIL_CUSTOM_SENDER_SPF_STRING: - hostname = self.simple_form.cleaned_data['mail_from'].split('@')[-1] - spf_record = get_spf_record(hostname) - if not spf_record: - spf_warning = _( - 'We could not find an SPF record set for the domain you are trying to use. You can still ' - 'proceed, but it will increase the chance of emails going to spam or being rejected. We ' - 'strongly recommend setting an SPF record on the domain. You can do so through the DNS ' - 'settings at the provider you registered your domain with.' - ) - elif not check_spf_record(settings.MAIL_CUSTOM_SENDER_SPF_STRING, spf_record): - spf_warning = _( - 'We found an SPF record set for the domain you are trying to use, but it does not include this ' - 'system\'s email server. This means that there is a very high chance most of the emails will be ' - 'rejected or marked as spam. You should update the DNS settings of your domain to include ' - 'this system in the SPF record.' - ) - - if settings.MAIL_CUSTOM_SENDER_VERIFICATION_REQUIRED: - if 'verification' in self.request.POST: - messages.error(request, _('The verification code was incorrect, please try again.')) - else: - self.request.session[session_key] = get_random_string(length=6, allowed_chars='1234567890') - mail( - self.simple_form.cleaned_data.get('mail_from'), - _('Sender address verification'), - 'pretixcontrol/email/email_setup.txt', - { - 'code': self.request.session[session_key], - 'address': self.simple_form.cleaned_data.get('mail_from'), - 'instance': settings.PRETIX_INSTANCE_NAME, - }, - None, - locale=self.request.LANGUAGE_CODE, - user=self.request.user - ) - - return self.response_class( - request=self.request, - template='pretixcontrol/email_setup_simple.html', - context={ - 'basetpl': self.basetpl, - 'object': self.object, - 'verification': settings.MAIL_CUSTOM_SENDER_VERIFICATION_REQUIRED, - 'spf_warning': spf_warning, - 'spf_record': spf_record, - 'spf_key': settings.MAIL_CUSTOM_SENDER_SPF_STRING, - 'recp': self.simple_form.cleaned_data.get('mail_from') - }, - using=self.template_engine, - ) - - elif request.POST.get('mode') == 'smtp': - if not self.smtp_form.is_valid(): - return super().get(request, *args, **kwargs) - - if request.POST.get('state') == 'save': - for k, v in self.smtp_form.cleaned_data.items(): - self.object.settings.set(k, v) - self.object.settings.smtp_use_custom = True - self.log_action({**self.smtp_form.cleaned_data, 'smtp_use_custom': True}) - messages.success(request, _('Your changes have been saved.')) - return redirect(self.get_success_url()) - else: - backend = get_connection( - backend=settings.EMAIL_CUSTOM_SMTP_BACKEND, - host=self.smtp_form.cleaned_data['smtp_host'], - port=self.smtp_form.cleaned_data['smtp_port'], - username=self.smtp_form.cleaned_data.get('smtp_username', ''), - password=self.smtp_form.cleaned_data.get('smtp_password', ''), - use_tls=self.smtp_form.cleaned_data.get('smtp_use_tls', False), - use_ssl=self.smtp_form.cleaned_data.get('smtp_use_ssl', False), - fail_silently=False, - timeout=10, - ) - try: - email.test_custom_smtp_backend(backend, self.smtp_form.cleaned_data.get('mail_from')) - except Exception as e: - messages.error(self.request, _('An error occurred while contacting the SMTP server: %s') % str(e)) - return self.get(request, *args, **kwargs) - - return self.response_class( - request=self.request, - template='pretixcontrol/email_setup_smtp.html', - context={ - 'basetpl': self.basetpl, - 'object': self.object, - 'known_host_problem': { - 'smtp.gmail.com': _( - 'We recommend not using Google Mail for transactional emails. If you try sending many ' - 'emails in a short amount of time, e.g. when sending information to all your ticket ' - 'buyers, there is a high chance Google will not deliver all of your emails since they ' - 'impose a maximum number of emails per time period.' - ), - }.get(self.smtp_form.cleaned_data['smtp_host']), - }, - using=self.template_engine, - ) diff --git a/src/pretix/control/views/organizer.py b/src/pretix/control/views/organizer.py index 60c01adb2f..32a7ea659d 100644 --- a/src/pretix/control/views/organizer.py +++ b/src/pretix/control/views/organizer.py @@ -64,6 +64,7 @@ from django.views.generic import ( from pretix.api.models import WebHook from pretix.base.auth import get_auth_backends from pretix.base.channels import get_all_sales_channels +from pretix.base.email import test_custom_smtp_backend from pretix.base.i18n import language from pretix.base.models import ( CachedFile, Customer, Device, Gate, GiftCard, Invoice, LogEntry, @@ -101,7 +102,6 @@ from pretix.control.permissions import ( ) from pretix.control.signals import nav_organizer from pretix.control.views import PaginationMixin -from pretix.control.views.mailsetup import MailSettingsSetupView from pretix.helpers.dicts import merge_dicts from pretix.helpers.urls import build_absolute_uri as build_global_uri from pretix.multidomain.urlreverse import build_absolute_uri @@ -262,6 +262,22 @@ class OrganizerMailSettings(OrganizerSettingsFormView): k: form.cleaned_data.get(k) for k in form.changed_data } ) + + if request.POST.get('test', '0').strip() == '1': + backend = self.request.organizer.get_mail_backend(force_custom=True, timeout=10) + try: + test_custom_smtp_backend(backend, self.request.organizer.settings.mail_from) + except Exception as e: + messages.warning(self.request, _('An error occurred while contacting the SMTP server: %s') % str(e)) + else: + if form.cleaned_data.get('smtp_use_custom'): + messages.success(self.request, _('Your changes have been saved and the connection attempt to ' + 'your SMTP server was successful.')) + else: + messages.success(self.request, _('We\'ve been able to contact the SMTP server you configured. ' + 'Remember to check the "use custom SMTP server" checkbox, ' + 'otherwise your SMTP server will not be used.')) + else: messages.success(self.request, _('Your changes have been saved.')) return redirect(self.get_success_url()) else: @@ -269,21 +285,6 @@ class OrganizerMailSettings(OrganizerSettingsFormView): return self.get(request) -class MailSettingsSetup(OrganizerPermissionRequiredMixin, MailSettingsSetupView): - permission = 'can_change_organizer_settings' - basetpl = 'pretixcontrol/base.html' - - def get_success_url(self): - return reverse('control:organizer.settings.mail', kwargs={ - 'organizer': self.request.organizer.slug, - }) - - def log_action(self, data): - self.request.organizer.log_action( - 'pretix.organizer.settings', user=self.request.user, data=data - ) - - class MailSettingsPreview(OrganizerPermissionRequiredMixin, View): permission = 'can_change_organizer_settings' diff --git a/src/pretix/settings.py b/src/pretix/settings.py index b52047db41..6a0782119d 100644 --- a/src/pretix/settings.py +++ b/src/pretix/settings.py @@ -213,12 +213,8 @@ ALLOWED_HOSTS = ['*'] LANGUAGE_CODE = config.get('locale', 'default', fallback='en') TIME_ZONE = config.get('locale', 'timezone', fallback='UTC') -MAIL_FROM = SERVER_EMAIL = DEFAULT_FROM_EMAIL = config.get('mail', 'from', fallback='pretix@localhost') -MAIL_FROM_NOTIFICATIONS = config.get('mail', 'from_notifications', fallback=MAIL_FROM) -MAIL_FROM_ORGANIZERS = config.get('mail', 'from_organizers', fallback=MAIL_FROM) -MAIL_CUSTOM_SENDER_VERIFICATION_REQUIRED = config.getboolean('mail', 'custom_sender_verification_required', fallback=True) -MAIL_CUSTOM_SENDER_SPF_STRING = config.get('mail', 'custom_sender_spf_string', fallback='') -MAIL_CUSTOM_SMTP_ALLOW_PRIVATE_NETWORKS = config.getboolean('mail', 'custom_smtp_allow_private_networks', fallback=False) +MAIL_FROM = SERVER_EMAIL = DEFAULT_FROM_EMAIL = config.get( + 'mail', 'from', fallback='pretix@localhost') EMAIL_HOST = config.get('mail', 'host', fallback='localhost') EMAIL_PORT = config.getint('mail', 'port', fallback=25) EMAIL_HOST_USER = config.get('mail', 'user', fallback='') diff --git a/src/pretix/static/pretixcontrol/scss/_forms.scss b/src/pretix/static/pretixcontrol/scss/_forms.scss index 60a410368d..7908028d9a 100644 --- a/src/pretix/static/pretixcontrol/scss/_forms.scss +++ b/src/pretix/static/pretixcontrol/scss/_forms.scss @@ -21,9 +21,6 @@ td > .form-group > .checkbox { .static-form-row { padding-top: 7px; } -.static-form-row-with-btn { - padding-top: 3px; -} .form-inline .form-control { max-width: 100%; @@ -343,7 +340,6 @@ input[type=number].short { .propagated-settings-box.locked { .propagated-settings-form { opacity: 0.7; - pointer-events: none; } .panel-body.help-text { border-bottom: 1px solid $panel-default-heading-bg; diff --git a/src/setup.py b/src/setup.py index 5cbd2fe045..fbf5031b44 100644 --- a/src/setup.py +++ b/src/setup.py @@ -190,7 +190,6 @@ setup( 'django-scopes==1.2.*', 'django-statici18n==2.1.*', 'djangorestframework==3.12.*', - 'dnspython==2.2.*', 'drf_ujson2==1.6.*', 'isoweek', 'jsonschema', diff --git a/src/tests/control/test_events.py b/src/tests/control/test_events.py index cc9a6db658..71a45c27ae 100644 --- a/src/tests/control/test_events.py +++ b/src/tests/control/test_events.py @@ -36,11 +36,8 @@ import datetime import time from decimal import Decimal -from smtplib import SMTPResponseException -import pytest import pytz -from django.test.utils import override_settings from django.utils.timezone import now from django_scopes import scopes_disabled from i18nfield.strings import LazyI18nString @@ -51,12 +48,6 @@ from pretix.base.models import Event, LogEntry, Order, Organizer, Team, User from pretix.testutils.mock import mocker_context -@pytest.fixture -def class_monkeypatch(request, monkeypatch): - request.cls.monkeypatch = monkeypatch - - -@pytest.mark.usefixtures("class_monkeypatch") class EventsTest(SoupTest): @scopes_disabled() def setUp(self): @@ -515,204 +506,25 @@ class EventsTest(SoupTest): assert self.event1.settings.primary_color == self.orga1.settings.primary_color def test_email_settings(self): - doc = self.get_doc('/control/event/%s/%s/settings/email' % (self.orga1.slug, self.event1.slug)) - data = extract_form_fields(doc.select("form")[0]) - data['mail_from_name'] = 'test' - doc = self.post_doc('/control/event/%s/%s/settings/email' % (self.orga1.slug, self.event1.slug), - data, follow=True) - assert doc.select('.alert-success') - self.event1.settings.flush() - assert self.event1.settings.mail_from_name == "test" - - def test_email_setup_system(self): - doc = self.post_doc( - '/control/event/%s/%s/settings/email/setup' % (self.orga1.slug, self.event1.slug), - { - 'mode': 'system' - }, - follow=True - ) - assert doc.select('.alert-success') - self.event1.settings.flush() - assert "mail_from" not in self.orga1.settings._cache() - assert not self.event1.settings.smtp_use_custom - - @override_settings(MAIL_CUSTOM_SENDER_VERIFICATION_REQUIRED=True, MAIL_CUSTOM_SENDER_SPF_STRING=False) - def test_email_setup_simple_with_verification(self): - doc = self.post_doc( - '/control/event/%s/%s/settings/email/setup' % (self.orga1.slug, self.event1.slug), - { - 'mode': 'simple', - 'simple-mail_from': 'test@test.pretix.dev', - }, - follow=True - ) - self.event1.settings.flush() - assert "mail_from" not in self.event1.settings._cache() - data = extract_form_fields(doc.select("form")[0]) - data['verification'] = self.client.session[ - f'sender_mail_verification_code_/control/event/{self.orga1.slug}/{self.event1.slug}/settings/email/setup_test@test.pretix.dev' - ] - doc = self.post_doc( - '/control/event/%s/%s/settings/email/setup' % (self.orga1.slug, self.event1.slug), - data, - follow=True - ) - assert doc.select('.alert-success') - self.event1.settings.flush() - assert self.event1.settings.mail_from == 'test@test.pretix.dev' - - @override_settings(MAIL_CUSTOM_SENDER_VERIFICATION_REQUIRED=True, MAIL_CUSTOM_SENDER_SPF_STRING=False) - def test_email_setup_simple_with_verification_wrong_code(self): - doc = self.post_doc( - '/control/event/%s/%s/settings/email/setup' % (self.orga1.slug, self.event1.slug), - { - 'mode': 'simple', - 'simple-mail_from': 'test@test.pretix.dev', - }, - follow=True - ) - self.event1.settings.flush() - assert "mail_from" not in self.event1.settings._cache() - data = extract_form_fields(doc.select("form")[0]) - data['verification'] = 'AAAA' - doc = self.post_doc( - '/control/event/%s/%s/settings/email/setup' % (self.orga1.slug, self.event1.slug), - data, - follow=True - ) - assert doc.select('.alert-danger') - self.event1.settings.flush() - assert "mail_from" not in self.event1.settings._cache() - - @staticmethod - def _fake_spf_record(hostname): - return { - 'test.pretix.dev': 'v=spf1 a mx include:level2.pretix.dev ~all', - 'level2.pretix.dev': 'v=spf1 a mx +include:level3.pretix.dev include:spftest.pretix.dev ' - '-include:level4.pretix.dev ~all', - 'level3.pretix.dev': 'v=spf1 a mx include:test2.pretix.dev ~all', - 'level4.pretix.dev': 'v=spf1 a mx include:test3.pretix.dev ~all', - 'test2.pretix.dev': None, - 'test3.pretix.dev': None, - 'spftest.pretix.dev': None, - }[hostname] - - @override_settings(MAIL_CUSTOM_SENDER_VERIFICATION_REQUIRED=False, MAIL_CUSTOM_SENDER_SPF_STRING="include:spftest.pretix.dev include:test2.pretix.dev") - def test_email_setup_no_verification_spf_success(self): - self.monkeypatch.setattr("pretix.control.views.mailsetup.get_spf_record", EventsTest._fake_spf_record) - doc = self.post_doc( - '/control/event/%s/%s/settings/email/setup' % (self.orga1.slug, self.event1.slug), - { - 'mode': 'simple', - 'simple-mail_from': 'test@test.pretix.dev', - }, - follow=True - ) - assert doc.select('.alert-success') - self.event1.settings.flush() - # not yet saved - assert "mail_from" not in self.event1.settings._cache() - data = extract_form_fields(doc.select("form")[0]) - doc = self.post_doc( - '/control/event/%s/%s/settings/email/setup' % (self.orga1.slug, self.event1.slug), - data, - follow=True - ) - assert doc.select('.alert-success') - self.event1.settings.flush() - assert self.event1.settings.mail_from == 'test@test.pretix.dev' - - @override_settings(MAIL_CUSTOM_SENDER_VERIFICATION_REQUIRED=False, MAIL_CUSTOM_SENDER_SPF_STRING="include:spftest.pretix.dev include:test3.pretix.dev") - def test_email_setup_no_verification_spf_warning(self): - self.monkeypatch.setattr("pretix.control.views.mailsetup.get_spf_record", EventsTest._fake_spf_record) - doc = self.post_doc( - '/control/event/%s/%s/settings/email/setup' % (self.orga1.slug, self.event1.slug), - { - 'mode': 'simple', - 'simple-mail_from': 'test@test.pretix.dev', - }, - follow=True - ) - assert doc.select('.alert-warning') - self.event1.settings.flush() - # not yet saved - assert "mail_from" not in self.event1.settings._cache() - - def test_email_setup_smtp(self): - self.monkeypatch.setattr("pretix.base.email.test_custom_smtp_backend", lambda b, a: None) - self.monkeypatch.setattr("socket.gethostbyname", lambda h: "8.8.8.8") - doc = self.post_doc( - '/control/event/%s/%s/settings/email/setup' % (self.orga1.slug, self.event1.slug), - { - 'mode': 'smtp', - 'smtp-mail_from': 'test@test.pretix.dev', - 'smtp-smtp_host': 'test.pretix.dev', - 'smtp-smtp_port': '587', - }, - follow=True - ) - assert doc.select('.alert-success') - # not yet saved - self.event1.settings.flush() - assert "smtp_use_custom" not in self.event1.settings._cache() - data = extract_form_fields(doc.select("form")[0]) - doc = self.post_doc( - '/control/event/%s/%s/settings/email/setup' % (self.orga1.slug, self.event1.slug), - data, - follow=True - ) - assert doc.select('.alert-success') - self.event1.settings.flush() - assert self.event1.settings.mail_from == 'test@test.pretix.dev' - assert self.event1.settings.smtp_host == 'test.pretix.dev' - assert self.event1.settings.smtp_port == 587 - assert self.event1.settings.smtp_use_custom - - def test_email_setup_smtp_failure(self): - def fail(a, b): - raise SMTPResponseException(400, 'Auth denied') - self.monkeypatch.setattr("pretix.base.email.test_custom_smtp_backend", fail) - self.monkeypatch.setattr("socket.gethostbyname", lambda h: "8.8.8.8") - doc = self.post_doc( - '/control/event/%s/%s/settings/email/setup' % (self.orga1.slug, self.event1.slug), - { - 'mode': 'smtp', - 'smtp-mail_from': 'test@test.pretix.dev', - 'smtp-smtp_host': 'test.pretix.dev', - 'smtp-smtp_port': '587', - }, - follow=True - ) - assert 'Auth denied' in doc.select('.alert-danger')[0].text - # not yet saved - self.event1.settings.flush() - assert "smtp_use_custom" not in self.event1.settings._cache() - assert "mail_from" not in self.event1.settings._cache() - - def test_email_setup_do_not_allow_private_ip_by_default(self): - doc = self.post_doc( - '/control/event/%s/%s/settings/email/setup' % (self.orga1.slug, self.event1.slug), - { - 'mode': 'simple', - 'smtp-mail_from': 'test@test.pretix.dev', - 'smtp-smtp_host': '10.0.1.1', - 'smtp-smtp_port': '587', - }, - follow=True - ) - assert doc.select('.has-error') - # not yet saved - self.event1.settings.flush() - assert "smtp_use_custom" not in self.event1.settings._cache() - assert "mail_from" not in self.event1.settings._cache() + with mocker_context() as mocker: + mocked = mocker.patch('pretix.control.views.event.test_custom_smtp_backend') + doc = self.get_doc('/control/event/%s/%s/settings/email' % (self.orga1.slug, self.event1.slug)) + data = extract_form_fields(doc.select("form")[0]) + data['test'] = '1' + doc = self.post_doc('/control/event/%s/%s/settings/email' % (self.orga1.slug, self.event1.slug), + data, follow=True) + print(doc) + assert doc.select('.alert-success') + self.event1.settings.flush() + assert mocked.called def test_ticket_settings(self): doc = self.get_doc('/control/event/%s/%s/settings/tickets' % (self.orga1.slug, self.event1.slug)) data = extract_form_fields(doc.select("form")[0]) data['ticket_download'] = 'on' data['ticketoutput_testdummy__enabled'] = 'on' - self.post_doc('/control/event/%s/%s/settings/tickets' % (self.orga1.slug, self.event1.slug), data, follow=True) + doc = self.post_doc('/control/event/%s/%s/settings/tickets' % (self.orga1.slug, self.event1.slug), + data, follow=True) self.event1.settings.flush() assert self.event1.settings.get('ticket_download', as_type=bool) diff --git a/src/tests/control/test_organizer.py b/src/tests/control/test_organizer.py index c2e87e38ca..ff8cda330e 100644 --- a/src/tests/control/test_organizer.py +++ b/src/tests/control/test_organizer.py @@ -20,15 +20,14 @@ # . # import datetime -from smtplib import SMTPResponseException import pytest from django.db import transaction -from django.test.utils import override_settings from django_scopes import scopes_disabled from tests.base import SoupTest, extract_form_fields from pretix.base.models import Event, Organizer, Team, User +from pretix.testutils.mock import mocker_context @pytest.fixture @@ -101,194 +100,13 @@ class OrganizerTest(SoupTest): assert called def test_email_settings(self): - doc = self.get_doc('/control/organizer/%s/settings/email' % self.orga1.slug) - data = extract_form_fields(doc.select("form")[0]) - data['mail_from_name'] = 'test' - doc = self.post_doc('/control/organizer/%s/settings/email' % self.orga1.slug, - data, follow=True) - assert doc.select('.alert-success') - self.orga1.settings.flush() - assert self.orga1.settings.mail_from_name == "test" - - def test_email_setup_system(self): - doc = self.post_doc( - '/control/organizer/%s/settings/email/setup' % self.orga1.slug, - { - 'mode': 'system' - }, - follow=True - ) - assert doc.select('.alert-success') - self.orga1.settings.flush() - assert "mail_from" not in self.orga1.settings._cache() - assert not self.orga1.settings.smtp_use_custom - - @override_settings(MAIL_CUSTOM_SENDER_VERIFICATION_REQUIRED=True, MAIL_CUSTOM_SENDER_SPF_STRING=False) - def test_email_setup_simple_with_verification(self): - doc = self.post_doc( - '/control/organizer/%s/settings/email/setup' % self.orga1.slug, - { - 'mode': 'simple', - 'simple-mail_from': 'test@test.pretix.dev', - }, - follow=True - ) - self.orga1.settings.flush() - assert "mail_from" not in self.orga1.settings._cache() - data = extract_form_fields(doc.select("form")[0]) - data['verification'] = self.client.session[ - f'sender_mail_verification_code_/control/organizer/{self.orga1.slug}/settings/email/setup_test@test.pretix.dev' - ] - doc = self.post_doc( - '/control/organizer/%s/settings/email/setup' % self.orga1.slug, - data, - follow=True - ) - assert doc.select('.alert-success') - self.orga1.settings.flush() - assert self.orga1.settings.mail_from == 'test@test.pretix.dev' - - @override_settings(MAIL_CUSTOM_SENDER_VERIFICATION_REQUIRED=True, MAIL_CUSTOM_SENDER_SPF_STRING=False) - def test_email_setup_simple_with_verification_wrong_code(self): - doc = self.post_doc( - '/control/organizer/%s/settings/email/setup' % self.orga1.slug, - { - 'mode': 'simple', - 'simple-mail_from': 'test@test.pretix.dev', - }, - follow=True - ) - self.orga1.settings.flush() - assert "mail_from" not in self.orga1.settings._cache() - data = extract_form_fields(doc.select("form")[0]) - data['verification'] = 'AAAA' - doc = self.post_doc( - '/control/organizer/%s/settings/email/setup' % self.orga1.slug, - data, - follow=True - ) - assert doc.select('.alert-danger') - self.orga1.settings.flush() - assert "mail_from" not in self.orga1.settings._cache() - - @staticmethod - def _fake_spf_record(hostname): - return { - 'test.pretix.dev': 'v=spf1 a mx include:level2.pretix.dev ~all', - 'level2.pretix.dev': 'v=spf1 a mx +include:level3.pretix.dev include:spftest.pretix.dev ' - '-include:level4.pretix.dev ~all', - 'level3.pretix.dev': 'v=spf1 a mx include:test2.pretix.dev ~all', - 'level4.pretix.dev': 'v=spf1 a mx include:test3.pretix.dev ~all', - 'test2.pretix.dev': None, - 'test3.pretix.dev': None, - 'spftest.pretix.dev': None, - }[hostname] - - @override_settings(MAIL_CUSTOM_SENDER_VERIFICATION_REQUIRED=False, MAIL_CUSTOM_SENDER_SPF_STRING="include:spftest.pretix.dev include:test2.pretix.dev") - def test_email_setup_no_verification_spf_success(self): - self.monkeypatch.setattr("pretix.control.views.mailsetup.get_spf_record", OrganizerTest._fake_spf_record) - doc = self.post_doc( - '/control/organizer/%s/settings/email/setup' % self.orga1.slug, - { - 'mode': 'simple', - 'simple-mail_from': 'test@test.pretix.dev', - }, - follow=True - ) - assert doc.select('.alert-success') - self.orga1.settings.flush() - # not yet saved - assert "mail_from" not in self.orga1.settings._cache() - data = extract_form_fields(doc.select("form")[0]) - doc = self.post_doc( - '/control/organizer/%s/settings/email/setup' % self.orga1.slug, - data, - follow=True - ) - assert doc.select('.alert-success') - self.orga1.settings.flush() - assert self.orga1.settings.mail_from == 'test@test.pretix.dev' - - @override_settings(MAIL_CUSTOM_SENDER_VERIFICATION_REQUIRED=False, MAIL_CUSTOM_SENDER_SPF_STRING="include:spftest.pretix.dev include:test3.pretix.dev") - def test_email_setup_no_verification_spf_warning(self): - self.monkeypatch.setattr("pretix.control.views.mailsetup.get_spf_record", OrganizerTest._fake_spf_record) - doc = self.post_doc( - '/control/organizer/%s/settings/email/setup' % self.orga1.slug, - { - 'mode': 'simple', - 'simple-mail_from': 'test@test.pretix.dev', - }, - follow=True - ) - assert doc.select('.alert-warning') - self.orga1.settings.flush() - # not yet saved - assert "mail_from" not in self.orga1.settings._cache() - - def test_email_setup_smtp(self): - self.monkeypatch.setattr("pretix.base.email.test_custom_smtp_backend", lambda b, a: None) - self.monkeypatch.setattr("socket.gethostbyname", lambda h: "8.8.8.8") - doc = self.post_doc( - '/control/organizer/%s/settings/email/setup' % self.orga1.slug, - { - 'mode': 'smtp', - 'smtp-mail_from': 'test@test.pretix.dev', - 'smtp-smtp_host': 'test.pretix.dev', - 'smtp-smtp_port': '587', - }, - follow=True - ) - assert doc.select('.alert-success') - # not yet saved - self.orga1.settings.flush() - assert "smtp_use_custom" not in self.orga1.settings._cache() - data = extract_form_fields(doc.select("form")[0]) - doc = self.post_doc( - '/control/organizer/%s/settings/email/setup' % self.orga1.slug, - data, - follow=True - ) - assert doc.select('.alert-success') - self.orga1.settings.flush() - assert self.orga1.settings.mail_from == 'test@test.pretix.dev' - assert self.orga1.settings.smtp_host == 'test.pretix.dev' - assert self.orga1.settings.smtp_port == 587 - assert self.orga1.settings.smtp_use_custom - - def test_email_setup_smtp_failure(self): - def fail(a, b): - raise SMTPResponseException(400, 'Auth denied') - self.monkeypatch.setattr("pretix.base.email.test_custom_smtp_backend", fail) - self.monkeypatch.setattr("socket.gethostbyname", lambda h: "8.8.8.8") - doc = self.post_doc( - '/control/organizer/%s/settings/email/setup' % self.orga1.slug, - { - 'mode': 'smtp', - 'smtp-mail_from': 'test@test.pretix.dev', - 'smtp-smtp_host': 'test.pretix.dev', - 'smtp-smtp_port': '587', - }, - follow=True - ) - assert 'Auth denied' in doc.select('.alert-danger')[0].text - # not yet saved - self.orga1.settings.flush() - assert "smtp_use_custom" not in self.orga1.settings._cache() - assert "mail_from" not in self.orga1.settings._cache() - - def test_email_setup_do_not_allow_private_ip_by_default(self): - doc = self.post_doc( - '/control/organizer/%s/settings/email/setup' % self.orga1.slug, - { - 'mode': 'simple', - 'smtp-mail_from': 'test@test.pretix.dev', - 'smtp-smtp_host': '10.0.1.1', - 'smtp-smtp_port': '587', - }, - follow=True - ) - assert doc.select('.has-error') - # not yet saved - self.orga1.settings.flush() - assert "smtp_use_custom" not in self.orga1.settings._cache() - assert "mail_from" not in self.orga1.settings._cache() + with mocker_context() as mocker: + mocked = mocker.patch('pretix.control.views.organizer.test_custom_smtp_backend') + doc = self.get_doc('/control/organizer/%s/settings/email' % self.orga1.slug) + data = extract_form_fields(doc.select("form")[0]) + data['test'] = '1' + doc = self.post_doc('/control/organizer/%s/settings/email' % self.orga1.slug, + data, follow=True) + assert doc.select('.alert-success') + self.event1.settings.flush() + assert mocked.called diff --git a/src/tests/control/test_permissions.py b/src/tests/control/test_permissions.py index 3d59295ae2..c00fd6106e 100644 --- a/src/tests/control/test_permissions.py +++ b/src/tests/control/test_permissions.py @@ -92,7 +92,6 @@ event_urls = [ "settings/payment", "settings/tickets", "settings/email", - "settings/email/setup", "settings/cancel", "settings/invoice", "settings/invoice/preview", @@ -172,8 +171,6 @@ event_urls = [ organizer_urls = [ 'organizer/abc/edit', 'organizer/abc/', - 'organizer/abc/settings/email', - 'organizer/abc/settings/email/setup', 'organizer/abc/teams', 'organizer/abc/team/1/', 'organizer/abc/team/1/edit', @@ -288,7 +285,6 @@ event_permission_urls = [ ("can_change_event_settings", "settings/payment", 200, HTTP_GET), ("can_change_event_settings", "settings/tickets", 200, HTTP_GET), ("can_change_event_settings", "settings/email", 200, HTTP_GET), - ("can_change_event_settings", "settings/email/setup", 200, HTTP_GET), ("can_change_event_settings", "settings/cancel", 200, HTTP_GET), ("can_change_event_settings", "settings/invoice", 200, HTTP_GET), ("can_change_event_settings", "settings/widget", 200, HTTP_GET), @@ -485,8 +481,6 @@ organizer_permission_urls = [ ("can_change_teams", "organizer/dummy/team/1/edit", 200), ("can_change_teams", "organizer/dummy/team/1/delete", 200), ("can_change_organizer_settings", "organizer/dummy/edit", 200), - ("can_change_organizer_settings", "organizer/dummy/settings/email", 200), - ("can_change_organizer_settings", "organizer/dummy/settings/email/setup", 200), ("can_change_organizer_settings", "organizer/dummy/devices", 200), ("can_change_organizer_settings", "organizer/dummy/device/add", 200), ("can_change_organizer_settings", "organizer/dummy/device/1/edit", 404),