Fixed #15915 -- Cleaned handling of duplicate permission codenames
Previously, a duplicate model, codename for permission would lead to database integrity error. Cleaned the implementation so that this case now raises an CommandError instead.
This commit is contained in:
parent
218abcc9e5
commit
8c427448d5
|
@ -9,6 +9,7 @@ import unicodedata
|
||||||
|
|
||||||
from django.contrib.auth import models as auth_app, get_user_model
|
from django.contrib.auth import models as auth_app, get_user_model
|
||||||
from django.core import exceptions
|
from django.core import exceptions
|
||||||
|
from django.core.management.base import CommandError
|
||||||
from django.db.models import get_models, signals
|
from django.db.models import get_models, signals
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
from django.utils.six.moves import input
|
from django.utils.six.moves import input
|
||||||
|
@ -18,13 +19,43 @@ def _get_permission_codename(action, opts):
|
||||||
return '%s_%s' % (action, opts.object_name.lower())
|
return '%s_%s' % (action, opts.object_name.lower())
|
||||||
|
|
||||||
|
|
||||||
def _get_all_permissions(opts):
|
def _get_all_permissions(opts, ctype):
|
||||||
"Returns (codename, name) for all permissions in the given opts."
|
"""
|
||||||
|
Returns (codename, name) for all permissions in the given opts.
|
||||||
|
"""
|
||||||
|
builtin = _get_builtin_permissions(opts)
|
||||||
|
custom = list(opts.permissions)
|
||||||
|
_check_permission_clashing(custom, builtin, ctype)
|
||||||
|
return builtin + custom
|
||||||
|
|
||||||
|
def _get_builtin_permissions(opts):
|
||||||
|
"""
|
||||||
|
Returns (codename, name) for all autogenerated permissions.
|
||||||
|
"""
|
||||||
perms = []
|
perms = []
|
||||||
for action in ('add', 'change', 'delete'):
|
for action in ('add', 'change', 'delete'):
|
||||||
perms.append((_get_permission_codename(action, opts), 'Can %s %s' % (action, opts.verbose_name_raw)))
|
perms.append((_get_permission_codename(action, opts),
|
||||||
return perms + list(opts.permissions)
|
'Can %s %s' % (action, opts.verbose_name_raw)))
|
||||||
|
return perms
|
||||||
|
|
||||||
|
def _check_permission_clashing(custom, builtin, ctype):
|
||||||
|
"""
|
||||||
|
Check that permissions for a model do not clash. Raises CommandError if
|
||||||
|
there are duplicate permissions.
|
||||||
|
"""
|
||||||
|
pool = set()
|
||||||
|
builtin_codenames = set(p[0] for p in builtin)
|
||||||
|
for codename, _name in custom:
|
||||||
|
if codename in pool:
|
||||||
|
raise CommandError(
|
||||||
|
"The permission codename '%s' is duplicated for model '%s.%s'." %
|
||||||
|
(codename, ctype.app_label, ctype.model_class().__name__))
|
||||||
|
elif codename in builtin_codenames:
|
||||||
|
raise CommandError(
|
||||||
|
"The permission codename '%s' clashes with a builtin permission "
|
||||||
|
"for model '%s.%s'." %
|
||||||
|
(codename, ctype.app_label, ctype.model_class().__name__))
|
||||||
|
pool.add(codename)
|
||||||
|
|
||||||
def create_permissions(app, created_models, verbosity, **kwargs):
|
def create_permissions(app, created_models, verbosity, **kwargs):
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
@ -39,7 +70,7 @@ def create_permissions(app, created_models, verbosity, **kwargs):
|
||||||
for klass in app_models:
|
for klass in app_models:
|
||||||
ctype = ContentType.objects.get_for_model(klass)
|
ctype = ContentType.objects.get_for_model(klass)
|
||||||
ctypes.add(ctype)
|
ctypes.add(ctype)
|
||||||
for perm in _get_all_permissions(klass._meta):
|
for perm in _get_all_permissions(klass._meta, ctype):
|
||||||
searched_perms.append((ctype, perm))
|
searched_perms.append((ctype, perm))
|
||||||
|
|
||||||
# Find all the Permissions that have a context_type for a model we're
|
# Find all the Permissions that have a context_type for a model we're
|
||||||
|
|
|
@ -2,6 +2,7 @@ from __future__ import unicode_literals
|
||||||
from datetime import date
|
from datetime import date
|
||||||
|
|
||||||
from django.contrib.auth import models, management
|
from django.contrib.auth import models, management
|
||||||
|
from django.contrib.auth.management import create_permissions
|
||||||
from django.contrib.auth.management.commands import changepassword
|
from django.contrib.auth.management.commands import changepassword
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.contrib.auth.tests import CustomUser
|
from django.contrib.auth.tests import CustomUser
|
||||||
|
@ -167,3 +168,43 @@ class CreatesuperuserManagementCommandTestCase(TestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(CustomUser.objects.count(), 0)
|
self.assertEqual(CustomUser.objects.count(), 0)
|
||||||
|
|
||||||
|
|
||||||
|
class PermissionDuplicationTestCase(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self._original_user_permission = models.User._meta.permissions
|
||||||
|
|
||||||
|
def tearUp(self):
|
||||||
|
models.User._meta.permissions = self._original_user_permissions
|
||||||
|
|
||||||
|
def test_duplicated_permissions(self):
|
||||||
|
"""
|
||||||
|
Test that we show proper error message if we are trying to create
|
||||||
|
duplicate permissions.
|
||||||
|
"""
|
||||||
|
# check duplicated default permission
|
||||||
|
models.Permission._meta.permissions = [
|
||||||
|
('change_permission', 'Can edit permission (duplicate)')]
|
||||||
|
self.assertRaisesRegexp(CommandError,
|
||||||
|
"The permission codename 'change_permission' clashes with a "
|
||||||
|
"builtin permission for model 'auth.Permission'.",
|
||||||
|
create_permissions, models, [], verbosity=0)
|
||||||
|
|
||||||
|
# check duplicated custom permissions
|
||||||
|
models.Permission._meta.permissions = [
|
||||||
|
('my_custom_permission', 'Some permission'),
|
||||||
|
('other_one', 'Some other permission'),
|
||||||
|
('my_custom_permission', 'Some permission with duplicate permission code'),
|
||||||
|
]
|
||||||
|
self.assertRaisesRegexp(CommandError,
|
||||||
|
"The permission codename 'my_custom_permission' is duplicated for model "
|
||||||
|
"'auth.Permission'.",
|
||||||
|
create_permissions, models, [], verbosity=0)
|
||||||
|
|
||||||
|
# should not raise anything
|
||||||
|
models.Permission._meta.permissions = [
|
||||||
|
('my_custom_permission', 'Some permission'),
|
||||||
|
('other_one', 'Some other permission'),
|
||||||
|
]
|
||||||
|
create_permissions(models, [], verbosity=0)
|
||||||
|
|
Loading…
Reference in New Issue