Merge lp:~salgado/launchpad/bug-415977 into lp:launchpad

Proposed by Guilherme Salgado
Status: Merged
Approved by: Guilherme Salgado
Approved revision: no longer in the source branch.
Merged at revision: not available
Proposed branch: lp:~salgado/launchpad/bug-415977
Merge into: lp:launchpad
Diff against target: 274 lines
2 files modified
lib/canonical/launchpad/browser/tests/test_password_reset.py (+50/-11)
lib/lp/registry/stories/foaf/xx-resetpassword.txt (+0/-162)
To merge this branch: bzr merge lp:~salgado/launchpad/bug-415977
Reviewer Review Type Date Requested Status
Abel Deuring (community) code Approve
Review via email: mp+14174@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Guilherme Salgado (salgado) wrote :

= Summary =

I created this branch to fix bug 415977, but after having a look at
xx-resetpassword.txt I realized a good chunk of it (i.e. the tests of
corner cases) could be turned into view tests, so I did that.

== Proposed fix ==

== Pre-implementation notes ==

== Implementation details ==

== Tests ==

./bin/test -vvt test_password_reset

== Demo and Q/A ==

= Launchpad lint =

Checking for conflicts. and issues in doctests and templates.
Running jslint, xmllint, pyflakes, and pylint.
Using normal rules.

Linting changed files:
  lib/canonical/launchpad/browser/tests/test_password_reset.py
  lib/lp/registry/stories/foaf/xx-resetpassword.txt

Revision history for this message
Abel Deuring (adeuring) :
review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/canonical/launchpad/browser/tests/test_password_reset.py'
2--- lib/canonical/launchpad/browser/tests/test_password_reset.py 2009-10-26 15:03:24 +0000
3+++ lib/canonical/launchpad/browser/tests/test_password_reset.py 2009-10-29 15:25:21 +0000
4@@ -4,6 +4,7 @@
5 import unittest
6
7 from zope.component import getUtility
8+from zope.security.proxy import removeSecurityProxy
9
10 import transaction
11 from canonical.launchpad.browser.logintoken import ResetPasswordView
12@@ -12,43 +13,81 @@
13 from canonical.launchpad.interfaces.authtoken import LoginTokenType
14 from canonical.launchpad.interfaces.emailaddress import EmailAddressStatus
15 from canonical.launchpad.interfaces.logintoken import ILoginTokenSet
16+from canonical.launchpad.interfaces.lpstorm import IMasterObject
17 from lp.testing import TestCaseWithFactory
18 from canonical.testing import DatabaseFunctionalLayer
19
20
21 class TestPasswordReset(TestCaseWithFactory):
22 layer = DatabaseFunctionalLayer
23+ email = 'foo@example.com'
24
25- def setUp(self):
26- TestCaseWithFactory.setUp(self)
27- self.email = 'foo@example.com'
28+ def _create_inactive_person(self):
29 self.person = self.factory.makePerson(
30 email=self.email, email_address_status=EmailAddressStatus.NEW)
31 self.account = self.person.account
32+ self.assertEquals(self.account.status, AccountStatus.NOACCOUNT)
33
34 def test_inactive_accounts_are_activated(self):
35 # Resetting the password of an account in the NOACCOUNT state will
36 # activate it.
37- self.assertEquals(self.account.status, AccountStatus.NOACCOUNT)
38+ self._create_inactive_person()
39+ self._resetPassword(ensure_no_errors=True)
40+ self.assertEquals(self.account.status, AccountStatus.ACTIVE)
41+ self.assertEquals(
42+ self.account.preferredemail.email, 'foo@example.com')
43+
44+ def _create_deactivated_person(self):
45+ self.person = self.factory.makePerson(email=self.email)
46+ removeSecurityProxy(self.person.deactivateAccount('Testing'))
47+ # Get the account from the master DB to make sure it has the changes
48+ # we did above.
49+ self.account = IMasterObject(self.person.account)
50+ self.assertEquals(self.account.status, AccountStatus.DEACTIVATED)
51+
52+ def test_deactivated_accounts_are_reactivated(self):
53+ # Resetting the password of an account in the DEACTIVATED state will
54+ # reactivate it.
55+ self._create_deactivated_person()
56+ self._resetPassword(ensure_no_errors=True)
57+ self.assertEquals(self.account.status, AccountStatus.ACTIVE)
58+ self.assertEquals(
59+ self.account.preferredemail.email, 'foo@example.com')
60+
61+ def _create_suspended_person(self):
62+ self.person = self.factory.makePerson(email=self.email)
63+ # Get the account from the master DB as we're going to change it.
64+ self.account = IMasterObject(self.person.account)
65+ removeSecurityProxy(self.account).status = AccountStatus.SUSPENDED
66+ self.assertEquals(self.account.status, AccountStatus.SUSPENDED)
67+
68+ def test_suspended_accounts_cannot_reset_password(self):
69+ # It's not possible to reset the password of a SUSPENDED account.
70+ self._create_suspended_person()
71+ harness = self._resetPassword()
72+ notifications = [notification.message
73+ for notification in harness.request.notifications]
74+ self.assertIn(
75+ 'Your password cannot be reset because your account is suspended',
76+ '\n'.join(notifications))
77+ self.assertEquals(self.account.status, AccountStatus.SUSPENDED)
78+
79+ def _resetPassword(self, ensure_no_errors=False):
80 token = getUtility(ILoginTokenSet).new(
81 self.person, self.email, self.email,
82 LoginTokenType.PASSWORDRECOVERY)
83- self._completePasswordReset(token)
84- self.assertEquals(self.account.status, AccountStatus.ACTIVE)
85- self.assertEquals(
86- self.account.preferredemail.email, 'foo@example.com')
87-
88- def _completePasswordReset(self, token):
89 harness = LaunchpadFormHarness(token, ResetPasswordView)
90 form = {'field.email': self.email,
91 'field.password': 'test',
92 'field.password_dupe': 'test'}
93 harness.submit('continue', form)
94- self.assertFalse(harness.hasErrors())
95+ if ensure_no_errors:
96+ self.assertFalse(harness.hasErrors())
97 # Need to manually commit because we're interested in testing the
98 # changes done by the view on the token's requester (i.e.
99 # self.person).
100 transaction.commit()
101+ return harness
102
103
104 def test_suite():
105
106=== modified file 'lib/lp/registry/stories/foaf/xx-resetpassword.txt'
107--- lib/lp/registry/stories/foaf/xx-resetpassword.txt 2009-10-16 16:13:00 +0000
108+++ lib/lp/registry/stories/foaf/xx-resetpassword.txt 2009-10-29 15:25:21 +0000
109@@ -184,165 +184,3 @@
110 >>> print_feedback_messages(browser.contents)
111 The email address support@ubuntu.com
112 belongs to a team, and teams cannot log in to Launchpad.
113-
114-
115-== Reactivating an account ==
116-
117-Users can reactivate their account using the reset password feature.
118-
119- # Create a new person and deactivate it. This is just so that we can show
120- # that it can be reactivated.
121- >>> login(ANONYMOUS)
122- >>> person = factory.makePerson(name='person', password='foo')
123- >>> email = person.preferredemail.email
124- >>> login_person(person)
125- >>> person.deactivateAccount('Testing')
126- >>> logout()
127- >>> transaction.commit()
128- >>> deactivated_person = person
129- >>> deactivated_person.account.status
130- <DBItem AccountStatus.DEACTIVATED...
131-
132-Former User realises his life is incomplete without Launchpad. He
133-tries to login, but his password does not work anymore.
134-
135- >>> browser = setupBrowser()
136- >>> browser.open('http://launchpad.dev/')
137- >>> browser.getLink('Log in / Register').click()
138- >>> browser.getControl('E-mail address:', index=0).value = email
139- >>> browser.getControl('Password').value = 'foo'
140- >>> browser.getControl('Log In').click()
141- >>> browser.title
142- 'Log in or register with Launchpad'
143-
144- >>> print_feedback_messages(browser.contents)
145- The email address belongs to a deactivated account. Use the
146- "Forgotten your password" link to reactivate it.
147-
148-He requests a new password.
149-
150- >>> browser.getLink('Forgotten your password?').click()
151- >>> browser.title
152- 'Need a new Launchpad password?'
153-
154- >>> browser.getControl(name='email').value = (
155- ... 'former-user@canonical.com')
156- >>> set_captcha_answer(browser)
157- >>> browser.getControl('Request Reset').click()
158-
159- >>> print extract_text(find_main_content(browser.contents).p)
160- We have sent you an email with instructions to reset your password.
161-
162-He retrieves the URL form the email and opens it in his browser.
163-
164- >>> len(stub.test_emails)
165- 1
166- >>> from_addr, to_addrs, raw_msg = stub.test_emails.pop()
167- >>> link = get_token_url_from_email(raw_msg)
168- >>> to_addrs
169- ['former-user@canonical.com']
170- >>> link
171- 'http://launchpad.dev/token/...'
172-
173- >>> browser.open(link)
174- >>> browser.url
175- 'http://launchpad.dev/token/.../+resetpassword'
176-
177-Former User types his password twice to verify it and submits
178-it with the Continue button. He is happy to see his name is restored
179-in the URL.
180-
181- >>> browser.getControl(
182- ... name='field.email').value = 'former-user@canonical.com'
183- >>> browser.getControl(name='field.password').value = 'test'
184- >>> browser.getControl(name='field.password_dupe').value = 'test'
185- >>> browser.getControl('Continue').click()
186-
187- >>> browser.url
188- 'http://launchpad.dev'
189-
190- >>> print_feedback_messages(browser.contents)
191- Welcome back to Launchpad.
192- Your password has been reset successfully.
193-
194- >>> print find_tag_by_id(browser.contents, 'not-lp-user-or-team')
195- None
196-
197-
198-== Suspended accounts cannot reset their password ==
199-
200-Users with suspended accounts cannot use reset password to gain access
201-to their accounts.
202-
203- # Open up the black box and shine a light on the test's inner workings.
204- # Create a user with a SUSPENDED account.
205- >>> from canonical.launchpad.interfaces.account import AccountStatus
206-
207- # Only admins can suspend an account.
208- >>> login('foo.bar@canonical.com')
209- >>> bad_user = factory.makePerson(
210- ... email='bad-user@canonical.com',
211- ... name='bad-user',
212- ... password='invalid')
213- >>> from canonical.launchpad.interfaces import IMasterObject
214- >>> IMasterObject(bad_user.account).status = AccountStatus.SUSPENDED
215- >>> logout()
216-
217- >>> import transaction
218- >>> transaction.commit()
219-
220-Bad User has a suspended account. He discovers that his account is disabled
221-when he visits his profile page.
222-
223- >>> browser = setupBrowser()
224- >>> browser.open('http://launchpad.dev/~bad-user')
225- >>> browser.title
226- 'Bad-user does not use Launchpad'
227-
228-He believes he can reactivate his account by resetting his password. He
229-requests an email that will have the link that will let him provide a
230-new password.
231-
232- >>> browser.getLink('Log in / Register').click()
233- >>> browser.title
234- 'Log in or register with Launchpad'
235-
236- >>> browser.getLink('Forgotten your password?').click()
237- >>> browser.title
238- 'Need a new Launchpad password?'
239- >>> browser.getControl(name='email').value = (
240- ... 'bad-user@canonical.com')
241- >>> set_captcha_answer(browser)
242- >>> browser.getControl('Request Reset').click()
243-
244- >>> print extract_text(find_main_content(browser.contents).p)
245- We have sent you an email with instructions to reset your password.
246-
247-Bad User retrieves the URL from the email and opens it in his browser.
248-
249- >>> len(stub.test_emails)
250- 1
251- >>> from_addr, to_addrs, raw_msg = stub.test_emails.pop()
252- >>> link = get_token_url_from_email(raw_msg)
253- >>> to_addrs
254- ['bad-user@canonical.com']
255- >>> link
256- 'http://launchpad.dev/token/...'
257-
258- >>> browser.open(link)
259- >>> browser.url
260- 'http://launchpad.dev/token/.../+resetpassword'
261-
262-Bad User types his password twice to verify it and submits it with the
263-Continue button. He is disappointed to see that his account is still
264-suspended, and that he is instructed to contact admin.
265-
266- >>> browser.getControl(
267- ... name='field.email').value = 'bad-user@canonical.com'
268- >>> browser.getControl(name='field.password').value = 'test'
269- >>> browser.getControl(name='field.password_dupe').value = 'test'
270- >>> browser.getControl('Continue').click()
271-
272- >>> print_feedback_messages(browser.contents)
273- Your password cannot be reset because your account is suspended.
274- Contact a Launchpad admin about this issue.