# Cache for discovered backends.
BACKENDS = {}
-def get_backend(name, *args, **kwargs):
- """Returns a backend by name. Backends are stored in the BACKENDS
- cache dict. If not found, each of the modules referenced in
+def get_backends(force_load=False):
+ """
+ Entry point to the BACKENDS cache. If BACKENDS hasn't been
+ populated, each of the modules referenced in
AUTHENTICATION_BACKENDS is imported and checked for a BACKENDS
- definition. If the named backend is found in the module's BACKENDS
- definition, it's then stored in the cache for future access.
+ definition and if enabled, added to the cache.
Previously all backends were attempted to be loaded at
import time of this module, which meant that backends that subclass
This new approach ensures that backends are allowed to subclass from
bases in this module and still be picked up.
+
+ A force_load boolean arg is also provided so that get_backend
+ below can retry a requested backend that may not yet be discovered.
+ """
+ if not BACKENDS or force_load:
+ for auth_backend in settings.AUTHENTICATION_BACKENDS:
+ module = import_module(auth_backend.rsplit(".", 1)[0])
+ backends = getattr(module, "BACKENDS", {})
+ for name, backend in backends.items():
+ if backend.enabled():
+ BACKENDS[name] = backend
+ return BACKENDS
+
+
+def get_backend(name, *args, **kwargs):
+ """Returns a backend by name. Backends are stored in the BACKENDS
+ cache dict. If not found, each of the modules referenced in
+ AUTHENTICATION_BACKENDS is imported and checked for a BACKENDS
+ definition. If the named backend is found in the module's BACKENDS
+ definition, it's then stored in the cache for future access.
"""
try:
# Cached backend which has previously been discovered.
return BACKENDS[name](*args, **kwargs)
except KeyError:
- pass
- # Look for a BACKENDS definition on each of the modules for
- # AUTHENTICATION_BACKENDS.
- for auth_backend in settings.AUTHENTICATION_BACKENDS:
- module = import_module(auth_backend.rsplit(".", 1)[0])
- backends = getattr(module, "BACKENDS", {})
+ # Force a reload of BACKENDS to ensure a missing
+ # backend hasn't been missed.
+ get_backends(force_load=True)
try:
- backend = backends[name]
+ return BACKENDS[name](*args, **kwargs)
except KeyError:
- pass
- else:
- # If the backend is enabled, add it to the cache and
- # return it.
- if backend.enabled():
- BACKENDS[name] = backend
- return backend(*args, **kwargs)
+ return None
-from social_auth.backends import BACKENDS
+from social_auth.backends import get_backends
from social_auth.utils import group_backend_by_type
from social_auth.models import User
with a hyphen have the hyphen replaced with an underscore, e.g.
google-oauth2 becomes google_oauth2 when referenced in templates.
"""
- keys = BACKENDS.keys()
+ keys = get_backends().keys()
accounts = dict(zip(keys, [None] * len(keys)))
if isinstance(request.user, User) and request.user.is_authenticated():
If user is not authenticated, then first list is empty, and there's no
difference between the second and third lists.
"""
- available = BACKENDS.keys()
+ available = get_backends().keys()
values = {'associated': [],
'not_associated': available,
'backends': available}
# Beware of cyclical imports!
from social_auth.backends import \
- BACKENDS, OpenIdAuth, BaseOAuth, BaseOAuth2
+ get_backends, OpenIdAuth, BaseOAuth, BaseOAuth2
result = defaultdict(list)
for item in items:
- backend = BACKENDS[key(item)]
+ backend = get_backends()[key(item)]
if issubclass(backend, OpenIdAuth):
result['openid'].append(item)
elif issubclass(backend, BaseOAuth2):