[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 0f7f5bc9e7 from master
This commit is contained in:
Tim Graham 2015-02-11 14:38:52 -05:00
parent 1904022f91
commit 596564e808
4 changed files with 40 additions and 6 deletions

View File

@ -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

View File

@ -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',
)

View File

@ -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'

View File

@ -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