Customer accounts: Add security notices (#5705)

* Customer accounts: Add security notices

* Apply suggestions from code review
This commit is contained in:
Raphael Michel
2026-02-10 17:55:53 +01:00
committed by GitHub
parent 27fcdff17f
commit 47f409171d
6 changed files with 75 additions and 0 deletions

View File

@@ -40,6 +40,7 @@ from i18nfield.fields import I18nCharField
from phonenumber_field.modelfields import PhoneNumberField
from pretix.base.banlist import banned
from pretix.base.i18n import language
from pretix.base.models.base import LoggedModel
from pretix.base.models.fields import MultiStringField
from pretix.base.models.giftcards import GiftCardTransaction
@@ -164,6 +165,28 @@ class Customer(LoggedModel):
self.attendee_profiles.all().delete()
self.invoice_addresses.all().delete()
def send_security_notice(self, message, email=None):
from pretix.base.services.mail import SendMailException, mail
from pretix.multidomain.urlreverse import build_absolute_uri
try:
with language(self.locale):
mail(
email or self.email,
self.organizer.settings.mail_subject_customer_security_notice,
self.organizer.settings.mail_text_customer_security_notice,
{
**self.get_email_context(),
'message': str(message),
'url': build_absolute_uri(self.organizer, 'presale:organizer.customer.index')
},
customer=self,
organizer=self.organizer,
locale=self.locale
)
except SendMailException:
pass # Already logged
@scopes_disabled()
def assign_identifier(self):
charset = list('ABCDEFGHJKLMNPQRSTUVWXYZ23456789')

View File

@@ -2948,6 +2948,28 @@ If you did not request a new password, please ignore this email.
Best regards,
Your {organizer} team""")) # noqa: W291
},
'mail_subject_customer_security_notice': {
'type': LazyI18nString,
'default': LazyI18nString.from_gettext(gettext_noop("Changes to your account at {organizer}")),
},
'mail_text_customer_security_notice': {
'type': LazyI18nString,
'default': LazyI18nString.from_gettext(gettext_noop("""Hello {name},
the following change has been made to your account at {organizer}:
{message}
You can review and change your account settings here:
{url}
If this change was not performed by you, please contact us immediately.
Best regards,
Your {organizer} team""")) # noqa: W291
},
'smtp_use_custom': {

View File

@@ -635,6 +635,16 @@ class MailSettingsForm(SettingsForm):
required=False,
widget=I18nMarkdownTextarea,
)
mail_subject_customer_security_notice = I18nFormField(
label=_("Subject"),
required=False,
widget=I18nTextInput,
)
mail_text_customer_security_notice = I18nFormField(
label=_("Text"),
required=False,
widget=I18nMarkdownTextarea,
)
base_context = {
'mail_text_customer_registration': ['customer', 'url'],
@@ -643,6 +653,8 @@ class MailSettingsForm(SettingsForm):
'mail_subject_customer_email_change': ['customer', 'url'],
'mail_text_customer_reset': ['customer', 'url'],
'mail_subject_customer_reset': ['customer', 'url'],
'mail_text_customer_security_notice': ['customer', 'url', 'message'],
'mail_subject_customer_security_notice': ['customer', 'url', 'message'],
}
def _get_sample_context(self, base_parameters):
@@ -656,6 +668,9 @@ class MailSettingsForm(SettingsForm):
'presale:organizer.customer.activate'
) + '?token=' + get_random_string(30)
if 'message' in base_parameters:
placeholders['message'] = _('Your password has been changed.')
if 'customer' in base_parameters:
placeholders['name'] = pgettext_lazy('person_name_sample', 'John Doe')
name_scheme = PERSON_NAME_SCHEMES[self.organizer.settings.name_scheme]

View File

@@ -65,6 +65,9 @@
{% blocktrans asvar title_reset %}Customer account password reset{% endblocktrans %}
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="reset" title=title_reset items="mail_subject_customer_reset,mail_text_customer_reset" %}
{% blocktrans asvar title_security_notice %}Customer account security notification{% endblocktrans %}
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="security_notice" title=title_security_notice items="mail_subject_customer_security_notice,mail_text_customer_security_notice" %}
</div>
</fieldset>
</div>

View File

@@ -286,6 +286,7 @@ class SetPasswordView(FormView):
self.customer.is_verified = True
self.customer.save()
self.customer.log_action('pretix.customer.password.set', {})
self.customer.send_security_notice(_("Your password has been changed."))
messages.success(
self.request,
_('Your new password has been set! You can now use it to log in.'),
@@ -541,6 +542,7 @@ class ChangePasswordView(CustomerAccountBaseMixin, FormView):
customer.set_password(form.cleaned_data['password'])
customer.save()
messages.success(self.request, _('Your changes have been saved.'))
customer.send_security_notice(_("Your password has been changed."))
update_customer_session_auth_hash(self.request, customer)
return HttpResponseRedirect(self.get_success_url())
@@ -631,11 +633,15 @@ class ConfirmChangeView(View):
try:
with transaction.atomic():
old_email = customer.email
customer.email = data['email']
customer.save()
customer.log_action('pretix.customer.changed', {
'email': data['email']
})
msg = _('Your email address has been changed from {old_email} to {email}.').format(old_email=old_email, email=customer.email)
customer.send_security_notice(msg, email=old_email)
customer.send_security_notice(msg, email=customer.email)
except IntegrityError:
messages.success(request, _('Your email address has not been updated since the address is already in use '
'for another customer account.'))