Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

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.
  • Loading branch information...
commit 8c427448d53ec0d860e1669f35deed73d0240ba1 1 parent 218abcc
@mhaligowski mhaligowski authored akaariai committed
View
41 django/contrib/auth/management/__init__.py
@@ -9,6 +9,7 @@
from django.contrib.auth import models as auth_app, get_user_model
from django.core import exceptions
+from django.core.management.base import CommandError
from django.db.models import get_models, signals
from django.utils import six
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())
-def _get_all_permissions(opts):
- "Returns (codename, name) for all permissions in the given opts."
+def _get_all_permissions(opts, ctype):
+ """
+ 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 = []
for action in ('add', 'change', 'delete'):
- perms.append((_get_permission_codename(action, opts), 'Can %s %s' % (action, opts.verbose_name_raw)))
- return perms + list(opts.permissions)
+ perms.append((_get_permission_codename(action, opts),
+ '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):
from django.contrib.contenttypes.models import ContentType
@@ -39,7 +70,7 @@ def create_permissions(app, created_models, verbosity, **kwargs):
for klass in app_models:
ctype = ContentType.objects.get_for_model(klass)
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))
# Find all the Permissions that have a context_type for a model we're
View
41 django/contrib/auth/tests/management.py
@@ -2,6 +2,7 @@
from datetime import date
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.models import User
from django.contrib.auth.tests import CustomUser
@@ -167,3 +168,43 @@ def test_swappable_user_missing_required_field(self):
)
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)

0 comments on commit 8c42744

Please sign in to comment.
Something went wrong with that request. Please try again.