views: enforce A2_EMAIL_IS_UNIQUE on email change (fixes #19712)

This commit is contained in:
Benjamin Dauvergne 2017-11-17 00:44:19 +01:00
parent 2ad841c017
commit 16afddc6b9
3 changed files with 95 additions and 3 deletions

View File

@ -1,4 +1,21 @@
{% load i18n %}{% autoescape off %}{% blocktrans with name=user.get_short_name old_email=user.email %}Hi {{ name }} !
{% load i18n %}{% autoescape off %}{% if email_is_not_unique%}{% blocktrans with name=user.get_short_name old_email=user.email %}Hi {{ name }} !
You asked for changing your email on {{ domain }} from:
{{ old_email }}
to:
{{ email }}
But this email is already linked to another account.
You can recover this account password using the password reset form:
{{ password_reset_url }}
--
{{ domain }}{% endblocktrans %}{% else %}{% blocktrans with name=user.get_short_name old_email=user.email %}Hi {{ name }} !
You asked for changing your email on {{ domain }} from:
@ -15,5 +32,4 @@ To validate this change please click on the following link:
This link will be valid for {{ token_lifetime }}.
--
{{ domain }}
{% endblocktrans %}{% endautoescape %}
{{ domain }}{% endblocktrans %}{% endif %}{% endautoescape %}

View File

@ -178,7 +178,14 @@ class EmailChangeView(cbv.TemplateNamesMixin, FormView):
'link': link,
'domain': request.get_host(),
'token_lifetime': utils.human_duration(app_settings.A2_EMAIL_CHANGE_TOKEN_LIFETIME),
'password_reset_url': request.build_absolute_uri(reverse('password_reset')),
}
qs = compat.get_user_model().objects.all()
if app_settings.A2_EMAIL_IS_UNIQUE:
ctx['email_is_not_unique'] = qs.filter(email=email).exclude(pk=user.pk).exists()
elif user.ou and user.ou.email_is_unique:
ctx['email_is_not_unique'] = qs.filter(email=email,
ou=user.ou).exclude(pk=user.pk).exists()
utils.send_templated_mail(
email,
@ -213,6 +220,14 @@ class EmailChangeVerifyView(TemplateView):
user_pk = token['user_pk']
email = token['email']
user = User.objects.get(pk=user_pk)
non_unique = False
if app_settings.A2_EMAIL_IS_UNIQUE:
non_unique = User.objects.filter(email=email).exclude(pk=user_pk).exists()
elif user.ou and user.ou.email_is_unique:
non_unique = User.objects.filter(email=email, ou=user.ou).exclude(
pk=user_pk).exists()
if non_unique:
raise forms.ValidationError(_('This email is already used by another account.'))
old_email = user.email
user.email = email
user.save()
@ -234,6 +249,8 @@ class EmailChangeVerifyView(TemplateView):
except User.DoesNotExist:
messages.error(request, _('your request for changing your email is for '
'an unknown user, try again'))
except forms.ValidationError as e:
messages.error(request, e.message)
else:
return shortcuts.redirect('account_management')
return shortcuts.redirect('email-change')

View File

@ -0,0 +1,59 @@
import re
import utils
def change_email(app, user, email, mailoutbox):
utils.login(app, user)
l = len(mailoutbox)
response = app.get('/accounts/change-email/')
response.form.set('email', email)
response.form.set('password', user.username)
response = response.form.submit()
assert len(mailoutbox) == l + 1
return mailoutbox[-1]
def test_change_email(app, simple_user, user_ou1, mailoutbox):
email = change_email(app, simple_user, user_ou1.email, mailoutbox)
links = re.findall('https?://[^ \n]*', email.body)
assert links
link = links[0]
app.get(link)
simple_user.refresh_from_db()
# ok it worked
assert simple_user.email == user_ou1.email
def test_change_email_email_is_unique(app, settings, simple_user, user_ou1, mailoutbox):
settings.A2_EMAIL_IS_UNIQUE = True
email = change_email(app, simple_user, user_ou1.email, mailoutbox)
links = re.findall('https?://[^ \n]*', email.body)
assert links
link = links[0]
# email change is impossible as email is already taken
assert 'password/reset' in link
def test_change_email_ou_email_is_unique(app, simple_user, user_ou1, user_ou2, mailoutbox):
user_ou1.ou.email_is_unique = True
user_ou1.ou.save()
user_ou2.email = 'john.doe-ou2@example.net'
user_ou2.save()
email = change_email(app, simple_user, user_ou2.email, mailoutbox)
links = re.findall('https?://[^ \n]*', email.body)
assert links
link = links[0]
app.get(link)
simple_user.refresh_from_db()
# ok it worked for a differnt ou
assert simple_user.email == user_ou2.email
# now set simple_user in same ou as user_ou1
simple_user.ou = user_ou1.ou
simple_user.save()
email = change_email(app, simple_user, user_ou1.email, mailoutbox)
links = re.findall('https?://[^ \n]*', email.body)
assert links
link = links[0]
# email change is impossible as email is already taken in the same ou
assert 'password/reset' in link