Permalink
Browse files

Merge remote-tracking branch 'upstream/master'

  • Loading branch information...
2 parents e7ac5f0 + 72c2491 commit 84fe3b8d07200abc1fefe06b8340c329665afffc @krvss krvss committed Jan 4, 2012
View
File renamed without changes.
File renamed without changes.
View
@@ -105,7 +105,7 @@ Configuration
'social_auth'
)
-- Add desired authentication backends to AUTHENTICATION_BACKENDS_ setting::
+- Add desired authentication backends to Django's AUTHENTICATION_BACKENDS_ setting::
AUTHENTICATION_BACKENDS = (
'social_auth.backends.twitter.TwitterBackend',
@@ -125,15 +125,6 @@ Configuration
'django.contrib.auth.backends.ModelBackend',
)
-- The application will try to import custom backends from the sources defined in::
-
- SOCIAL_AUTH_IMPORT_BACKENDS = (
- 'myproy.social_auth_extra_services',
- )
-
- This way it's easier to add new providers, check the already defined ones
- in ``social_auth.backends`` for examples.
-
Take into account that backends **must** be defined in AUTHENTICATION_BACKENDS_
or Django won't pick them when trying to authenticate the user.
@@ -298,7 +289,7 @@ Configuration
SOCIAL_AUTH_CREATE_USERS = False
It is also possible to associate multiple user accounts with a single email
- address as long as the rest of the user data is unique. Set value as True
+ address as long as the rest of the user data is unique. Set value as True
to enable, otherwise set as False to disable.
This behavior is disabled by default (false) unless specifically set::
@@ -326,6 +317,29 @@ Configuration
Defaults to ``LOGIN_ERROR_URL``.
+-------------
+Usage example
+-------------
+
+Authentication process starts with ``socialauth_begin`` URL.
+
+Template code example::
+
+ <ul>
+ <li>
+ <a href="{% url socialauth_begin 'twitter' %}">Enter using Twitter</a>
+ </li>
+ <li>
+ <a href="{% url socialauth_begin 'facebook' %}">Enter using Facebook</a>
+ </li>
+ </ul>
+
+In the example above we assume that Twitter and Facebook authentication backends enabled, and following settings provided::
+
+ TWITTER_CONSUMER_KEY = 'real key here'
+ TWITTER_CONSUMER_SECRET = 'real secret here'
+ FACEBOOK_APP_ID = 'real id here'
+ FACEBOOK_API_SECRET = 'real secret here'
-------
Signals
@@ -357,7 +371,7 @@ raised.
Also a new-user signal (``socialauth_registered``) is sent when new accounts are
created::
- from social_auth.signals import socialauth_registered
+ from social_auth.signals import socialauth_registered
def new_users_handler(sender, user, response, details, **kwargs):
user.is_new = True
@@ -577,7 +591,7 @@ GitHub works similar to Facebook (OAuth).
- also it's possible to define extra permissions with::
GITHUB_EXTENDED_PERMISSIONS = [...]
-
+
-------
Dropbox
View
@@ -8,7 +8,7 @@ Configuration
'social_auth'
)
-- Add desired authentication backends to AUTHENTICATION_BACKENDS_ setting::
+- Add desired authentication backends to Django's AUTHENTICATION_BACKENDS_ setting::
AUTHENTICATION_BACKENDS = (
'social_auth.backends.twitter.TwitterBackend',
@@ -26,15 +26,6 @@ Configuration
'django.contrib.auth.backends.ModelBackend',
)
-- The application will try to import custom backends from the sources defined in::
-
- SOCIAL_AUTH_IMPORT_BACKENDS = (
- 'myproy.social_auth_extra_services',
- )
-
- This way it's easier to add new providers, check the already defined ones
- in ``social_auth.backends`` for examples.
-
Take into account that backends **must** be defined in AUTHENTICATION_BACKENDS_
or Django won't pick them when trying to authenticate the user.
@@ -97,8 +88,8 @@ Configuration
- Configure authentication and association complete URL names to avoid
possible clashes::
- SOCIAL_AUTH_COMPLETE_URL_NAME = 'complete'
- SOCIAL_AUTH_ASSOCIATE_URL_NAME = 'associate_complete'
+ SOCIAL_AUTH_COMPLETE_URL_NAME = 'socialauth_complete'
+ SOCIAL_AUTH_ASSOCIATE_URL_NAME = 'socialauth_associate_complete'
- Add URLs entries::
View
@@ -1,5 +1,11 @@
from os.path import abspath, dirname, basename, join
+try:
+ import social_auth
+except ImportError:
+ import sys
+ sys.path.insert(0, "..")
+
DEBUG = True
TEMPLATE_DEBUG = DEBUG
@@ -89,8 +95,6 @@
'social_auth.context_processors.social_auth_by_type_backends',
)
-#SOCIAL_AUTH_ENABLED_BACKENDS = ('google', 'google-oauth', 'facebook')
-
LOGIN_REDIRECT_URL = '/'
try:
View
@@ -2,5 +2,5 @@
Django-social-auth application, allows OpenId or OAuth user
registration/authentication just adding a few configurations.
"""
-version = (0, 6, 0)
+version = (0, 6, 1)
__version__ = '.'.join(map(str, version))
@@ -12,12 +12,9 @@
import logging
logger = logging.getLogger(__name__)
-from os import walk
-from os.path import basename
from urllib2 import Request, urlopen
from urllib import urlencode
from urlparse import urlsplit
-from collections import defaultdict
from openid.consumer.consumer import Consumer, SUCCESS, CANCEL, FAILURE
from openid.consumer.discover import DiscoveryFailure
@@ -33,7 +30,6 @@
from django.utils import simplejson
from django.utils.importlib import import_module
-from social_auth.models import UserSocialAuth
from social_auth.utils import setting
from social_auth.store import DjangoOpenIDStore
from social_auth.backends.exceptions import StopPipeline
@@ -105,46 +101,46 @@ def authenticate(self, *args, **kwargs):
response = kwargs.get('response')
details = self.get_user_details(response)
uid = self.get_user_id(details, response)
- user = kwargs.get('user')
- request = kwargs.get('request')
-
- # Pipeline:
- # Arguments:
- # request, backend, social_user, uid, response, details
- # user, is_new, args, kwargs
- kwargs = kwargs.copy()
- kwargs.update({
- 'backend': self,
- 'request': request,
- 'uid': uid,
- 'user': user,
- 'social_user': None,
- 'response': response,
- 'details': details,
- 'is_new': False,
- })
- for name in PIPELINE:
+
+ out = self.pipeline(PIPELINE, backend=self, uid=uid,
+ social_user=None, details=details,
+ is_new=False, *args, **kwargs)
+ if not isinstance(out, dict):
+ return out
+
+ social_user = out.get('social_user')
+ if social_user:
+ # define user.social_user attribute to track current social
+ # account
+ user = social_user.user
+ user.social_user = social_user
+ user.is_new = kwargs.get('is_new')
+ return user
+
+ def pipeline(self, pipeline, request, *args, **kwargs):
+ """Pipeline"""
+ out = kwargs.copy()
+
+ for name in pipeline:
mod_name, func_name = name.rsplit('.', 1)
try:
mod = import_module(mod_name)
except ImportError:
logger.exception('Error importing pipeline %s', name)
else:
- pipeline = getattr(mod, func_name, None)
- if callable(pipeline):
+ func = getattr(mod, func_name, None)
+
+ if callable(func):
try:
- kwargs.update(pipeline(*args, **kwargs) or {})
+ result = func(*args, **out) or {}
except StopPipeline:
break
- social_user = kwargs.get('social_user')
- if social_user:
- # define user.social_user attribute to track current social
- # account
- user = social_user.user
- user.social_user = social_user
- user.is_new = kwargs.get('is_new')
- return user
+ if isinstance(result, dict):
+ out.update(result)
+ else:
+ return result
+ return out
def extra_data(self, user, uid, response, details):
"""Return default blank user extra data"""
@@ -171,6 +167,7 @@ def get_user(self, user_id):
except User.DoesNotExist:
return None
+
class OAuthBackend(SocialAuthBackend):
"""OAuth authentication backend base class.
@@ -286,6 +283,7 @@ class BaseAuth(object):
@AUTH_BACKEND Authorization backend related with this service
"""
+
AUTH_BACKEND = None
def __init__(self, request, redirect):
@@ -311,7 +309,7 @@ def auth_extra_arguments(self):
"""Return extra argumens needed on auth process, setting is per bancked
and defined by <backend name in uppercase>_AUTH_EXTRA_ARGUMENTS.
"""
- name = self.AUTH_BACKEND.name.upper() + '_AUTH_EXTRA_ARGUMENTS'
+ name = self.AUTH_BACKEND.name.upper().replace('-','_') + '_AUTH_EXTRA_ARGUMENTS'
return getattr(settings, name, {})
@property
@@ -456,8 +454,7 @@ def auth_url(self):
token = self.unauthorized_token()
name = self.AUTH_BACKEND.name + 'unauthorized_token_name'
self.request.session[name] = token.to_string()
- return self.oauth_request(token, self.AUTHORIZATION_URL,
- self.auth_extra_arguments()).to_url()
+ return self.oauth_authorization_request(token).to_url()
def auth_complete(self, *args, **kwargs):
"""Return user, might be logged in"""
@@ -484,6 +481,11 @@ def unauthorized_token(self):
response = self.fetch_response(request)
return Token.from_string(response)
+ def oauth_authorization_request(self, token):
+ """Generate OAuth request to authorize token."""
+ return self.oauth_request(token, self.AUTHORIZATION_URL,
+ self.auth_extra_arguments())
+
def oauth_request(self, token, url, extra_params=None):
"""Generate OAuth request, setups callback url"""
params = {'oauth_callback': self.redirect_uri}
@@ -602,49 +604,60 @@ def get_key_and_secret(self):
setting(self.SETTINGS_SECRET_NAME)
-# import sources from where check for auth backends
-SOCIAL_AUTH_IMPORT_SOURCES = (
- 'social_auth.backends',
- 'social_auth.backends.contrib',
-) + setting('SOCIAL_AUTH_IMPORT_BACKENDS', ())
-
-def get_backends():
- enabled = setting('SOCIAL_AUTH_ENABLED_BACKENDS')
- if enabled:
- enabled = defaultdict(lambda: False, ((bak, True) for bak in enabled))
- else:
- enabled = defaultdict(lambda: True)
-
- backends = {}
- for mod_name in SOCIAL_AUTH_IMPORT_SOURCES:
- try:
- mod = import_module(mod_name)
- except ImportError:
- logger.exception('Error importing %s', mod_name)
- continue
+# Backend loading was previously performed via the
+# SOCIAL_AUTH_IMPORT_BACKENDS setting - as it's no longer used,
+# provide a deprecation warning.
+if setting('SOCIAL_AUTH_IMPORT_BACKENDS'):
+ from warnings import warn
+ warn("SOCIAL_AUTH_IMPORT_SOURCES is deprecated")
- for directory, subdir, files in walk(mod.__path__[0]):
- for name in filter(lambda name: name.endswith('.py'), files):
- try:
- name = basename(name).replace('.py', '')
- sub = import_module(mod_name + '.' + name)
+# Cache for discovered backends.
+BACKENDS = {}
- # register only enabled backends
- new = ((key, val) for key, val in sub.BACKENDS.items()
- if val.enabled() and enabled[key])
- backends.update(new)
- except (ImportError, AttributeError):
- pass
-
- if enabled[OpenIdAuth.AUTH_BACKEND.name]:
- backends[OpenIdAuth.AUTH_BACKEND.name] = OpenIdAuth
- return backends
-
-
-# load backends from defined modules
-BACKENDS = get_backends()
+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 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
+ bases found in this module would not have the chance to be loaded
+ by the time they were added to this module's BACKENDS dict. See:
+ https://github.com/omab/django-social-auth/issues/204
+
+ 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):
- """Return auth backend instance *if* it's registered, None in other case"""
- return BACKENDS.get(name, lambda *args, **kwargs: None)(*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:
+ # Force a reload of BACKENDS to ensure a missing
+ # backend hasn't been missed.
+ get_backends(force_load=True)
+ try:
+ return BACKENDS[name](*args, **kwargs)
+ except KeyError:
+ return None
Oops, something went wrong.

0 comments on commit 84fe3b8

Please sign in to comment.