Fixed #19060 -- Corrected assumptions about the name of the User model in the ModelBackend.
Thanks to Ivan Virabyan for the report and initial patch.
This commit is contained in:
parent
d99639da03
commit
b9039268a1
|
@ -30,7 +30,9 @@ class ModelBackend(object):
|
||||||
if user_obj.is_superuser:
|
if user_obj.is_superuser:
|
||||||
perms = Permission.objects.all()
|
perms = Permission.objects.all()
|
||||||
else:
|
else:
|
||||||
perms = Permission.objects.filter(group__user=user_obj)
|
user_groups_field = get_user_model()._meta.get_field('groups')
|
||||||
|
user_groups_query = 'group__%s' % user_groups_field.related_query_name()
|
||||||
|
perms = Permission.objects.filter(**{user_groups_query: user_obj})
|
||||||
perms = perms.values_list('content_type__app_label', 'codename').order_by()
|
perms = perms.values_list('content_type__app_label', 'codename').order_by()
|
||||||
user_obj._group_perm_cache = set(["%s.%s" % (ct, name) for ct, name in perms])
|
user_obj._group_perm_cache = set(["%s.%s" % (ct, name) for ct, name in perms])
|
||||||
return user_obj._group_perm_cache
|
return user_obj._group_perm_cache
|
||||||
|
|
|
@ -1,24 +1,29 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
from datetime import date
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth.models import User, Group, Permission, AnonymousUser
|
from django.contrib.auth.models import User, Group, Permission, AnonymousUser
|
||||||
from django.contrib.auth.tests.utils import skipIfCustomUser
|
from django.contrib.auth.tests.utils import skipIfCustomUser
|
||||||
|
from django.contrib.auth.tests.custom_user import ExtensionUser
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.test.utils import override_settings
|
from django.test.utils import override_settings
|
||||||
|
|
||||||
|
|
||||||
@skipIfCustomUser
|
class BaseModelBackendTest(object):
|
||||||
class BackendTest(TestCase):
|
"""
|
||||||
|
A base class for tests that need to validate the ModelBackend
|
||||||
|
with different User models. Subclasses should define a class
|
||||||
|
level UserModel attribute, and a create_users() method to
|
||||||
|
construct two users for test purposes.
|
||||||
|
"""
|
||||||
backend = 'django.contrib.auth.backends.ModelBackend'
|
backend = 'django.contrib.auth.backends.ModelBackend'
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.curr_auth = settings.AUTHENTICATION_BACKENDS
|
self.curr_auth = settings.AUTHENTICATION_BACKENDS
|
||||||
settings.AUTHENTICATION_BACKENDS = (self.backend,)
|
settings.AUTHENTICATION_BACKENDS = (self.backend,)
|
||||||
User.objects.create_user('test', 'test@example.com', 'test')
|
self.create_users()
|
||||||
User.objects.create_superuser('test2', 'test2@example.com', 'test')
|
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
settings.AUTHENTICATION_BACKENDS = self.curr_auth
|
settings.AUTHENTICATION_BACKENDS = self.curr_auth
|
||||||
|
@ -28,7 +33,7 @@ class BackendTest(TestCase):
|
||||||
ContentType.objects.clear_cache()
|
ContentType.objects.clear_cache()
|
||||||
|
|
||||||
def test_has_perm(self):
|
def test_has_perm(self):
|
||||||
user = User.objects.get(username='test')
|
user = self.UserModel.objects.get(username='test')
|
||||||
self.assertEqual(user.has_perm('auth.test'), False)
|
self.assertEqual(user.has_perm('auth.test'), False)
|
||||||
user.is_staff = True
|
user.is_staff = True
|
||||||
user.save()
|
user.save()
|
||||||
|
@ -47,14 +52,14 @@ class BackendTest(TestCase):
|
||||||
self.assertEqual(user.has_perm('auth.test'), False)
|
self.assertEqual(user.has_perm('auth.test'), False)
|
||||||
|
|
||||||
def test_custom_perms(self):
|
def test_custom_perms(self):
|
||||||
user = User.objects.get(username='test')
|
user = self.UserModel.objects.get(username='test')
|
||||||
content_type=ContentType.objects.get_for_model(Group)
|
content_type = ContentType.objects.get_for_model(Group)
|
||||||
perm = Permission.objects.create(name='test', content_type=content_type, codename='test')
|
perm = Permission.objects.create(name='test', content_type=content_type, codename='test')
|
||||||
user.user_permissions.add(perm)
|
user.user_permissions.add(perm)
|
||||||
user.save()
|
user.save()
|
||||||
|
|
||||||
# reloading user to purge the _perm_cache
|
# reloading user to purge the _perm_cache
|
||||||
user = User.objects.get(username='test')
|
user = self.UserModel.objects.get(username='test')
|
||||||
self.assertEqual(user.get_all_permissions() == set(['auth.test']), True)
|
self.assertEqual(user.get_all_permissions() == set(['auth.test']), True)
|
||||||
self.assertEqual(user.get_group_permissions(), set([]))
|
self.assertEqual(user.get_group_permissions(), set([]))
|
||||||
self.assertEqual(user.has_module_perms('Group'), False)
|
self.assertEqual(user.has_module_perms('Group'), False)
|
||||||
|
@ -65,7 +70,7 @@ class BackendTest(TestCase):
|
||||||
perm = Permission.objects.create(name='test3', content_type=content_type, codename='test3')
|
perm = Permission.objects.create(name='test3', content_type=content_type, codename='test3')
|
||||||
user.user_permissions.add(perm)
|
user.user_permissions.add(perm)
|
||||||
user.save()
|
user.save()
|
||||||
user = User.objects.get(username='test')
|
user = self.UserModel.objects.get(username='test')
|
||||||
self.assertEqual(user.get_all_permissions(), set(['auth.test2', 'auth.test', 'auth.test3']))
|
self.assertEqual(user.get_all_permissions(), set(['auth.test2', 'auth.test', 'auth.test3']))
|
||||||
self.assertEqual(user.has_perm('test'), False)
|
self.assertEqual(user.has_perm('test'), False)
|
||||||
self.assertEqual(user.has_perm('auth.test'), True)
|
self.assertEqual(user.has_perm('auth.test'), True)
|
||||||
|
@ -75,7 +80,7 @@ class BackendTest(TestCase):
|
||||||
group.permissions.add(perm)
|
group.permissions.add(perm)
|
||||||
group.save()
|
group.save()
|
||||||
user.groups.add(group)
|
user.groups.add(group)
|
||||||
user = User.objects.get(username='test')
|
user = self.UserModel.objects.get(username='test')
|
||||||
exp = set(['auth.test2', 'auth.test', 'auth.test3', 'auth.test_group'])
|
exp = set(['auth.test2', 'auth.test', 'auth.test3', 'auth.test_group'])
|
||||||
self.assertEqual(user.get_all_permissions(), exp)
|
self.assertEqual(user.get_all_permissions(), exp)
|
||||||
self.assertEqual(user.get_group_permissions(), set(['auth.test_group']))
|
self.assertEqual(user.get_group_permissions(), set(['auth.test_group']))
|
||||||
|
@ -87,8 +92,8 @@ class BackendTest(TestCase):
|
||||||
|
|
||||||
def test_has_no_object_perm(self):
|
def test_has_no_object_perm(self):
|
||||||
"""Regressiontest for #12462"""
|
"""Regressiontest for #12462"""
|
||||||
user = User.objects.get(username='test')
|
user = self.UserModel.objects.get(username='test')
|
||||||
content_type=ContentType.objects.get_for_model(Group)
|
content_type = ContentType.objects.get_for_model(Group)
|
||||||
perm = Permission.objects.create(name='test', content_type=content_type, codename='test')
|
perm = Permission.objects.create(name='test', content_type=content_type, codename='test')
|
||||||
user.user_permissions.add(perm)
|
user.user_permissions.add(perm)
|
||||||
user.save()
|
user.save()
|
||||||
|
@ -100,9 +105,65 @@ class BackendTest(TestCase):
|
||||||
|
|
||||||
def test_get_all_superuser_permissions(self):
|
def test_get_all_superuser_permissions(self):
|
||||||
"A superuser has all permissions. Refs #14795"
|
"A superuser has all permissions. Refs #14795"
|
||||||
user = User.objects.get(username='test2')
|
user = self.UserModel.objects.get(username='test2')
|
||||||
self.assertEqual(len(user.get_all_permissions()), len(Permission.objects.all()))
|
self.assertEqual(len(user.get_all_permissions()), len(Permission.objects.all()))
|
||||||
|
|
||||||
|
|
||||||
|
@skipIfCustomUser
|
||||||
|
class ModelBackendTest(BaseModelBackendTest, TestCase):
|
||||||
|
"""
|
||||||
|
Tests for the ModelBackend using the default User model.
|
||||||
|
"""
|
||||||
|
UserModel = User
|
||||||
|
|
||||||
|
def create_users(self):
|
||||||
|
User.objects.create_user(
|
||||||
|
username='test',
|
||||||
|
email='test@example.com',
|
||||||
|
password='test',
|
||||||
|
)
|
||||||
|
User.objects.create_superuser(
|
||||||
|
username='test2',
|
||||||
|
email='test2@example.com',
|
||||||
|
password='test',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@override_settings(AUTH_USER_MODEL='auth.ExtensionUser')
|
||||||
|
class ExtensionUserModelBackendTest(BaseModelBackendTest, TestCase):
|
||||||
|
"""
|
||||||
|
Tests for the ModelBackend using the custom ExtensionUser model.
|
||||||
|
|
||||||
|
This isn't a perfect test, because both the User and ExtensionUser are
|
||||||
|
synchronized to the database, which wouldn't ordinary happen in
|
||||||
|
production. As a result, it doesn't catch errors caused by the non-
|
||||||
|
existence of the User table.
|
||||||
|
|
||||||
|
The specific problem is queries on .filter(groups__user) et al, which
|
||||||
|
makes an implicit assumption that the user model is called 'User'. In
|
||||||
|
production, the auth.User table won't exist, so the requested join
|
||||||
|
won't exist either; in testing, the auth.User *does* exist, and
|
||||||
|
so does the join. However, the join table won't contain any useful
|
||||||
|
data; for testing, we check that the data we expect actually does exist.
|
||||||
|
"""
|
||||||
|
|
||||||
|
UserModel = ExtensionUser
|
||||||
|
|
||||||
|
def create_users(self):
|
||||||
|
ExtensionUser.objects.create_user(
|
||||||
|
username='test',
|
||||||
|
email='test@example.com',
|
||||||
|
password='test',
|
||||||
|
date_of_birth=date(2006, 4, 25)
|
||||||
|
)
|
||||||
|
ExtensionUser.objects.create_superuser(
|
||||||
|
username='test2',
|
||||||
|
email='test2@example.com',
|
||||||
|
password='test',
|
||||||
|
date_of_birth=date(1976, 11, 8)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestObj(object):
|
class TestObj(object):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -110,7 +171,7 @@ class TestObj(object):
|
||||||
class SimpleRowlevelBackend(object):
|
class SimpleRowlevelBackend(object):
|
||||||
def has_perm(self, user, perm, obj=None):
|
def has_perm(self, user, perm, obj=None):
|
||||||
if not obj:
|
if not obj:
|
||||||
return # We only support row level perms
|
return # We only support row level perms
|
||||||
|
|
||||||
if isinstance(obj, TestObj):
|
if isinstance(obj, TestObj):
|
||||||
if user.username == 'test2':
|
if user.username == 'test2':
|
||||||
|
@ -128,7 +189,7 @@ class SimpleRowlevelBackend(object):
|
||||||
|
|
||||||
def get_all_permissions(self, user, obj=None):
|
def get_all_permissions(self, user, obj=None):
|
||||||
if not obj:
|
if not obj:
|
||||||
return [] # We only support row level perms
|
return [] # We only support row level perms
|
||||||
|
|
||||||
if not isinstance(obj, TestObj):
|
if not isinstance(obj, TestObj):
|
||||||
return ['none']
|
return ['none']
|
||||||
|
@ -142,7 +203,7 @@ class SimpleRowlevelBackend(object):
|
||||||
|
|
||||||
def get_group_permissions(self, user, obj=None):
|
def get_group_permissions(self, user, obj=None):
|
||||||
if not obj:
|
if not obj:
|
||||||
return # We only support row level perms
|
return # We only support row level perms
|
||||||
|
|
||||||
if not isinstance(obj, TestObj):
|
if not isinstance(obj, TestObj):
|
||||||
return ['none']
|
return ['none']
|
||||||
|
@ -189,7 +250,6 @@ class RowlevelBackendTest(TestCase):
|
||||||
self.assertEqual(self.user2.get_all_permissions(), set([]))
|
self.assertEqual(self.user2.get_all_permissions(), set([]))
|
||||||
|
|
||||||
def test_get_group_permissions(self):
|
def test_get_group_permissions(self):
|
||||||
content_type=ContentType.objects.get_for_model(Group)
|
|
||||||
group = Group.objects.create(name='test_group')
|
group = Group.objects.create(name='test_group')
|
||||||
self.user3.groups.add(group)
|
self.user3.groups.add(group)
|
||||||
self.assertEqual(self.user3.get_group_permissions(TestObj()), set(['group_perm']))
|
self.assertEqual(self.user3.get_group_permissions(TestObj()), set(['group_perm']))
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
|
from django.db import models
|
||||||
|
from django.contrib.auth.models import BaseUserManager, AbstractBaseUser, AbstractUser, UserManager
|
||||||
|
|
||||||
|
|
||||||
# The custom User uses email as the unique identifier, and requires
|
# The custom User uses email as the unique identifier, and requires
|
||||||
# that every user provide a date of birth. This lets us test
|
# that every user provide a date of birth. This lets us test
|
||||||
# changes in username datatype, and non-text required fields.
|
# changes in username datatype, and non-text required fields.
|
||||||
|
|
||||||
from django.db import models
|
|
||||||
from django.contrib.auth.models import BaseUserManager, AbstractBaseUser
|
|
||||||
|
|
||||||
|
|
||||||
class CustomUserManager(BaseUserManager):
|
class CustomUserManager(BaseUserManager):
|
||||||
def create_user(self, email, date_of_birth, password=None):
|
def create_user(self, email, date_of_birth, password=None):
|
||||||
"""
|
"""
|
||||||
|
@ -73,3 +73,18 @@ class CustomUser(AbstractBaseUser):
|
||||||
@property
|
@property
|
||||||
def is_staff(self):
|
def is_staff(self):
|
||||||
return self.is_admin
|
return self.is_admin
|
||||||
|
|
||||||
|
|
||||||
|
# The extension user is a simple extension of the built-in user class,
|
||||||
|
# adding a required date_of_birth field. This allows us to check for
|
||||||
|
# any hard references to the name "User" in forms/handlers etc.
|
||||||
|
|
||||||
|
class ExtensionUser(AbstractUser):
|
||||||
|
date_of_birth = models.DateField()
|
||||||
|
|
||||||
|
objects = UserManager()
|
||||||
|
|
||||||
|
REQUIRED_FIELDS = AbstractUser.REQUIRED_FIELDS + ['date_of_birth']
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
app_label = 'auth'
|
||||||
|
|
Loading…
Reference in New Issue