@@ -196,9 +196,11 @@ class ProviderUserState(object):
196196 lms/templates/dashboard.html.
197197 """
198198
199- def __init__ (self , enabled_provider , user , state ):
199+ def __init__ (self , enabled_provider , user , association_id = None ):
200+ # UserSocialAuth row ID
201+ self .association_id = association_id
200202 # Boolean. Whether the user has an account associated with the provider
201- self .has_account = state
203+ self .has_account = association_id is not None
202204 # provider.BaseProvider child. Callers must verify that the provider is
203205 # enabled.
204206 self .provider = enabled_provider
@@ -215,7 +217,7 @@ def get(request):
215217 return request .session .get ('partial_pipeline' )
216218
217219
218- def get_authenticated_user (username , backend_name ):
220+ def get_authenticated_user (auth_provider , username , uid ):
219221 """Gets a saved user authenticated by a particular backend.
220222
221223 Between pipeline steps User objects are not saved. We need to reconstitute
@@ -224,26 +226,26 @@ def get_authenticated_user(username, backend_name):
224226 authenticate().
225227
226228 Args:
229+ auth_provider: the third_party_auth provider in use for the current pipeline.
227230 username: string. Username of user to get.
228- backend_name: string. The name of the third-party auth backend from
229- the running pipeline.
231+ uid: string. The user ID according to the third party.
230232
231233 Returns:
232234 User if user is found and has a social auth from the passed
233- backend_name .
235+ provider .
234236
235237 Raises:
236238 User.DoesNotExist: if no user matching user is found, or the matching
237239 user has no social auth associated with the given backend.
238240 AssertionError: if the user is not authenticated.
239241 """
240- user = models .DjangoStorage .user .user_model ().objects .get (username = username )
241- match = models .DjangoStorage .user .get_social_auth_for_user (user , provider = backend_name )
242+ match = models .DjangoStorage .user .get_social_auth (provider = auth_provider .BACKEND_CLASS .name , uid = uid )
242243
243- if not match :
244+ if not match or match . user . username != username :
244245 raise User .DoesNotExist
245246
246- user .backend = provider .Registry .get_by_backend_name (backend_name ).get_authentication_backend ()
247+ user = match .user
248+ user .backend = auth_provider .get_authentication_backend ()
247249 return user
248250
249251
@@ -257,10 +259,12 @@ def _get_enabled_provider_by_name(provider_name):
257259 return enabled_provider
258260
259261
260- def _get_url (view_name , backend_name , auth_entry = None , redirect_url = None ):
262+ def _get_url (view_name , backend_name , auth_entry = None , redirect_url = None ,
263+ extra_params = None , url_params = None ):
261264 """Creates a URL to hook into social auth endpoints."""
262- kwargs = {'backend' : backend_name }
263- url = reverse (view_name , kwargs = kwargs )
265+ url_params = url_params or {}
266+ url_params ['backend' ] = backend_name
267+ url = reverse (view_name , kwargs = url_params )
264268
265269 query_params = OrderedDict ()
266270 if auth_entry :
@@ -269,6 +273,9 @@ def _get_url(view_name, backend_name, auth_entry=None, redirect_url=None):
269273 if redirect_url :
270274 query_params [AUTH_REDIRECT_KEY ] = redirect_url
271275
276+ if extra_params :
277+ query_params .update (extra_params )
278+
272279 return u"{url}?{params}" .format (
273280 url = url ,
274281 params = urllib .urlencode (query_params )
@@ -288,29 +295,32 @@ def get_complete_url(backend_name):
288295 Raises:
289296 ValueError: if no provider is enabled with the given backend_name.
290297 """
291- enabled_provider = provider .Registry .get_by_backend_name (backend_name )
292-
293- if not enabled_provider :
298+ if not any (provider .Registry .get_enabled_by_backend_name (backend_name )):
294299 raise ValueError ('Provider with backend %s not enabled' % backend_name )
295300
296301 return _get_url ('social:complete' , backend_name )
297302
298303
299- def get_disconnect_url (provider_name ):
304+ def get_disconnect_url (provider_name , association_id ):
300305 """Gets URL for the endpoint that starts the disconnect pipeline.
301306
302307 Args:
303308 provider_name: string. Name of the provider.BaseProvider child you want
304309 to disconnect from.
310+ association_id: int. Optional ID of a specific row in the UserSocialAuth
311+ table to disconnect (useful if multiple providers use a common backend)
305312
306313 Returns:
307314 String. URL that starts the disconnection pipeline.
308315
309316 Raises:
310- ValueError: if no provider is enabled with the given backend_name .
317+ ValueError: if no provider is enabled with the given name .
311318 """
312- enabled_provider = _get_enabled_provider_by_name (provider_name )
313- return _get_url ('social:disconnect' , enabled_provider .BACKEND_CLASS .name )
319+ backend_name = _get_enabled_provider_by_name (provider_name ).BACKEND_CLASS .name
320+ if association_id :
321+ return _get_url ('social:disconnect_individual' , backend_name , url_params = {'association_id' : association_id })
322+ else :
323+ return _get_url ('social:disconnect' , backend_name )
314324
315325
316326def get_login_url (provider_name , auth_entry , redirect_url = None ):
@@ -340,6 +350,7 @@ def get_login_url(provider_name, auth_entry, redirect_url=None):
340350 enabled_provider .BACKEND_CLASS .name ,
341351 auth_entry = auth_entry ,
342352 redirect_url = redirect_url ,
353+ extra_params = enabled_provider .get_url_params (),
343354 )
344355
345356
@@ -355,7 +366,7 @@ def get_duplicate_provider(messages):
355366 unfortunately not in a reusable constant.
356367
357368 Returns:
358- provider.BaseProvider child instance. The provider of the duplicate
369+ string name of the python-social-auth backend that has the duplicate
359370 account, or None if there is no duplicate (and hence no error).
360371 """
361372 social_auth_messages = [m for m in messages if m .message .endswith ('is already in use.' )]
@@ -364,7 +375,8 @@ def get_duplicate_provider(messages):
364375 return
365376
366377 assert len (social_auth_messages ) == 1
367- return provider .Registry .get_by_backend_name (social_auth_messages [0 ].extra_tags .split ()[1 ])
378+ backend_name = social_auth_messages [0 ].extra_tags .split ()[1 ]
379+ return backend_name
368380
369381
370382def get_provider_user_states (user ):
@@ -378,13 +390,16 @@ def get_provider_user_states(user):
378390 each enabled provider.
379391 """
380392 states = []
381- found_user_backends = [
382- social_auth .provider for social_auth in models .DjangoStorage .user .get_social_auth_for_user (user )
383- ]
393+ found_user_auths = list (models .DjangoStorage .user .get_social_auth_for_user (user ))
384394
385395 for enabled_provider in provider .Registry .enabled ():
396+ association_id = None
397+ for auth in found_user_auths :
398+ if enabled_provider .match_social_auth (auth ):
399+ association_id = auth .id
400+ break
386401 states .append (
387- ProviderUserState (enabled_provider , user , enabled_provider . BACKEND_CLASS . name in found_user_backends )
402+ ProviderUserState (enabled_provider , user , association_id )
388403 )
389404
390405 return states
0 commit comments