Merge lp:~mhall119/django-openid-auth/fixes-642132 into lp:~django-openid-auth/django-openid-auth/trunk

Proposed by Michael Hall
Status: Merged
Approved by: James Henstridge
Approved revision: 84
Merged at revision: 81
Proposed branch: lp:~mhall119/django-openid-auth/fixes-642132
Merge into: lp:~django-openid-auth/django-openid-auth/trunk
Diff against target: 433 lines (+283/-34)
3 files modified
README.txt (+10/-0)
django_openid_auth/auth.py (+55/-13)
django_openid_auth/tests/test_views.py (+218/-21)
To merge this branch: bzr merge lp:~mhall119/django-openid-auth/fixes-642132
Reviewer Review Type Date Requested Status
Michael Hall Approve
Anthony Lenton Needs Fixing
James Henstridge Approve
Review via email: mp+54193@code.launchpad.net

This proposal supersedes a proposal from 2010-10-13.

Description of the change

Adds a new settings paramater: OPENID_FOLLOW_RENAMES

When set to True, and when OPENID_UPDATE_DETAILS_FROM_SREG is also True, the django User.username for the User with a matching identity_url will be updated with the nickname from the OpenID response.

Several checks are performed to make sure we don't conflict with an existing user with the same username.

To post a comment you must log in.
Revision history for this message
Michael Hall (mhall119) wrote : Posted in a previous version of this proposal

Can somebody please review this, it is needed for loco.ubuntu.com and summit.ubuntu.com

Revision history for this message
Anthony Lenton (elachuni) wrote : Posted in a previous version of this proposal

Hi Michael,

Thanks for your work on this. The code around lines 8-10 of the diff would need to be a bit more robust when updating the User's username, as there might be some other user in the system with the new username already.

I think the underlying problem is what we really want to do in this case (ie if 'joesmith' changes his openid username to 'joebobs', but you already have a 'joebobs' in your system). Skipping the username update in that case would work but it'll leave you with out-of-sync data. Refusing to sign the user in would be quite violent, and merging the two accounts would be definitely a bad idea. What would work in your use case?

Sites with a single OpenID provider would be less affected by this issue, but it could still bite you.

Otoh, I'm not sure why django-openid-auth doesn't currently update username together with fullname and email when OPENID_UPDATE_DETAILS_FROM_SREG is True, but using a second setting for this as your code does seems like a good way to make it work for everybody.

Revision history for this message
Michael Hall (mhall119) wrote : Posted in a previous version of this proposal

I think in the case of an existing username conflict, then we could go back to $username+1 increments until we find a unique one. Unfortunately it would do this incrementing process every time the user logged in, until their new LP username is available in Django, but I don't think that would be such a performance hit.

Revision history for this message
James Henstridge (jamesh) wrote : Posted in a previous version of this proposal

This should do the check to see if anyone else has the same user name, and should ideally use the same name checking code as create_user_from_openid() does (that way we only need to update the algorithm once if we want to change it).

I would suggest only changing the user name if the existing user name does not have the sreg nickname as a prefix. This should prevent us from trying to change the nickname on every login if there is a duplicate nickname.

With respect to the tests, how about factoring out the body of test_login_update_details() into a helper method, from the first client.post() call and returning the response object returned by client.get('/getuser/'). That helper could then be used to run both tests. I'd also like to see a test of when there is a conflict for the new nickname.

Lastly, a more basic question: do we want to have a setting for this behaviour? If it has minimal overhead and is something most people would want, is there any reason not to always do this?

review: Needs Fixing
Revision history for this message
Michael Hall (mhall119) wrote : Posted in a previous version of this proposal

There may be cases where renames aren't desirable, for example if the username is used to link tables, rather than a constant user id.

Revision history for this message
Ronnie (ronnie.vd.c) wrote : Posted in a previous version of this proposal

I would suggest checking the OpenId-Provider (url like: https://login.launchpad.net/) from the claimed id.

If the new username exists, but claimed_id does not match (but is from the same provider), The old username should be called OpenIdUserX,

OPENIDPROVIDER1 (Google)
    - {'nickname': 'Ben', 'claimed_id': 'http://login.google.com/ABCD', 'display_id': '...'}

OPENIDPROVIDER2 (Launchpad)
    - {'nickname': 'Ben', 'claimed_id': 'https://login.launchpad.net/+id/AAAA', 'display_id': '...'}
    - {'nickname': 'Cees', 'claimed_id': 'https://login.launchpad.net/+id/ABCD', 'display_id': '...'}
======
    - Ben deletes his account on launchpad
    - Cees changed name to Ben

OPENIDCONSUMER
    - Ben (http://login.google.com/ABCD) register => username 'Ben'
    - Ben (https://login.launchpad.net/+id/AAAA) registers => username 'Ben1'
    - Cees (https://login.launchpad.net/+id/ABCD) registers => username 'Cees'
=======
    - Ben (previous Cees | https://login.launchpad.net/+id/ABCD) logs in =>
        -- user 'Ben1' should be renamed to 'OpenIdUserX'
        -- user Cees should be renamed to 'Ben1'

small catch: What to do if Ben (https://login.launchpad.net/+id/AAAA) changes his name to 'Ben1' on the openid-provider?

Revision history for this message
Ronnie (ronnie.vd.c) wrote : Posted in a previous version of this proposal

A more general approach, which also solves the small catch:

The username should always be renamed if it has changed, one exception for this is when the same username exists for another openid-provider. If there is already an user with that username (same provider) then the old_user should be renamed to the name without number (if available) else to the name + first_available_number.

If the same username is kept by another openid-provider, the user should keep its old username (if exists), if a new user is created, it should be username1 (username2 etc which first is available)

Therefore in the example above:
  OPENIDPROVIDER
    User Ben(https://login.launchpad.net/+id/AAAA) change name to Ben1
    User Cees(https://login.launchpad.net/+id/ABCD) change name to Ben
  OPENIDCONSUMER
    Ben (previous Cees | https://login.launchpad.net/+id/ABCD) logs in =>
       user 'Ben1' (https://login.launchpad.net/+id/AAAA) should be renamed to 'Ben2'
       user 'Cees' should be renamed to 'Ben1'
    Ben2 (https://login.launchpad.net/+id/AAAA) logs in with username 'Ben1'
       user 'Ben1' (previous Cees | https://login.launchpad.net/+id/ABCD) should be renamed to 'Ben3'
       user 'Ben2' should be renamed to 'Ben1'

All next logins for Ben3 (Cees/Ben (https://login.launchpad.net/+id/ABCD)) should check if 'Ben' is available. If not, keep the old username

84. By Michael Hall

Add new option to README.txt and removed an unecessary check in _get_available_username()

Revision history for this message
Anthony Lenton (elachuni) wrote :

Discussed this with Michael via IRC, it looks good to me now.

JamesH, let us know if you have comments or complaints about this one in its current state, it'll be landing soon if not!

review: Approve
Revision history for this message
James Henstridge (jamesh) wrote :

Looks good.

review: Approve
85. By Michael Hall

Updates from trunk

Revision history for this message
Anthony Lenton (elachuni) wrote :

I now get 1 fail and 1 error after merging into trunk:

http://pastebin.ubuntu.com/599377/

review: Needs Fixing
86. By Michael Hall

Check for STRICT_USERNAMES before defaulting the nickname to openiduser

Revision history for this message
Michael Hall (mhall119) wrote :

Fixed the failing tests.

review: Needs Resubmitting
Revision history for this message
Michael Hall (mhall119) :
review: Approve
87. By Michael Hall

Make sure auto-mapping is turned off when testing teams->group

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'README.txt'
--- README.txt 2011-03-23 19:26:19 +0000
+++ README.txt 2011-05-12 10:19:59 +0000
@@ -126,6 +126,16 @@
126The easiest way to resolve this is to use traditional authentication (OPENID_USE_AS_ADMIN_LOGIN = False) to sign in as your first user with a password and authorise your 126The easiest way to resolve this is to use traditional authentication (OPENID_USE_AS_ADMIN_LOGIN = False) to sign in as your first user with a password and authorise your
127openid user to be staff.127openid user to be staff.
128128
129== Change Django usernames if the nickname changes on the provider ==
130
131If you want your Django username to change when a user updates the nickname on their provider, add the following setting:
132
133 OPENID_FOLLOW_RENAMES = True
134
135If the new nickname is available as a Django username, the user is renamed.
136Otherwise the user will be renamed to nickname+i for an incrememnting value of i until no conflict occurs.
137If the user has already been renamed to nickname+1 due to a conflict, and the nickname is still not available, the user will keep their existing username.
138
129== Require a valid nickname ==139== Require a valid nickname ==
130140
131If you must have a valid, unique nickname in order to create a user accont, add the following setting:141If you must have a valid, unique nickname in order to create a user accont, add the following setting:
132142
=== modified file 'django_openid_auth/auth.py'
--- django_openid_auth/auth.py 2011-03-18 20:01:05 +0000
+++ django_openid_auth/auth.py 2011-05-12 10:19:59 +0000
@@ -86,7 +86,7 @@
8686
87 if getattr(settings, 'OPENID_UPDATE_DETAILS_FROM_SREG', False):87 if getattr(settings, 'OPENID_UPDATE_DETAILS_FROM_SREG', False):
88 details = self._extract_user_details(openid_response)88 details = self._extract_user_details(openid_response)
89 self.update_user_details(user, details)89 self.update_user_details(user, details, openid_response)
9090
91 teams_response = teams.TeamsResponse.fromSuccessResponse(91 teams_response = teams.TeamsResponse.fromSuccessResponse(
92 openid_response)92 openid_response)
@@ -103,7 +103,6 @@
103 email = sreg_response.get('email')103 email = sreg_response.get('email')
104 fullname = sreg_response.get('fullname')104 fullname = sreg_response.get('fullname')
105 nickname = sreg_response.get('nickname')105 nickname = sreg_response.get('nickname')
106
107 # If any attributes are provided via Attribute Exchange, use106 # If any attributes are provided via Attribute Exchange, use
108 # them in preference.107 # them in preference.
109 fetch_response = ax.FetchResponse.fromSuccessResponse(openid_response)108 fetch_response = ax.FetchResponse.fromSuccessResponse(openid_response)
@@ -142,17 +141,49 @@
142 return dict(email=email, nickname=nickname,141 return dict(email=email, nickname=nickname,
143 first_name=first_name, last_name=last_name)142 first_name=first_name, last_name=last_name)
144143
145 def create_user_from_openid(self, openid_response):144 def _get_available_username(self, nickname, identity_url):
146 details = self._extract_user_details(openid_response)145 # If we're being strict about usernames, throw an error if we didn't
147 nickname = details['nickname'] or 'openiduser'146 # get one back from the provider
148 email = details['email'] or ''
149
150 if getattr(settings, 'OPENID_STRICT_USERNAMES', False):147 if getattr(settings, 'OPENID_STRICT_USERNAMES', False):
151 if details['nickname'] is None or details['nickname'] == '':148 if nickname is None or nickname == '':
152 raise StrictUsernameViolation("No username")149 raise StrictUsernameViolation("No username")
150
151 # If we don't have a nickname, and we're not being strict, use a default
152 nickname = nickname or 'openiduser'
153
154 # See if we already have this nickname assigned to a username
155 try:
156 user = User.objects.get(username__exact=nickname)
157 except User.DoesNotExist:
158 # No conflict, we can use this nickname
159 return nickname
160
161 # Check if we already have nickname+i for this identity_url
162 try:
163 user_openid = UserOpenID.objects.get(
164 claimed_id__exact=identity_url,
165 user__username__startswith=nickname)
166 # No exception means we have an existing user for this identity
167 # that starts with this nickname, so it's possible we've had to
168 # assign them to nickname+i already.
169 oid_username = user_openid.user.username
170 if len(oid_username) > len(nickname):
171 try:
172 # check that it ends with a number
173 int(oid_username[len(nickname):])
174 return oid_username
175 except ValueError:
176 # username starts with nickname, but isn't nickname+#
177 pass
178 except UserOpenID.DoesNotExist:
179 # No user associated with this identity_url
180 pass
181
182
183 if getattr(settings, 'OPENID_STRICT_USERNAMES', False):
153 if User.objects.filter(username__exact=nickname).count() > 0:184 if User.objects.filter(username__exact=nickname).count() > 0:
154 raise StrictUsernameViolation("Duplicate username: %s" % nickname)185 raise StrictUsernameViolation("Duplicate username: %s" % nickname)
155 186
156 # Pick a username for the user based on their nickname,187 # Pick a username for the user based on their nickname,
157 # checking for conflicts.188 # checking for conflicts.
158 i = 1189 i = 1
@@ -161,15 +192,23 @@
161 if i > 1:192 if i > 1:
162 username += str(i)193 username += str(i)
163 try:194 try:
164 User.objects.get(username__exact=username)195 user = User.objects.get(username__exact=username)
165 except User.DoesNotExist:196 except User.DoesNotExist:
166 break197 break
167 i += 1198 i += 1
199 return username
200
201 def create_user_from_openid(self, openid_response):
202 details = self._extract_user_details(openid_response)
203 nickname = details['nickname'] or 'openiduser'
204 email = details['email'] or ''
205
206 username = self._get_available_username(details['nickname'], openid_response.identity_url)
168207
169 user = User.objects.create_user(username, email, password=None)208 user = User.objects.create_user(username, email, password=None)
170 self.update_user_details(user, details)
171
172 self.associate_openid(user, openid_response)209 self.associate_openid(user, openid_response)
210 self.update_user_details(user, details, openid_response)
211
173 return user212 return user
174213
175 def associate_openid(self, user, openid_response):214 def associate_openid(self, user, openid_response):
@@ -192,7 +231,7 @@
192231
193 return user_openid232 return user_openid
194233
195 def update_user_details(self, user, details):234 def update_user_details(self, user, details, openid_response):
196 updated = False235 updated = False
197 if details['first_name']:236 if details['first_name']:
198 user.first_name = details['first_name']237 user.first_name = details['first_name']
@@ -203,6 +242,9 @@
203 if details['email']:242 if details['email']:
204 user.email = details['email']243 user.email = details['email']
205 updated = True244 updated = True
245 if getattr(settings, 'OPENID_FOLLOW_RENAMES', False):
246 user.username = self._get_available_username(details['nickname'], openid_response.identity_url)
247 updated = True
206248
207 if updated:249 if updated:
208 user.save()250 user.save()
209251
=== modified file 'django_openid_auth/tests/test_views.py'
--- django_openid_auth/tests/test_views.py 2011-03-18 20:01:05 +0000
+++ django_openid_auth/tests/test_views.py 2011-05-12 10:19:59 +0000
@@ -136,6 +136,7 @@
136 self.old_sso_server_url = getattr(settings, 'OPENID_SSO_SERVER_URL', None)136 self.old_sso_server_url = getattr(settings, 'OPENID_SSO_SERVER_URL', None)
137 self.old_teams_map = getattr(settings, 'OPENID_LAUNCHPAD_TEAMS_MAPPING', {})137 self.old_teams_map = getattr(settings, 'OPENID_LAUNCHPAD_TEAMS_MAPPING', {})
138 self.old_use_as_admin_login = getattr(settings, 'OPENID_USE_AS_ADMIN_LOGIN', False)138 self.old_use_as_admin_login = getattr(settings, 'OPENID_USE_AS_ADMIN_LOGIN', False)
139 self.old_follow_renames = getattr(settings, 'OPENID_FOLLOW_RENAMES', False)
139140
140 settings.OPENID_CREATE_USERS = False141 settings.OPENID_CREATE_USERS = False
141 settings.OPENID_STRICT_USERNAMES = False142 settings.OPENID_STRICT_USERNAMES = False
@@ -143,6 +144,7 @@
143 settings.OPENID_SSO_SERVER_URL = None144 settings.OPENID_SSO_SERVER_URL = None
144 settings.OPENID_LAUNCHPAD_TEAMS_MAPPING = {}145 settings.OPENID_LAUNCHPAD_TEAMS_MAPPING = {}
145 settings.OPENID_USE_AS_ADMIN_LOGIN = False146 settings.OPENID_USE_AS_ADMIN_LOGIN = False
147 settings.OPENID_FOLLOW_RENAMES = False
146148
147 def tearDown(self):149 def tearDown(self):
148 settings.LOGIN_REDIRECT_URL = self.old_login_redirect_url150 settings.LOGIN_REDIRECT_URL = self.old_login_redirect_url
@@ -152,6 +154,7 @@
152 settings.OPENID_SSO_SERVER_URL = self.old_sso_server_url154 settings.OPENID_SSO_SERVER_URL = self.old_sso_server_url
153 settings.OPENID_LAUNCHPAD_TEAMS_MAPPING = self.old_teams_map155 settings.OPENID_LAUNCHPAD_TEAMS_MAPPING = self.old_teams_map
154 settings.OPENID_USE_AS_ADMIN_LOGIN = self.old_use_as_admin_login156 settings.OPENID_USE_AS_ADMIN_LOGIN = self.old_use_as_admin_login
157 settings.OPENID_FOLLOW_RENAMES = self.old_follow_renames
155158
156 setDefaultFetcher(None)159 setDefaultFetcher(None)
157 super(RelyingPartyTests, self).tearDown()160 super(RelyingPartyTests, self).tearDown()
@@ -289,6 +292,213 @@
289 self.assertEquals(user.last_name, 'User')292 self.assertEquals(user.last_name, 'User')
290 self.assertEquals(user.email, 'foo@example.com')293 self.assertEquals(user.email, 'foo@example.com')
291294
295 def _do_user_login(self, openid_req, openid_resp):
296 # Posting in an identity URL begins the authentication request:
297 response = self.client.post('/openid/login/', openid_req)
298 self.assertContains(response, 'OpenID transaction in progress')
299
300 # Complete the request, passing back some simple registration
301 # data. The user is redirected to the next URL.
302 openid_request = self.provider.parseFormPost(response.content)
303 sreg_request = sreg.SRegRequest.fromOpenIDRequest(openid_request)
304 openid_response = openid_request.answer(True)
305 sreg_response = sreg.SRegResponse.extractResponse(
306 sreg_request, openid_resp)
307 openid_response.addExtension(sreg_response)
308 response = self.complete(openid_response)
309 self.assertRedirects(response, 'http://testserver/getuser/')
310
311 def test_login_without_nickname(self):
312 settings.OPENID_CREATE_USERS = True
313
314 openid_req = {'openid_identifier': 'http://example.com/identity',
315 'next': '/getuser/'}
316 openid_resp = {'nickname': '', 'fullname': 'Openid User',
317 'email': 'foo@example.com'}
318 self._do_user_login(openid_req, openid_resp)
319 response = self.client.get('/getuser/')
320
321 # username defaults to 'openiduser'
322 self.assertEquals(response.content, 'openiduser')
323
324 # The user's full name and email have been updated.
325 user = User.objects.get(username=response.content)
326 self.assertEquals(user.first_name, 'Openid')
327 self.assertEquals(user.last_name, 'User')
328 self.assertEquals(user.email, 'foo@example.com')
329
330 def test_login_follow_rename(self):
331 settings.OPENID_FOLLOW_RENAMES = True
332 settings.OPENID_UPDATE_DETAILS_FROM_SREG = True
333 user = User.objects.create_user('testuser', 'someone@example.com')
334 useropenid = UserOpenID(
335 user=user,
336 claimed_id='http://example.com/identity',
337 display_id='http://example.com/identity')
338 useropenid.save()
339
340 openid_req = {'openid_identifier': 'http://example.com/identity',
341 'next': '/getuser/'}
342 openid_resp = {'nickname': 'someuser', 'fullname': 'Some User',
343 'email': 'foo@example.com'}
344 self._do_user_login(openid_req, openid_resp)
345 response = self.client.get('/getuser/')
346
347 # If OPENID_FOLLOW_RENAMES, they are logged in as
348 # someuser (the passed in nickname has changed the username)
349 self.assertEquals(response.content, 'someuser')
350
351 # The user's full name and email have been updated.
352 user = User.objects.get(username=response.content)
353 self.assertEquals(user.first_name, 'Some')
354 self.assertEquals(user.last_name, 'User')
355 self.assertEquals(user.email, 'foo@example.com')
356
357 def test_login_follow_rename_conflict(self):
358 settings.OPENID_FOLLOW_RENAMES = True
359 settings.OPENID_UPDATE_DETAILS_FROM_SREG = True
360 # Setup existing user who's name we're going to switch to
361 user = User.objects.create_user('testuser', 'someone@example.com')
362 UserOpenID.objects.get_or_create(
363 user=user,
364 claimed_id='http://example.com/existing_identity',
365 display_id='http://example.com/existing_identity')
366
367 # Setup user who is going to try to change username to 'testuser'
368 renamed_user = User.objects.create_user('renameuser', 'someone@example.com')
369 UserOpenID.objects.get_or_create(
370 user=renamed_user,
371 claimed_id='http://example.com/identity',
372 display_id='http://example.com/identity')
373
374 # identity url is for 'renameuser'
375 openid_req = {'openid_identifier': 'http://example.com/identity',
376 'next': '/getuser/'}
377 # but returned username is for 'testuser', which already exists for another identity
378 openid_resp = {'nickname': 'testuser', 'fullname': 'Rename User',
379 'email': 'rename@example.com'}
380 self._do_user_login(openid_req, openid_resp)
381 response = self.client.get('/getuser/')
382
383 # If OPENID_FOLLOW_RENAMES, attempt to change username to 'testuser'
384 # but since that username is already taken by someone else, we go through
385 # the process of adding +i to it, and get testuser2.
386 self.assertEquals(response.content, 'testuser2')
387
388 # The user's full name and email have been updated.
389 user = User.objects.get(username=response.content)
390 self.assertEquals(user.first_name, 'Rename')
391 self.assertEquals(user.last_name, 'User')
392 self.assertEquals(user.email, 'rename@example.com')
393
394 def test_login_follow_rename_false_onlyonce(self):
395 settings.OPENID_FOLLOW_RENAMES = True
396 settings.OPENID_UPDATE_DETAILS_FROM_SREG = True
397 # Setup existing user who's name we're going to switch to
398 user = User.objects.create_user('testuser', 'someone@example.com')
399 UserOpenID.objects.get_or_create(
400 user=user,
401 claimed_id='http://example.com/existing_identity',
402 display_id='http://example.com/existing_identity')
403
404 # Setup user who is going to try to change username to 'testuser'
405 renamed_user = User.objects.create_user('testuser2000eight', 'someone@example.com')
406 UserOpenID.objects.get_or_create(
407 user=renamed_user,
408 claimed_id='http://example.com/identity',
409 display_id='http://example.com/identity')
410
411 # identity url is for 'testuser2000eight'
412 openid_req = {'openid_identifier': 'http://example.com/identity',
413 'next': '/getuser/'}
414 # but returned username is for 'testuser', which already exists for another identity
415 openid_resp = {'nickname': 'testuser2', 'fullname': 'Rename User',
416 'email': 'rename@example.com'}
417 self._do_user_login(openid_req, openid_resp)
418 response = self.client.get('/getuser/')
419
420 # If OPENID_FOLLOW_RENAMES, attempt to change username to 'testuser'
421 # but since that username is already taken by someone else, we go through
422 # the process of adding +i to it. Even though it looks like the username
423 # follows the nickname+i scheme, it has non-numbers in the suffix, so
424 # it's not an auto-generated one. The regular process of renaming to
425 # 'testuser' has a conflict, so we get +2 at the end.
426 self.assertEquals(response.content, 'testuser2')
427
428 # The user's full name and email have been updated.
429 user = User.objects.get(username=response.content)
430 self.assertEquals(user.first_name, 'Rename')
431 self.assertEquals(user.last_name, 'User')
432 self.assertEquals(user.email, 'rename@example.com')
433
434 def test_login_follow_rename_conflict_onlyonce(self):
435 settings.OPENID_FOLLOW_RENAMES = True
436 settings.OPENID_UPDATE_DETAILS_FROM_SREG = True
437 # Setup existing user who's name we're going to switch to
438 user = User.objects.create_user('testuser', 'someone@example.com')
439 UserOpenID.objects.get_or_create(
440 user=user,
441 claimed_id='http://example.com/existing_identity',
442 display_id='http://example.com/existing_identity')
443
444 # Setup user who is going to try to change username to 'testuser'
445 renamed_user = User.objects.create_user('testuser2000', 'someone@example.com')
446 UserOpenID.objects.get_or_create(
447 user=renamed_user,
448 claimed_id='http://example.com/identity',
449 display_id='http://example.com/identity')
450
451 # identity url is for 'testuser2000'
452 openid_req = {'openid_identifier': 'http://example.com/identity',
453 'next': '/getuser/'}
454 # but returned username is for 'testuser', which already exists for another identity
455 openid_resp = {'nickname': 'testuser', 'fullname': 'Rename User',
456 'email': 'rename@example.com'}
457 self._do_user_login(openid_req, openid_resp)
458 response = self.client.get('/getuser/')
459
460 # If OPENID_FOLLOW_RENAMES, attempt to change username to 'testuser'
461 # but since that username is already taken by someone else, we go through
462 # the process of adding +i to it. Since the user for this identity url
463 # already has a name matching that pattern, check if first.
464 self.assertEquals(response.content, 'testuser2000')
465
466 # The user's full name and email have been updated.
467 user = User.objects.get(username=response.content)
468 self.assertEquals(user.first_name, 'Rename')
469 self.assertEquals(user.last_name, 'User')
470 self.assertEquals(user.email, 'rename@example.com')
471
472 def test_login_follow_rename_false_conflict(self):
473 settings.OPENID_FOLLOW_RENAMES = True
474 settings.OPENID_UPDATE_DETAILS_FROM_SREG = True
475 # Setup existing user who's username matches the name+i pattern
476 user = User.objects.create_user('testuser2', 'someone@example.com')
477 UserOpenID.objects.get_or_create(
478 user=user,
479 claimed_id='http://example.com/identity',
480 display_id='http://example.com/identity')
481
482 # identity url is for 'testuser2'
483 openid_req = {'openid_identifier': 'http://example.com/identity',
484 'next': '/getuser/'}
485 # but returned username is for 'testuser', which looks like we've done
486 # a username+1 for them already, but 'testuser' isn't actually taken
487 openid_resp = {'nickname': 'testuser', 'fullname': 'Same User',
488 'email': 'same@example.com'}
489 self._do_user_login(openid_req, openid_resp)
490 response = self.client.get('/getuser/')
491
492 # If OPENID_FOLLOW_RENAMES, username should be changed to 'testuser'
493 # because it wasn't currently taken
494 self.assertEquals(response.content, 'testuser')
495
496 # The user's full name and email have been updated.
497 user = User.objects.get(username=response.content)
498 self.assertEquals(user.first_name, 'Same')
499 self.assertEquals(user.last_name, 'User')
500 self.assertEquals(user.email, 'same@example.com')
501
292 def test_strict_username_no_nickname(self):502 def test_strict_username_no_nickname(self):
293 settings.OPENID_CREATE_USERS = True503 settings.OPENID_CREATE_USERS = True
294 settings.OPENID_STRICT_USERNAMES = True504 settings.OPENID_STRICT_USERNAMES = True
@@ -354,31 +564,17 @@
354 display_id='http://example.com/identity')564 display_id='http://example.com/identity')
355 useropenid.save()565 useropenid.save()
356566
357 # Posting in an identity URL begins the authentication request:567 openid_req = {'openid_identifier': 'http://example.com/identity',
358 response = self.client.post('/openid/login/',568 'next': '/getuser/'}
359 {'openid_identifier': 'http://example.com/identity',569 openid_resp = {'nickname': 'testuser', 'fullname': 'Some User',
360 'next': '/getuser/'})570 'email': 'foo@example.com'}
361 self.assertContains(response, 'OpenID transaction in progress')571 self._do_user_login(openid_req, openid_resp)
362
363 # Complete the request, passing back some simple registration
364 # data. The user is redirected to the next URL.
365 openid_request = self.provider.parseFormPost(response.content)
366 sreg_request = sreg.SRegRequest.fromOpenIDRequest(openid_request)
367 openid_response = openid_request.answer(True)
368 sreg_response = sreg.SRegResponse.extractResponse(
369 sreg_request, {'nickname': 'someuser', 'fullname': 'Some User',
370 'email': 'foo@example.com'})
371 openid_response.addExtension(sreg_response)
372 response = self.complete(openid_response)
373 self.assertRedirects(response, 'http://testserver/getuser/')
374
375 # And they are now logged in as testuser (the passed in
376 # nickname has not caused the username to change).
377 response = self.client.get('/getuser/')572 response = self.client.get('/getuser/')
573
378 self.assertEquals(response.content, 'testuser')574 self.assertEquals(response.content, 'testuser')
379575
380 # The user's full name and email have been updated.576 # The user's full name and email have been updated.
381 user = User.objects.get(username='testuser')577 user = User.objects.get(username=response.content)
382 self.assertEquals(user.first_name, 'Some')578 self.assertEquals(user.first_name, 'Some')
383 self.assertEquals(user.last_name, 'User')579 self.assertEquals(user.last_name, 'User')
384 self.assertEquals(user.email, 'foo@example.com')580 self.assertEquals(user.email, 'foo@example.com')
@@ -473,6 +669,7 @@
473 self.assertEquals(user.email, 'foo@example.com')669 self.assertEquals(user.email, 'foo@example.com')
474670
475 def test_login_teams(self):671 def test_login_teams(self):
672 settings.OPENID_LAUNCHPAD_TEAMS_MAPPING_AUTO = False
476 settings.OPENID_LAUNCHPAD_TEAMS_MAPPING = {'teamname': 'groupname',673 settings.OPENID_LAUNCHPAD_TEAMS_MAPPING = {'teamname': 'groupname',
477 'otherteam': 'othergroup'}674 'otherteam': 'othergroup'}
478 user = User.objects.create_user('testuser', 'someone@example.com')675 user = User.objects.create_user('testuser', 'someone@example.com')

Subscribers

People subscribed via source and target branches