Merge lp:~matiasb/canonical-identity-provider/working-on-1192555 into lp:canonical-identity-provider/release

Proposed by Matias Bordese
Status: Merged
Approved by: Matias Bordese
Approved revision: no longer in the source branch.
Merged at revision: 981
Proposed branch: lp:~matiasb/canonical-identity-provider/working-on-1192555
Merge into: lp:canonical-identity-provider/release
Diff against target: 89 lines (+56/-3)
2 files modified
src/webui/tests/test_views_ui.py (+47/-1)
src/webui/views/ui.py (+9/-2)
To merge this branch: bzr merge lp:~matiasb/canonical-identity-provider/working-on-1192555
Reviewer Review Type Date Requested Status
Ricardo Kirkner (community) Approve
Review via email: mp+176474@code.launchpad.net

Commit message

Fixed reset password view to handle email in a case insensitive way.

Description of the change

Fixed reset password view to handle email in a case insensitive way.

Reset password tokens generated through the API v1.0 allow the creation of tokens with email with different cases (this does not affect API v2 because since ALLOW_UNVERIFIED is enabled for everyone, the preferred email is selected over the email in the token).

This also arises the question: should we normalize email addresses before saving to db (and before searching on db)? Right now emails are stored as passed by the user.

To post a comment you must log in.
Revision history for this message
Ricardo Kirkner (ricardokirkner) wrote :

LGTM

review: Approve
Revision history for this message
Ricardo Kirkner (ricardokirkner) wrote :

> This also arises the question: should we normalize email addresses before saving to db (and before searching > on db)? Right now emails are stored as passed by the user.

I think we should store the emails as provided by the users (because according to the RFC the username part of the email can be case sensitive), but search for them in a case insensitive way.because people actually treat them as case insensitive.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/webui/tests/test_views_ui.py'
2--- src/webui/tests/test_views_ui.py 2013-07-08 18:35:43 +0000
3+++ src/webui/tests/test_views_ui.py 2013-07-23 20:54:23 +0000
4@@ -630,7 +630,7 @@
5 self.account.displayname)
6
7
8-class ForgotPasswordTestCase(UIViewsBaseTestCase):
9+class ForgotPasswordTestCase(LogHandlerTestCase, UIViewsBaseTestCase):
10
11 def test_forgot_password_includes_rp_analytics(self):
12 rpconfig = OpenIDRPConfig.objects.create(
13@@ -706,6 +706,52 @@
14 r = self.client.post(self._token_url(), data)
15 self.assertRedirects(r, reverse('account-index'))
16
17+ def test_reset_password_email_is_case_insensitive(self):
18+ r = self.client.post(reverse('forgot_password'), {'email': self.email})
19+
20+ # update requester email in token
21+ auth_token = AuthToken.objects.get(
22+ email=self.email, token_type=TokenType.PASSWORDRECOVERY)
23+ auth_token.requester_email = self.email.upper()
24+ assert self.email.upper() != self.email
25+ auth_token.save()
26+
27+ # make sure the account is active
28+ self.account.status = AccountStatus.ACTIVE
29+ self.account.save()
30+
31+ # confirm account
32+ data = {'password': 'Password1', 'passwordconfirm': 'Password1'}
33+ r = self.client.post(self._token_url(), data)
34+ self.assertRedirects(r, reverse('account-index'))
35+
36+ def test_reset_password_email_not_found(self):
37+ r = self.client.post(reverse('forgot_password'), {'email': self.email})
38+
39+ # update requester email in token
40+ auth_token = AuthToken.objects.get(
41+ email=self.email, token_type=TokenType.PASSWORDRECOVERY)
42+ auth_token.requester_email = 'another-email@example.com'
43+ auth_token.save()
44+
45+ # make sure the account is active
46+ self.account.status = AccountStatus.ACTIVE
47+ self.account.save()
48+
49+ # confirm account
50+ data = {'password': 'Password1', 'passwordconfirm': 'Password1'}
51+
52+ mock_is_enabled = Mock(return_value=True)
53+ with patch.object(self.root_logger, 'isEnabledFor', mock_is_enabled):
54+ with patch('webui.views.ui.logger', self.root_logger):
55+ r = self.client.post(self._token_url(), data)
56+
57+ self.assertRedirects(r, reverse('bad_token'))
58+ self.assertLogLevelContains(
59+ 'WARNING', "Could not reset password, invalid email address "
60+ "provided in the token: %s (account %s)" % (
61+ auth_token.requester_email, self.account.id))
62+
63 def test_reset_password_with_lowered_password_requirements(self):
64 r = self.client.post(reverse('forgot_password'), {'email': self.email})
65
66
67=== modified file 'src/webui/views/ui.py'
68--- src/webui/views/ui.py 2013-07-08 18:35:43 +0000
69+++ src/webui/views/ui.py 2013-07-23 20:54:23 +0000
70@@ -829,10 +829,17 @@
71 form = ResetPasswordForm(request.POST)
72 if form.is_valid():
73 password = form.cleaned_data['password']
74+ try:
75+ email_obj = EmailAddress.objects.get(
76+ account=account, email__iexact=atrequest.requester_email)
77+ except EmailAddress.DoesNotExist:
78+ logger.warning(
79+ 'Could not reset password, invalid email address provided '
80+ 'in the token: %s (account %s)',
81+ atrequest.requester_email, account.id)
82+ return HttpResponseRedirect('/+bad-token')
83 # since the user must have received this token url via this email,
84 # we know it's valid.
85- email_obj = EmailAddress.objects.get(
86- account=account, email=atrequest.requester_email)
87 email_obj.validate()
88 if not account.is_active and account.can_reactivate:
89 account.preferredemail = email_obj