From 596564e808089f1263469dbf4744dcaa05b79222 Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Wed, 11 Feb 2015 14:38:52 -0500 Subject: [PATCH] [1.8.x] Fixed #24161 -- Stored the user primary key as a serialized value in the session. This allows using a UUIDField primary key along with the JSON session serializer. Thanks to Trac alias jamesbeith for the report and Simon Charette for the initial patch. Backport of 0f7f5bc9e7a94ab91c2b3db29ef7cf000eff593f from master --- django/contrib/auth/__init__.py | 12 +++++++++--- tests/auth_tests/models/__init__.py | 3 ++- tests/auth_tests/models/uuid_pk.py | 13 +++++++++++++ tests/auth_tests/test_auth_backends.py | 18 ++++++++++++++++-- 4 files changed, 40 insertions(+), 6 deletions(-) create mode 100644 tests/auth_tests/models/uuid_pk.py diff --git a/django/contrib/auth/__init__.py b/django/contrib/auth/__init__.py index a9eb41a609..be0ab52761 100644 --- a/django/contrib/auth/__init__.py +++ b/django/contrib/auth/__init__.py @@ -53,6 +53,12 @@ def _clean_credentials(credentials): return credentials +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()._meta.pk.to_python(request.session[SESSION_KEY]) + + def authenticate(**credentials): """ If the given credentials are valid, return a User object. @@ -93,7 +99,7 @@ def login(request, user): session_auth_hash = user.get_session_auth_hash() if SESSION_KEY in request.session: - if request.session[SESSION_KEY] != user.pk or ( + if _get_user_session_key(request) != user.pk or ( session_auth_hash and request.session.get(HASH_SESSION_KEY) != session_auth_hash): # To avoid reusing another user's session, create a new, empty @@ -102,7 +108,7 @@ def login(request, user): request.session.flush() else: request.session.cycle_key() - request.session[SESSION_KEY] = user.pk + request.session[SESSION_KEY] = user._meta.pk.value_to_string(user) request.session[BACKEND_SESSION_KEY] = user.backend request.session[HASH_SESSION_KEY] = session_auth_hash if hasattr(request, 'user'): @@ -158,7 +164,7 @@ def get_user(request): from .models import AnonymousUser user = None try: - user_id = request.session[SESSION_KEY] + user_id = _get_user_session_key(request) backend_path = request.session[BACKEND_SESSION_KEY] except KeyError: pass diff --git a/tests/auth_tests/models/__init__.py b/tests/auth_tests/models/__init__.py index edeaef5347..72afdf2358 100644 --- a/tests/auth_tests/models/__init__.py +++ b/tests/auth_tests/models/__init__.py @@ -5,9 +5,10 @@ from .invalid_models import ( CustomUserBadRequiredFields, ) from .with_foreign_key import CustomUserWithFK, Email +from .uuid_pk import UUIDUser __all__ = ( 'CustomPermissionsUser', 'CustomUserNonUniqueUsername', 'CustomUserNonListRequiredFields', 'CustomUserBadRequiredFields', - 'CustomUserWithFK', 'Email', 'IsActiveTestUser1', + 'CustomUserWithFK', 'Email', 'IsActiveTestUser1', 'UUIDUser', ) diff --git a/tests/auth_tests/models/uuid_pk.py b/tests/auth_tests/models/uuid_pk.py new file mode 100644 index 0000000000..44ec4410aa --- /dev/null +++ b/tests/auth_tests/models/uuid_pk.py @@ -0,0 +1,13 @@ +import uuid + +from django.contrib.auth.models import AbstractUser +from django.contrib.auth.tests.custom_user import RemoveGroupsAndPermissions +from django.db import models + +with RemoveGroupsAndPermissions(): + class UUIDUser(AbstractUser): + """A user with a UUID as primary key""" + id = models.UUIDField(default=uuid.uuid4, primary_key=True) + + class Meta: + app_label = 'auth' diff --git a/tests/auth_tests/test_auth_backends.py b/tests/auth_tests/test_auth_backends.py index 6cdcb96671..dad91e5ec2 100644 --- a/tests/auth_tests/test_auth_backends.py +++ b/tests/auth_tests/test_auth_backends.py @@ -3,7 +3,9 @@ from __future__ import unicode_literals from datetime import date from django.conf import settings -from django.contrib.auth import BACKEND_SESSION_KEY, authenticate, get_user +from django.contrib.auth import ( + BACKEND_SESSION_KEY, SESSION_KEY, authenticate, get_user, +) from django.contrib.auth.backends import ModelBackend from django.contrib.auth.hashers import MD5PasswordHasher from django.contrib.auth.models import AnonymousUser, Group, Permission, User @@ -13,7 +15,7 @@ from django.core.exceptions import ImproperlyConfigured, PermissionDenied from django.http import HttpRequest from django.test import TestCase, modify_settings, override_settings -from .models import CustomPermissionsUser +from .models import CustomPermissionsUser, UUIDUser class CountingMD5PasswordHasher(MD5PasswordHasher): @@ -287,6 +289,18 @@ class CustomUserModelBackendAuthenticateTest(TestCase): self.assertEqual(test_user, authenticated_user) +@override_settings(AUTH_USER_MODEL='auth.UUIDUser') +class UUIDUserTests(TestCase): + + def test_login(self): + """ + A custom user with a UUID primary key should be able to login. + """ + user = UUIDUser.objects.create_user(username='uuid', password='test') + self.assertTrue(self.client.login(username='uuid', password='test')) + self.assertEqual(UUIDUser.objects.get(pk=self.client.session[SESSION_KEY]), user) + + class TestObj(object): pass