From 5a68a223c7451a14d36a817d95e1141c764c4854 Mon Sep 17 00:00:00 2001 From: Michael Mulholland Date: Wed, 22 Jan 2020 21:14:24 -0500 Subject: [PATCH] Fixed #31200 -- Added system checks for permissions codenames max length. --- django/contrib/auth/checks.py | 39 +++++++++++++++++++++++++++++++++ docs/ref/checks.txt | 4 ++++ tests/auth_tests/test_checks.py | 33 ++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+) diff --git a/django/contrib/auth/checks.py b/django/contrib/auth/checks.py index ec133e80ee..b77cfe2645 100644 --- a/django/contrib/auth/checks.py +++ b/django/contrib/auth/checks.py @@ -102,6 +102,7 @@ def check_models_permissions(app_configs=None, **kwargs): Permission = apps.get_model('auth', 'Permission') permission_name_max_length = Permission._meta.get_field('name').max_length + permission_codename_max_length = Permission._meta.get_field('codename').max_length errors = [] for model in models: @@ -126,6 +127,29 @@ def check_models_permissions(app_configs=None, **kwargs): id='auth.E007', ) ) + # Check builtin permission codename length. + max_builtin_permission_codename_length = ( + max(len(codename) for codename in builtin_permissions.keys()) + if builtin_permissions else 0 + ) + if max_builtin_permission_codename_length > permission_codename_max_length: + model_name_max_length = permission_codename_max_length - ( + max_builtin_permission_codename_length - len(opts.model_name) + ) + errors.append( + checks.Error( + "The name of model '%s.%s' must be at most %d characters " + "for its builtin permission codenames to be at most %d " + "characters." % ( + opts.app_label, + opts.object_name, + model_name_max_length, + permission_codename_max_length, + ), + obj=model, + id='auth.E011', + ) + ) codenames = set() for codename, name in opts.permissions: # Check custom permission name length. @@ -139,6 +163,21 @@ def check_models_permissions(app_configs=None, **kwargs): id='auth.E008', ) ) + # Check custom permission codename length. + if len(codename) > permission_codename_max_length: + errors.append( + checks.Error( + "The permission codenamed '%s' of model '%s.%s' is " + "longer than %d characters." % ( + codename, + opts.app_label, + opts.object_name, + permission_codename_max_length, + ), + obj=model, + id='auth.E012', + ) + ) # Check custom permissions codename clashing. if codename in builtin_permissions: errors.append( diff --git a/docs/ref/checks.txt b/docs/ref/checks.txt index a080b5bdf5..e7b0a5ec8c 100644 --- a/docs/ref/checks.txt +++ b/docs/ref/checks.txt @@ -724,6 +724,10 @@ The following checks are performed on the default * **auth.C010**: ``.is_authenticated`` must be an attribute or property rather than a method. Ignoring this is a security issue as anonymous users will be treated as authenticated! +* **auth.E011**: The name of model ```` must be at most 93 characters + for its builtin permission names to be at most 100 characters. +* **auth.E012**: The permission codenamed ```` of model ```` + is longer than 100 characters. ``contenttypes`` ---------------- diff --git a/tests/auth_tests/test_checks.py b/tests/auth_tests/test_checks.py index 8c3e323efa..dbcfefcbb7 100644 --- a/tests/auth_tests/test_checks.py +++ b/tests/auth_tests/test_checks.py @@ -176,6 +176,20 @@ class ModelsPermissionsChecksTests(SimpleTestCase): ), ]) + def test_model_name_max_length(self): + model_name = 'X' * 94 + model = type(model_name, (models.Model,), {'__module__': self.__module__}) + errors = checks.run_checks(self.apps.get_app_configs()) + self.assertEqual(errors, [ + checks.Error( + "The name of model 'auth_tests.%s' must be at most 93 " + "characters for its builtin permission codenames to be at " + "most 100 characters." % model_name, + obj=model, + id='auth.E011', + ), + ]) + def test_custom_permission_name_max_length(self): custom_permission_name = 'some ridiculously long verbose name that is out of control' * 5 @@ -194,6 +208,25 @@ class ModelsPermissionsChecksTests(SimpleTestCase): ), ]) + def test_custom_permission_codename_max_length(self): + custom_permission_codename = 'x' * 101 + + class Checked(models.Model): + class Meta: + permissions = [ + (custom_permission_codename, 'Custom permission'), + ] + + errors = checks.run_checks(self.apps.get_app_configs()) + self.assertEqual(errors, [ + checks.Error( + "The permission codenamed '%s' of model 'auth_tests.Checked' " + "is longer than 100 characters." % custom_permission_codename, + obj=Checked, + id='auth.E012', + ), + ]) + def test_empty_default_permissions(self): class Checked(models.Model): class Meta: