import datetime from warnings import warn from django.core.exceptions import ImproperlyConfigured from django.utils.importlib import import_module from django.contrib.auth.signals import user_logged_in, user_logged_out SESSION_KEY = '_auth_user_id' BACKEND_SESSION_KEY = '_auth_user_backend' REDIRECT_FIELD_NAME = 'next' def load_backend(path): i = path.rfind('.') module, attr = path[:i], path[i+1:] try: mod = import_module(module) except ImportError, e: raise ImproperlyConfigured('Error importing authentication backend %s: "%s"' % (module, e)) except ValueError, e: raise ImproperlyConfigured('Error importing authentication backends. Is AUTHENTICATION_BACKENDS a correctly defined list or tuple?') try: cls = getattr(mod, attr) except AttributeError: raise ImproperlyConfigured('Module "%s" does not define a "%s" authentication backend' % (module, attr)) if not hasattr(cls, "supports_object_permissions"): warn("Authentication backends without a `supports_object_permissions` attribute are deprecated. Please define it in %s." % cls, DeprecationWarning) cls.supports_object_permissions = False if not hasattr(cls, 'supports_anonymous_user'): warn("Authentication backends without a `supports_anonymous_user` attribute are deprecated. Please define it in %s." % cls, DeprecationWarning) cls.supports_anonymous_user = False return cls() def get_backends(): from django.conf import settings backends = [] for backend_path in settings.AUTHENTICATION_BACKENDS: backends.append(load_backend(backend_path)) if not backends: raise ImproperlyConfigured('No authentication backends have been defined. Does AUTHENTICATION_BACKENDS contain anything?') return backends def authenticate(**credentials): """ If the given credentials are valid, return a User object. """ for backend in get_backends(): try: user = backend.authenticate(**credentials) except TypeError: # This backend doesn't accept these credentials as arguments. Try the next one. continue if user is None: continue # Annotate the user object with the path of the backend. user.backend = "%s.%s" % (backend.__module__, backend.__class__.__name__) return user def login(request, user): """ Persist a user id and a backend in the request. This way a user doesn't have to reauthenticate on every request. """ if user is None: user = request.user # TODO: It would be nice to support different login methods, like signed cookies. user_logged_in.send(sender=user.__class__, request=request, user=user) if SESSION_KEY in request.session: if request.session[SESSION_KEY] != user.id: # To avoid reusing another user's session, create a new, empty # session if the existing session corresponds to a different # authenticated user. request.session.flush() else: request.session.cycle_key() request.session[SESSION_KEY] = user.id request.session[BACKEND_SESSION_KEY] = user.backend if hasattr(request, 'user'): request.user = user def logout(request): """ Removes the authenticated user's ID from the request and flushes their session data. """ # Dispatch the signal before the user is logged out so the receivers have a # chance to find out *who* logged out. user = getattr(request, 'user', None) if hasattr(user, 'is_authenticated') and not user.is_authenticated(): user = None user_logged_out.send(sender=user.__class__, request=request, user=user) request.session.flush() if hasattr(request, 'user'): from django.contrib.auth.models import AnonymousUser request.user = AnonymousUser() def get_user(request): from django.contrib.auth.models import AnonymousUser try: user_id = request.session[SESSION_KEY] backend_path = request.session[BACKEND_SESSION_KEY] backend = load_backend(backend_path) user = backend.get_user(user_id) or AnonymousUser() except KeyError: user = AnonymousUser() return user