""" Modified version of django.contrib.auth module (https://docs.djangoproject.com/en/2.0/_modules/django/contrib/auth/). """ from django.apps import apps as django_apps from django.conf import settings from django.contrib.auth import _get_backends, load_backend from django.contrib.auth.signals import user_logged_in from django.middleware.csrf import rotate_token from django.utils.crypto import constant_time_compare BACKEND_SESSION_KEY = '_auth_user_backend' CLASS_SESSION_KEY = '_auth_user_class' HASH_SESSION_KEY = '_auth_user_hash' SESSION_KEY = '_auth_user_id' def get_user_model(user_class): """ Return the user model (User/Admin from usermerge.models - see models.py) that is active in the session. """ # user_class is either 'usermerge.User' or 'usermerge.Admin' (the format should necessarily be 'app_label.model_name'). return django_apps.get_model(user_class, require_ready = False) def _get_user_session_key(request): # This value in the session is always serialized to a string, so we need # to convert it back to Python whenever we access it. return get_user_model(request.session[CLASS_SESSION_KEY])._meta.pk.to_python(request.session[SESSION_KEY]) def login(request, user, backend = None): """ Persist a user id and a backend in the request. This way a user doesn't have to reauthenticate on every request. Note that data set during the anonymous session is retained when the user logs in. """ session_auth_hash = '' if user is None: user = request.user if hasattr(user, 'get_session_auth_hash'): session_auth_hash = user.get_session_auth_hash() if SESSION_KEY in request.session: if _get_user_session_key(request) != user.pk or ( session_auth_hash and not constant_time_compare(request.session.get(HASH_SESSION_KEY, ''), session_auth_hash)): # 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() try: backend = backend or user.backend except AttributeError: backends = _get_backends(return_tuples = True) if len(backends) == 1: _, backend = backends[0] else: raise ValueError( 'You have multiple authentication backends configured and ' 'therefore must provide the `backend` argument or set the ' '`backend` attribute on the user.' ) request.session[SESSION_KEY] = user._meta.pk.value_to_string(user) # The value of CLASS_SESSION_KEY is needed to clarify if the authenticated user # is a User ('usermerge.User') or an Admin ('usermerge.Admin'). request.session[CLASS_SESSION_KEY] = str(user.__class__).replace('.models', '').split("'")[1] request.session[BACKEND_SESSION_KEY] = backend request.session[HASH_SESSION_KEY] = session_auth_hash if hasattr(request, 'user'): request.user = user rotate_token(request) user_logged_in.send(sender = user.__class__, request = request, user = user) def get_user(request): """ Return the user model instance associated with the given request session. If no user is retrieved, return an instance of `AnonymousUser`. """ from django.contrib.auth.models import AnonymousUser user = None try: user_id = _get_user_session_key(request) backend_path = request.session[BACKEND_SESSION_KEY] except KeyError: pass else: if backend_path in settings.AUTHENTICATION_BACKENDS: backend = load_backend(backend_path) user = backend.get_user(request.session[CLASS_SESSION_KEY], user_id) # Verify the session. if hasattr(user, 'get_session_auth_hash'): session_hash = request.session.get(HASH_SESSION_KEY) session_hash_verified = session_hash and constant_time_compare( session_hash, user.get_session_auth_hash() ) if not session_hash_verified: request.session.flush() user = None return user or AnonymousUser()