Merge lp:~salgado/launchpad/remove-logintoken-unused-code into lp:launchpad
- remove-logintoken-unused-code
- Merge into devel
Proposed by
Guilherme Salgado
on 2010-04-02
| Status: | Merged |
|---|---|
| Merged at revision: | not available |
| Proposed branch: | lp:~salgado/launchpad/remove-logintoken-unused-code |
| Merge into: | lp:launchpad |
| Prerequisite: | lp:~salgado/launchpad/remove-auth-store |
| Diff against target: |
1474 lines (+43/-1077) 23 files modified
lib/canonical/launchpad/browser/ftests/logintoken-corner-cases.txt (+10/-11) lib/canonical/launchpad/browser/logintoken.py (+7/-228) lib/canonical/launchpad/browser/tests/test_logintoken.py (+1/-7) lib/canonical/launchpad/browser/tests/test_password_reset.py (+0/-94) lib/canonical/launchpad/database/logintoken.py (+0/-17) lib/canonical/launchpad/doc/logintoken-pages.txt (+13/-295) lib/canonical/launchpad/emailtemplates/forgottenpassword-neutral.txt (+0/-14) lib/canonical/launchpad/emailtemplates/forgottenpassword.txt (+0/-11) lib/canonical/launchpad/emailtemplates/newuser-email-neutral.txt (+0/-15) lib/canonical/launchpad/emailtemplates/newuser-email.txt (+0/-19) lib/canonical/launchpad/interfaces/authtoken.py (+3/-53) lib/canonical/launchpad/templates/logintoken-newaccount.pt (+0/-19) lib/canonical/launchpad/templates/logintoken-resetpassword.pt (+0/-18) lib/canonical/launchpad/tests/test_login.py (+1/-20) lib/canonical/launchpad/tests/test_token_creation.py (+2/-2) lib/canonical/launchpad/webapp/login.py (+2/-20) lib/canonical/launchpad/webapp/publication.py (+1/-1) lib/canonical/launchpad/zcml/logintoken.zcml (+0/-16) lib/lp/registry/browser/configure.zcml (+0/-6) lib/lp/registry/browser/person.py (+2/-32) lib/lp/registry/interfaces/person.py (+1/-40) lib/lp/registry/stories/person/xx-new-profile.txt (+0/-110) lib/lp/registry/templates/people-newperson.pt (+0/-29) |
| To merge this branch: | bzr merge lp:~salgado/launchpad/remove-logintoken-unused-code |
| Related bugs: |
| Reviewer | Review Type | Date Requested | Status |
|---|---|---|---|
| Jonathan Lange (community) | 2010-04-02 | Approve on 2010-04-05 | |
|
Review via email:
|
|||
Commit Message
Description of the Change
Remove views (and logintoken code) for registering new accounts and resetting passwords as this is now handled by the OpenID provider.
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
| 1 | === modified file 'lib/canonical/launchpad/browser/ftests/logintoken-corner-cases.txt' |
| 2 | --- lib/canonical/launchpad/browser/ftests/logintoken-corner-cases.txt 2009-07-17 15:26:05 +0000 |
| 3 | +++ lib/canonical/launchpad/browser/ftests/logintoken-corner-cases.txt 2010-04-05 17:13:29 +0000 |
| 4 | @@ -7,25 +7,24 @@ |
| 5 | |
| 6 | === Double Post on the NewAccountView === |
| 7 | |
| 8 | -Using the +newaccount view on a token that was already confirmed should |
| 9 | +Using the +validateemail view on a token that was already consumed should |
| 10 | redirect to the default token view. This would happen if for example the |
| 11 | -user tried to re-post the form after registering his or her account. |
| 12 | +user tried to re-post the form after validating one of their email addresses. |
| 13 | |
| 14 | + >>> from lp.registry.interfaces.person import IPersonSet |
| 15 | >>> from canonical.launchpad.interfaces import ( |
| 16 | ... ILoginTokenSet, LoginTokenType) |
| 17 | - >>> from canonical.launchpad.browser import NewUserAccountView |
| 18 | + >>> from canonical.launchpad.browser import ValidateEmailView |
| 19 | >>> from canonical.launchpad.webapp.servers import LaunchpadTestRequest |
| 20 | |
| 21 | + >>> foo_bar = getUtility(IPersonSet).getByName('name16') |
| 22 | >>> token = getUtility(ILoginTokenSet).new( |
| 23 | - ... requester=None, requesteremail=None, |
| 24 | - ... email='foo@barino.com', tokentype=LoginTokenType.NEWACCOUNT) |
| 25 | + ... requester=foo_bar, requesteremail='foo.bar@canonical.com', |
| 26 | + ... email='foo@barino.com', tokentype=LoginTokenType.VALIDATEEMAIL) |
| 27 | >>> token.consume() |
| 28 | - >>> form = {'field.actions.continue': 'Continue', |
| 29 | - ... 'field.displayname': 'Alex', |
| 30 | - ... 'field.hide_email_addresses': 'off', |
| 31 | - ... 'field.password': '23-is-the-answer', |
| 32 | - ... 'field.password_dupe': '23-is-the-answer'} |
| 33 | - >>> view = NewUserAccountView(token, LaunchpadTestRequest(form=form)) |
| 34 | + >>> form = {'field.actions.continue': 'Continue'} |
| 35 | + >>> view = ValidateEmailView( |
| 36 | + ... token, LaunchpadTestRequest(form=form, method='POST')) |
| 37 | >>> view.initialize() |
| 38 | |
| 39 | >>> response = view.request.response |
| 40 | |
| 41 | === modified file 'lib/canonical/launchpad/browser/logintoken.py' |
| 42 | --- lib/canonical/launchpad/browser/logintoken.py 2010-03-12 19:56:06 +0000 |
| 43 | +++ lib/canonical/launchpad/browser/logintoken.py 2010-04-05 17:13:29 +0000 |
| 44 | @@ -11,8 +11,6 @@ |
| 45 | 'LoginTokenSetNavigation', |
| 46 | 'LoginTokenView', |
| 47 | 'MergePeopleView', |
| 48 | - 'NewUserAccountView', |
| 49 | - 'ResetPasswordView', |
| 50 | 'ValidateEmailView', |
| 51 | 'ValidateTeamEmailView', |
| 52 | 'ValidateGPGKeyView', |
| 53 | @@ -24,16 +22,13 @@ |
| 54 | |
| 55 | from zope.app.form.browser import TextAreaWidget |
| 56 | from zope.component import getUtility |
| 57 | -from zope.event import notify |
| 58 | from zope.interface import alsoProvides, directlyProvides, Interface |
| 59 | -from zope.lifecycleevent import ObjectCreatedEvent |
| 60 | from zope.security.proxy import removeSecurityProxy |
| 61 | |
| 62 | from canonical.database.sqlbase import flush_database_updates |
| 63 | from canonical.launchpad import _ |
| 64 | -from canonical.launchpad.interfaces.account import AccountStatus, IAccountSet |
| 65 | -from canonical.launchpad.interfaces.authtoken import ( |
| 66 | - IAuthToken, LoginTokenType) |
| 67 | +from canonical.launchpad.interfaces.account import AccountStatus |
| 68 | +from canonical.launchpad.interfaces.authtoken import LoginTokenType |
| 69 | from canonical.launchpad.interfaces.emailaddress import ( |
| 70 | EmailAddressStatus, IEmailAddressSet) |
| 71 | from canonical.launchpad.interfaces.gpghandler import ( |
| 72 | @@ -53,8 +48,7 @@ |
| 73 | from canonical.widgets import LaunchpadRadioWidget, PasswordChangeWidget |
| 74 | |
| 75 | from lp.registry.browser.team import HasRenewalPolicyMixin |
| 76 | -from lp.registry.interfaces.person import ( |
| 77 | - INewPersonForm, IPerson, IPersonSet, ITeam, PersonCreationRationale) |
| 78 | +from lp.registry.interfaces.person import IPerson, IPersonSet, ITeam |
| 79 | |
| 80 | |
| 81 | UTC = pytz.UTC |
| 82 | @@ -77,15 +71,9 @@ |
| 83 | they got this token because they tried to do something that required email |
| 84 | address confirmation, but that confirmation is already concluded. |
| 85 | """ |
| 86 | - auth_token_pages = { |
| 87 | - LoginTokenType.NEWPERSONLESSACCOUNT: '+newaccount', |
| 88 | - LoginTokenType.PASSWORDRECOVERY: '+resetpassword', |
| 89 | + PAGES = { |
| 90 | + LoginTokenType.ACCOUNTMERGE: '+accountmerge', |
| 91 | LoginTokenType.VALIDATEEMAIL: '+validateemail', |
| 92 | - } |
| 93 | - login_token_pages = { |
| 94 | - LoginTokenType.NEWPROFILE: '+newaccount', |
| 95 | - LoginTokenType.NEWACCOUNT: '+newaccount', |
| 96 | - LoginTokenType.ACCOUNTMERGE: '+accountmerge', |
| 97 | LoginTokenType.VALIDATETEAMEMAIL: '+validateteamemail', |
| 98 | LoginTokenType.VALIDATEGPG: '+validategpg', |
| 99 | LoginTokenType.VALIDATESIGNONLYGPG: '+validatesignonlygpg', |
| 100 | @@ -93,8 +81,6 @@ |
| 101 | LoginTokenType.TEAMCLAIM: '+claimteam', |
| 102 | LoginTokenType.BUGTRACKER: '+bugtracker-handshake', |
| 103 | } |
| 104 | - login_token_pages.update(auth_token_pages) |
| 105 | - PAGES = login_token_pages |
| 106 | page_title = 'You have already done this' |
| 107 | label = 'Confirmation already concluded' |
| 108 | |
| 109 | @@ -198,81 +184,6 @@ |
| 110 | return True |
| 111 | |
| 112 | |
| 113 | -class ResetPasswordView(BaseTokenView, LaunchpadFormView): |
| 114 | - |
| 115 | - schema = IAuthToken |
| 116 | - field_names = ['email', 'password'] |
| 117 | - custom_widget('password', PasswordChangeWidget) |
| 118 | - label = 'Reset password' |
| 119 | - expected_token_types = (LoginTokenType.PASSWORDRECOVERY,) |
| 120 | - |
| 121 | - def initialize(self): |
| 122 | - self.redirectIfInvalidOrConsumedToken() |
| 123 | - super(ResetPasswordView, self).initialize() |
| 124 | - |
| 125 | - def validate(self, form_values): |
| 126 | - """Validate the email address.""" |
| 127 | - email = form_values.get("email", "").strip() |
| 128 | - # All operations with email addresses must be case-insensitive. We |
| 129 | - # enforce that in EmailAddressSet, but here we only do a comparison, |
| 130 | - # so we have to .lower() them first. |
| 131 | - if email.lower() != self.context.email.lower(): |
| 132 | - self.addError(_( |
| 133 | - "The email address you provided didn't match the address " |
| 134 | - "you provided when requesting the password reset.")) |
| 135 | - |
| 136 | - @property |
| 137 | - def default_next_url(self): |
| 138 | - if self.context.redirection_url is not None: |
| 139 | - return self.context.redirection_url |
| 140 | - else: |
| 141 | - return self.request.getApplicationURL() |
| 142 | - |
| 143 | - @action(_('Continue'), name='continue') |
| 144 | - def continue_action(self, action, data): |
| 145 | - """Reset the user's password. When password is successfully changed, |
| 146 | - the AuthToken (self.context) used is consumed, so nobody can use |
| 147 | - it again. |
| 148 | - """ |
| 149 | - account = self.context.requester_account |
| 150 | - # Suspended accounts cannot reset their password. |
| 151 | - reason = ('Your password cannot be reset because your account ' |
| 152 | - 'is suspended.') |
| 153 | - if self.accountWasSuspended(account, reason): |
| 154 | - return |
| 155 | - |
| 156 | - naked_account = removeSecurityProxy(account) |
| 157 | - # Reset password can be used to reactivate a deactivated account. |
| 158 | - inactive_states = [AccountStatus.DEACTIVATED, AccountStatus.NOACCOUNT] |
| 159 | - if account.status in inactive_states: |
| 160 | - self.reactivate(data) |
| 161 | - self.request.response.addInfoNotification( |
| 162 | - _('Welcome back to Launchpad.')) |
| 163 | - else: |
| 164 | - naked_account.password = data.get('password') |
| 165 | - |
| 166 | - self.context.consume() |
| 167 | - self.logInPrincipalByEmail(self.context.email) |
| 168 | - |
| 169 | - self.request.response.addInfoNotification( |
| 170 | - _('Your password has been reset successfully.')) |
| 171 | - |
| 172 | - @action(_('Cancel'), name='cancel', validator='validate_cancel') |
| 173 | - def cancel_action(self, action, data): |
| 174 | - self._cancel() |
| 175 | - |
| 176 | - def reactivate(self, data): |
| 177 | - """Reactivate the person (and account) of this token.""" |
| 178 | - emailaddress = getUtility(IEmailAddressSet).getByEmail( |
| 179 | - self.context.email) |
| 180 | - # Need to remove the security proxy of the account because at this |
| 181 | - # point the user is not logged in. |
| 182 | - removeSecurityProxy(self.context.requester).reactivate( |
| 183 | - comment="User reactivated the account using reset password.", |
| 184 | - password=data['password'], |
| 185 | - preferred_email=emailaddress) |
| 186 | - |
| 187 | - |
| 188 | class ClaimProfileView(BaseTokenView, LaunchpadFormView): |
| 189 | schema = IPerson |
| 190 | field_names = ['displayname', 'hide_email_addresses', 'password'] |
| 191 | @@ -545,7 +456,8 @@ |
| 192 | label = 'Confirm e-mail address' |
| 193 | |
| 194 | def initialize(self): |
| 195 | - self.redirectIfInvalidOrConsumedToken() |
| 196 | + if self.redirectIfInvalidOrConsumedToken(): |
| 197 | + return |
| 198 | super(ValidateEmailView, self).initialize() |
| 199 | |
| 200 | def validate(self, data): |
| 201 | @@ -746,136 +658,3 @@ |
| 202 | self.request.response.setStatus(200) |
| 203 | self.request.response.setHeader('Content-type', 'text/plain') |
| 204 | return "Handshake token validated." |
| 205 | - |
| 206 | - |
| 207 | -class NewUserAccountView(BaseTokenView, LaunchpadFormView): |
| 208 | - """Page to create a new Launchpad user account.""" |
| 209 | - |
| 210 | - created_person = None |
| 211 | - |
| 212 | - schema = INewPersonForm |
| 213 | - field_names = ['displayname', 'hide_email_addresses', 'password'] |
| 214 | - custom_widget('password', PasswordChangeWidget) |
| 215 | - label = 'Complete your registration' |
| 216 | - expected_token_types = ( |
| 217 | - LoginTokenType.NEWACCOUNT, LoginTokenType.NEWPROFILE) |
| 218 | - |
| 219 | - def initialize(self): |
| 220 | - if self.redirectIfInvalidOrConsumedToken(): |
| 221 | - return |
| 222 | - else: |
| 223 | - self.email = getUtility(IEmailAddressSet).getByEmail( |
| 224 | - self.context.email) |
| 225 | - super(NewUserAccountView, self).initialize() |
| 226 | - |
| 227 | - @property |
| 228 | - def default_next_url(self): |
| 229 | - if self.context.redirection_url: |
| 230 | - return self.context.redirection_url |
| 231 | - elif self.created_person is not None: |
| 232 | - return canonical_url(self.created_person) |
| 233 | - else: |
| 234 | - return None |
| 235 | - |
| 236 | - def validate(self, form_values): |
| 237 | - """Verify if the email address is not used by an existing account.""" |
| 238 | - if self.email is not None: |
| 239 | - if self.email.person is not None: |
| 240 | - person = IMasterObject(self.email.person) |
| 241 | - if person.is_valid_person: |
| 242 | - self.addError(_( |
| 243 | - 'The email address ${email} is already registered.', |
| 244 | - mapping=dict(email=self.context.email))) |
| 245 | - else: |
| 246 | - self.addError(_( |
| 247 | - 'The email address ${email} is already registered in ' |
| 248 | - 'the Launchpad Login Service (used by the Ubuntu shop ' |
| 249 | - 'and other OpenID sites). Please use the same email and ' |
| 250 | - 'password to log into Launchpad.', |
| 251 | - mapping=dict(email=self.context.email))) |
| 252 | - |
| 253 | - |
| 254 | - @action(_('Continue'), name='continue') |
| 255 | - def continue_action(self, action, data): |
| 256 | - """Create a new Person with the context's email address and set a |
| 257 | - preferred email and password to it, or use an existing Person |
| 258 | - associated with the context's email address, setting it as the |
| 259 | - preferred address and also setting the password. |
| 260 | - |
| 261 | - If everything went ok, we consume the LoginToken (self.context), so |
| 262 | - nobody can use it again. |
| 263 | - """ |
| 264 | - if self.email is not None: |
| 265 | - assert self.email.person is not None, ( |
| 266 | - "People trying to register using emails associated with " |
| 267 | - "personless accounts should be told to just use their Login " |
| 268 | - "Service credentials to log into LP") |
| 269 | - # This is a placeholder profile automatically created by one of |
| 270 | - # our scripts, let's just confirm its email address and set a |
| 271 | - # password. |
| 272 | - person = getUtility(IPersonSet).get( |
| 273 | - removeSecurityProxy(self.email).personID) |
| 274 | - assert not person.is_valid_person, ( |
| 275 | - 'Account %s has already been claimed and this should ' |
| 276 | - 'have been caught by the validate() method.' % person.name) |
| 277 | - email = self.email |
| 278 | - # The user is not yet logged in, but we need to set some |
| 279 | - # things on his new account, so we need to remove the security |
| 280 | - # proxy from it. |
| 281 | - # XXX: Guilherme Salgado 2006-09-27 bug=62674: |
| 282 | - # We should be able to login with this person and set the |
| 283 | - # password, to avoid removing the security proxy, but it didn't |
| 284 | - # work, so I'm leaving this hack for now. |
| 285 | - naked_person = removeSecurityProxy(person) |
| 286 | - # Suspended accounts cannot reactivate their profile. |
| 287 | - reason = ('This profile cannot be claimed because the account ' |
| 288 | - 'is suspended.') |
| 289 | - if self.accountWasSuspended(person.account, reason): |
| 290 | - return |
| 291 | - naked_person.displayname = data['displayname'] |
| 292 | - naked_person.hide_email_addresses = data['hide_email_addresses'] |
| 293 | - # Need to remove the security proxy of the account because at this |
| 294 | - # point the user is not logged in. |
| 295 | - account = removeSecurityProxy(IMasterObject(person.account)) |
| 296 | - account.activate( |
| 297 | - "Activated by new account.", |
| 298 | - password=data['password'], |
| 299 | - preferred_email=self.email) |
| 300 | - naked_person.creation_rationale = self._getCreationRationale() |
| 301 | - naked_person.creation_comment = None |
| 302 | - else: |
| 303 | - account, person, email = self._createAccountEmailAndMaybePerson( |
| 304 | - data['displayname'], data['hide_email_addresses'], |
| 305 | - data['password']) |
| 306 | - |
| 307 | - self.context.consume() |
| 308 | - self.logInPrincipalByEmail(removeSecurityProxy(email).email) |
| 309 | - self.created_person = person |
| 310 | - self.request.response.addInfoNotification(_( |
| 311 | - "Registration completed successfully")) |
| 312 | - |
| 313 | - def _getCreationRationale(self): |
| 314 | - """The creation rationale that should be used for this account.""" |
| 315 | - return PersonCreationRationale.OWNER_CREATED_LAUNCHPAD |
| 316 | - |
| 317 | - def _createAccountEmailAndMaybePerson( |
| 318 | - self, displayname, hide_email_addresses, password): |
| 319 | - """Create and return a new Account, Person and EmailAddress. |
| 320 | - |
| 321 | - This method will always create an Account (in the ACTIVE state), |
| 322 | - an EmailAddress as the account's preferred one and a Person. |
| 323 | - |
| 324 | - Also fire ObjectCreatedEvents for both the newly created Account, |
| 325 | - EmailAddress, and Person. |
| 326 | - """ |
| 327 | - person, email = getUtility(IPersonSet).createPersonAndEmail( |
| 328 | - self.context.email, rationale=self._getCreationRationale(), |
| 329 | - displayname=displayname, password=password, |
| 330 | - passwordEncrypted=True, hide_email_addresses=hide_email_addresses) |
| 331 | - person.validateAndEnsurePreferredEmail(email) |
| 332 | - account = getUtility(IAccountSet).get(person.accountID) |
| 333 | - removeSecurityProxy(account).status = AccountStatus.ACTIVE |
| 334 | - notify(ObjectCreatedEvent(person)) |
| 335 | - notify(ObjectCreatedEvent(account)) |
| 336 | - notify(ObjectCreatedEvent(email)) |
| 337 | - return account, person, email |
| 338 | |
| 339 | === modified file 'lib/canonical/launchpad/browser/tests/test_logintoken.py' |
| 340 | --- lib/canonical/launchpad/browser/tests/test_logintoken.py 2009-11-03 17:13:21 +0000 |
| 341 | +++ lib/canonical/launchpad/browser/tests/test_logintoken.py 2010-04-05 17:13:29 +0000 |
| 342 | @@ -6,7 +6,7 @@ |
| 343 | from zope.component import getUtility |
| 344 | |
| 345 | from canonical.launchpad.browser.logintoken import ( |
| 346 | - ClaimTeamView, ResetPasswordView, ValidateEmailView, ValidateGPGKeyView) |
| 347 | + ClaimTeamView, ValidateEmailView, ValidateGPGKeyView) |
| 348 | from canonical.launchpad.ftests import LaunchpadFormHarness |
| 349 | from canonical.launchpad.interfaces.authtoken import LoginTokenType |
| 350 | from canonical.launchpad.interfaces.logintoken import ILoginTokenSet |
| 351 | @@ -29,12 +29,6 @@ |
| 352 | self.email = self.person.preferredemail.email |
| 353 | self.expected_next_url = 'http://127.0.0.1/~test-user' |
| 354 | |
| 355 | - def test_ResetPasswordView(self): |
| 356 | - token = getUtility(ILoginTokenSet).new( |
| 357 | - self.person, self.email, self.email, |
| 358 | - LoginTokenType.PASSWORDRECOVERY) |
| 359 | - self._testCancelAction(ResetPasswordView, token) |
| 360 | - |
| 361 | def test_ClaimTeamView(self): |
| 362 | token = getUtility(ILoginTokenSet).new( |
| 363 | self.person, self.email, self.email, LoginTokenType.TEAMCLAIM) |
| 364 | |
| 365 | === removed file 'lib/canonical/launchpad/browser/tests/test_password_reset.py' |
| 366 | --- lib/canonical/launchpad/browser/tests/test_password_reset.py 2010-03-11 20:54:36 +0000 |
| 367 | +++ lib/canonical/launchpad/browser/tests/test_password_reset.py 1970-01-01 00:00:00 +0000 |
| 368 | @@ -1,94 +0,0 @@ |
| 369 | -# Copyright 2009 Canonical Ltd. This software is licensed under the |
| 370 | -# GNU Affero General Public License version 3 (see the file LICENSE). |
| 371 | - |
| 372 | -import unittest |
| 373 | - |
| 374 | -from zope.component import getUtility |
| 375 | -from zope.security.proxy import removeSecurityProxy |
| 376 | - |
| 377 | -import transaction |
| 378 | -from canonical.launchpad.browser.logintoken import ResetPasswordView |
| 379 | -from canonical.launchpad.ftests import LaunchpadFormHarness |
| 380 | -from canonical.launchpad.interfaces.account import AccountStatus |
| 381 | -from canonical.launchpad.interfaces.authtoken import LoginTokenType |
| 382 | -from canonical.launchpad.interfaces.emailaddress import EmailAddressStatus |
| 383 | -from canonical.launchpad.interfaces.logintoken import ILoginTokenSet |
| 384 | -from canonical.launchpad.interfaces.lpstorm import IMasterObject |
| 385 | -from lp.testing import TestCaseWithFactory |
| 386 | -from canonical.testing import DatabaseFunctionalLayer |
| 387 | - |
| 388 | - |
| 389 | -class TestPasswordReset(TestCaseWithFactory): |
| 390 | - layer = DatabaseFunctionalLayer |
| 391 | - email = 'foo@example.com' |
| 392 | - |
| 393 | - def _create_inactive_person(self): |
| 394 | - self.person = self.factory.makePerson( |
| 395 | - email=self.email, email_address_status=EmailAddressStatus.NEW) |
| 396 | - self.account = self.person.account |
| 397 | - self.assertEquals(self.account.status, AccountStatus.NOACCOUNT) |
| 398 | - |
| 399 | - def test_inactive_accounts_are_activated(self): |
| 400 | - # Resetting the password of an account in the NOACCOUNT state will |
| 401 | - # activate it. |
| 402 | - self._create_inactive_person() |
| 403 | - self._resetPassword(ensure_no_errors=True) |
| 404 | - self.assertEquals(self.account.status, AccountStatus.ACTIVE) |
| 405 | - self.assertEquals( |
| 406 | - self.account.preferredemail.email, 'foo@example.com') |
| 407 | - |
| 408 | - def _create_deactivated_person(self): |
| 409 | - self.person = self.factory.makePerson(email=self.email) |
| 410 | - removeSecurityProxy(self.person).deactivateAccount('Testing') |
| 411 | - # Get the account from the master DB to make sure it has the changes |
| 412 | - # we did above. |
| 413 | - self.account = IMasterObject(self.person.account) |
| 414 | - self.assertEquals(self.account.status, AccountStatus.DEACTIVATED) |
| 415 | - |
| 416 | - def test_deactivated_accounts_are_reactivated(self): |
| 417 | - # Resetting the password of an account in the DEACTIVATED state will |
| 418 | - # reactivate it. |
| 419 | - self._create_deactivated_person() |
| 420 | - self._resetPassword(ensure_no_errors=True) |
| 421 | - self.assertEquals(self.account.status, AccountStatus.ACTIVE) |
| 422 | - self.assertEquals( |
| 423 | - self.account.preferredemail.email, 'foo@example.com') |
| 424 | - |
| 425 | - def _create_suspended_person(self): |
| 426 | - self.person = self.factory.makePerson(email=self.email) |
| 427 | - # Get the account from the master DB as we're going to change it. |
| 428 | - self.account = IMasterObject(self.person.account) |
| 429 | - removeSecurityProxy(self.account).status = AccountStatus.SUSPENDED |
| 430 | - self.assertEquals(self.account.status, AccountStatus.SUSPENDED) |
| 431 | - |
| 432 | - def test_suspended_accounts_cannot_reset_password(self): |
| 433 | - # It's not possible to reset the password of a SUSPENDED account. |
| 434 | - self._create_suspended_person() |
| 435 | - harness = self._resetPassword() |
| 436 | - notifications = [notification.message |
| 437 | - for notification in harness.request.notifications] |
| 438 | - self.assertIn( |
| 439 | - 'Your password cannot be reset because your account is suspended', |
| 440 | - '\n'.join(notifications)) |
| 441 | - self.assertEquals(self.account.status, AccountStatus.SUSPENDED) |
| 442 | - |
| 443 | - def _resetPassword(self, ensure_no_errors=False): |
| 444 | - token = getUtility(ILoginTokenSet).new( |
| 445 | - self.person, self.email, self.email, |
| 446 | - LoginTokenType.PASSWORDRECOVERY) |
| 447 | - harness = LaunchpadFormHarness(token, ResetPasswordView) |
| 448 | - form = {'field.email': self.email, |
| 449 | - 'field.password': 'test', |
| 450 | - 'field.password_dupe': 'test'} |
| 451 | - harness.submit('continue', form) |
| 452 | - if ensure_no_errors: |
| 453 | - self.assertFalse(harness.hasErrors()) |
| 454 | - # Need to manually commit because we're interested in testing the |
| 455 | - # changes done by the view on the token's requester (i.e. |
| 456 | - # self.person). |
| 457 | - transaction.commit() |
| 458 | - return harness |
| 459 | - |
| 460 | - |
| 461 | -def test_suite(): |
| 462 | - return unittest.TestLoader().loadTestsFromName(__name__) |
| 463 | |
| 464 | === modified file 'lib/canonical/launchpad/database/logintoken.py' |
| 465 | --- lib/canonical/launchpad/database/logintoken.py 2010-03-05 13:04:06 +0000 |
| 466 | +++ lib/canonical/launchpad/database/logintoken.py 2010-04-05 17:13:29 +0000 |
| 467 | @@ -152,23 +152,6 @@ |
| 468 | subject = 'Launchpad: Confirm your OpenPGP Key' |
| 469 | self._send_email(from_name, subject, text) |
| 470 | |
| 471 | - def sendPasswordResetEmail(self): |
| 472 | - """See ILoginToken.""" |
| 473 | - template = get_email_template('forgottenpassword.txt') |
| 474 | - from_name = "Launchpad" |
| 475 | - message = template % dict(token_url=canonical_url(self)) |
| 476 | - subject = "Launchpad: Forgotten Password" |
| 477 | - self._send_email(from_name, subject, message) |
| 478 | - |
| 479 | - def sendNewUserEmail(self): |
| 480 | - """See ILoginToken.""" |
| 481 | - template = get_email_template('newuser-email.txt') |
| 482 | - message = template % dict(token_url=canonical_url(self)) |
| 483 | - |
| 484 | - from_name = "Launchpad" |
| 485 | - subject = "Launchpad: complete your registration" |
| 486 | - self._send_email(from_name, subject, message) |
| 487 | - |
| 488 | def sendProfileCreatedEmail(self, profile, comment): |
| 489 | """See ILoginToken.""" |
| 490 | template = get_email_template('profile-created.txt') |
| 491 | |
| 492 | === modified file 'lib/canonical/launchpad/doc/logintoken-pages.txt' |
| 493 | --- lib/canonical/launchpad/doc/logintoken-pages.txt 2009-04-17 10:32:16 +0000 |
| 494 | +++ lib/canonical/launchpad/doc/logintoken-pages.txt 2010-04-05 17:13:29 +0000 |
| 495 | @@ -1,19 +1,18 @@ |
| 496 | = LoginToken pages = |
| 497 | |
| 498 | -Users interact with login tokens for operations that require the user to |
| 499 | -prove he has access to a resource that is external to Launchpad. For |
| 500 | -example, claiming an email address or resetting a password require the |
| 501 | -user to use the login token sent to him in an email. |
| 502 | - |
| 503 | - |
| 504 | -== Reset password view == |
| 505 | - |
| 506 | -A user can reset his password if he forgets it. A link for a LoginToken |
| 507 | -is sent to the email address that the user claims is his. The view |
| 508 | -displays a form asking the user to confirm his email address and provide |
| 509 | -a new password. |
| 510 | - |
| 511 | - >>> from zope.component import getMultiAdapter, getUtility |
| 512 | +Users interact with login tokens for operations that require the user to prove |
| 513 | +he has access to a resource that is external to Launchpad. For example, |
| 514 | +claiming an email address or a OpenPGP key require the user to use the login |
| 515 | +token sent to him in an email. |
| 516 | + |
| 517 | + |
| 518 | +== Claiming a profile == |
| 519 | + |
| 520 | +The ClaimProfileView allows users to claim accounts created by Launchpad |
| 521 | +processes. The user profile for Matsubara was created during an import. |
| 522 | +The user profile does not have a preferred email address or a password. |
| 523 | + |
| 524 | + >>> from zope.component import getMultiAdapter |
| 525 | >>> from canonical.launchpad.interfaces.authtoken import LoginTokenType |
| 526 | >>> from canonical.launchpad.interfaces.logintoken import ( |
| 527 | ... ILoginTokenSet) |
| 528 | @@ -24,159 +23,7 @@ |
| 529 | # of gettint it as a zope secured utility. If we don't do that we'd have |
| 530 | # to remove the security proxy of the person objects all the time. |
| 531 | >>> person_set = PersonSet() |
| 532 | - >>> no_priv = person_set.getByEmail('no-priv@canonical.com') |
| 533 | - >>> old_password = no_priv.account.password |
| 534 | - |
| 535 | >>> login_token_set = getUtility(ILoginTokenSet) |
| 536 | - >>> login_token = login_token_set.new( |
| 537 | - ... no_priv, 'no-priv@canonical.com', |
| 538 | - ... 'no-priv@canonical.com', LoginTokenType.PASSWORDRECOVERY) |
| 539 | - >>> request = LaunchpadTestRequest( |
| 540 | - ... SERVER_URL='http://launchpad.dev', |
| 541 | - ... PATH_INFO='/token/%s/+resetpassword' % login_token.token, |
| 542 | - ... method='POST', |
| 543 | - ... form={ |
| 544 | - ... 'field.email': 'no-priv@canonical.com', |
| 545 | - ... 'field.password': 'test1', |
| 546 | - ... 'field.password_dupe': 'test1', |
| 547 | - ... 'field.actions.continue': 'Continue', |
| 548 | - ... }) |
| 549 | - >>> login(ANONYMOUS, request) |
| 550 | - >>> resetpassword_view = getMultiAdapter( |
| 551 | - ... (login_token, request), name="+resetpassword") |
| 552 | - >>> resetpassword_view.initialize() |
| 553 | - >>> old_password == no_priv.account.password |
| 554 | - False |
| 555 | - |
| 556 | -If the user submits the wrong email address for the token, an error is |
| 557 | -stored. The password is not reset. |
| 558 | - |
| 559 | - >>> login_token = login_token_set.new( |
| 560 | - ... no_priv, 'no-priv@canonical.com', |
| 561 | - ... 'no-priv@canonical.com', LoginTokenType.PASSWORDRECOVERY) |
| 562 | - >>> request = LaunchpadTestRequest( |
| 563 | - ... SERVER_URL='http://launchpad.dev', |
| 564 | - ... PATH_INFO='/token/%s/+resetpassword' % login_token.token, |
| 565 | - ... method='POST', |
| 566 | - ... form={ |
| 567 | - ... 'field.email': 'wrong@canonical.com', |
| 568 | - ... 'field.password': 'test2', |
| 569 | - ... 'field.password_dupe': 'test2', |
| 570 | - ... 'field.actions.continue': 'Continue', |
| 571 | - ... }) |
| 572 | - >>> login(ANONYMOUS, request) |
| 573 | - >>> resetpassword_view = getMultiAdapter( |
| 574 | - ... (login_token, request), name="+resetpassword") |
| 575 | - >>> resetpassword_view.initialize() |
| 576 | - >>> resetpassword_view.errors |
| 577 | - [u"The email address ... didn't match ... the password reset."] |
| 578 | - |
| 579 | - >>> old_password == no_priv.account.password |
| 580 | - True |
| 581 | - |
| 582 | - |
| 583 | -=== Reactivating an account using reset password === |
| 584 | - |
| 585 | -A user with a DEACTIVATED account does not have a password--he must use |
| 586 | -the +resetpassword view to set his account to ACTIVE and add a password. |
| 587 | - |
| 588 | - >>> from canonical.launchpad.interfaces import IMasterObject |
| 589 | - >>> former_user = IMasterObject( |
| 590 | - ... person_set.getByEmail('former-user@canonical.com')) |
| 591 | - >>> former_user.name |
| 592 | - u'former-user-deactivatedaccount' |
| 593 | - |
| 594 | - >>> former_user.account.status |
| 595 | - <DBItem AccountStatus.DEACTIVATED, ...> |
| 596 | - >>> print former_user.account.password |
| 597 | - None |
| 598 | - |
| 599 | - >>> login_token = login_token_set.new( |
| 600 | - ... former_user, 'former-user@canonical.com', |
| 601 | - ... 'former-user@canonical.com', LoginTokenType.PASSWORDRECOVERY) |
| 602 | - >>> transaction.commit() |
| 603 | - |
| 604 | -If the user provides the correct email address for the token, the |
| 605 | -password is reset, and the account status is set ACTIVE. |
| 606 | - |
| 607 | - >>> request = LaunchpadTestRequest( |
| 608 | - ... SERVER_URL='http://launchpad.dev', |
| 609 | - ... PATH_INFO='/token/%s/+resetpassword' % login_token.token, |
| 610 | - ... method='POST', |
| 611 | - ... form={ |
| 612 | - ... 'field.email': 'former-user@canonical.com', |
| 613 | - ... 'field.password': 'test3', |
| 614 | - ... 'field.password_dupe': 'test3', |
| 615 | - ... 'field.actions.continue': 'Continue', |
| 616 | - ... }) |
| 617 | - >>> login(ANONYMOUS, request) |
| 618 | - >>> resetpassword_view = getMultiAdapter( |
| 619 | - ... (login_token, request), name="+resetpassword") |
| 620 | - >>> resetpassword_view.initialize() |
| 621 | - >>> transaction.commit() |
| 622 | - |
| 623 | - >>> former_user.name |
| 624 | - u'former-user' |
| 625 | - >>> former_user.account.status |
| 626 | - <DBItem AccountStatus.ACTIVE, ...> |
| 627 | - >>> former_user.is_valid_person |
| 628 | - True |
| 629 | - |
| 630 | - |
| 631 | -== Reset password cannot reactivate a Suspended account == |
| 632 | - |
| 633 | -A user with a SUSPENDED account cannot use +resetpassword view to set his |
| 634 | -account to ACTIVE, nor will it reset his password. |
| 635 | - |
| 636 | - >>> from canonical.launchpad.interfaces.account import AccountStatus |
| 637 | - |
| 638 | - # Admins can suspend an account and access the user's password. |
| 639 | - >>> login('foo.bar@canonical.com') |
| 640 | - >>> suspended_user = factory.makePerson( |
| 641 | - ... email='suspended-user@canonical.com', |
| 642 | - ... name='suspended-user', |
| 643 | - ... password='invalid') |
| 644 | - >>> suspended_account = IMasterObject(suspended_user.account) |
| 645 | - >>> suspended_account.status = AccountStatus.SUSPENDED |
| 646 | - >>> original_password = suspended_account.password |
| 647 | - >>> transaction.commit() |
| 648 | - >>> suspended_user.is_valid_person |
| 649 | - False |
| 650 | - >>> suspended_account.status |
| 651 | - <DBItem AccountStatus.SUSPENDED, ...> |
| 652 | - |
| 653 | - >>> login_token = login_token_set.new( |
| 654 | - ... suspended_user, 'suspended-user@canonical.com', |
| 655 | - ... 'suspended-user@canonical.com', LoginTokenType.PASSWORDRECOVERY) |
| 656 | - >>> request = LaunchpadTestRequest( |
| 657 | - ... SERVER_URL='http://launchpad.dev', |
| 658 | - ... PATH_INFO='/token/%s/+resetpassword' % login_token.token, |
| 659 | - ... method='POST', |
| 660 | - ... form={ |
| 661 | - ... 'field.email': 'suspended-user@canonical.com', |
| 662 | - ... 'field.password': 'bad-intentions', |
| 663 | - ... 'field.password_dupe': 'bad-intentions', |
| 664 | - ... 'field.actions.continue': 'Continue', |
| 665 | - ... }) |
| 666 | - >>> login(ANONYMOUS, request) |
| 667 | - >>> resetpassword_view = getMultiAdapter( |
| 668 | - ... (login_token, request), name="+resetpassword") |
| 669 | - >>> resetpassword_view.initialize() |
| 670 | - |
| 671 | - >>> login('foo.bar@canonical.com') |
| 672 | - >>> suspended_user.is_valid_person |
| 673 | - False |
| 674 | - >>> suspended_user.account.password == original_password |
| 675 | - True |
| 676 | - >>> suspended_user.account.status |
| 677 | - <DBItem AccountStatus.SUSPENDED, ...> |
| 678 | - |
| 679 | - |
| 680 | -== Claiming a profile == |
| 681 | - |
| 682 | -The ClaimProfileView allows users to claim accounts created by Launchpad |
| 683 | -processes. The user profile for Matsubara was created during an import. |
| 684 | -The user profile does not have a preferred email address or a password. |
| 685 | |
| 686 | >>> matsubara = person_set.getByEmail('matsubara@async.com.br') |
| 687 | >>> matsubara.name |
| 688 | @@ -240,135 +87,6 @@ |
| 689 | True |
| 690 | |
| 691 | |
| 692 | -== Creating a new ordinary account == |
| 693 | - |
| 694 | -Unlike accounts created using OpenID, which don't have an associated Person |
| 695 | -(as shown in sso-workflow-register.txt), ordinary accounts will always have |
| 696 | -a Person entry associated with them. |
| 697 | - |
| 698 | - >>> login_token = login_token_set.new( |
| 699 | - ... None, 'foo@debian.org', 'foo@debian.org', |
| 700 | - ... LoginTokenType.NEWACCOUNT) |
| 701 | - >>> request = LaunchpadTestRequest( |
| 702 | - ... SERVER_URL='http://launchpad.dev', |
| 703 | - ... PATH_INFO='/token/%s/+newaccount' % login_token.token, |
| 704 | - ... method='POST', |
| 705 | - ... form={ |
| 706 | - ... 'field.displayname': 'Andre Lopes', |
| 707 | - ... 'field.hide_email_addresses.used': '', |
| 708 | - ... 'field.password': 'test', |
| 709 | - ... 'field.password_dupe': 'test', |
| 710 | - ... 'field.actions.continue': 'Continue', |
| 711 | - ... }) |
| 712 | - >>> login(ANONYMOUS, request) |
| 713 | - >>> newaccount_view = getMultiAdapter( |
| 714 | - ... (login_token, request), name="+newaccount") |
| 715 | - >>> newaccount_view.initialize() |
| 716 | - >>> newaccount_view.errors |
| 717 | - [] |
| 718 | - >>> person_set.getByEmail('foo@debian.org') |
| 719 | - <Person... |
| 720 | - |
| 721 | - |
| 722 | -== Creating a new account from an existing profile == |
| 723 | - |
| 724 | -If a user's email address matches the email address of an unclaimed |
| 725 | -account, the existing profile is activated and given to the user. |
| 726 | -Andre Lopes (andrelop) has an account created via an automated process. |
| 727 | - |
| 728 | - >>> andrelop = person_set.getByEmail('andrelop@debian.org') |
| 729 | - >>> andrelop.name |
| 730 | - u'andrelop' |
| 731 | - >>> print andrelop.preferredemail |
| 732 | - None |
| 733 | - |
| 734 | - >>> andrelop.account.status |
| 735 | - <DBItem AccountStatus.NOACCOUNT, ...> |
| 736 | - >>> andrelop.is_valid_person |
| 737 | - False |
| 738 | - |
| 739 | -Andre would normally create a new account by claiming a LoginToken |
| 740 | -that verifies his access to the email address his provided. |
| 741 | - |
| 742 | - >>> login_token = login_token_set.new( |
| 743 | - ... andrelop, 'andrelop@debian.org', |
| 744 | - ... 'andrelop@debian.org', LoginTokenType.NEWACCOUNT) |
| 745 | - >>> transaction.commit() |
| 746 | - |
| 747 | -The NewAccountView will activate the existing account and update |
| 748 | -the Person object. |
| 749 | - |
| 750 | - >>> request = LaunchpadTestRequest( |
| 751 | - ... SERVER_URL='http://launchpad.dev', |
| 752 | - ... PATH_INFO='/token/%s/+newaccount' % login_token.token, |
| 753 | - ... method='POST', |
| 754 | - ... form={ |
| 755 | - ... 'field.displayname': 'Andre Lopes', |
| 756 | - ... 'field.hide_email_addresses.used': '', |
| 757 | - ... 'field.password': 'test5', |
| 758 | - ... 'field.password_dupe': 'test5', |
| 759 | - ... 'field.actions.continue': 'Continue', |
| 760 | - ... }) |
| 761 | - >>> login(ANONYMOUS, request) |
| 762 | - >>> claimprofile_view = getMultiAdapter( |
| 763 | - ... (login_token, request), name="+newaccount") |
| 764 | - >>> claimprofile_view.initialize() |
| 765 | - >>> claimprofile_view.errors |
| 766 | - [] |
| 767 | - >>> transaction.commit() |
| 768 | - |
| 769 | -The existing Account, Person and EmailAddress are updated. |
| 770 | - |
| 771 | - >>> andrelop.displayname |
| 772 | - u'Andre Lopes' |
| 773 | - >>> andrelop.hide_email_addresses |
| 774 | - False |
| 775 | - >>> andrelop.account.preferredemail.email |
| 776 | - u'andrelop@debian.org' |
| 777 | - |
| 778 | - >>> andrelop.account.status |
| 779 | - <DBItem AccountStatus.ACTIVE, ...> |
| 780 | - >>> andrelop.account.status_comment |
| 781 | - u'Activated by new account.' |
| 782 | - >>> andrelop.is_valid_person |
| 783 | - True |
| 784 | - |
| 785 | - |
| 786 | -== Creating a new account cannot reactivate a suspended profile == |
| 787 | - |
| 788 | -An existing account that is SUSPENDED cannot be reactivated by claiming |
| 789 | -the email address during account creation. |
| 790 | - |
| 791 | - >>> login_token = login_token_set.new( |
| 792 | - ... suspended_user, 'suspended-user@canonical.com', |
| 793 | - ... 'suspended-user@canonical.com', LoginTokenType.NEWACCOUNT) |
| 794 | - >>> request = LaunchpadTestRequest( |
| 795 | - ... SERVER_URL='http://launchpad.dev', |
| 796 | - ... PATH_INFO='/token/%s/+newaccount' % login_token.token, |
| 797 | - ... method='POST', |
| 798 | - ... form={ |
| 799 | - ... 'field.displayname': 'Bad User', |
| 800 | - ... 'field.hide_email_addresses.used': '', |
| 801 | - ... 'field.password': 'bad-intentions', |
| 802 | - ... 'field.password_dupe': 'bad-intentions', |
| 803 | - ... 'field.actions.continue': 'Continue', |
| 804 | - ... }) |
| 805 | - >>> login(ANONYMOUS, request) |
| 806 | - >>> claimprofile_view = getMultiAdapter( |
| 807 | - ... (login_token, request), name="+newaccount") |
| 808 | - >>> claimprofile_view.initialize() |
| 809 | - >>> claimprofile_view.errors |
| 810 | - [] |
| 811 | - |
| 812 | - >>> login('foo.bar@canonical.com') |
| 813 | - >>> suspended_user.is_valid_person |
| 814 | - False |
| 815 | - >>> suspended_user.account.password == original_password |
| 816 | - True |
| 817 | - >>> suspended_user.account.status |
| 818 | - <DBItem AccountStatus.SUSPENDED, ...> |
| 819 | - |
| 820 | - |
| 821 | == Validating GPG keys == |
| 822 | |
| 823 | In order to add a GPG key to Launchpad we require that the person |
| 824 | |
| 825 | === removed file 'lib/canonical/launchpad/emailtemplates/forgottenpassword-neutral.txt' |
| 826 | --- lib/canonical/launchpad/emailtemplates/forgottenpassword-neutral.txt 2007-06-19 15:54:26 +0000 |
| 827 | +++ lib/canonical/launchpad/emailtemplates/forgottenpassword-neutral.txt 1970-01-01 00:00:00 +0000 |
| 828 | @@ -1,14 +0,0 @@ |
| 829 | -Hello |
| 830 | - |
| 831 | -You have requested a new password for your Launchpad Login Service account. |
| 832 | - |
| 833 | -To change your password: |
| 834 | - |
| 835 | - %(token_url)s |
| 836 | - |
| 837 | -If you don't know what this is about, then someone else has entered |
| 838 | -your e-mail address at the Launchpad Login Service. Sorry about |
| 839 | -that. You don't need to do anything further, just delete this message. |
| 840 | - |
| 841 | -Regards, |
| 842 | -The Launchpad Login Service team |
| 843 | |
| 844 | === removed file 'lib/canonical/launchpad/emailtemplates/forgottenpassword.txt' |
| 845 | --- lib/canonical/launchpad/emailtemplates/forgottenpassword.txt 2006-08-31 17:52:22 +0000 |
| 846 | +++ lib/canonical/launchpad/emailtemplates/forgottenpassword.txt 1970-01-01 00:00:00 +0000 |
| 847 | @@ -1,11 +0,0 @@ |
| 848 | -Hi |
| 849 | - |
| 850 | -You, or someone posing as you, has requested a new password for |
| 851 | -your Launchpad account. |
| 852 | - |
| 853 | -To reset your password, please click on the link below. |
| 854 | -You will need to enter this email address, enter and confirm your new password. |
| 855 | - |
| 856 | - %(token_url)s |
| 857 | - |
| 858 | -The Launchpad Team |
| 859 | |
| 860 | === removed file 'lib/canonical/launchpad/emailtemplates/newuser-email-neutral.txt' |
| 861 | --- lib/canonical/launchpad/emailtemplates/newuser-email-neutral.txt 2007-06-19 15:54:26 +0000 |
| 862 | +++ lib/canonical/launchpad/emailtemplates/newuser-email-neutral.txt 1970-01-01 00:00:00 +0000 |
| 863 | @@ -1,15 +0,0 @@ |
| 864 | -Hello |
| 865 | - |
| 866 | -Thank you for registering with the Launchpad Login Service. |
| 867 | - |
| 868 | -To complete your registration: |
| 869 | - |
| 870 | - %(token_url)s |
| 871 | - |
| 872 | -If you don't know what this is about, then someone has probably |
| 873 | -entered your e-mail address by mistake at the Launchpad Login Service |
| 874 | -Web site. Sorry about that. You don't need to do anything further, |
| 875 | -just delete this message. |
| 876 | - |
| 877 | -Regards, |
| 878 | -The Launchpad Login Service team |
| 879 | |
| 880 | === removed file 'lib/canonical/launchpad/emailtemplates/newuser-email.txt' |
| 881 | --- lib/canonical/launchpad/emailtemplates/newuser-email.txt 2008-11-17 17:17:33 +0000 |
| 882 | +++ lib/canonical/launchpad/emailtemplates/newuser-email.txt 1970-01-01 00:00:00 +0000 |
| 883 | @@ -1,19 +0,0 @@ |
| 884 | -Hello! |
| 885 | - |
| 886 | -Thank you for registering at Launchpad. To complete your |
| 887 | -registration, please follow the instructions here: |
| 888 | - |
| 889 | - %(token_url)s |
| 890 | - |
| 891 | -Launchpad is a suite of applications for people who work with |
| 892 | -free software projects. Using Launchpad you can report bugs, help |
| 893 | -with translation, find the answer to a question, find and share |
| 894 | -code and host your own free software project. |
| 895 | - |
| 896 | -Once you've completed the registration, take a look at our |
| 897 | -"getting started" guide here: |
| 898 | - |
| 899 | - https://help.launchpad.net/NewToLaunchpad |
| 900 | - |
| 901 | -Thanks, |
| 902 | -The Launchpad team. |
| 903 | |
| 904 | === modified file 'lib/canonical/launchpad/interfaces/authtoken.py' |
| 905 | --- lib/canonical/launchpad/interfaces/authtoken.py 2009-10-13 16:59:43 +0000 |
| 906 | +++ lib/canonical/launchpad/interfaces/authtoken.py 2010-04-05 17:13:29 +0000 |
| 907 | @@ -10,7 +10,6 @@ |
| 908 | __all__ = [ |
| 909 | 'LoginTokenType', |
| 910 | 'IAuthToken', |
| 911 | - 'IAuthTokenSet', |
| 912 | ] |
| 913 | |
| 914 | from zope.schema import Choice, Datetime, Int, Text, TextLine |
| 915 | @@ -118,6 +117,9 @@ |
| 916 | """) |
| 917 | |
| 918 | |
| 919 | +# XXX: Guilherme Salgado, 2010-03-30: This interface was created to be used by |
| 920 | +# our old OpenID provider, but that doesn't exist anymore, so we should merge |
| 921 | +# it with ILoginToken. |
| 922 | class IAuthToken(Interface): |
| 923 | """The object that stores one time tokens used for validating email |
| 924 | addresses and other tasks that require verifying if an email address is |
| 925 | @@ -193,55 +195,3 @@ |
| 926 | """Send an email message to the requester with a magic URL that allows |
| 927 | him to finish the Launchpad registration process. |
| 928 | """ |
| 929 | - |
| 930 | - |
| 931 | -class IAuthTokenSet(Interface): |
| 932 | - """The set of AuthTokens.""" |
| 933 | - |
| 934 | - title = Attribute('Title') |
| 935 | - |
| 936 | - def get(id, default=None): |
| 937 | - """Return the AuthToken object with the given id. |
| 938 | - |
| 939 | - Return the default value if there's no such AuthToken. |
| 940 | - """ |
| 941 | - |
| 942 | - def searchByEmailAccountAndType(email, account, type, consumed=None): |
| 943 | - """Return all AuthTokens for the given email, account and type. |
| 944 | - |
| 945 | - :param email: The email address to search for. |
| 946 | - :param account: The Account object representing the requester |
| 947 | - to search for. |
| 948 | - :param type: The AuthTokenType to search for. |
| 949 | - :param consumed: A flag indicating whether to return consumed tokens. |
| 950 | - If False, only unconsumed tokens will be returned. |
| 951 | - If True, only consumed tokens will be returned. |
| 952 | - If None, this parameter will be ignored and all tokens will be |
| 953 | - returned. |
| 954 | - """ |
| 955 | - |
| 956 | - def deleteByEmailAccountAndType(email, account, type): |
| 957 | - """Delete all AuthToken entries with the given email, |
| 958 | - requester account and type.""" |
| 959 | - |
| 960 | - def new(requester, requesteremail, email, tokentype, redirection_url): |
| 961 | - """Create a new AuthToken object. |
| 962 | - |
| 963 | - :param requester: a Person object or None (in case of a new |
| 964 | - account) |
| 965 | - :param requesteremail: the email address used to login on the |
| 966 | - system. Can also be None in case of a new account |
| 967 | - :param email: the email address that this request will be sent |
| 968 | - to. It should be previously validated by valid_email() |
| 969 | - :param tokentype: the type of the request, according to |
| 970 | - LoginTokenType. |
| 971 | - :param redirection_url: the URL the user will be forwarded to |
| 972 | - after consuming the token. May be None. |
| 973 | - """ |
| 974 | - |
| 975 | - def __getitem__(id): |
| 976 | - """Returns the AuthToken with the given id. |
| 977 | - |
| 978 | - Raises KeyError if there is no such AuthToken. |
| 979 | - """ |
| 980 | - |
| 981 | |
| 982 | === removed file 'lib/canonical/launchpad/templates/logintoken-newaccount.pt' |
| 983 | --- lib/canonical/launchpad/templates/logintoken-newaccount.pt 2009-09-16 20:51:57 +0000 |
| 984 | +++ lib/canonical/launchpad/templates/logintoken-newaccount.pt 1970-01-01 00:00:00 +0000 |
| 985 | @@ -1,19 +0,0 @@ |
| 986 | -<html |
| 987 | - xmlns="http://www.w3.org/1999/xhtml" |
| 988 | - xmlns:tal="http://xml.zope.org/namespaces/tal" |
| 989 | - xmlns:metal="http://xml.zope.org/namespaces/metal" |
| 990 | - xmlns:i18n="http://xml.zope.org/namespaces/i18n" |
| 991 | - metal:use-macro="view/macro:page/locationless" |
| 992 | - i18n:domain="launchpad" |
| 993 | -> |
| 994 | - <body> |
| 995 | - |
| 996 | - <div metal:fill-slot="main"> |
| 997 | - |
| 998 | - <div metal:use-macro="context/@@launchpad_form/form" /> |
| 999 | - |
| 1000 | - </div> |
| 1001 | - </body> |
| 1002 | - |
| 1003 | -</html> |
| 1004 | - |
| 1005 | |
| 1006 | === removed file 'lib/canonical/launchpad/templates/logintoken-resetpassword.pt' |
| 1007 | --- lib/canonical/launchpad/templates/logintoken-resetpassword.pt 2009-09-16 20:51:57 +0000 |
| 1008 | +++ lib/canonical/launchpad/templates/logintoken-resetpassword.pt 1970-01-01 00:00:00 +0000 |
| 1009 | @@ -1,18 +0,0 @@ |
| 1010 | -<html |
| 1011 | - xmlns="http://www.w3.org/1999/xhtml" |
| 1012 | - xmlns:tal="http://xml.zope.org/namespaces/tal" |
| 1013 | - xmlns:metal="http://xml.zope.org/namespaces/metal" |
| 1014 | - xmlns:i18n="http://xml.zope.org/namespaces/i18n" |
| 1015 | - metal:use-macro="view/macro:page/locationless" |
| 1016 | - i18n:domain="launchpad" |
| 1017 | -> |
| 1018 | - <body> |
| 1019 | - |
| 1020 | -<div metal:fill-slot="main"> |
| 1021 | - |
| 1022 | - <div metal:use-macro="context/@@launchpad_form/form" /> |
| 1023 | - |
| 1024 | -</div> |
| 1025 | - |
| 1026 | -</body> |
| 1027 | -</html> |
| 1028 | |
| 1029 | === modified file 'lib/canonical/launchpad/tests/test_login.py' |
| 1030 | --- lib/canonical/launchpad/tests/test_login.py 2009-07-17 00:26:05 +0000 |
| 1031 | +++ lib/canonical/launchpad/tests/test_login.py 2010-04-05 17:13:29 +0000 |
| 1032 | @@ -13,13 +13,11 @@ |
| 1033 | from canonical.launchpad.ftests import ANONYMOUS, login |
| 1034 | from canonical.launchpad.interfaces.account import ( |
| 1035 | AccountCreationRationale, IAccountSet) |
| 1036 | -from lp.registry.interfaces.person import IPerson |
| 1037 | from lp.testing import TestCaseWithFactory |
| 1038 | from canonical.launchpad.webapp.authentication import LaunchpadPrincipal |
| 1039 | from canonical.launchpad.webapp.interfaces import ( |
| 1040 | CookieAuthLoggedInEvent, ILaunchpadPrincipal, IPlacelessAuthUtility) |
| 1041 | -from canonical.launchpad.webapp.login import ( |
| 1042 | - logInPrincipal, logInPrincipalAndMaybeCreatePerson, logoutPerson) |
| 1043 | +from canonical.launchpad.webapp.login import logInPrincipal, logoutPerson |
| 1044 | from canonical.launchpad.webapp.servers import LaunchpadTestRequest |
| 1045 | from canonical.testing import DatabaseFunctionalLayer |
| 1046 | |
| 1047 | @@ -137,23 +135,6 @@ |
| 1048 | self.failUnless(ILaunchpadPrincipal.providedBy(principal)) |
| 1049 | self.failUnless(principal.person is None) |
| 1050 | |
| 1051 | - def test_logInPrincipalAndMaybeCreatePerson(self): |
| 1052 | - # logInPrincipalAndMaybeCreatePerson() will log the given principal in |
| 1053 | - # and create a Person entry associated with it if one doesn't exist |
| 1054 | - # already. |
| 1055 | - logInPrincipalAndMaybeCreatePerson( |
| 1056 | - self.request, self.principal, 'foo@example.com') |
| 1057 | - |
| 1058 | - # This is so that the authenticate() call below uses cookie auth. |
| 1059 | - self.request.response.setCookie( |
| 1060 | - config.launchpad_session.cookie, 'xxx') |
| 1061 | - |
| 1062 | - principal = getUtility(IPlacelessAuthUtility).authenticate( |
| 1063 | - self.request) |
| 1064 | - self.failUnless(ILaunchpadPrincipal.providedBy(principal)) |
| 1065 | - person = IPerson(principal.account) |
| 1066 | - self.failUnless(IPerson.providedBy(person)) |
| 1067 | - |
| 1068 | |
| 1069 | def test_suite(): |
| 1070 | return unittest.TestLoader().loadTestsFromName(__name__) |
| 1071 | |
| 1072 | === modified file 'lib/canonical/launchpad/tests/test_token_creation.py' |
| 1073 | --- lib/canonical/launchpad/tests/test_token_creation.py 2009-06-25 05:30:52 +0000 |
| 1074 | +++ lib/canonical/launchpad/tests/test_token_creation.py 2010-04-05 17:13:29 +0000 |
| 1075 | @@ -36,9 +36,9 @@ |
| 1076 | |
| 1077 | # Now insert the token in the table so that the next time we call |
| 1078 | # create_unique_token_for_table() we get a different token. |
| 1079 | - login_token = LoginToken( |
| 1080 | + LoginToken( |
| 1081 | requester=None, token=token2, email='email@example.com', |
| 1082 | - tokentype=LoginTokenType.NEWACCOUNT, created=UTC_NOW) |
| 1083 | + tokentype=LoginTokenType.ACCOUNTMERGE, created=UTC_NOW) |
| 1084 | random.seed(0) |
| 1085 | token3 = create_unique_token_for_table(99, LoginToken.token) |
| 1086 | self.assertNotEquals(token1, token3) |
| 1087 | |
| 1088 | === modified file 'lib/canonical/launchpad/webapp/login.py' |
| 1089 | --- lib/canonical/launchpad/webapp/login.py 2010-03-30 14:57:02 +0000 |
| 1090 | +++ lib/canonical/launchpad/webapp/login.py 2010-04-05 17:13:29 +0000 |
| 1091 | @@ -38,8 +38,8 @@ |
| 1092 | from canonical.launchpad.webapp.dbpolicy import MasterDatabasePolicy |
| 1093 | from canonical.launchpad.webapp.error import SystemErrorView |
| 1094 | from canonical.launchpad.webapp.interfaces import ( |
| 1095 | - CookieAuthLoggedInEvent, ILaunchpadApplication, ILaunchpadPrincipal, |
| 1096 | - IPlacelessAuthUtility, IPlacelessLoginSource, LoggedOutEvent) |
| 1097 | + CookieAuthLoggedInEvent, ILaunchpadApplication, IPlacelessAuthUtility, |
| 1098 | + IPlacelessLoginSource, LoggedOutEvent) |
| 1099 | from canonical.launchpad.webapp.metazcml import ILaunchpadPermission |
| 1100 | from canonical.launchpad.webapp.publisher import LaunchpadView |
| 1101 | from canonical.launchpad.webapp.url import urlappend |
| 1102 | @@ -378,24 +378,6 @@ |
| 1103 | notify(CookieAuthLoggedInEvent(request, email)) |
| 1104 | |
| 1105 | |
| 1106 | -def logInPrincipalAndMaybeCreatePerson(request, principal, email): |
| 1107 | - """Log the principal in, creating a Person if necessary. |
| 1108 | - |
| 1109 | - If the given principal has no associated person, we create a new |
| 1110 | - person, fetch a new principal and set it in the request. |
| 1111 | - |
| 1112 | - Password validation must be done in callsites. |
| 1113 | - """ |
| 1114 | - logInPrincipal(request, principal, email) |
| 1115 | - if ILaunchpadPrincipal.providedBy(principal) and principal.person is None: |
| 1116 | - person = principal.account.createPerson( |
| 1117 | - PersonCreationRationale.OWNER_CREATED_LAUNCHPAD) |
| 1118 | - new_principal = getUtility(IPlacelessLoginSource).getPrincipal( |
| 1119 | - principal.id) |
| 1120 | - assert ILaunchpadPrincipal.providedBy(new_principal) |
| 1121 | - request.setPrincipal(new_principal) |
| 1122 | - |
| 1123 | - |
| 1124 | def expireSessionCookie(request, client_id_manager=None, |
| 1125 | delta=timedelta(minutes=10)): |
| 1126 | if client_id_manager is None: |
| 1127 | |
| 1128 | === modified file 'lib/canonical/launchpad/webapp/publication.py' |
| 1129 | --- lib/canonical/launchpad/webapp/publication.py 2010-03-30 14:33:26 +0000 |
| 1130 | +++ lib/canonical/launchpad/webapp/publication.py 2010-04-05 17:13:29 +0000 |
| 1131 | @@ -317,7 +317,7 @@ |
| 1132 | if request.method != 'POST': |
| 1133 | return |
| 1134 | # XXX: jamesh 2007-11-23 bug=124421: |
| 1135 | - # Allow offsite posts to our OpenID endpoint. Ideally we'd |
| 1136 | + # Allow offsite posts to our TestOpenID endpoint. Ideally we'd |
| 1137 | # have a better way of marking this URL as allowing offsite |
| 1138 | # form posts. |
| 1139 | if request['PATH_INFO'] == '/+openid': |
| 1140 | |
| 1141 | === modified file 'lib/canonical/launchpad/zcml/logintoken.zcml' |
| 1142 | --- lib/canonical/launchpad/zcml/logintoken.zcml 2009-07-18 00:05:49 +0000 |
| 1143 | +++ lib/canonical/launchpad/zcml/logintoken.zcml 2010-04-05 17:13:29 +0000 |
| 1144 | @@ -36,22 +36,6 @@ |
| 1145 | /> |
| 1146 | |
| 1147 | <browser:page |
| 1148 | - name="+newaccount" |
| 1149 | - for="canonical.launchpad.interfaces.ILoginToken" |
| 1150 | - class="canonical.launchpad.browser.logintoken.NewUserAccountView" |
| 1151 | - permission="zope.Public" |
| 1152 | - template="../../launchpad/templates/logintoken-newaccount.pt" |
| 1153 | - /> |
| 1154 | - |
| 1155 | - <browser:page |
| 1156 | - for="canonical.launchpad.interfaces.ILoginToken" |
| 1157 | - name="+resetpassword" |
| 1158 | - permission="zope.Public" |
| 1159 | - class="canonical.launchpad.browser.logintoken.ResetPasswordView" |
| 1160 | - template="../templates/logintoken-resetpassword.pt" |
| 1161 | - /> |
| 1162 | - |
| 1163 | - <browser:page |
| 1164 | for="canonical.launchpad.interfaces.ILoginToken" |
| 1165 | name="+validateemail" |
| 1166 | permission="zope.Public" |
| 1167 | |
| 1168 | === modified file 'lib/lp/registry/browser/configure.zcml' |
| 1169 | --- lib/lp/registry/browser/configure.zcml 2010-03-24 22:06:30 +0000 |
| 1170 | +++ lib/lp/registry/browser/configure.zcml 2010-04-05 17:13:29 +0000 |
| 1171 | @@ -1192,12 +1192,6 @@ |
| 1172 | classes=" |
| 1173 | PersonSetNavigation"/> |
| 1174 | <browser:page |
| 1175 | - name="+newperson" |
| 1176 | - for="lp.registry.interfaces.person.IPersonSet" |
| 1177 | - class="lp.registry.browser.person.PersonAddView" |
| 1178 | - permission="launchpad.Admin" |
| 1179 | - template="../templates/people-newperson.pt"/> |
| 1180 | - <browser:page |
| 1181 | name="+newteam" |
| 1182 | for="lp.registry.interfaces.person.IPersonSet" |
| 1183 | class="lp.registry.browser.team.TeamAddView" |
| 1184 | |
| 1185 | === modified file 'lib/lp/registry/browser/person.py' |
| 1186 | --- lib/lp/registry/browser/person.py 2010-03-25 14:50:31 +0000 |
| 1187 | +++ lib/lp/registry/browser/person.py 2010-04-05 17:13:29 +0000 |
| 1188 | @@ -10,10 +10,8 @@ |
| 1189 | 'BeginTeamClaimView', |
| 1190 | 'BugSubscriberPackageBugsSearchListingView', |
| 1191 | 'EmailToPersonView', |
| 1192 | - 'PeopleSearchMenu' |
| 1193 | 'PeopleSearchView', |
| 1194 | 'PersonAccountAdministerView', |
| 1195 | - 'PersonAddView', |
| 1196 | 'PersonAdministerView', |
| 1197 | 'PersonAnswerContactForView', |
| 1198 | 'PersonAnswersMenu', |
| 1199 | @@ -163,9 +161,8 @@ |
| 1200 | from lp.registry.interfaces.mailinglistsubscription import ( |
| 1201 | MailingListAutoSubscribePolicy) |
| 1202 | from lp.registry.interfaces.person import ( |
| 1203 | - INewPerson, IPerson, IPersonClaim, IPersonSet, ITeam, ITeamReassignment, |
| 1204 | - PersonCreationRationale, PersonVisibility, TeamMembershipRenewalPolicy, |
| 1205 | - TeamSubscriptionPolicy) |
| 1206 | + IPerson, IPersonClaim, IPersonSet, ITeam, ITeamReassignment, |
| 1207 | + PersonVisibility, TeamMembershipRenewalPolicy, TeamSubscriptionPolicy) |
| 1208 | from lp.registry.interfaces.poll import IPollSet, IPollSubset |
| 1209 | from lp.registry.interfaces.ssh import ISSHKeySet, SSHKeyType |
| 1210 | from lp.registry.interfaces.teammembership import ( |
| 1211 | @@ -1375,33 +1372,6 @@ |
| 1212 | return BatchNavigator(results, self.request) |
| 1213 | |
| 1214 | |
| 1215 | -class PersonAddView(LaunchpadFormView): |
| 1216 | - """The page where users can create new Launchpad profiles.""" |
| 1217 | - |
| 1218 | - page_title = "Create a new Launchpad profile" |
| 1219 | - label = page_title |
| 1220 | - schema = INewPerson |
| 1221 | - |
| 1222 | - custom_widget('creation_comment', TextAreaWidget, height=5, width=60) |
| 1223 | - |
| 1224 | - @action(_("Create Profile"), name="create") |
| 1225 | - def create_action(self, action, data): |
| 1226 | - emailaddress = data['emailaddress'] |
| 1227 | - displayname = data['displayname'] |
| 1228 | - creation_comment = data['creation_comment'] |
| 1229 | - person, ignored = getUtility(IPersonSet).createPersonAndEmail( |
| 1230 | - emailaddress, PersonCreationRationale.USER_CREATED, |
| 1231 | - displayname=displayname, comment=creation_comment, |
| 1232 | - registrant=self.user) |
| 1233 | - self.next_url = canonical_url(person) |
| 1234 | - logintokenset = getUtility(ILoginTokenSet) |
| 1235 | - token = logintokenset.new( |
| 1236 | - requester=self.user, |
| 1237 | - requesteremail=self.user.preferredemail.email, |
| 1238 | - email=emailaddress, tokentype=LoginTokenType.NEWPROFILE) |
| 1239 | - token.sendProfileCreatedEmail(person, creation_comment) |
| 1240 | - |
| 1241 | - |
| 1242 | class DeactivateAccountSchema(Interface): |
| 1243 | comment = copy_field( |
| 1244 | IPerson['account_status_comment'], readonly=False, __name__='comment') |
| 1245 | |
| 1246 | === modified file 'lib/lp/registry/interfaces/person.py' |
| 1247 | --- lib/lp/registry/interfaces/person.py 2010-03-25 14:17:56 +0000 |
| 1248 | +++ lib/lp/registry/interfaces/person.py 2010-04-05 17:13:29 +0000 |
| 1249 | @@ -11,11 +11,8 @@ |
| 1250 | 'IAdminPeopleMergeSchema', |
| 1251 | 'IAdminTeamMergeSchema', |
| 1252 | 'IHasStanding', |
| 1253 | - 'INewPerson', |
| 1254 | - 'INewPersonForm', |
| 1255 | 'IObjectReassignment', |
| 1256 | 'IPerson', |
| 1257 | - 'IPersonChangePassword', |
| 1258 | 'IPersonClaim', |
| 1259 | 'IPersonPublic', # Required for a monkey patch in interfaces/archive.py |
| 1260 | 'IPersonSet', |
| 1261 | @@ -72,8 +69,7 @@ |
| 1262 | from canonical.launchpad.interfaces.emailaddress import IEmailAddress |
| 1263 | from canonical.launchpad.interfaces.launchpad import ( |
| 1264 | IHasIcon, IHasLogo, IHasMugshot, IPrivacy) |
| 1265 | -from canonical.launchpad.interfaces.validation import ( |
| 1266 | - validate_new_person_email, validate_new_team_email) |
| 1267 | +from canonical.launchpad.interfaces.validation import validate_new_team_email |
| 1268 | from canonical.launchpad.validators import LaunchpadValidationError |
| 1269 | from canonical.launchpad.validators.email import email_validator |
| 1270 | from canonical.launchpad.validators.name import name_validator |
| 1271 | @@ -435,37 +431,12 @@ |
| 1272 | super(PersonNameField, self)._validate(input) |
| 1273 | |
| 1274 | |
| 1275 | -# XXX: salgado, 2010/03/05, bug=532688: This is currently used by c-i-p, so it |
| 1276 | -# can't be removed yet. As soon as we stop using c-i-p, though, we'll be able |
| 1277 | -# to remove this. |
| 1278 | -class IPersonChangePassword(Interface): |
| 1279 | - """The schema used by Person +changepassword form.""" |
| 1280 | - |
| 1281 | - currentpassword = PasswordField( |
| 1282 | - title=_('Current password'), required=True, readonly=False) |
| 1283 | - |
| 1284 | - password = PasswordField( |
| 1285 | - title=_('New password'), required=True, readonly=False) |
| 1286 | - |
| 1287 | - |
| 1288 | class IPersonClaim(Interface): |
| 1289 | """The schema used by IPerson's +claim form.""" |
| 1290 | |
| 1291 | emailaddress = TextLine(title=_('Email address'), required=True) |
| 1292 | |
| 1293 | |
| 1294 | -class INewPerson(Interface): |
| 1295 | - """The schema used by IPersonSet's +newperson form.""" |
| 1296 | - |
| 1297 | - emailaddress = StrippedTextLine( |
| 1298 | - title=_('Email address'), required=True, |
| 1299 | - constraint=validate_new_person_email) |
| 1300 | - displayname = StrippedTextLine(title=_('Display name'), required=True) |
| 1301 | - creation_comment = Text( |
| 1302 | - title=_('Creation reason'), required=True, |
| 1303 | - description=_("The reason why you're creating this profile.")) |
| 1304 | - |
| 1305 | - |
| 1306 | # This has to be defined here to avoid circular import problems. |
| 1307 | class IHasStanding(Interface): |
| 1308 | """An object that can have personal standing.""" |
| 1309 | @@ -1544,16 +1515,6 @@ |
| 1310 | PersonChoice.schema = IPerson |
| 1311 | |
| 1312 | |
| 1313 | -class INewPersonForm(IPerson): |
| 1314 | - """Interface used to create new Launchpad accounts. |
| 1315 | - |
| 1316 | - The only change with `IPerson` is a customised Password field. |
| 1317 | - """ |
| 1318 | - |
| 1319 | - password = PasswordField( |
| 1320 | - title=_('Create password'), required=True, readonly=False) |
| 1321 | - |
| 1322 | - |
| 1323 | class ITeamPublic(Interface): |
| 1324 | """Public attributes of a Team.""" |
| 1325 | |
| 1326 | |
| 1327 | === removed file 'lib/lp/registry/stories/person/xx-new-profile.txt' |
| 1328 | --- lib/lp/registry/stories/person/xx-new-profile.txt 2009-11-22 15:43:16 +0000 |
| 1329 | +++ lib/lp/registry/stories/person/xx-new-profile.txt 1970-01-01 00:00:00 +0000 |
| 1330 | @@ -1,110 +0,0 @@ |
| 1331 | - |
| 1332 | -We allow Launchpad admins to create random profiles that can latter be used |
| 1333 | -throughout Launchpad. These profiles are not considered validated and because |
| 1334 | -of that we won't send any email notification to them, except for an initial |
| 1335 | -email explaining that a given user has created a Launchpad profile for that |
| 1336 | -email address' owner. |
| 1337 | - |
| 1338 | - >>> admin_browser.open('http://launchpad.dev/people/+newperson') |
| 1339 | - >>> admin_browser.url |
| 1340 | - 'http://launchpad.dev/people/+newperson' |
| 1341 | - |
| 1342 | -All fields (email address, displayname and creation reason) in that form |
| 1343 | -are required. |
| 1344 | - |
| 1345 | - >>> admin_browser.getControl('Create Profile').click() |
| 1346 | - >>> print admin_browser.contents |
| 1347 | - <... |
| 1348 | - ...class="error message">There are 3 errors... |
| 1349 | - |
| 1350 | -Trying to create a profile with an invalid or already used email address |
| 1351 | -is not allowed. |
| 1352 | - |
| 1353 | - >>> admin_browser.getControl('Email address').value = 'foo@' |
| 1354 | - >>> admin_browser.getControl('Display name').value = 'Foo' |
| 1355 | - >>> admin_browser.getControl('Creation reason').value = ( |
| 1356 | - ... 'I was just playing around and found this nice form, so I ' |
| 1357 | - ... 'decided to create a new profile to test.') |
| 1358 | - >>> admin_browser.getControl('Create Profile').click() |
| 1359 | - >>> for tag in find_tags_by_class(admin_browser.contents, 'message'): |
| 1360 | - ... print tag.renderContents() |
| 1361 | - There is 1 error. |
| 1362 | - foo@ isn't a valid email address. |
| 1363 | - |
| 1364 | - >>> admin_browser.getControl('Email address').value = 'test@canonical.com' |
| 1365 | - >>> admin_browser.getControl('Create Profile').click() |
| 1366 | - >>> for tag in find_tags_by_class(admin_browser.contents, 'message'): |
| 1367 | - ... print tag.renderContents() |
| 1368 | - There is 1 error. |
| 1369 | - The profile you're trying to create already exists:... |
| 1370 | - |
| 1371 | -When all fields are filled correctly, the new profile is created. |
| 1372 | - |
| 1373 | - >>> admin_browser.getControl('Email address').value = 'test2@canonical.com' |
| 1374 | - >>> admin_browser.getControl('Create Profile').click() |
| 1375 | - >>> admin_browser.url |
| 1376 | - 'http://launchpad.dev/~test2' |
| 1377 | - |
| 1378 | - >>> print admin_browser.contents |
| 1379 | - <... |
| 1380 | - ...This page was created... |
| 1381 | - ...by ... |
| 1382 | - ...and the reason given by that user for its creation is:... |
| 1383 | - ...I was just playing around and found this nice form,... |
| 1384 | - |
| 1385 | -The creation of this new profile will trigger an email notification to the |
| 1386 | -profile's email address. This email will contain the name of the person who |
| 1387 | -created the profile as well as the reason left by that person. It'll also |
| 1388 | -contain a link to a page where it's possible to finish the registration |
| 1389 | -process in order to validate that profile. |
| 1390 | - |
| 1391 | - >>> import email |
| 1392 | - >>> from lp.services.mail import stub |
| 1393 | - >>> from canonical.launchpad.ftests.logintoken import ( |
| 1394 | - ... get_token_url_from_email) |
| 1395 | - >>> len(stub.test_emails) |
| 1396 | - 1 |
| 1397 | - >>> from_addr, to_addrs, raw_msg = stub.test_emails.pop() |
| 1398 | - >>> token_url = get_token_url_from_email(raw_msg) |
| 1399 | - |
| 1400 | - >>> print email.message_from_string(raw_msg).get_payload() |
| 1401 | - Hello! |
| 1402 | - ... has created a new Launchpad profile [1] for |
| 1403 | - you, and left the following comment as the reason for its creation: |
| 1404 | - ... |
| 1405 | - If you wish to use this profile, please follow the instructions here: |
| 1406 | - ... |
| 1407 | - |
| 1408 | - >>> browser.open(token_url) |
| 1409 | - >>> browser.url |
| 1410 | - 'http://launchpad.dev/token/.../+newaccount' |
| 1411 | - |
| 1412 | -We don't prefill the new account's display name because the one we have was |
| 1413 | -not entered by the users themselves. |
| 1414 | - |
| 1415 | - >>> browser.getControl('Display Name').value |
| 1416 | - '' |
| 1417 | - |
| 1418 | -We also explain the users what to write in each form: |
| 1419 | - |
| 1420 | - >>> for tag in find_tags_by_class(browser.contents, 'formHelp'): |
| 1421 | - ... print extract_text(tag) |
| 1422 | - Your name as you would like it displayed throughout Launchpad... |
| 1423 | - |
| 1424 | -Now we fill the form and save it to create the new account. |
| 1425 | - |
| 1426 | - >>> browser.getControl('Display Name').value = 'My name' |
| 1427 | - >>> browser.getControl('Create password').value = 'aaa' |
| 1428 | - >>> browser.getControl(name='field.password_dupe').value = 'aaa' |
| 1429 | - >>> browser.getControl('Continue').click() |
| 1430 | - |
| 1431 | - >>> browser.url |
| 1432 | - 'http://launchpad.dev/~test2' |
| 1433 | - |
| 1434 | - |
| 1435 | -Non-launchpad admins can't acces the page to ceate a new profile. |
| 1436 | - |
| 1437 | - >>> user_browser.open('http://launchpad.dev/people/+newperson') |
| 1438 | - Traceback (most recent call last): |
| 1439 | - ... |
| 1440 | - Unauthorized: ... |
| 1441 | |
| 1442 | === removed file 'lib/lp/registry/templates/people-newperson.pt' |
| 1443 | --- lib/lp/registry/templates/people-newperson.pt 2009-08-31 17:46:33 +0000 |
| 1444 | +++ lib/lp/registry/templates/people-newperson.pt 1970-01-01 00:00:00 +0000 |
| 1445 | @@ -1,29 +0,0 @@ |
| 1446 | -<html |
| 1447 | - xmlns="http://www.w3.org/1999/xhtml" |
| 1448 | - xmlns:tal="http://xml.zope.org/namespaces/tal" |
| 1449 | - xmlns:metal="http://xml.zope.org/namespaces/metal" |
| 1450 | - xmlns:i18n="http://xml.zope.org/namespaces/i18n" |
| 1451 | - metal:use-macro="view/macro:page/main_only" |
| 1452 | - i18n:domain="launchpad" |
| 1453 | - > |
| 1454 | - <body> |
| 1455 | - <div metal:fill-slot="main"> |
| 1456 | - <div metal:use-macro="context/@@launchpad_form/form"> |
| 1457 | - <div metal:fill-slot="extra_info" class="application-summary"> |
| 1458 | - <p> |
| 1459 | - Launchpad will send a message to the email address you provide |
| 1460 | - below. This message explains that you created a Launchpad profile |
| 1461 | - for the email address's owner, and it will include your name and |
| 1462 | - email address so that the user can contact you if they want to. |
| 1463 | - </p> |
| 1464 | - <p> |
| 1465 | - It will not be possible to log in with this new profile, and |
| 1466 | - Launchpad will not send any other messages to it before its email |
| 1467 | - address is confirmed. The registration process must be finished by |
| 1468 | - the person represented by the new profile. |
| 1469 | - </p> |
| 1470 | - </div> |
| 1471 | - </div> |
| 1472 | - </div> |
| 1473 | - </body> |
| 1474 | -</html> |

Rubber-stamp. Haven't actually examined the diff.