forked from CGM_Public/pretix_original
Customer accounts: Add security notices (#5705)
* Customer accounts: Add security notices * Apply suggestions from code review
This commit is contained in:
@@ -40,6 +40,7 @@ from i18nfield.fields import I18nCharField
|
|||||||
from phonenumber_field.modelfields import PhoneNumberField
|
from phonenumber_field.modelfields import PhoneNumberField
|
||||||
|
|
||||||
from pretix.base.banlist import banned
|
from pretix.base.banlist import banned
|
||||||
|
from pretix.base.i18n import language
|
||||||
from pretix.base.models.base import LoggedModel
|
from pretix.base.models.base import LoggedModel
|
||||||
from pretix.base.models.fields import MultiStringField
|
from pretix.base.models.fields import MultiStringField
|
||||||
from pretix.base.models.giftcards import GiftCardTransaction
|
from pretix.base.models.giftcards import GiftCardTransaction
|
||||||
@@ -164,6 +165,28 @@ class Customer(LoggedModel):
|
|||||||
self.attendee_profiles.all().delete()
|
self.attendee_profiles.all().delete()
|
||||||
self.invoice_addresses.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()
|
@scopes_disabled()
|
||||||
def assign_identifier(self):
|
def assign_identifier(self):
|
||||||
charset = list('ABCDEFGHJKLMNPQRSTUVWXYZ23456789')
|
charset = list('ABCDEFGHJKLMNPQRSTUVWXYZ23456789')
|
||||||
|
|||||||
@@ -2948,6 +2948,28 @@ If you did not request a new password, please ignore this email.
|
|||||||
|
|
||||||
Best regards,
|
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
|
Your {organizer} team""")) # noqa: W291
|
||||||
},
|
},
|
||||||
'smtp_use_custom': {
|
'smtp_use_custom': {
|
||||||
|
|||||||
@@ -635,6 +635,16 @@ class MailSettingsForm(SettingsForm):
|
|||||||
required=False,
|
required=False,
|
||||||
widget=I18nMarkdownTextarea,
|
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 = {
|
base_context = {
|
||||||
'mail_text_customer_registration': ['customer', 'url'],
|
'mail_text_customer_registration': ['customer', 'url'],
|
||||||
@@ -643,6 +653,8 @@ class MailSettingsForm(SettingsForm):
|
|||||||
'mail_subject_customer_email_change': ['customer', 'url'],
|
'mail_subject_customer_email_change': ['customer', 'url'],
|
||||||
'mail_text_customer_reset': ['customer', 'url'],
|
'mail_text_customer_reset': ['customer', 'url'],
|
||||||
'mail_subject_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):
|
def _get_sample_context(self, base_parameters):
|
||||||
@@ -656,6 +668,9 @@ class MailSettingsForm(SettingsForm):
|
|||||||
'presale:organizer.customer.activate'
|
'presale:organizer.customer.activate'
|
||||||
) + '?token=' + get_random_string(30)
|
) + '?token=' + get_random_string(30)
|
||||||
|
|
||||||
|
if 'message' in base_parameters:
|
||||||
|
placeholders['message'] = _('Your password has been changed.')
|
||||||
|
|
||||||
if 'customer' in base_parameters:
|
if 'customer' in base_parameters:
|
||||||
placeholders['name'] = pgettext_lazy('person_name_sample', 'John Doe')
|
placeholders['name'] = pgettext_lazy('person_name_sample', 'John Doe')
|
||||||
name_scheme = PERSON_NAME_SCHEMES[self.organizer.settings.name_scheme]
|
name_scheme = PERSON_NAME_SCHEMES[self.organizer.settings.name_scheme]
|
||||||
|
|||||||
@@ -65,6 +65,9 @@
|
|||||||
|
|
||||||
{% blocktrans asvar title_reset %}Customer account password reset{% endblocktrans %}
|
{% 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" %}
|
{% 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>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -286,6 +286,7 @@ class SetPasswordView(FormView):
|
|||||||
self.customer.is_verified = True
|
self.customer.is_verified = True
|
||||||
self.customer.save()
|
self.customer.save()
|
||||||
self.customer.log_action('pretix.customer.password.set', {})
|
self.customer.log_action('pretix.customer.password.set', {})
|
||||||
|
self.customer.send_security_notice(_("Your password has been changed."))
|
||||||
messages.success(
|
messages.success(
|
||||||
self.request,
|
self.request,
|
||||||
_('Your new password has been set! You can now use it to log in.'),
|
_('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.set_password(form.cleaned_data['password'])
|
||||||
customer.save()
|
customer.save()
|
||||||
messages.success(self.request, _('Your changes have been saved.'))
|
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)
|
update_customer_session_auth_hash(self.request, customer)
|
||||||
return HttpResponseRedirect(self.get_success_url())
|
return HttpResponseRedirect(self.get_success_url())
|
||||||
|
|
||||||
@@ -631,11 +633,15 @@ class ConfirmChangeView(View):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
|
old_email = customer.email
|
||||||
customer.email = data['email']
|
customer.email = data['email']
|
||||||
customer.save()
|
customer.save()
|
||||||
customer.log_action('pretix.customer.changed', {
|
customer.log_action('pretix.customer.changed', {
|
||||||
'email': data['email']
|
'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:
|
except IntegrityError:
|
||||||
messages.success(request, _('Your email address has not been updated since the address is already in use '
|
messages.success(request, _('Your email address has not been updated since the address is already in use '
|
||||||
'for another customer account.'))
|
'for another customer account.'))
|
||||||
|
|||||||
@@ -164,6 +164,7 @@ def test_org_resetpw(env, client):
|
|||||||
customer.refresh_from_db()
|
customer.refresh_from_db()
|
||||||
assert customer.check_password('PANioMR62')
|
assert customer.check_password('PANioMR62')
|
||||||
assert customer.is_verified
|
assert customer.is_verified
|
||||||
|
assert len(djmail.outbox) == 2
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
@@ -623,6 +624,7 @@ def test_change_email(env, client):
|
|||||||
customer.refresh_from_db()
|
customer.refresh_from_db()
|
||||||
assert customer.email == 'john@example.org'
|
assert customer.email == 'john@example.org'
|
||||||
assert len(djmail.outbox) == 1
|
assert len(djmail.outbox) == 1
|
||||||
|
assert djmail.outbox[0].to == ['john@example.com']
|
||||||
|
|
||||||
token = dumps({
|
token = dumps({
|
||||||
'customer': customer.pk,
|
'customer': customer.pk,
|
||||||
@@ -632,6 +634,9 @@ def test_change_email(env, client):
|
|||||||
assert r.status_code == 302
|
assert r.status_code == 302
|
||||||
customer.refresh_from_db()
|
customer.refresh_from_db()
|
||||||
assert customer.email == 'john@example.com'
|
assert customer.email == 'john@example.com'
|
||||||
|
assert len(djmail.outbox) == 3
|
||||||
|
assert djmail.outbox[1].to == ['john@example.org']
|
||||||
|
assert djmail.outbox[2].to == ['john@example.com']
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
@@ -673,6 +678,7 @@ def test_change_pw(env, client, client2):
|
|||||||
|
|
||||||
r = client.get('/bigevents/account/password')
|
r = client.get('/bigevents/account/password')
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
|
assert len(djmail.outbox) == 1
|
||||||
|
|
||||||
# Client 2 got logged out
|
# Client 2 got logged out
|
||||||
r = client2.post('/bigevents/account/password')
|
r = client2.post('/bigevents/account/password')
|
||||||
|
|||||||
Reference in New Issue
Block a user