Merge lp:~salgado/launchpad/remove-login-and-password-reset into lp:launchpad

Proposed by Guilherme Salgado
Status: Merged
Approved by: Francis J. Lacoste
Approved revision: not available
Merged at revision: not available
Proposed branch: lp:~salgado/launchpad/remove-login-and-password-reset
Merge into: lp:launchpad
Prerequisite: lp:~salgado/launchpad/openid-consumer
Diff against target: 2456 lines (+1/-2307)
20 files modified
lib/canonical/launchpad/browser/tests/test_registration.py (+0/-61)
lib/canonical/launchpad/doc/login-pages.txt (+0/-139)
lib/canonical/launchpad/pagetests/standalone/xx-invalid-people-cant-login.txt (+0/-56)
lib/canonical/launchpad/pagetests/standalone/xx-login-and-join-links.txt (+0/-65)
lib/canonical/launchpad/pagetests/standalone/xx-login-without-preferredemail.txt (+0/-76)
lib/canonical/launchpad/pagetests/standalone/xx-new-account-redirection-url.txt (+0/-64)
lib/canonical/launchpad/templates/launchpad-forgottenpassword.pt (+0/-91)
lib/canonical/launchpad/templates/launchpad-login.pt (+0/-237)
lib/canonical/launchpad/templates/launchpad-restrictedinfo.pt (+0/-45)
lib/canonical/launchpad/templates/launchpad-restrictedlogin.pt (+0/-119)
lib/canonical/launchpad/webapp/login.py (+1/-364)
lib/canonical/launchpad/webapp/tests/test_login.py (+0/-34)
lib/canonical/launchpad/zcml/launchpad.zcml (+0/-33)
lib/lp/registry/stories/person/xx-changepassword.txt (+0/-62)
lib/lp/registry/stories/person/xx-createaccount.txt (+0/-179)
lib/lp/registry/stories/person/xx-login.txt (+0/-85)
lib/lp/registry/stories/person/xx-reg-with-existing-email.txt (+0/-191)
lib/lp/registry/stories/person/xx-resetpassword.txt (+0/-186)
lib/lp/registry/stories/team/xx-team-restricted.txt (+0/-137)
lib/lp/services/openid/stories/xx-resetpassword-of-sso-account.txt (+0/-83)
To merge this branch: bzr merge lp:~salgado/launchpad/remove-login-and-password-reset
Reviewer Review Type Date Requested Status
Francis J. Lacoste (community) Approve
Review via email: mp+19590@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Guilherme Salgado (salgado) wrote :

Remove the views for the old login and reset password forms, together with tests that were exercising them.

Revision history for this message
Francis J. Lacoste (flacoste) wrote :

Removing >2k lines... that must have felt good!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== removed file 'lib/canonical/launchpad/browser/tests/test_registration.py'
2--- lib/canonical/launchpad/browser/tests/test_registration.py 2009-06-25 05:30:52 +0000
3+++ lib/canonical/launchpad/browser/tests/test_registration.py 1970-01-01 00:00:00 +0000
4@@ -1,61 +0,0 @@
5-# Copyright 2009 Canonical Ltd. This software is licensed under the
6-# GNU Affero General Public License version 3 (see the file LICENSE).
7-
8-import unittest
9-
10-from zope.component import getUtility
11-
12-from canonical.launchpad.browser.tests.registration import (
13- finish_registration_through_the_web, start_registration_through_the_web)
14-from canonical.launchpad.interfaces.authtoken import LoginTokenType
15-from canonical.launchpad.interfaces.logintoken import ILoginTokenSet
16-from lp.registry.interfaces.person import IPerson
17-from lp.testing import TestCaseWithFactory
18-from canonical.launchpad.testing.pages import get_feedback_messages
19-from canonical.testing import DatabaseFunctionalLayer
20-
21-
22-class TestEmailsOfPersonlessAccountsCantBeUsedToRegister(TestCaseWithFactory):
23- # If a user tries to create a LP account using an email address that
24- # is already associated with a personless account, we'll just tell them
25- # they should use their SSO credentials to login.
26- layer = DatabaseFunctionalLayer
27-
28- def setUp(self):
29- TestCaseWithFactory.setUp(self)
30- self.email = u'testX@example.com'
31- self.expected_error = (
32- 'The email address %s is already registered in the '
33- 'Launchpad Login Service (used by the Ubuntu shop and '
34- 'other OpenID sites). Please use the same email and '
35- 'password to log into Launchpad.' % self.email)
36- self.personless_account = self.factory.makeAccount(
37- 'Test account', email=self.email)
38-
39- def test_first_step(self):
40- # The +login page will prevent users from attempting to register a new
41- # account using an email address associated with an existing
42- # SSO (personless) account and instead tell them to use the SSO
43- # account's credentials to login.
44- browser = start_registration_through_the_web(self.email)
45- errors = get_feedback_messages(browser.contents)
46- self.assertIn(self.expected_error, errors)
47-
48- def test_last_step(self):
49- # Just like the +login page, the +newaccount page of a LoginToken will
50- # render an error if the email address stored on the token is
51- # associated with an existing SSO account.
52- self.assertIs(IPerson(self.personless_account, None), None)
53- # Need to manually create the token here because the check on the
54- # +login page (shown by the test above) prevents us from creating it
55- # using the web UI.
56- self.token = getUtility(ILoginTokenSet).new(
57- requester=None, requesteremail=None, email=self.email,
58- tokentype=LoginTokenType.NEWACCOUNT, redirection_url=None)
59- browser = finish_registration_through_the_web(self.token)
60- errors = get_feedback_messages(browser.contents)
61- self.assertIn(self.expected_error, errors)
62-
63-
64-def test_suite():
65- return unittest.TestLoader().loadTestsFromName(__name__)
66
67=== removed file 'lib/canonical/launchpad/doc/login-pages.txt'
68--- lib/canonical/launchpad/doc/login-pages.txt 2009-04-17 10:32:16 +0000
69+++ lib/canonical/launchpad/doc/login-pages.txt 1970-01-01 00:00:00 +0000
70@@ -1,139 +0,0 @@
71-= Login pages =
72-
73-The LoginOrRegister view is responsible for the login form and how it
74-operates within the Launchpad workflows.
75-
76- # Import some stuff we'll use througout the test.
77- >>> from zope.component import getMultiAdapter, getUtility
78- >>> from zope.security.proxy import removeSecurityProxy
79- >>> from lp.registry.interfaces.person import IPersonSet
80- >>> from canonical.launchpad.webapp.publisher import rootObject
81- >>> from canonical.launchpad.webapp.servers import LaunchpadTestRequest
82- >>> person_set = getUtility(IPersonSet)
83-
84- >>> def create_request(form):
85- ... return LaunchpadTestRequest(
86- ... SERVER_URL='http://launchpad.dev',
87- ... PATH_INFO='/+login', method='POST', form=form)
88-
89-
90-== Unregistered email addresses can't be used to login ==
91-
92-Trying to login with an email address that is not registered in Launchpad will
93-cause an error message to be displayed.
94-
95- >>> request = create_request(
96- ... form={'loginpage_email': 'non-registered@canonical.com',
97- ... 'loginpage_password': u'test',
98- ... 'loginpage_submit_login': 'Log In'})
99- >>> login_view = getMultiAdapter(
100- ... (rootObject, request), name="+login")
101- >>> login_view.process_login_form()
102- >>> login_view.login_success()
103- False
104- >>> print login_view.login_error
105- The email address and password do not match.
106-
107-
108-== Can't login if using the wrong password ==
109-
110-If a user tries to login using a password that is not the correct one for the
111-account with the given email address, an error message is displayed.
112-
113-This is what happens if you have a full-fledged account:
114-
115- >>> person = factory.makePerson()
116- >>> request = create_request(
117- ... form={'loginpage_email': person.preferredemail.email,
118- ... 'loginpage_password': person.password + 'foo',
119- ... 'loginpage_submit_login': 'Log In'})
120- >>> login_view = getMultiAdapter(
121- ... (rootObject, request), name="+login")
122- >>> login_view.process_login_form()
123- >>> login_view.login_success()
124- False
125- >>> print login_view.login_error
126- The email address and password do not match.
127-
128-Or a personless one:
129-
130- >>> account = factory.makeAccount(
131- ... 'Personless account', email='foo@example.com', password='foo')
132- >>> request = create_request(
133- ... form={'loginpage_email': 'foo@example.com',
134- ... 'loginpage_password': u'wrong password',
135- ... 'loginpage_submit_login': 'Log In'})
136- >>> login_view = getMultiAdapter(
137- ... (rootObject, request), name="+login")
138- >>> login_view.process_login_form()
139- >>> login_view.login_success()
140- False
141- >>> print login_view.login_error
142- The email address and password do not match.
143-
144-
145-== Deactivated accounts cannot login ==
146-
147-A deactivated account cannot use the LoginOrRegister view to login.
148-The login_error message explains the situation to the user. Former User
149-is an example of a user with a DEACTIVATED account.
150-
151- >>> former_user = person_set.getByEmail('former-user@canonical.com')
152- >>> former_user_account = removeSecurityProxy(former_user.account)
153- >>> former_user_account.status
154- <DBItem AccountStatus.DEACTIVATED, ...>
155-
156- # Set the person's password so that we can try logging in.
157- >>> from canonical.launchpad.interfaces.launchpad import IPasswordEncryptor
158- >>> former_user_account.password = getUtility(
159- ... IPasswordEncryptor).encrypt('foo')
160-
161- >>> request = create_request(
162- ... form={'loginpage_email': 'former-user@canonical.com',
163- ... 'loginpage_password': u'foo',
164- ... 'loginpage_submit_login': 'Log In'})
165- >>> login_view = getMultiAdapter(
166- ... (rootObject, request), name="+login")
167- >>> login_view.process_login_form()
168- >>> login_view.login_success()
169- False
170- >>> print login_view.login_error
171- The email address belongs to a deactivated account.
172- Use the "Forgotten your password" link to reactivate it.
173-
174-The user can use +resetpassword to reactivate his account.
175-See "Reactivating an account using reset password" in `logintoken-pages`.
176-
177-
178-== Suspended account cannot login ==
179-
180-A user with a suspended account cannot login. The view's login_error
181-explains the problem.
182-
183- >>> from canonical.launchpad.interfaces.account import AccountStatus
184-
185- # Only admins can suspend an account.
186- >>> login('foo.bar@canonical.com')
187- >>> bad_user = factory.makePerson(
188- ... email='bad-user@canonical.com',
189- ... name='bad-user',
190- ... password='password')
191- >>> from canonical.launchpad.interfaces import IMasterObject
192- >>> IMasterObject(bad_user.account).status = AccountStatus.SUSPENDED
193- >>> import transaction
194- >>> transaction.commit()
195-
196- >>> login(ANONYMOUS, request)
197- >>> request = create_request(
198- ... form={'loginpage_email': 'bad-user@canonical.com',
199- ... 'loginpage_password': u'password',
200- ... 'loginpage_submit_login': 'Log In'})
201- >>> login_view = getMultiAdapter(
202- ... (rootObject, request), name="+login")
203- >>> login_view.process_login_form()
204- >>> login_view.login_success()
205- False
206- >>> print login_view.login_error
207- The email address belongs to a suspended account.
208- Contact a <a href="mailto:feedback@launchpad.net?subject=SU...">Launchpad
209- admin</a> about this issue.
210
211=== removed file 'lib/canonical/launchpad/pagetests/standalone/xx-invalid-people-cant-login.txt'
212--- lib/canonical/launchpad/pagetests/standalone/xx-invalid-people-cant-login.txt 2009-07-23 17:49:31 +0000
213+++ lib/canonical/launchpad/pagetests/standalone/xx-invalid-people-cant-login.txt 1970-01-01 00:00:00 +0000
214@@ -1,56 +0,0 @@
215-We need to ensure that an invalid account, such as the result of an
216-account merge, can no longer authenticate using their existing
217-credentials. See Bug #33427
218-
219-First we log in via cookie
220-
221- >>> browser.open('http://launchpad.dev/+login')
222- >>> browser.url
223- 'http://launchpad.dev/+login'
224- >>> browser.getControl('E-mail', index=0).value = 'foo.bar@canonical.com'
225- >>> browser.getControl('Password').value = 'test'
226- >>> browser.getControl('Log In').click()
227- >>> browser.url
228- 'http://launchpad.dev'
229- >>> print extract_text(find_tag_by_id(browser.contents, 'logincontrol'))
230- Foo Bar...
231-
232-Now invalidate that person
233-
234- >>> from canonical.launchpad.interfaces import AccountStatus
235- >>> from lp.registry.model.person import Person
236- >>> foobar = Person.byName('name16')
237- >>> from canonical.launchpad.interfaces import IMasterObject
238- >>> IMasterObject(foobar.account).status = AccountStatus.DEACTIVATED
239- >>> import transaction
240- >>> transaction.commit()
241-
242-Now if we reload the front page, we should be logged out.
243-
244- >>> browser.open('http://launchpad.dev')
245- >>> browser.url
246- 'http://launchpad.dev'
247- >>> print extract_text(find_tag_by_id(browser.contents, 'logincontrol'))
248- Log in / Register
249-
250-We also can only connect via Basic auth when then user is valid
251-
252- >>> IMasterObject(foobar.account).status = AccountStatus.ACTIVE
253- >>> transaction.commit()
254-
255- >>> browser = setupBrowser(auth='Basic foo.bar@canonical.com:test')
256- >>> browser.open('http://launchpad.dev')
257- >>> browser.url
258- 'http://launchpad.dev'
259- >>> print extract_text(find_tag_by_id(browser.contents, 'logincontrol'))
260- Foo Bar...
261-
262- >>> IMasterObject(foobar.account).status = AccountStatus.SUSPENDED
263- >>> transaction.commit()
264-
265- >>> browser = setupBrowser(auth='Basic foo.bar@canonical.com:test')
266- >>> browser.open('http://launchpad.dev')
267- >>> browser.url
268- 'http://launchpad.dev'
269- >>> print extract_text(find_tag_by_id(browser.contents, 'logincontrol'))
270- Log in / Register
271
272=== removed file 'lib/canonical/launchpad/pagetests/standalone/xx-login-and-join-links.txt'
273--- lib/canonical/launchpad/pagetests/standalone/xx-login-and-join-links.txt 2008-10-12 00:44:28 +0000
274+++ lib/canonical/launchpad/pagetests/standalone/xx-login-and-join-links.txt 1970-01-01 00:00:00 +0000
275@@ -1,65 +0,0 @@
276-Check that a page in launchpad has a login link.
277-
278- >>> print http(r"""
279- ... GET /bugs HTTP/1.1
280- ... """)
281- HTTP/1.1 200 Ok
282- ...
283- ...<a href=".../bugs/+login">...
284- ...
285-
286-Check that a page in launchpad that is given query parameters has a login link
287-that preserves those parameters.
288-
289- >>> print http(r"""
290- ... GET /bugs?foo=14&bar=23 HTTP/1.1
291- ... """)
292- HTTP/1.1 200 Ok
293- ...
294- ...<a href=".../bugs/+login?bar=23&amp;foo=14">...
295- ...
296-
297-The login page preserves all the query parameters it is given
298-
299- >>> result = http(r"""
300- ... GET /bugs/+login?foo=mandrill&foo=bonobo&bar=tsimpson HTTP/1.1
301- ... """).getBody()
302- >>> '<input type="hidden" name="foo" value="mandrill" />' in result
303- True
304- >>> '<input type="hidden" name="foo" value="bonobo" />' in result
305- True
306- >>> '<input type="hidden" name="bar" value="tsimpson" />' in result
307- True
308-
309-Check that basic auth works.
310-
311- >>> print http(r"""
312- ... GET /bugs HTTP/1.1
313- ... Authorization: Basic Zm9vLmJhckBjYW5vbmljYWwuY29tOnRlc3Q=
314- ... """)
315- HTTP/1.1 200 Ok
316- ...
317- ...<input type="submit" name="logout" value="Log Out" />...
318- ...
319-
320-Check that the logout page redirects appropriately.
321-
322- >>> print http(r"""
323- ... GET /+logout HTTP/1.1
324- ... Authorization: Basic Zm9vLmJhckBjYW5vbmljYWwuY29tOnRlc3Q=
325- ... """)
326- HTTP/1.1 303 See Other
327- ...
328- Location: http://localhost/?loggingout=1
329- ...
330-
331-Check that the logout page redirects appropriately from a more interesting url.
332-
333- >>> print http(r"""
334- ... GET /firefox/+logout HTTP/1.1
335- ... Authorization: Basic Zm9vLmJhckBjYW5vbmljYWwuY29tOnRlc3Q=
336- ... """)
337- HTTP/1.1 303 See Other
338- ...
339- Location: http://localhost/firefox/?loggingout=1
340- ...
341
342=== removed file 'lib/canonical/launchpad/pagetests/standalone/xx-login-without-preferredemail.txt'
343--- lib/canonical/launchpad/pagetests/standalone/xx-login-without-preferredemail.txt 2009-09-09 23:16:08 +0000
344+++ lib/canonical/launchpad/pagetests/standalone/xx-login-without-preferredemail.txt 1970-01-01 00:00:00 +0000
345@@ -1,76 +0,0 @@
346-This test ensures that a user cannot login without a preferred email
347-address.
348-
349-First, we need to set the password to something I know
350-
351- >>> import transaction
352- >>> from canonical.launchpad.interfaces import IMasterStore
353- >>> from canonical.launchpad.database.account import AccountPassword
354- >>> IMasterStore(AccountPassword).execute("""
355- ... UPDATE AccountPassword
356- ... SET password='K7Qmeansl6RbuPfulfcmyDQOzp70OxVh5Fcf'
357- ... FROM Person, Account
358- ... WHERE name='martin-pitt' AND Person.account = Account.id
359- ... AND Account.id = AccountPassword.account
360- ... """, noresult=True)
361- >>> transaction.commit()
362-
363-'martin-pitt' doesn't have a preferred email address.
364-He can't login without first validating his email address.
365-
366- >>> browser.open('http://launchpad.dev/+login')
367- >>> browser.getControl('E-mail', index=0).value = 'martin.pitt@canonical.com'
368- >>> browser.getControl('Password').value = 'test'
369- >>> browser.getControl('Log In').click()
370- >>> print '\n' + browser.contents
371- <BLANKLINE>
372- ...
373- ...The email address 'martin.pitt@canonical.com', which you're...
374- ...trying to use to login has not yet been validated to use in Launchpad...
375- ...We sent an email to that address with instructions on how to confirm...
376- ...that it belongs to you...
377- ...
378-
379-
380- When this happens, we automatically send an email to that address so the
381- user can validate it and login.
382-
383- >>> import email, re
384- >>> from lp.services.mail import stub
385- >>> from_addr, to_addrs, raw_msg = stub.test_emails.pop()
386-
387- Check if the email was sent to the right address.
388-
389- >>> to_addrs
390- ['martin.pitt@canonical.com']
391-
392- Get the token from the email address, in order to confirm the email address
393- and then be able to login.
394-
395- >>> msg = email.message_from_string(raw_msg)
396- >>> body = msg.get_payload()
397- >>> link = re.findall(r'http.*/token/.*', body)[0]
398- >>> token = re.sub(r'.*token/', '', link)
399- >>> base_path = 'http://launchpad.dev/token/%s' % token
400-
401- >>> path = "%s/+validateemail" % base_path
402- >>> browser.open(path)
403- >>> print browser.title
404- Confirm e-mail address
405-
406- >>> browser.getControl('Continue').click()
407- >>> browser.url
408- 'http://launchpad.dev/~martin-pitt'
409- >>> print '\n' + browser.contents
410- <BLANKLINE>
411- ...informational message">Email address successfully confirmed...
412- ...
413-
414- This is the first email 'martin-pitt' is validating, it must be set as the
415- preferred one.
416-
417- >>> from canonical.launchpad.database import EmailAddressSet
418- >>> [to_addr] = to_addrs
419- >>> e = EmailAddressSet().getByEmail(to_addr)
420- >>> e.status.title
421- 'Preferred Email Address'
422
423=== removed file 'lib/canonical/launchpad/pagetests/standalone/xx-new-account-redirection-url.txt'
424--- lib/canonical/launchpad/pagetests/standalone/xx-new-account-redirection-url.txt 2009-10-16 16:13:00 +0000
425+++ lib/canonical/launchpad/pagetests/standalone/xx-new-account-redirection-url.txt 1970-01-01 00:00:00 +0000
426@@ -1,64 +0,0 @@
427-= Redirecting users after the registration process =
428-
429-We want to be nice with your users (the new ones, at least ;) and redirect
430-them to any page they were trying to access before they were asked to create
431-the new account.
432-
433-The most important case for us is when people get a file-bug URL from apport
434-on an Ubuntu system. When this happens we need to make sure they're
435-redirected to exactly the same URL they got from apport.
436-
437-Let's say Granny was playing around with Evolution (everybody's #1 MUA) and
438-it crashed. Apport then gave her the following URL (which she promptly
439-clicked): http://launchpad.dev/firefox/+filebug/121212
440-
441- >>> anon_browser.handleErrors = True
442- >>> anon_browser.open('http://launchpad.dev/firefox/+filebug/121212')
443- >>> anon_browser.url
444- 'http://launchpad.dev/firefox/+filebug/121212/+login'
445-
446- >>> anon_browser.getControl(name='redirection_url').value
447- 'http://launchpad.dev/firefox/+filebug/121212'
448-
449-
450-Similarly, if she wants to join a launchpad team, we'll try hard to redirect
451-them back to that same team, after the registration is complete.
452-
453- >>> anon_browser.open('http://launchpad.dev/~ubuntu-team/+join')
454- >>> anon_browser.url
455- 'http://launchpad.dev/%7Eubuntu-team/+join/+login'
456-
457- >>> anon_browser.getControl(name='redirection_url').value
458- 'http://launchpad.dev/%7Eubuntu-team/+join'
459-
460- # Clean up stub.test_emails to make sure other tests don't fuck us over.
461- >>> from lp.services.mail import stub
462- >>> stub.test_emails = []
463-
464-
465-If she now creates a new account, we'll certainly redirect her back to
466-whatever we had stored on the redirection_url hidden field.
467-
468- >>> from lp.testing.registration import set_captcha_answer
469- >>> anon_browser.getControl('E-mail address:', index=1).value = (
470- ... 'granny@canonical.com')
471- >>> set_captcha_answer(anon_browser, prefix='loginpage_')
472- >>> anon_browser.getControl('Register').click()
473- >>> print anon_browser.contents
474- <...
475- <h1>Registration mail sent</h1>
476- ...
477-
478- >>> from canonical.launchpad.ftests.logintoken import (
479- ... get_token_url_from_email)
480- >>> from_addr, to_addr, msg = stub.test_emails.pop()
481- >>> url = get_token_url_from_email(msg)
482- >>> anon_browser.open(url)
483-
484- >>> anon_browser.getControl('Display Name').value = 'Granny'
485- >>> anon_browser.getControl(name='field.password').value = 'foo'
486- >>> anon_browser.getControl(name='field.password_dupe').value = 'foo'
487- >>> anon_browser.getControl('Continue').click()
488-
489- >>> anon_browser.url
490- 'http://launchpad.dev/%7Eubuntu-team/+join'
491
492=== removed file 'lib/canonical/launchpad/templates/launchpad-forgottenpassword.pt'
493--- lib/canonical/launchpad/templates/launchpad-forgottenpassword.pt 2009-11-23 03:10:04 +0000
494+++ lib/canonical/launchpad/templates/launchpad-forgottenpassword.pt 1970-01-01 00:00:00 +0000
495@@ -1,91 +0,0 @@
496-<tal:root
497- xmlns:tal="http://xml.zope.org/namespaces/tal"
498- xmlns:metal="http://xml.zope.org/namespaces/metal"
499- xmlns:i18n="http://xml.zope.org/namespaces/i18n"
500- omit-tag="">
501-<tal:do-this-first tal:content="view/process_form" />
502-<html
503- metal:use-macro="view/macro:page/main_only">
504- <head metal:fill-slot="head_epilogue">
505- <meta name="robots" content="noindex" />
506- </head>
507-
508-<body>
509-
510- <div class="top-portlet" metal:fill-slot="main">
511-
512- <div>
513-
514- <h1>Need a new Launchpad password?</h1>
515-
516- <div tal:condition="not: view/success">
517-
518- <tal:block condition="view/errortext">
519- <h1>Your password reset request was unsuccessful</h1>
520- <p class="error message"
521- style="margin-top: 2em;"
522- tal:content="structure view/errortext" />
523- </tal:block>
524-
525- <p>
526- Enter your e-mail address, and we will send you instructions
527- on how to reset your password. You'll also need to answer a simple
528- question so we know that you're human.
529- </p>
530-
531- <form name="forgottenpassword" method="POST">
532- <input type="hidden"
533- tal:attributes="name view/captcha_hash;
534- value view/get_captcha_hash" />
535- <div class="field">
536- <table>
537- <tr>
538- <th>
539- <label for="email">Email address:</label>
540- </th>
541- <td>
542- <input type="text" size="50" name="email" />
543- </td>
544- </tr>
545- <tr>
546- <th>
547- <label>Random question:</label>
548- </th>
549- <td>
550- <span id="problem" tal:content="view/captcha_problem" />
551- <input type="text" size="5"
552- id="captcha_submission" value=""
553- tal:attributes="name view/captcha_submission"
554- />
555- </td>
556- </tr>
557- </table>
558- <div class="formHelp">
559- <p>
560- Enter the email address registered for your Launchpad account.
561- </p>
562- <p>
563- If you are claiming an existing account but don't know the email
564- address that was used, <a href="/feedback">contact a
565- Launchpad administrator</a>.
566- </p>
567- </div>
568- <div class="formControls">
569- <input type="submit" value="Request Reset" />
570- </div>
571- </div>
572- </form>
573- </div>
574- </div>
575-
576- <div tal:condition="view/success">
577- <br />
578- <p>We have sent you an email with instructions to reset your
579- password.</p>
580- </div>
581-
582- </div>
583-
584-</body>
585-</html>
586-</tal:root>
587
588=== removed file 'lib/canonical/launchpad/templates/launchpad-login.pt'
589--- lib/canonical/launchpad/templates/launchpad-login.pt 2009-11-18 08:09:58 +0000
590+++ lib/canonical/launchpad/templates/launchpad-login.pt 1970-01-01 00:00:00 +0000
591@@ -1,237 +0,0 @@
592-<tal:root
593- xmlns:tal="http://xml.zope.org/namespaces/tal"
594- xmlns:metal="http://xml.zope.org/namespaces/metal"
595- xmlns:i18n="http://xml.zope.org/namespaces/i18n"
596- omit-tag="">
597-<tal:do-this-first tal:content="view/process_form" />
598-<html
599- metal:use-macro="view/macro:page/locationless"
600->
601- <head metal:fill-slot="head_epilogue">
602- <meta name="robots" content="noindex" />
603- </head>
604- <body>
605- <tal:comment condition="nothing">
606- This page handles cases in this order:
607- 1. already logged in and shouldn't be here
608- 2. just logged in successfully
609- 3. just applied for registration successfully
610- 4. might want to log in
611- 4a. didn't try to log in last time
612- 4b. just tried to log in and failed
613- 5. might want to register
614- 5a. didn't try to register last time
615- 5b. just tried to register and failed.
616- The usual case is 4 + 5.
617-
618- See also launchpad-restictedlogin.pt, which also uses
619- these numbers in comments.
620- </tal:comment>
621- <div class="top-portlet" metal:fill-slot="main">
622-
623- <!-- 1. already logged in and shouldn't be here: -->
624- <tal:block condition="request/lp:person">
625-
626- <!-- 2. just logged in successfully: -->
627- <p tal:condition="view/login_success">
628- You are now logged in as <strong
629- tal:content="request/lp:person/displayname">Person Name</strong>.
630- </p>
631-
632- <tal:block condition="not: view/login_success">
633- <h1>You&#8217;re already logged in</h1>
634- <p>
635- You are already logged in as
636- <strong tal:content="request/lp:person/displayname">person
637- name</strong>. If this is not you, please
638- <a href="/+logout">log out now</a>.
639- </p>
640- </tal:block>
641- </tal:block>
642-
643- <tal:block condition="not: request/lp:person">
644-
645- <tal:block condition="not: view/login_success">
646-
647- <!-- 3. just registered successfully: -->
648- <tal:registered condition="view/registration_success">
649- <h1>Registration mail sent</h1>
650- <p id="address">
651- Instructions on completing your registration have been sent to
652- <strong><code tal:content="view/email"
653- >email@domain.com</code></strong>.
654- </p>
655- <p>
656- If you don&rsquo;t receive the instructions within a few
657- minutes, it might be because:
658- </p>
659- <ul>
660- <li>
661- <p>
662- Your mail provider uses &ldquo;greylisting&rdquo; to reduce
663- spam. If so, you&rsquo;ll need to wait an hour or two
664- for the message to arrive.
665- </p>
666- </li>
667- <li>
668- <p>
669- Your mail provider mistakenly blocks messages from Launchpad.
670- Try signing up using a service like
671- <a href="http://gmail.com/">Gmail</a> or
672- <a href="http://mail.yahoo.com/">Yahoo Mail</a>.
673- If that works, complain to your usual mail provider.
674- </p>
675- </li>
676- <li>
677- <p>
678- If neither of those work, Launchpad might be broken somehow.
679- Let us know by sending a message to
680- <a href="mailto:feedback@launchpad.net"
681- >feedback@launchpad.net</a>.
682- </p>
683- </li>
684- </ul>
685- </tal:registered>
686-
687- <tal:block condition="not: view/registration_success">
688- <!-- 4a. didn't try to log in last time: -->
689- <h1 tal:condition="not: view/login_error">
690- Already registered?
691- </h1>
692- <!-- 4b. just tried to log in and failed: -->
693- <div tal:condition="view/login_error">
694- <h1>Your login was unsuccessful</h1>
695- <p class="error message" tal:content="structure view/login_error" />
696- </div>
697- <!-- 4. might want to log in: -->
698- <p>
699- To log in, enter your e-mail address
700- and Launchpad password.
701- </p>
702- <form name="login" method="POST">
703- <table>
704- <tr>
705- <th>
706- <label for="email">E-mail address:</label>
707- </th>
708- <td>
709- <input type="text"
710- name="loginpage_email"
711- id="email"
712- size="30"
713- tal:attributes="value request/email | nothing"
714- />
715- <script>
716- <!--
717- document.forms['login'].loginpage_email.focus();
718- //-->
719- </script>
720- </td>
721- </tr>
722- <tr>
723- <th><label for="password">Password:</label></th>
724- <td>
725- <input
726- type="password"
727- id="password"
728- name="loginpage_password"
729- />
730- </td>
731- </tr>
732- <tr>
733- <th></th>
734- <td>
735- <div>
736- <input
737- type="submit"
738- name="loginpage_submit_login"
739- value="Log In" />
740- [<a href="/+forgottenpassword">Forgotten your password?</a>]
741- </div>
742- </td>
743- </tr>
744- </table>
745- <!-- Preserve extra query parameters as hidden fields. -->
746- <tal:replacement tal:replace="structure view/preserve_query" />
747- </form>
748- <p><strong>Note:</strong> You must enable cookies in your browser
749- to log into or register for Launchpad.</p>
750- <!-- 5a. didn't try to register last time: -->
751- <h1 tal:condition="not: view/registration_error">
752- Not registered yet?
753- </h1>
754- <!-- 5b. just tried to register and failed: -->
755- <tal:block condition="view/registration_error">
756- <h1>Your registration was unsuccessful</h1>
757- <p class="error message"
758- style="margin-top: 2em;"
759- tal:content="structure view/registration_error" />
760- </tal:block>
761- <!-- 5. might want to register: -->
762- <p>
763- Creating your Launchpad account is easy. Here's what to do:</p>
764-
765- <ol class="subordinate">
766- <li>Make sure cookies are enabled in your browser.</li>
767- <li>Enter your e-mail address and answer our random question
768- so we know that you're human.
769- </li>
770- <li>Follow the URL in the confirmation e-mail that Launchpad sends and you're done!</li>
771- </ol>
772-
773-
774-
775- <form name="join" method="POST">
776- <input type="hidden" name="redirection_url"
777- tal:condition="not: request/form/redirection_url|nothing"
778- tal:attributes="value view/getRedirectionURL" />
779- <input type="hidden"
780- tal:attributes="name view/captcha_hash;
781- value view/get_captcha_hash" />
782- <table>
783- <tr>
784- <th>
785- <label for="registeremail">E-mail address:</label>
786- </th>
787- <td>
788- <input type="text" size="30" name="loginpage_email"
789- id="registeremail"
790- tal:attributes="value view/email|nothing"
791- />
792- </td>
793- </tr>
794- <tr>
795- <th>
796- <label>Random question:</label>
797- </th>
798- <td>
799- <span id="problem" tal:content="view/captcha_problem" />
800- <input type="text" size="5"
801- id="captcha_submission" value=""
802- tal:attributes="name view/captcha_submission"
803- />
804- </td>
805- </tr>
806- <tr>
807- <th></th>
808- <td>
809- <input
810- type="submit"
811- name="loginpage_submit_registration"
812- value="Register" />
813- </td>
814- </tr>
815- </table>
816- <p><strong>Note:</strong> We will never disclose, share or sell your
817- personal information, nor send you e-mail not
818- directly related to Launchpad.</p>
819- <!-- Preserve extra query parameters as hidden fields. -->
820- <tal:replacement tal:replace="structure view/preserve_query" />
821- </form>
822- </tal:block>
823- </tal:block>
824- </tal:block>
825- </div>
826- </body>
827-</html>
828-</tal:root>
829
830=== removed file 'lib/canonical/launchpad/templates/launchpad-restrictedinfo.pt'
831--- lib/canonical/launchpad/templates/launchpad-restrictedinfo.pt 2009-10-13 20:59:13 +0000
832+++ lib/canonical/launchpad/templates/launchpad-restrictedinfo.pt 1970-01-01 00:00:00 +0000
833@@ -1,45 +0,0 @@
834-<html
835- xmlns="http://www.w3.org/1999/xhtml"
836- xmlns:tal="http://xml.zope.org/namespaces/tal"
837- xmlns:metal="http://xml.zope.org/namespaces/metal"
838- xmlns:i18n="http://xml.zope.org/namespaces/i18n"
839- xml:lang="en"
840- lang="en"
841- dir="ltr"
842- i18n:domain="launchpad"
843->
844- <head>
845- <title>Launchpad</title>
846- <style type="text/css">
847- html {
848- font-family: bitstream vera sans, dejavu sans, verdana, sans-serif;
849- font-size: 8pt;
850- }
851- h1 {background: none; color: #83ad23; font-size: 3em; font-weight: normal;}
852- </style>
853- </head>
854- <body>
855- <tal:block condition="view/isTeamRestrictedServer"
856- define="non_restricted_url request/form/production|nothing">
857- <h1>Restricted Launchpad test site</h1>
858-
859- <p>This site is accessible by launchpad admins and members of the
860- <a tal:attributes="href view/getAllowedTeamURL"
861- tal:content="view/getAllowedTeamDescription" />
862- team only.
863- </p>
864- <p tal:condition="non_restricted_url">You may be able to
865- access this page
866- <a tal:attributes="href non_restricted_url">on the main
867- Launchpad site</a> instead.</p>
868-
869- <p tal:condition="not:non_restricted_url">You may use
870- <a href="https://launchpad.net/">the main Launchpad site</a> instead.</p>
871-
872- <p>You can <a href="/+logout">log out</a>.</p>
873- </tal:block>
874- <tal:block condition="not: view/isTeamRestrictedServer">
875- <p>This server is open to anonymous access and registered users.</p>
876- </tal:block>
877- </body>
878-</html>
879
880=== removed file 'lib/canonical/launchpad/templates/launchpad-restrictedlogin.pt'
881--- lib/canonical/launchpad/templates/launchpad-restrictedlogin.pt 2009-10-13 20:59:13 +0000
882+++ lib/canonical/launchpad/templates/launchpad-restrictedlogin.pt 1970-01-01 00:00:00 +0000
883@@ -1,119 +0,0 @@
884-<html
885- xmlns="http://www.w3.org/1999/xhtml"
886- xmlns:tal="http://xml.zope.org/namespaces/tal"
887- xmlns:metal="http://xml.zope.org/namespaces/metal"
888- xmlns:i18n="http://xml.zope.org/namespaces/i18n"
889- xml:lang="en"
890- lang="en"
891- dir="ltr"
892- i18n:domain="launchpad"
893->
894-<tal:do-this-first tal:content="view/process_restricted_form" />
895- <head>
896- <title>Log in to Launchpad</title>
897- <style type="text/css">
898- html {
899- font-family: bitstream vera sans, dejavu sans, verdana, sans-serif;
900- font-size: 8pt;
901- }
902- h1 {background: none; color: #83ad23; font-size: 3em; font-weight: normal;}
903- th {font-weight: normal; text-align: right;}
904- .oopsid {color: #cc0000; font-weight: bold;}
905- </style>
906- </head>
907- <body>
908- <h1>Log in to a Launchpad test site</h1>
909-
910- <tal:comment condition="nothing">
911- See launchpad-login.pt for an explanation of the numbered comments.
912- </tal:comment>
913-
914- <!-- 1. already logged in and shouldn't be here: -->
915- <tal:block condition="request/lp:person">
916-
917- <!-- 2. just logged in successfully: -->
918- <p tal:condition="view/login_success">
919- You are now logged in as <strong
920- tal:content="request/lp:person/displayname">Person Name</strong>.
921- </p>
922-
923- <tal:block condition="not: view/login_success">
924- <h1>You&#8217;re already logged in</h1>
925- <p>
926- You are already logged in as
927- <strong tal:content="request/lp:person/displayname">person
928- name</strong>. If this is not you, please
929- <a href="/+logout">log out now</a>.
930- </p>
931- </tal:block>
932- </tal:block>
933-
934- <tal:block condition="not: request/lp:person">
935-
936- <h1 tal:condition="not: view/login_error">
937- Already registered?
938- </h1>
939- <!-- 4b. just tried to log in and failed: -->
940- <div tal:condition="view/login_error">
941- <h1>Your login was unsuccessful</h1>
942- <p class="error message" tal:content="view/login_error" />
943- </div>
944- <!-- 4. might want to log in: -->
945- <p>
946- To log in, enter your e-mail address
947- and Launchpad password.
948- </p>
949- <form name="login" method="POST">
950- <table>
951- <tr>
952- <th>
953- <label for="email">E-mail address:</label>
954- </th>
955- <td>
956- <input type="text"
957- name="loginpage_email"
958- id="email"
959- size="30"
960- tal:attributes="value request/email | nothing"
961- />
962- <script>
963- <!--
964- document.forms['login'].loginpage_email.focus();
965- //-->
966- </script>
967- </td>
968- </tr>
969- <tr>
970- <th><label for="password">Password:</label></th>
971- <td>
972- <input
973- type="password"
974- id="password"
975- name="loginpage_password"
976- />
977- </td>
978- </tr>
979- <tr>
980- <th></th>
981- <td>
982- <div>
983- <input
984- type="submit"
985- name="loginpage_submit_login"
986- value="Log In" />
987- </div>
988- </td>
989- </tr>
990- </table>
991- </form>
992-
993- <p tal:define="non_restricted_url request/form/production|nothing"
994- tal:condition="non_restricted_url">
995- You may be able to access this page
996- <a tal:attributes="href non_restricted_url">on the main
997- Launchpad site</a> instead.
998- </p>
999- </tal:block>
1000- </body>
1001-</html>
1002-
1003
1004=== modified file 'lib/canonical/launchpad/webapp/login.py'
1005--- lib/canonical/launchpad/webapp/login.py 2010-02-18 11:01:16 +0000
1006+++ lib/canonical/launchpad/webapp/login.py 2010-02-18 11:01:19 +0000
1007@@ -5,9 +5,6 @@
1008
1009 __metaclass__ = type
1010
1011-import cgi
1012-import hashlib
1013-import random
1014 import urllib
1015
1016 from datetime import datetime, timedelta
1017@@ -34,16 +31,9 @@
1018 from canonical.config import config
1019 from canonical.launchpad import _
1020 from canonical.launchpad.interfaces.account import AccountStatus, IAccountSet
1021-from canonical.launchpad.interfaces.authtoken import (
1022- IAuthTokenSet, LoginTokenType)
1023-from canonical.launchpad.interfaces.emailaddress import IEmailAddressSet
1024-from canonical.launchpad.interfaces.logintoken import ILoginTokenSet
1025 from canonical.launchpad.interfaces.openidconsumer import IOpenIDConsumerStore
1026-from lp.registry.interfaces.person import (
1027- IPerson, IPersonSet, PersonCreationRationale)
1028-from canonical.launchpad.interfaces.validation import valid_password
1029+from lp.registry.interfaces.person import IPerson, PersonCreationRationale
1030 from canonical.launchpad.readonly import is_read_only
1031-from canonical.launchpad.validators.email import valid_email
1032 from canonical.launchpad.webapp.error import SystemErrorView
1033 from canonical.launchpad.webapp.interfaces import (
1034 CookieAuthLoggedInEvent, ILaunchpadApplication, ILaunchpadPrincipal,
1035@@ -164,60 +154,6 @@
1036 name='+basiclogin')
1037
1038
1039-class RestrictedLoginInfo:
1040- """On a team-restricted launchpad server, show who may access the server.
1041-
1042- Otherwise, show that this is an unrestricted server.
1043- """
1044-
1045- def isTeamRestrictedServer(self):
1046- return bool(config.launchpad.restrict_to_team)
1047-
1048- def getAllowedTeamURL(self):
1049- return 'https://launchpad.net/people/%s' % (
1050- config.launchpad.restrict_to_team)
1051-
1052- def getAllowedTeamDescription(self):
1053- return getUtility(IPersonSet).getByName(
1054- config.launchpad.restrict_to_team).title
1055-
1056-class CaptchaMixin:
1057- """Mixin class to provide simple captcha capabilities."""
1058- def validateCaptcha(self):
1059- """Validate the submitted captcha value matches what we expect."""
1060- expected = self.request.form.get(self.captcha_hash)
1061- submitted = self.request.form.get(self.captcha_submission)
1062- if expected is not None and submitted is not None:
1063- return hashlib.md5(submitted).hexdigest() == expected
1064- return False
1065-
1066- @cachedproperty
1067- def captcha_answer(self):
1068- """Get the answer for the current captcha challenge.
1069-
1070- With each failed attempt a new challenge will be given. Our answer
1071- space is acknowledged to be ridiculously small but is chosen in the
1072- interest of ease-of-use. We're not trying to create an iron-clad
1073- challenge but only a minimal obstacle to dumb bots.
1074- """
1075- return random.randint(10, 20)
1076-
1077- @property
1078- def get_captcha_hash(self):
1079- """Get the captcha hash.
1080-
1081- The hash is the value we put in the form for later comparison.
1082- """
1083- return hashlib.md5(str(self.captcha_answer)).hexdigest()
1084-
1085- @property
1086- def captcha_problem(self):
1087- """Create the captcha challenge."""
1088- op1 = random.randint(1, self.captcha_answer - 1)
1089- op2 = self.captcha_answer - op1
1090- return '%d + %d =' % (op1, op2)
1091-
1092-
1093 # The Python OpenID package uses pycurl by default, but pycurl chokes on
1094 # self-signed certificates (like the ones we use when developing), so we
1095 # change the default to urllib2 here. That's also a good thing because it
1096@@ -372,252 +308,6 @@
1097 template = ViewPageTemplateFile("../templates/login-already.pt")
1098
1099
1100-class LoginOrRegister(CaptchaMixin):
1101- """Merges the former CookieLoginPage and JoinLaunchpadView classes
1102- to allow the two forms to appear on a single page.
1103-
1104- This page is a self-posting form. Actually, there are two forms
1105- on the page. The first time this page is loaded, when there's been
1106- no POST of its form, we want to preserve any query parameters, and put
1107- them into hidden inputs.
1108- """
1109-
1110- # Names used in the template's HTML form.
1111- form_prefix = 'loginpage_'
1112- submit_login = form_prefix + 'submit_login'
1113- submit_registration = form_prefix + 'submit_registration'
1114- input_email = form_prefix + 'email'
1115- input_password = form_prefix + 'password'
1116- captcha_submission = form_prefix + 'captcha_submission'
1117- captcha_hash = form_prefix + 'captcha_hash'
1118-
1119- # Instance variables that represent the state of the form.
1120- login_error = None
1121- registration_error = None
1122- submitted = False
1123- email = None
1124-
1125- def process_restricted_form(self):
1126- """Entry-point for the team-restricted login page.
1127-
1128- If we're not running in team-restricted mode, then redirect to a
1129- regular login page. Otherwise, process_form as usual.
1130- """
1131- if config.launchpad.restrict_to_team:
1132- self.process_form()
1133- else:
1134- self.request.response.redirect('/+login',
1135- temporary_if_possible=True)
1136-
1137- def process_form(self):
1138- """Determines whether this is the login form or the register
1139- form, and delegates to the appropriate function.
1140- """
1141- if self.request.method != "POST":
1142- return
1143-
1144- self.submitted = True
1145- if self.request.form.get(self.submit_login):
1146- self.process_login_form()
1147- elif self.request.form.get(self.submit_registration):
1148- self.process_registration_form()
1149-
1150- def getRedirectionURL(self):
1151- """Return the URL we should redirect the user to, after finishing a
1152- registration or password reset process.
1153-
1154- IOW, the current URL without the "/+login" bit.
1155- """
1156- return self.request.getURL(1)
1157-
1158- def process_login_form(self):
1159- """Process the form data.
1160-
1161- If there is an error, assign a string containing a description
1162- of the error to self.login_error for presentation to the user.
1163- """
1164- email = self.request.form.get(self.input_email)
1165- password = self.request.form.get(self.input_password)
1166- if not email or not password:
1167- self.login_error = _("Enter your email address and password.")
1168- return
1169-
1170- # XXX matsubara 2006-05-08 bug=43675: This class should inherit from
1171- # LaunchpadFormView, that way we could take advantage of Zope's widget
1172- # validation, instead of checking manually for password validity.
1173- if not valid_password(password):
1174- self.login_error = _(
1175- "The password provided contains non-ASCII characters.")
1176- return
1177-
1178- loginsource = getUtility(IPlacelessLoginSource)
1179- principal = loginsource.getPrincipalByLogin(email)
1180- if principal is None or not principal.validate(password):
1181- self.login_error = "The email address and password do not match."
1182- elif principal.person is None:
1183- logInPrincipalAndMaybeCreatePerson(self.request, principal, email)
1184- self.redirectMinusLogin()
1185- elif principal.person.account_status == AccountStatus.DEACTIVATED:
1186- self.login_error = _(
1187- 'The email address belongs to a deactivated account. '
1188- 'Use the "Forgotten your password" link to reactivate it.')
1189- elif principal.person.account_status == AccountStatus.SUSPENDED:
1190- email_link = (
1191- 'mailto:feedback@launchpad.net?subject=SUSPENDED%20account')
1192- self.login_error = _(
1193- 'The email address belongs to a suspended account. '
1194- 'Contact a <a href="%s">Launchpad admin</a> '
1195- 'about this issue.' % email_link)
1196- else:
1197- person = getUtility(IPersonSet).getByEmail(email)
1198- if person.preferredemail is None:
1199- self.login_error = _(
1200- "The email address '%s', which you're trying to use to "
1201- "login has not yet been validated to use in Launchpad. "
1202- "We sent an email to that address with instructions on "
1203- "how to confirm that it belongs to you. As soon as we "
1204- "have that confirmation you'll be able to log into "
1205- "Launchpad."
1206- ) % email
1207- token = getUtility(ILoginTokenSet).new(
1208- person, email, email, LoginTokenType.VALIDATEEMAIL)
1209- token.sendEmailValidationRequest()
1210- return
1211- if person.is_valid_person:
1212- logInPrincipal(self.request, principal, email)
1213- self.redirectMinusLogin()
1214- else:
1215- # Normally invalid accounts will have a NULL password
1216- # so this will be rarely seen, if ever. An account with no
1217- # valid email addresses might end up in this situation,
1218- # such as having them flagged as OLD by a email bounce
1219- # processor or manual changes by the DBA.
1220- self.login_error = "This account cannot be used."
1221-
1222- def process_registration_form(self):
1223- """A user has asked to join launchpad.
1224-
1225- Check if everything is ok with the email address and send an email
1226- with a link to the user complete the registration process.
1227- """
1228- request = self.request
1229- # For some reason, redirection_url can sometimes be a list, and
1230- # sometimes a string. See OOPS-68D508, where redirection_url has
1231- # the following value:
1232- # [u'https://launchpad.net/.../it/+translate/+login', u'']
1233- redirection_url = request.form.get('redirection_url')
1234- if isinstance(redirection_url, list):
1235- # Remove blank entries.
1236- redirection_url_list = [url for url in redirection_url if url]
1237- # XXX Guilherme Salgado 2006-09-27:
1238- # Shouldn't this be an UnexpectedFormData?
1239- assert len(redirection_url_list) == 1, redirection_url_list
1240- redirection_url = redirection_url_list[0]
1241-
1242- self.email = request.form.get(self.input_email).strip()
1243-
1244- if not valid_email(self.email):
1245- self.registration_error = (
1246- "The email address you provided isn't valid. "
1247- "Please verify it and try again.")
1248- return
1249-
1250- # Validate the user is human, more or less.
1251- if not self.validateCaptcha():
1252- self.registration_error = (
1253- "The answer to the simple math question was incorrect "
1254- "or missing. Please try again.")
1255- return
1256-
1257- registered_email = getUtility(IEmailAddressSet).getByEmail(self.email)
1258- if registered_email is not None:
1259- person = registered_email.person
1260- if person is not None:
1261- if person.is_valid_person:
1262- self.registration_error = (
1263- "Sorry, someone with the address %s already has a "
1264- "Launchpad account. If this is you and you've "
1265- "forgotten your password, Launchpad can "
1266- '<a href="/+forgottenpassword">reset it for you.</a>'
1267- % cgi.escape(self.email))
1268- return
1269- else:
1270- # This is an unvalidated profile; let's move on with the
1271- # registration process as if we had never seen it.
1272- pass
1273- else:
1274- account = registered_email.account
1275- assert IPerson(account, None) is None, (
1276- "This email address should be linked to the person who "
1277- "owns it.")
1278- self.registration_error = (
1279- 'The email address %s is already registered in the '
1280- 'Launchpad Login Service (used by the Ubuntu shop and '
1281- 'other OpenID sites). Please use the same email and '
1282- 'password to log into Launchpad.'
1283- % cgi.escape(self.email))
1284- return
1285-
1286- logintokenset = getUtility(ILoginTokenSet)
1287- token = logintokenset.new(
1288- requester=None, requesteremail=None, email=self.email,
1289- tokentype=LoginTokenType.NEWACCOUNT,
1290- redirection_url=redirection_url)
1291- token.sendNewUserEmail()
1292-
1293- def login_success(self):
1294- return (self.submitted and
1295- self.request.form.get(self.submit_login) and
1296- not self.login_error)
1297-
1298- def registration_success(self):
1299- return (self.submitted and
1300- self.request.form.get(self.submit_registration) and
1301- not self.registration_error)
1302-
1303- def redirectMinusLogin(self):
1304- """Redirect to the URL with the '/+login' removed from the end.
1305-
1306- Also, take into account the preserved query from the URL.
1307- """
1308- target = self.request.URL[-1]
1309- query_string = urllib.urlencode(
1310- list(self.iter_form_items()), doseq=True)
1311- if query_string:
1312- target = '%s?%s' % (target, query_string)
1313- self.request.response.redirect(target, temporary_if_possible=True)
1314-
1315- def iter_form_items(self):
1316- """Iterate over keys and single values, excluding stuff we don't
1317- want such as '-C' and things starting with self.form_prefix.
1318- """
1319- for name, value in self.request.form.items():
1320- # XXX SteveAlexander 2005-04-11: Exclude '-C' because this is
1321- # left in from sys.argv in Zope3 using python's
1322- # cgi.FieldStorage to process requests.
1323- if name == '-C' or name == 'loggingout':
1324- continue
1325- if name.startswith(self.form_prefix):
1326- continue
1327- if isinstance(value, list):
1328- value_list = value
1329- else:
1330- value_list = [value]
1331- for value_list_item in value_list:
1332- yield (name, value_list_item)
1333-
1334- def preserve_query(self):
1335- """Return zero or more hidden inputs that preserve the URL's query."""
1336- L = []
1337- html = u'<input type="hidden" name="%s" value="%s" />'
1338- for name, value in self.iter_form_items():
1339- # Thanks to apport (https://launchpad.net/bugs/61171), we need to
1340- # do this here.
1341- value = UnicodeDammit(value).markup
1342- L.append(html % (name, cgi.escape(value, quote=True)))
1343- return '\n'.join(L)
1344-
1345-
1346 def logInPrincipal(request, principal, email):
1347 """Log the principal in. Password validation must be done in callsites."""
1348 session = ISession(request)
1349@@ -726,59 +416,6 @@
1350 return ''
1351
1352
1353-class ForgottenPasswordPage(CaptchaMixin):
1354-
1355- errortext = None
1356- submitted = False
1357- captcha_submission = 'captcha_submission'
1358- captcha_hash = 'captcha_hash'
1359- page_title = 'Need a new Launchpad password?'
1360-
1361- def process_form(self):
1362- request = self.request
1363- if request.method != "POST":
1364- return
1365-
1366- # Validate the user is human, more or less.
1367- if not self.validateCaptcha():
1368- self.errortext = (
1369- "The answer to the simple math question was incorrect "
1370- "or missing. Please try again.")
1371- return
1372-
1373- email = request.form.get("email").strip()
1374- email_address = getUtility(IEmailAddressSet).getByEmail(email)
1375- if email_address is None:
1376- self.errortext = ("Your account details have not been found. "
1377- "Please check your subscription email "
1378- "address and try again.")
1379- return
1380-
1381- person = email_address.person
1382- if person is not None and person.isTeam():
1383- self.errortext = ("The email address <strong>%s</strong> "
1384- "belongs to a team, and teams cannot log in to "
1385- "Launchpad." % email)
1386- return
1387-
1388- if person is None:
1389- account = email_address.account
1390- redirection_url = urlappend(
1391- self.request.getApplicationURL(), '+login')
1392- token = getUtility(IAuthTokenSet).new(
1393- account, email, email, LoginTokenType.PASSWORDRECOVERY,
1394- redirection_url=redirection_url)
1395- else:
1396- token = getUtility(ILoginTokenSet).new(
1397- person, email, email, LoginTokenType.PASSWORDRECOVERY)
1398- token.sendPasswordResetEmail()
1399- self.submitted = True
1400- return
1401-
1402- def success(self):
1403- return self.submitted and not self.errortext
1404-
1405-
1406 class FeedsUnauthorizedView(UnauthorizedView):
1407 """All users of feeds are anonymous, so don't redirect to login."""
1408
1409
1410=== removed file 'lib/canonical/launchpad/webapp/tests/test_login.py'
1411--- lib/canonical/launchpad/webapp/tests/test_login.py 2009-07-31 19:56:48 +0000
1412+++ lib/canonical/launchpad/webapp/tests/test_login.py 1970-01-01 00:00:00 +0000
1413@@ -1,34 +0,0 @@
1414-# Copyright 2009 Canonical Ltd. This software is licensed under the
1415-# GNU Affero General Public License version 3 (see the file LICENSE).
1416-
1417-__metaclass__ = type
1418-
1419-import unittest
1420-
1421-from canonical.launchpad.ftests import logout
1422-from canonical.launchpad.testing.pages import setupBrowser
1423-from canonical.testing import DatabaseFunctionalLayer
1424-
1425-from lp.testing import TestCase
1426-
1427-
1428-class TestLoginOrRegister_preserve_query(TestCase):
1429- layer = DatabaseFunctionalLayer
1430-
1431- def test_non_ascii_characters_in_query_string_ascii_encoded(self):
1432- # Apport can construct ASCII-encoded URLs containing non-ASCII
1433- # characters (in the query string), where they send users to so that
1434- # they can finish reporting bugs. Apport shouldn't do that but we
1435- # can't OOPS when it does, so we use UnicodeDammit to figure out its
1436- # original encoding and decode it back. If UnicodeDammit can't figure
1437- # out the correct encoding, we replace those non-ASCII characters. For
1438- # more details, see https://launchpad.net/bugs/61171.
1439- logout()
1440- browser = setupBrowser()
1441- browser.open('http://launchpad.dev/+login?foo=subproc%E9s')
1442- self.assertEquals(
1443- 'subproc\xc3\xa9s', browser.getControl(name='foo', index=0).value)
1444-
1445-
1446-def test_suite():
1447- return unittest.TestLoader().loadTestsFromName(__name__)
1448
1449=== modified file 'lib/canonical/launchpad/zcml/launchpad.zcml'
1450--- lib/canonical/launchpad/zcml/launchpad.zcml 2010-02-18 11:01:16 +0000
1451+++ lib/canonical/launchpad/zcml/launchpad.zcml 2010-02-18 11:01:19 +0000
1452@@ -212,22 +212,6 @@
1453 />
1454
1455 <browser:page
1456- for="canonical.launchpad.webapp.interfaces.ILaunchpadApplication"
1457- template="../templates/launchpad-login.pt"
1458- class="canonical.launchpad.webapp.login.LoginOrRegister"
1459- permission="zope.Public"
1460- name="+login-old"
1461- />
1462-
1463- <browser:page
1464- for="canonical.launchpad.webapp.interfaces.ILaunchpadApplication"
1465- template="../templates/launchpad-forgottenpassword.pt"
1466- class="canonical.launchpad.webapp.login.ForgottenPasswordPage"
1467- permission="zope.Public"
1468- name="+forgottenpassword"
1469- />
1470-
1471- <browser:page
1472 for="*"
1473 attribute="logout"
1474 class="canonical.launchpad.webapp.login.CookieLogoutPage"
1475@@ -243,23 +227,6 @@
1476 permission="zope.Public"
1477 />
1478
1479- <!-- Special login and information pages for a site restricted so that just
1480- one team given in launchpad.conf may access it. -->
1481- <browser:page
1482- for="canonical.launchpad.interfaces.ILaunchpadRoot"
1483- name="+restricted-login"
1484- template="../templates/launchpad-restrictedlogin.pt"
1485- permission="zope.Public"
1486- class="canonical.launchpad.webapp.login.LoginOrRegister"
1487- />
1488- <browser:page
1489- for="canonical.launchpad.interfaces.ILaunchpadRoot"
1490- name="+restricted-info"
1491- template="../templates/launchpad-restrictedinfo.pt"
1492- permission="zope.Public"
1493- class="canonical.launchpad.webapp.login.RestrictedLoginInfo"
1494- />
1495-
1496 <!-- Other non-application-specific pages. -->
1497
1498 <browser:page
1499
1500=== removed file 'lib/lp/registry/stories/person/xx-changepassword.txt'
1501--- lib/lp/registry/stories/person/xx-changepassword.txt 2009-09-22 20:56:12 +0000
1502+++ lib/lp/registry/stories/person/xx-changepassword.txt 1970-01-01 00:00:00 +0000
1503@@ -1,62 +0,0 @@
1504-==================
1505-Changing passwords
1506-==================
1507-
1508-No Privileges Person wants to change his password.
1509-
1510- >>> user_browser.open('http://launchpad.dev/~no-priv/+changepassword')
1511-
1512-He accidentally uses an illegal character in the new password.
1513-
1514- >>> user_browser.getControl('Current password').value = 'test'
1515- >>> user_browser.getControl('New password').value = 'tésté'
1516- >>> user_browser.getControl(name='field.password_dupe').value = 'tésté'
1517- >>> user_browser.getControl('Change Password').click()
1518- >>> print_feedback_messages(user_browser.contents)
1519- There is 1 error.
1520- The password provided contains non-ASCII characters.
1521-
1522-No Priv tries again, but this time he enters the wrong password!
1523-
1524- >>> user_browser.getControl('Current password').value = 'wrong'
1525- >>> user_browser.getControl('New password').value = 'test'
1526- >>> user_browser.getControl(name='field.password_dupe').value = 'test'
1527- >>> user_browser.getControl('Change Password').click()
1528- >>> print_feedback_messages(user_browser.contents)
1529- There is 1 error.
1530- The provided password doesn't match your current password.
1531-
1532-He uses an illegal character in his current password, which is also the wrong
1533-password.
1534-
1535- >>> user_browser.getControl('Current password').value = 'ééé'
1536- >>> user_browser.getControl('New password').value = 'test'
1537- >>> user_browser.getControl(name='field.password_dupe').value = 'test'
1538- >>> user_browser.getControl('Change Password').click()
1539- >>> print_feedback_messages(user_browser.contents)
1540- There is 1 error.
1541- The provided password doesn't match your current password.
1542-
1543-And finally he uses a legal password, containing only ascii characters.
1544-
1545- >>> user_browser.getControl('Current password').value = 'test'
1546- >>> user_browser.getControl('New password').value = 'abc'
1547- >>> user_browser.getControl(name='field.password_dupe').value = 'abc'
1548- >>> user_browser.getControl('Change Password').click()
1549- >>> print user_browser.url
1550- http://launchpad.dev/~no-priv
1551- >>> print_feedback_messages(user_browser.contents)
1552- Password changed successfully
1553-
1554-Now, No Priv logs in with his new password.
1555-
1556- >>> anon_browser.open('http://launchpad.dev/+login')
1557- >>> anon_browser.getControl('E-mail address:', index=0).value = (
1558- ... 'no-priv@canonical.com')
1559- >>> anon_browser.getControl('Password:').value = 'abc'
1560- >>> anon_browser.getControl('Log In').click()
1561- >>> print anon_browser.url
1562- http://launchpad.dev
1563- >>> print extract_text(find_main_content(anon_browser.contents))
1564- Recent Launchpad blog posts
1565- ...
1566
1567=== removed file 'lib/lp/registry/stories/person/xx-createaccount.txt'
1568--- lib/lp/registry/stories/person/xx-createaccount.txt 2009-10-16 16:13:00 +0000
1569+++ lib/lp/registry/stories/person/xx-createaccount.txt 1970-01-01 00:00:00 +0000
1570@@ -1,179 +0,0 @@
1571-=================
1572-Create an account
1573-=================
1574-
1575-Jane wants to register with Launchpad, so she goes to the registration/login
1576-page.
1577-
1578- >>> browser = setupBrowser()
1579- >>> browser.open('http://launchpad.dev/+login')
1580- >>> print extract_text(find_main_content(browser.contents))
1581- Already registered?
1582- ...
1583- Not registered yet?
1584- ...
1585-
1586-She enters an invalid email address, and gets a warning message.
1587-
1588- >>> browser.getControl(name='loginpage_email', index=1).value = 'ab'
1589- >>> browser.getControl('Register').click()
1590- >>> print_feedback_messages(browser.contents)
1591- The email address you provided isn't valid. Please verify it and try
1592- again.
1593-
1594-Next she enters a valid email address but enters a wrong answer for
1595-the incredibly trivial math captcha. She gets an error message.
1596-
1597- >>> browser.getControl(name='loginpage_email', index=1).value = (
1598- ... 'jane@example.com')
1599- >>> browser.getControl(name='loginpage_captcha_submission').value = '-1'
1600- >>> browser.getControl('Register').click()
1601- >>> print_feedback_messages(browser.contents)
1602- The answer to the simple math question was incorrect or missing.
1603- Please try again.
1604-
1605-Jane tries again, providing a the correct captcha answer. Her valid
1606-email address from before has been retained in the form.
1607-
1608- >>> from lp.testing.registration import set_captcha_answer
1609- >>> set_captcha_answer(browser, prefix='loginpage_')
1610- >>> browser.getControl('Register').click()
1611- >>> print_feedback_messages(browser.contents)
1612- >>> print extract_text(find_main_content(browser.contents))
1613- Registration mail sent
1614- Instructions on completing your registration have been sent to
1615- jane@example.com.
1616- ...
1617-
1618-Launchpad sends Jane an email message containing a token she must use to
1619-complete the registration process.
1620-
1621- >>> import email
1622- >>> from lp.services.mail import stub
1623- >>> from canonical.launchpad.ftests.logintoken import (
1624- ... get_token_url_from_email)
1625-
1626- >>> from_addr, to_addrs, raw_msg = stub.test_emails.pop()
1627- >>> token_url = get_token_url_from_email(raw_msg)
1628-
1629-The email was sent to the address Jane registered.
1630-
1631- >>> to_addrs
1632- ['jane@example.com']
1633-
1634-Jane follows the link, but gets distracted before she can complete the
1635-registration process.
1636-
1637- >>> browser.open(token_url)
1638- >>> print extract_text(find_main_content(browser.contents))
1639- Display Name:
1640- Your name as you would like it displayed throughout Launchpad. Most
1641- people use their full name here.
1642- Hide my email addresses from other Launchpad users
1643- Create password:
1644- Retype the password:
1645-
1646-While Jane was deciding whether to hide her email address, Sneaky Salgado
1647-manages to register her email address out from under her.
1648-
1649- >>> from lp.registry.interfaces.person import IPersonSet
1650- >>> from zope.component import getUtility
1651-
1652- >>> login('admin@canonical.com')
1653- >>> salgado = getUtility(IPersonSet).getByName('salgado')
1654- >>> email = factory.makeEmail('jane@example.com', salgado)
1655- >>> transaction.commit()
1656-
1657- >>> [email.email for email in salgado.validatedemails]
1658- [u'jane@example.com']
1659- >>> logout()
1660-
1661-Now Jane attempts completes the registration, but she gets an error.
1662-
1663- >>> browser.getControl('Display Name').value = 'Janerie'
1664- >>> browser.getControl('Create password').value = 'foo'
1665- >>> browser.getControl('Retype the password').value = 'foo'
1666- >>> browser.getControl('Continue').click()
1667- >>> print_feedback_messages(browser.contents)
1668- There is 1 error.
1669- The email address jane@example.com is already registered.
1670-
1671-Jane tries again with a different email address.
1672-
1673- >>> browser.open('http://launchpad.dev/+login')
1674- >>> browser.getControl(name='loginpage_email', index=1).value = (
1675- ... 'jperson@example.com')
1676- >>> set_captcha_answer(browser, prefix='loginpage_')
1677- >>> browser.getControl('Register').click()
1678-
1679- >>> from_addr, to_addrs, raw_msg = stub.test_emails.pop()
1680- >>> token_url = get_token_url_from_email(raw_msg)
1681-
1682- >>> to_addrs
1683- ['jperson@example.com']
1684-
1685-Unfortunately, Jane forgets to enter her display name.
1686-
1687- >>> browser.open(token_url)
1688- >>> browser.getControl('Create password').value = 'foo'
1689- >>> browser.getControl('Retype the password').value = 'foo'
1690- >>> browser.getControl('Continue').click()
1691- >>> print_feedback_messages(browser.contents)
1692- There is 1 error.
1693- Required input is missing.
1694-
1695-She tries again, but forgets to enter a password.
1696-
1697- >>> browser.getControl('Display Name').value = 'Janerie'
1698- >>> browser.getControl('Continue').click()
1699- >>> print_feedback_messages(browser.contents)
1700- There is 1 error.
1701- You must enter a password.
1702-
1703-Third time's a charm? No; she forgets to confirm her password.
1704-
1705- >>> browser.getControl('Display Name').value = 'Janerie'
1706- >>> browser.getControl('Create password').value = 'foo'
1707- >>> browser.getControl('Continue').click()
1708- >>> print_feedback_messages(browser.contents)
1709- There is 1 error.
1710- Passwords do not match.
1711-
1712-The fourth time she tries a non-ascii password, which isn't allowed.
1713-
1714- >>> browser.getControl('Display Name').value = 'Janerie'
1715- >>> browser.getControl('Create password').value = 'tésté'
1716- >>> browser.getControl('Retype the password').value = 'tésté'
1717- >>> browser.getControl('Continue').click()
1718- >>> print_feedback_messages(browser.contents)
1719- There is 1 error.
1720- The password provided contains non-ASCII characters.
1721-
1722-Now Jane enters all the necessary information to create the new account. She
1723-decides to hide her email address.
1724-
1725- >>> browser.getControl('Display Name').value = 'Janerie'
1726- >>> browser.getControl('Create password').value = 'foo'
1727- >>> browser.getControl('Retype the password').value = 'foo'
1728- >>> browser.getControl(
1729- ... 'Hide my email addresses from other Launchpad users').click()
1730- >>> browser.getControl('Continue').click()
1731- >>> print_feedback_messages(browser.contents)
1732- Registration completed successfully
1733-
1734-And now the account is created.
1735-
1736- >>> login('admin@canonical.com')
1737- >>> jane = getUtility(IPersonSet).getByEmail('jperson@example.com')
1738- >>> print jane.creation_rationale.name
1739- OWNER_CREATED_LAUNCHPAD
1740- >>> jane.hide_email_addresses
1741- True
1742- >>> logout()
1743-
1744-If we try to go to that token's page again, we'll see a message explaining
1745-that the confirmation that token was created for is already concluded, so we
1746-don't need to do anything else.
1747-
1748- >>> browser.open(token_url)
1749- >>> print_feedback_messages(browser.contents)
1750
1751=== removed file 'lib/lp/registry/stories/person/xx-login.txt'
1752--- lib/lp/registry/stories/person/xx-login.txt 2009-12-23 20:29:05 +0000
1753+++ lib/lp/registry/stories/person/xx-login.txt 1970-01-01 00:00:00 +0000
1754@@ -1,85 +0,0 @@
1755-======
1756-Log in
1757-======
1758-
1759-New user Foo chooses to log into Launchpad.
1760-
1761- >>> browser = setupBrowser()
1762- >>> browser.open('http://launchpad.dev/')
1763- >>> browser.getLink('Log in / Register').click()
1764- >>> print browser.title
1765- Log in or register with Launchpad
1766-
1767-If Foo inadvertently use a non-ascii character in his password, an error
1768-message is displayed.
1769-
1770- >>> browser.getControl(
1771- ... 'E-mail address:', index=0).value = 'foo@baz.com'
1772- >>> browser.getControl('Password').value = 't\xC3\xA9st'
1773- >>> browser.getControl('Log In').click()
1774- >>> print browser.title
1775- Log in or register with Launchpad
1776-
1777- >>> print_feedback_messages(browser.contents)
1778- The password provided contains non-ASCII characters.
1779-
1780-When the user logs in, his browser is redirected to the page he was on
1781-before the login page.
1782-
1783- >>> browser.getControl('E-mail address:', index=0).value = 'foo@baz.com'
1784- >>> browser.getControl('Password').value = 'f'
1785- >>> browser.getControl('Log In').click()
1786- >>> print browser.title
1787- Log in or register with Launchpad
1788-
1789-
1790-Users with suspended account cannot log in
1791-==========================================
1792-
1793-Users with suspended accounts cannot log into Launchpad.
1794-
1795- # Open up the black box and shine a light on the test's inner workings.
1796- # Create a user with a SUSPENDED account.
1797- >>> from canonical.launchpad.interfaces.account import AccountStatus
1798-
1799- # Only admins can suspend an account.
1800- >>> login('admin@canonical.com')
1801- >>> bad_user = factory.makePerson(
1802- ... email='bad-user@canonical.com',
1803- ... name='bad-user',
1804- ... password='test')
1805- >>> from canonical.launchpad.interfaces import IMasterObject
1806- >>> IMasterObject(bad_user.account).status = AccountStatus.SUSPENDED
1807- >>> logout()
1808- >>> transaction.commit()
1809-
1810-Bad User has a suspended account. He discovers that his account is disabled
1811-when he visits his profile page.
1812-
1813- >>> browser = setupBrowser()
1814- >>> browser.handleErrors = True
1815- >>> browser.raiseHttpErrors = False
1816- >>> browser.open('http://launchpad.dev/~bad-user')
1817- >>> print browser.title
1818- Error: Page gone
1819-
1820-He believes he can still use his Launchpad profile because he knows his
1821-password.
1822-
1823- >>> browser.getLink('Log in / Register').click()
1824- >>> print browser.title
1825- Log in or register with Launchpad
1826-
1827- >>> browser.getControl('E-mail address:', index=0).value = (
1828- ... 'bad-user@canonical.com')
1829- >>> browser.getControl('Password').value = 'test'
1830- >>> browser.getControl('Log In').click()
1831- >>> print browser.title
1832- Log in or register with Launchpad
1833-
1834-But he cannot log in. He sees a error message that states he needs to contact
1835-a Launchpad admin.
1836-
1837- >>> print_errors(browser.contents)
1838- The email address belongs to a suspended account. Contact a Launchpad
1839- admin about this issue.
1840
1841=== removed file 'lib/lp/registry/stories/person/xx-reg-with-existing-email.txt'
1842--- lib/lp/registry/stories/person/xx-reg-with-existing-email.txt 2009-12-23 16:17:12 +0000
1843+++ lib/lp/registry/stories/person/xx-reg-with-existing-email.txt 1970-01-01 00:00:00 +0000
1844@@ -1,191 +0,0 @@
1845-= Registering new accounts =
1846-
1847-When registering new accounts, we always check if the email address used
1848-is not already registered in Launchpad.
1849-
1850-
1851-== Email addresses cannot be registered twice ==
1852-
1853-If the email address is registered and assigned to an account that has
1854-been validated, we show a message and point the user to the forgotten
1855-password page.
1856-
1857- >>> browser.open('http://launchpad.dev/')
1858- >>> browser.getLink('Log in / Register').click()
1859-
1860- >>> browser.getControl('E-mail address:', index=1).value = (
1861- ... 'test@canonical.com')
1862- >>> from lp.testing.registration import set_captcha_answer
1863- >>> set_captcha_answer(browser, prefix='loginpage_')
1864- >>> browser.getControl('Register').click()
1865-
1866- >>> for message in get_feedback_messages(browser.contents):
1867- ... print message
1868- Sorry, someone with the address test@canonical.com already has a
1869- Launchpad account. If this is you and you've forgotten your password,
1870- Launchpad can reset it for you.
1871-
1872-
1873-== Claiming a profile via registration ==
1874-
1875-On the other hand, if the email is assigned to a profile that has not
1876-been validated, we pretend that we have never seen this email and move
1877-on with the registration process.
1878-
1879- >>> browser.getControl('E-mail address:', index=1).value = 'mpo@iki.fi'
1880- >>> set_captcha_answer(browser, prefix='loginpage_')
1881- >>> browser.getControl('Register').click()
1882-
1883- >>> print extract_text(find_tag_by_id(browser.contents, 'address'))
1884- Instructions on completing your registration have been sent to...
1885-
1886- >>> from lp.services.mail import stub
1887- >>> from canonical.launchpad.ftests.logintoken import (
1888- ... get_token_url_from_email)
1889- >>> from_addr, to_addr, msg = stub.test_emails.pop()
1890- >>> token_url = get_token_url_from_email(msg)
1891- >>> token_url
1892- 'http://launchpad.dev/token/...'
1893-
1894- >>> browser.open(token_url)
1895- >>> browser.url == '%s/+newaccount' % token_url
1896- True
1897-
1898- >>> browser.getControl('Name').value = 'My name'
1899- >>> label = 'Hide my email addresses from other Launchpad users'
1900- >>> browser.getControl(label).selected = True
1901- >>> browser.getControl(name='field.password').value = 'foo'
1902- >>> browser.getControl(name='field.password_dupe').value = 'foo'
1903-
1904- >>> browser.getControl('Continue').click()
1905- >>> browser.url
1906- 'http://launchpad.dev'
1907-
1908- >>> for message in get_feedback_messages(browser.contents):
1909- ... print message
1910- Registration completed successfully
1911-
1912-It's also possible that a person tries to register a new account using an
1913-email address that is already set as the preferred email of an existing
1914-unvalidated profile, like some accounts imported from Ubuntu's bugzilla.
1915-Although these accounts are not considered valid (that's why we allow the
1916-users to proceed with the registration as if we haven't heard of the given
1917-email address before), they /do/ have a preferred email and we need to handle
1918-this.
1919-
1920- (To see kiko's email we must be logged in, so we'll use user_browser)
1921- >>> user_browser.open('http://launchpad.dev/~kiko')
1922- >>> unregistered_user_info = find_tag_by_id(
1923- ... user_browser.contents, 'not-lp-user-or-team')
1924- >>> unregistered_user_info.li.renderContents()
1925- '...christian.reis@ubuntulinux.com...'
1926-
1927- >>> anon_browser.open('http://launchpad.dev/')
1928- >>> anon_browser.getLink('Log in / Register').click()
1929-
1930- >>> anon_browser.getControl('E-mail address:', index=1).value = (
1931- ... 'christian.reis@ubuntulinux.com')
1932- >>> set_captcha_answer(anon_browser, prefix='loginpage_')
1933- >>> anon_browser.getControl('Register').click()
1934-
1935- >>> from_addr, to_addr, msg = stub.test_emails.pop()
1936- >>> to_addr
1937- ['christian.reis@ubuntulinux.com']
1938- >>> token_url = get_token_url_from_email(msg)
1939- >>> token_url
1940- 'http://launchpad.dev/token/...'
1941-
1942- >>> anon_browser.open(token_url)
1943- >>> anon_browser.url == "%s/+newaccount" % token_url
1944- True
1945-
1946- >>> anon_browser.getControl('Name').value = 'My name'
1947- >>> anon_browser.getControl(name='field.password').value = 'foo'
1948- >>> anon_browser.getControl(name='field.password_dupe').value = 'foo'
1949- >>> anon_browser.getControl('Continue').click()
1950-
1951- >>> anon_browser.url
1952- 'http://launchpad.dev'
1953- >>> for message in get_feedback_messages(anon_browser.contents):
1954- ... print message
1955- Registration completed successfully
1956-
1957-
1958-== Suspended email addresses cannot use registration ==
1959-
1960-Users with suspended accounts cannot reactivate their account using
1961-registration.
1962-
1963- # Open up the black box and shine a light on the test's inner workings.
1964- # Create a user with a SUSPENDED account.
1965- >>> from canonical.launchpad.interfaces.account import AccountStatus
1966-
1967- # Only admins can suspend an account.
1968- >>> login('foo.bar@canonical.com')
1969- >>> bad_user = factory.makePerson(
1970- ... email='bad-user@canonical.com',
1971- ... name='bad-user',
1972- ... password='invalid')
1973- >>> from canonical.launchpad.interfaces import IMasterObject
1974- >>> IMasterObject(bad_user.account).status = AccountStatus.SUSPENDED
1975- >>> logout()
1976-
1977-Bad User has a suspended account. He discovers that his account is disabled
1978-when he visits his profile page.
1979-
1980- >>> browser = setupBrowser()
1981- >>> browser.handleErrors = True
1982- >>> browser.raiseHttpErrors = False
1983- >>> browser.open('http://launchpad.dev/~bad-user')
1984- >>> browser.title
1985- 'Error: Page gone'
1986-
1987-He believes he can reactivate his account by re-registering with his
1988-email address.
1989-
1990- >>> browser.getLink('Log in / Register').click()
1991- >>> browser.title
1992- 'Log in or register with Launchpad'
1993-
1994- >>> browser.getControl('E-mail address:', index=1).value = (
1995- ... 'bad-user@canonical.com')
1996- >>> set_captcha_answer(browser, prefix='loginpage_')
1997- >>> browser.getControl('Register').click()
1998-
1999- >>> print extract_text(find_main_content(browser.contents).p)
2000- Instructions on completing your registration have been sent to
2001- bad-user@canonical.com.
2002-
2003-Bad User retrieves the URL from the email and opens it in his browser.
2004-
2005- >>> len(stub.test_emails)
2006- 1
2007- >>> from_addr, to_addrs, raw_msg = stub.test_emails.pop()
2008- >>> link = get_token_url_from_email(raw_msg)
2009- >>> to_addrs
2010- ['bad-user@canonical.com']
2011- >>> link
2012- 'http://launchpad.dev/token/...'
2013-
2014- >>> browser.open(link)
2015- >>> browser.url
2016- 'http://launchpad.dev/token/.../+newaccount'
2017-
2018-Bad User types his name and password twice to verify it and submits it with
2019-the Continue button. He is disappointed to see that his account is still
2020-suspended, and that he is instructed to contact an admin.
2021-
2022- >>> browser.getControl('Name').value = 'Bad User'
2023- >>> browser.getControl(name='field.password').value = 'test'
2024- >>> browser.getControl(name='field.password_dupe').value = 'test'
2025- >>> browser.getControl('Continue').click()
2026- Traceback (most recent call last):
2027- ...
2028- HTTPError: HTTP Error 410: Gone
2029-
2030- >>> for tag in find_tags_by_class(
2031- ... browser.contents, 'warning'):
2032- ... print tag
2033- <div ...>This profile cannot be claimed because the account is suspended.
2034- Contact a <a href="mailto:feedback@launchpad.net?subject=SU...">Launchpad
2035- admin</a> about this issue.</div>
2036
2037=== removed file 'lib/lp/registry/stories/person/xx-resetpassword.txt'
2038--- lib/lp/registry/stories/person/xx-resetpassword.txt 2009-10-29 15:09:28 +0000
2039+++ lib/lp/registry/stories/person/xx-resetpassword.txt 1970-01-01 00:00:00 +0000
2040@@ -1,186 +0,0 @@
2041-= Reset Password =
2042-
2043-David Allouche forgot his password and he's going to use the forgotten
2044-password form to reset it.
2045-
2046-He locates the forgotten password link on the login page to visit
2047-the page.
2048-
2049- >>> browser.open('http://launchpad.dev/+login')
2050- >>> browser.getLink('Forgotten your password?').click()
2051-
2052- >>> browser.url
2053- 'http://launchpad.dev/+forgottenpassword'
2054-
2055- >>> browser.title
2056- 'Need a new Launchpad password?'
2057-
2058-He types the email address registered in Launchpad and submits the form.
2059-
2060- >>> from lp.testing.registration import set_captcha_answer
2061- >>> browser.getControl(name='email').value = (
2062- ... 'david.allouche@canonical.com')
2063- >>> set_captcha_answer(browser)
2064- >>> browser.getControl('Request Reset').click()
2065- >>> print extract_text(find_main_content(browser.contents))
2066- Need a new Launchpad password?
2067- We have sent you an email with instructions to reset your password.
2068-
2069-David receives an email from Launchpad.
2070-
2071- # Get the to-addresses and link form the email.
2072- >>> from lp.services.mail import stub
2073- >>> from canonical.launchpad.ftests.logintoken import (
2074- ... get_token_url_from_email)
2075- >>> len(stub.test_emails)
2076- 1
2077- >>> from_addr, to_addrs, raw_msg = stub.test_emails.pop()
2078- >>> link = get_token_url_from_email(raw_msg)
2079-
2080-The email was sent to him.
2081-
2082- >>> to_addrs
2083- ['david.allouche@canonical.com']
2084-
2085-David uses the link in the email to visit the reset password page.
2086-
2087- >>> link
2088- 'http://launchpad.dev/token/...'
2089- >>> browser.open(link)
2090- >>> browser.url
2091- 'http://launchpad.dev/token/.../+resetpassword'
2092-
2093-The token can only be used to reset the password. Manually typing
2094-another login token view name will redirect to the correct one:
2095-
2096- >>> wrong_link = link + "/+validateemail"
2097- >>> browser.open(wrong_link)
2098- >>> browser.url
2099- 'http://launchpad.dev/token/.../+resetpassword'
2100-
2101-He may now enter his password. Unfortunately, he picks a password with
2102-invalid characters:
2103-
2104- >>> browser.getControl(
2105- ... name='field.email').value = 'david.allouche@canonical.com'
2106- >>> browser.getControl(name='field.password').value = 'é'
2107- >>> browser.getControl(name='field.password_dupe').value = 'é'
2108- >>> browser.getControl('Continue').click()
2109-
2110- >>> browser.url
2111- 'http://launchpad.dev/token/.../+resetpassword'
2112-
2113- >>> print_feedback_messages(browser.contents)
2114- There is 1 error.
2115- The password provided contains non-ASCII characters.
2116-
2117-This time, David corrects the password and continues:
2118-
2119- >>> browser.getControl(name='field.password').value = 'test2'
2120- >>> browser.getControl(name='field.password_dupe').value = 'test2'
2121- >>> browser.getControl('Continue').click()
2122-
2123- >>> browser.url
2124- 'http://launchpad.dev'
2125-
2126- >>> print_feedback_messages(browser.contents)
2127- Your password has been reset successfully.
2128-
2129- >>> print extract_text(find_tag_by_id(browser.contents, 'logincontrol'))
2130- David Allouche...
2131-
2132-Now that the token is consumed, he's not able to use it again.
2133-
2134- >>> browser.open(link)
2135- >>> print browser.title
2136- You have already done this
2137- >>> print extract_text(find_main_content(browser.contents))
2138- You reached this page probably because you followed a link received by
2139- email. That link was sent to confirm you have access to the email
2140- address it was sent to, but this confirmation was already concluded, so
2141- you don't need to do anything else.
2142-
2143-It is now possible for David to log in with his new password. He
2144-logs out and then logs in again:
2145-
2146- >>> browser.open('http://launchpad.dev/+logout')
2147- >>> print_feedback_messages(browser.contents)
2148- You have been logged out
2149-
2150- >>> browser.getLink('Log in / Register').click()
2151-
2152- >>> browser.getControl(
2153- ... 'E-mail address:', index=0).value = 'david.allouche@canonical.com'
2154- >>> browser.getControl('Password').value = 'test2'
2155- >>> browser.getControl('Log In').click()
2156-
2157- >>> browser.url
2158- 'http://launchpad.dev'
2159-
2160- >>> print extract_text(find_tag_by_id(browser.contents, 'logincontrol'))
2161- David Allouche...
2162-
2163-
2164-== Reset is protected by a math captcha ==
2165-
2166-When requesting a password reset David is presented with a simple math
2167-problem which must be solved correctly before proceeding.
2168-
2169- >>> browser.open('http://launchpad.dev/+forgottenpassword')
2170- >>> browser.getControl(name='email').value = (
2171- ... 'david.allouche@canonical.com')
2172- >>> browser.getControl(name='captcha_submission').value = '-1'
2173- >>> browser.getControl('Request Reset').click()
2174- >>> print_feedback_messages(browser.contents)
2175- The answer to the simple math question was incorrect or missing. Please try again.
2176-
2177-
2178-== Using email addresses other than the preferred one ==
2179-
2180-Any of a person's validated email addresses can be used to reset his
2181-password.
2182-
2183-David has a second email address (david@canonical.com), which he will
2184-use to reset his password now.
2185-
2186- >>> browser = setupBrowser()
2187- >>> browser.open('http://launchpad.dev/+login')
2188- >>> browser.getLink('Forgotten your password?').click()
2189- >>> browser.getControl(name='email').value = 'david@canonical.com'
2190- >>> set_captcha_answer(browser)
2191- >>> browser.getControl('Request Reset').click()
2192- >>> print_feedback_messages(browser.contents)
2193-
2194-He follows the link sent to his email just like he did before.
2195-
2196- >>> len(stub.test_emails)
2197- 1
2198- >>> from_addr, to_addrs, raw_msg = stub.test_emails.pop()
2199- >>> link = get_token_url_from_email(raw_msg)
2200- >>> browser.open(link)
2201- >>> browser.url
2202- 'http://launchpad.dev/token/.../+resetpassword'
2203-
2204-And then resets his password.
2205-
2206- >>> browser.getControl(name='field.email').value = 'david@canonical.com'
2207- >>> browser.getControl(name='field.password').value = 'test3'
2208- >>> browser.getControl(name='field.password_dupe').value = 'test3'
2209- >>> browser.getControl('Continue').click()
2210- >>> browser.url
2211- 'http://launchpad.dev'
2212- >>> print_feedback_messages(browser.contents)
2213- Your password has been reset successfully.
2214-
2215-
2216-== Teams do not have passwords ==
2217-
2218-Teams do not have passwords, they cannot be reset.
2219-
2220- >>> browser.open('http://launchpad.dev/+forgottenpassword')
2221- >>> browser.getControl(name='email').value = 'support@ubuntu.com'
2222- >>> set_captcha_answer(browser)
2223- >>> browser.getControl('Request Reset').click()
2224- >>> print_feedback_messages(browser.contents)
2225- The email address support@ubuntu.com
2226- belongs to a team, and teams cannot log in to Launchpad.
2227
2228=== removed file 'lib/lp/registry/stories/team/xx-team-restricted.txt'
2229--- lib/lp/registry/stories/team/xx-team-restricted.txt 2009-11-22 15:43:16 +0000
2230+++ lib/lp/registry/stories/team/xx-team-restricted.txt 1970-01-01 00:00:00 +0000
2231@@ -1,137 +0,0 @@
2232-= Team-restricted Launchpad =
2233-
2234-It is possible to run Launchpad in a special mode where access is restricted
2235-to Launchpad admins and members of a particular team.
2236-
2237-You turn on this feature by setting the 'restrict_to_team' variable in the
2238-launchpad.conf file.
2239-
2240- <launchpad>
2241- restrict_to_team teamname
2242- ...
2243- </launchpad>
2244-
2245-We'll temporarily restrict Launchpad to be used only by Launchpad admins
2246-and Landscape developers.
2247-
2248- >>> from canonical.config import config
2249- >>> config.launchpad.restrict_to_team == ''
2250- True
2251-
2252- >>> restrict_data = """
2253- ... [launchpad]
2254- ... restrict_to_team: landscape-developers
2255- ... """
2256- >>> config.push('restrict_data', restrict_data)
2257-
2258-When in this condition, anonymous users are shown a special trimmed-down
2259-login page when they go to any page.
2260-
2261- >>> browser.open("http://launchpad.dev/people")
2262- >>> print browser.url
2263- http://launchpad.dev/+restricted-login?production=http%3A//launchpad.net/people
2264-
2265-A link is provided to the equivalent page on the non-restricted
2266-Launchpad instance:
2267-
2268- >>> print browser.contents
2269- <...
2270- <p>
2271- You may be able to access this page
2272- <a href="http://launchpad.net/people">on the main
2273- Launchpad site</a> instead.
2274- </p>
2275- ...
2276-
2277-This includes nonsense URLs and not-found URLs.
2278-
2279- >>> browser.open("http://bugs.launchpad.dev/p/p/p/p?key=value")
2280- >>> print browser.url
2281- http://bugs.launchpad.dev/+restricted-login?production=http%3A//bugs.launchpad.net/p/p/p/p%3Fkey%3Dvalue
2282- >>> print browser.contents
2283- <...
2284- <p>
2285- You may be able to access this page
2286- <a href="http://bugs.launchpad.net/p/p/p/p?key=value">on the main
2287- Launchpad site</a> instead.
2288- </p>
2289- ...
2290-
2291-That is, any page except an information page about the restriction.
2292-
2293- >>> browser.open("http://launchpad.dev/+restricted-info")
2294- >>> print browser.url
2295- http://launchpad.dev/+restricted-info
2296-
2297-Anyway, the anonymous user logs in as name16 (foo.bar@canonical.com), who
2298-is in the admin team, and gets redirected to the front page.
2299-
2300- >>> def submit_login(browser_instance, email, password):
2301- ... emailcontrol = browser_instance.getControl(name="loginpage_email")
2302- ... passwordcontrol = browser_instance.getControl(
2303- ... name="loginpage_password")
2304- ... emailcontrol.value = email
2305- ... passwordcontrol.value = password
2306- ... submitbutton = browser_instance.getControl(
2307- ... name="loginpage_submit_login")
2308- ... submitbutton.click()
2309-
2310- >>> browser.open("http://launchpad.dev/p/p/p/p")
2311- >>> print browser.url
2312- http://launchpad.dev/+restricted-login?production=http%3A//launchpad.net/p/p/p/p
2313- >>> submit_login(browser, 'foo.bar@canonical.com', 'test')
2314- >>> print browser.url
2315- http://launchpad.dev
2316-
2317-Let's log out name16 (foo.bar@canonical.com), and log in someone else.
2318-name12 (test@canonical.com) is in the landscape-developers team, so will
2319-be able to log in.
2320-
2321- >>> browser.getControl(name="logout").click()
2322- >>> print browser.url
2323- http://launchpad.dev/+restricted-login?production=http%3A//launchpad.net/%3Floggingout%3D1
2324- >>> submit_login(browser, 'test@canonical.com', 'test')
2325- >>> print browser.url
2326- http://launchpad.dev
2327-
2328-We'll now log out name12 (test@canonical.com), and try to log in cprov, who
2329-isn't an admin, and isn't a landscape-developer. So, he gets sent to the info
2330-page to explain why he can't use the server.
2331-
2332- >>> browser.getControl(name="logout").click()
2333- >>> print browser.url
2334- http://launchpad.dev/+restricted-login?production=http%3A//launchpad.net/%3Floggingout%3D1
2335- >>> submit_login(browser, 'celso.providelo@canonical.com', 'cprov')
2336- >>> print browser.url
2337- http://launchpad.dev/+restricted-info?production=http%3A//launchpad.net/
2338-
2339-Now that the user is logged in, they will be directed to the
2340-+restricted-info page if they try to browse other pages:
2341-
2342- >>> browser.open("http://bugs.launchpad.dev/evolution")
2343- >>> print browser.url
2344- http://bugs.launchpad.dev/+restricted-info?production=http%3A//bugs.launchpad.net/evolution
2345- >>> print browser.contents
2346- <...
2347- <p>You may be able to
2348- access this page
2349- <a href="http://bugs.launchpad.net/evolution">on the main
2350- Launchpad site</a> instead.</p>
2351- ...
2352-
2353-Set restrict_to_team back to being a server that is open to anyone.
2354-
2355- >>> config_data = config.pop('restrict_data')
2356-
2357-Note that the info page still works even when the server is open.
2358-
2359- >>> browser.open("http://launchpad.dev/+restricted-info")
2360- >>> print browser.contents
2361- <...This server is open to anonymous access and registered users...
2362-
2363-The restricted login page redirects to the normal login page.
2364-
2365- >>> browser.open("http://launchpad.dev/+restricted-login")
2366- >>> print browser.url
2367- http://launchpad.dev/+login
2368-
2369
2370=== removed file 'lib/lp/services/openid/stories/xx-resetpassword-of-sso-account.txt'
2371--- lib/lp/services/openid/stories/xx-resetpassword-of-sso-account.txt 2009-12-04 22:36:22 +0000
2372+++ lib/lp/services/openid/stories/xx-resetpassword-of-sso-account.txt 1970-01-01 00:00:00 +0000
2373@@ -1,83 +0,0 @@
2374-= Resetting the password of SSO-only accounts =
2375-
2376-Every once in a while people come to register Launchpad accounts using email
2377-addresses that belong to SSO-only accounts. In these cases we explain to them
2378-that they should use the SSO credentials to log into Launchpad, but it's
2379-possible that they've forgotten their password so they'll try to use the
2380-'Forgotten your password?' link to reset it.
2381-
2382-Since their account is SSO-only (i.e. it has no Person record associated
2383-with), we can't use Launchpad's workflow for resetting the password -- it
2384-expects a Person entry to be associated with the given email addres. Because
2385-of that we'll allow the user to initiate the password-reset workflow on
2386-Launchpad but we'll force the use of SSO's password-reset workflow to complete
2387-it.
2388-
2389- # Create a personless account to demostrate what we want.
2390- >>> login(ANONYMOUS)
2391- >>> account = factory.makeAccount('Test', email='test@example.com')
2392- >>> logout()
2393-
2394-Trying to register will give an error, telling the user to use the SSO
2395-credentials to login.
2396-
2397- >>> from lp.testing.registration import set_captcha_answer
2398- >>> browser.open('http://launchpad.dev/+login')
2399- >>> browser.getControl(name='loginpage_email', index=1).value = (
2400- ... 'test@example.com')
2401- >>> set_captcha_answer(browser, prefix='loginpage_')
2402- >>> browser.getControl('Register').click()
2403- >>> print_feedback_messages(browser.contents)
2404- The email address test@example.com is already registered in the Launchpad
2405- Login Service... Please use the same email and password to log into
2406- Launchpad.
2407-
2408-But the user has forgotten their credentials, so they'll have to reset the
2409-password.
2410-
2411- >>> browser.getLink('Forgotten your password?').click()
2412- >>> browser.url
2413- 'http://launchpad.dev/+forgottenpassword'
2414- >>> browser.getControl(name='email').value = 'test@example.com'
2415- >>> set_captcha_answer(browser)
2416- >>> browser.getControl('Request Reset').click()
2417- >>> print extract_text(find_main_content(browser.contents))
2418- Need a new Launchpad password?
2419- We have sent you an email with instructions to reset your password.
2420-
2421- # Get the link from the email.
2422- >>> from lp.services.mail import stub
2423- >>> from canonical.launchpad.ftests.logintoken import (
2424- ... get_token_url_from_email)
2425- >>> len(stub.test_emails)
2426- 1
2427- >>> from_addr, to_addrs, raw_msg = stub.test_emails.pop()
2428- >>> link = get_token_url_from_email(raw_msg)
2429- >>> to_addrs
2430- ['test@example.com']
2431-
2432-Following the link sent by email will take the user to the SSO site to
2433-complete the password reset.
2434-
2435- >>> browser.open(link)
2436- >>> browser.url
2437- 'http://openid.launchpad.dev/token/.../+resetpassword'
2438- >>> browser.getControl(name='field.email').value = 'test@example.com'
2439- >>> browser.getControl(name='field.password').value = 'test'
2440- >>> browser.getControl(name='field.password_dupe').value = 'test'
2441- >>> browser.getControl('Continue').click()
2442- >>> print_feedback_messages(browser.contents)
2443- Your password has been reset successfully.
2444-
2445-Since the password reset was done on SSO, the user won't be logged into
2446-Launchpad, so we redirect the user back to Launchpad's +login page for them to
2447-login.
2448-
2449- >>> browser.url
2450- 'http://launchpad.dev/+login'
2451- >>> browser.getControl(
2452- ... 'E-mail address:', index=0).value = 'test@example.com'
2453- >>> browser.getControl('Password').value = 'test'
2454- >>> browser.getControl('Log In').click()
2455- >>> print extract_text(find_tag_by_id(browser.contents, 'logincontrol'))
2456- Test...