From: Matías Aguirre Date: Fri, 10 Dec 2010 14:24:56 +0000 (-0200) Subject: Removed unnecessary base module, moved clases where they are used X-Git-Url: https://git.parisson.com/?a=commitdiff_plain;h=c903e12c7dfb43044d4d5f8a3803d63b9e93411a;p=django-social-auth.git Removed unnecessary base module, moved clases where they are used --- diff --git a/social_auth/auth.py b/social_auth/auth.py index 9192299..c3ea7a9 100644 --- a/social_auth/auth.py +++ b/social_auth/auth.py @@ -13,7 +13,6 @@ from django.conf import settings from django.utils import simplejson from django.contrib.auth import authenticate -from .base import BaseAuth from .store import DjangoOpenIDStore from .backends import TwitterBackend, OrkutBackend, FacebookBackend, \ OpenIDBackend @@ -28,6 +27,32 @@ from .conf import AX_ATTRS, SREG_ATTR, OPENID_ID_FIELD, SESSION_NAME, \ ORKUT_EXTRA_DATA +class BaseAuth(object): + """Base authentication class, new authenticators should subclass + and implement needed methods""" + def __init__(self, request, redirect): + self.request = request + self.redirect = redirect + + def auth_url(self): + """Must return redirect URL to auth provider""" + raise NotImplementedError, 'Implement in subclass' + + def auth_html(self): + """Must return login HTML content returned by provider""" + raise NotImplementedError, 'Implement in subclass' + + def auth_complete(self, *args, **kwargs): + """Completes loging process, must return user instance""" + raise NotImplementedError, 'Implement in subclass' + + @property + def uses_redirect(self): + """Return True if this provider uses redirect url method, + otherwise return false.""" + return True + + class OpenIdAuth(BaseAuth): """OpenId process handling""" def auth_url(self): diff --git a/social_auth/backends.py b/social_auth/backends.py index 1fef420..e27a02f 100644 --- a/social_auth/backends.py +++ b/social_auth/backends.py @@ -1,11 +1,165 @@ """ Authentication backeds for django.contrib.auth AUTHENTICATION_BACKENDS setting """ +import os +import md5 from openid.extensions import ax, sreg -from .base import SocialAuthBackend +from django.conf import settings +from django.contrib.auth.backends import ModelBackend +from django.contrib.auth.models import UNUSABLE_PASSWORD + +from .models import UserSocialAuth from .conf import OLD_AX_ATTRS, AX_SCHEMA_ATTRS +# get User class, could not be auth.User +User = UserSocialAuth._meta.get_field('user').rel.to + + +class SocialAuthBackend(ModelBackend): + """A django.contrib.auth backend that authenticates the user based on + a authentication provider response""" + name = '' # provider name, it's stored in database + + def authenticate(self, *args, **kwargs): + """Authenticate user using social credentials + + Authentication is made if this is the correct backend, backend + verification is made by kwargs inspection for current backend + name presence. + """ + # Validate backend and arguments. Require that the OAuth response + # be passed in as a keyword argument, to make sure we don't match + # the username/password calling conventions of authenticate. + if not (self.name and kwargs.get(self.name) and 'response' in kwargs): + return None + + response = kwargs.get('response') + details = self.get_user_details(response) + uid = self.get_user_id(details, response) + try: + auth_user = UserSocialAuth.objects.select_related('user')\ + .get(provider=self.name, + uid=uid) + except UserSocialAuth.DoesNotExist: + if not getattr(settings, 'SOCIAL_AUTH_CREATE_USERS', False): + return None + user = self.create_user(details=details, *args, **kwargs) + else: + user = auth_user.user + self.update_user_details(user, details) + return user + + def get_username(self, details): + """Return an unique username, if SOCIAL_AUTH_FORCE_RANDOM_USERNAME + setting is True, then username will be a random 30 chars md5 hash + """ + def get_random_username(): + """Return hash from random string cut at 30 chars""" + return md5.md5(str(os.urandom(10))).hexdigest()[:30] + + if getattr(settings, 'SOCIAL_AUTH_FORCE_RANDOM_USERNAME', False): + username = get_random_username() + elif 'username' in details: + username = details['username'] + elif hasattr(settings, 'SOCIAL_AUTH_DEFAULT_USERNAME'): + username = settings.SOCIAL_AUTH_DEFAULT_USERNAME + if callable(username): + username = username() + else: + username = get_random_username() + + name, idx = username, 2 + while True: + try: + User.objects.get(username=name) + name = username + str(idx) + idx += 1 + except User.DoesNotExist: + username = name + break + return username + + def create_user(self, response, details, *args, **kwargs): + """Create user with unique username. New social credentials are + associated with @user if this parameter is not None.""" + user = kwargs.get('user') + if user is None: # create user, otherwise associate the new credential + username = self.get_username(details) + email = details.get('email', '') + + if hasattr(User.objects, 'create_user'): # auth.User + user = User.objects.create_user(username, email) + else: # create user setting password to an unusable value + user = User.objects.create(username=username, email=email, + password=UNUSABLE_PASSWORD) + + # update details and associate account with social credentials + self.update_user_details(user, details) + self.associate_auth(user, response, details) + return user + + def associate_auth(self, user, response, details): + """Associate an OAuth with a user account.""" + # Check to see if this OAuth has already been claimed. + uid = self.get_user_id(details, response) + try: + user_social = UserSocialAuth.objects.select_related('user')\ + .get(provider=self.name, + uid=uid) + except UserSocialAuth.DoesNotExist: + if getattr(settings, 'SOCIAL_AUTH_EXTRA_DATA', True): + extra_data = self.extra_data(user, uid, response, details) + else: + extra_data = '' + user_social = UserSocialAuth.objects.create(user=user, uid=uid, + provider=self.name, + extra_data=extra_data) + else: + if user_social.user != user: + raise ValueError, 'Identity already claimed' + return user_social + + def extra_data(self, user, uid, response, details): + """Return default blank user extra data""" + return '' + + def update_user_details(self, user, details): + """Update user details with new (maybe) data""" + fields = (name for name in ('first_name', 'last_name', 'email') + if user._meta.get_field(name)) + changed = False + + for name in fields: + value = details.get(name) + if value and value != getattr(user, name, value): + setattr(user, name, value) + changed = True + + if changed: + user.save() + + def get_user_id(self, details, response): + """Must return a unique ID from values returned on details""" + raise NotImplementedError, 'Implement in subclass' + + def get_user_details(self, response): + """Must return user details in a know internal struct: + {'email': , + 'username': , + 'fullname': , + 'first_name': , + 'last_name': } + """ + raise NotImplementedError, 'Implement in subclass' + + def get_user(self, user_id): + """Return user instance for @user_id""" + try: + return User.objects.get(pk=user_id) + except User.DoesNotExist: + return None + class OAuthBackend(SocialAuthBackend): """OAuth authentication backend base class"""