mirror of
https://github.com/django/django.git
synced 2025-10-24 06:06:09 +00:00
Fixed #26470 -- Converted auth permission validation to system checks.
Thanks Tim for the review.
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
from django.apps import AppConfig
|
||||
from django.contrib.auth.checks import check_user_model
|
||||
from django.core import checks
|
||||
from django.db.models.signals import post_migrate
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from .checks import check_models_permissions, check_user_model
|
||||
from .management import create_permissions
|
||||
|
||||
|
||||
@@ -15,3 +15,4 @@ class AuthConfig(AppConfig):
|
||||
post_migrate.connect(create_permissions,
|
||||
dispatch_uid="django.contrib.auth.management.create_permissions")
|
||||
checks.register(check_user_model, checks.Tags.models)
|
||||
checks.register(check_models_permissions, checks.Tags.models)
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from itertools import chain
|
||||
|
||||
from django.apps import apps
|
||||
from django.conf import settings
|
||||
from django.core import checks
|
||||
|
||||
from .management import _get_builtin_permissions
|
||||
|
||||
|
||||
def check_user_model(app_configs=None, **kwargs):
|
||||
if app_configs is None:
|
||||
@@ -70,3 +74,72 @@ def check_user_model(app_configs=None, **kwargs):
|
||||
)
|
||||
|
||||
return errors
|
||||
|
||||
|
||||
def check_models_permissions(app_configs=None, **kwargs):
|
||||
if app_configs is None:
|
||||
models = apps.get_models()
|
||||
else:
|
||||
models = chain.from_iterable(app_config.get_models() for app_config in app_configs)
|
||||
|
||||
Permission = apps.get_model('auth', 'Permission')
|
||||
permission_name_max_length = Permission._meta.get_field('name').max_length
|
||||
errors = []
|
||||
|
||||
for model in models:
|
||||
opts = model._meta
|
||||
builtin_permissions = dict(_get_builtin_permissions(opts))
|
||||
# Check builtin permission name length.
|
||||
max_builtin_permission_name_length = max(len(name) for name in builtin_permissions.values())
|
||||
if max_builtin_permission_name_length > permission_name_max_length:
|
||||
verbose_name_max_length = (
|
||||
permission_name_max_length - (max_builtin_permission_name_length - len(opts.verbose_name_raw))
|
||||
)
|
||||
errors.append(
|
||||
checks.Error(
|
||||
"The verbose_name of model '%s.%s' must be at most %d characters "
|
||||
"for its builtin permission names to be at most %d characters." % (
|
||||
opts.app_label, opts.object_name, verbose_name_max_length, permission_name_max_length
|
||||
),
|
||||
obj=model,
|
||||
id='auth.E007',
|
||||
)
|
||||
)
|
||||
codenames = set()
|
||||
for codename, name in opts.permissions:
|
||||
# Check custom permission name length.
|
||||
if len(name) > permission_name_max_length:
|
||||
errors.append(
|
||||
checks.Error(
|
||||
"The permission named '%s' of model '%s.%s' is longer than %d characters." % (
|
||||
name, opts.app_label, opts.object_name, permission_name_max_length
|
||||
),
|
||||
obj=model,
|
||||
id='auth.E008',
|
||||
)
|
||||
)
|
||||
# Check custom permissions codename clashing.
|
||||
if codename in builtin_permissions:
|
||||
errors.append(
|
||||
checks.Error(
|
||||
"The permission codenamed '%s' clashes with a builtin permission "
|
||||
"for model '%s.%s'." % (
|
||||
codename, opts.app_label, opts.object_name
|
||||
),
|
||||
obj=model,
|
||||
id='auth.E005',
|
||||
)
|
||||
)
|
||||
elif codename in codenames:
|
||||
errors.append(
|
||||
checks.Error(
|
||||
"The permission codenamed '%s' is duplicated for model '%s.%s'." % (
|
||||
codename, opts.app_label, opts.object_name
|
||||
),
|
||||
obj=model,
|
||||
id='auth.E006',
|
||||
)
|
||||
)
|
||||
codenames.add(codename)
|
||||
|
||||
return errors
|
||||
|
||||
@@ -9,19 +9,17 @@ import unicodedata
|
||||
from django.apps import apps
|
||||
from django.contrib.auth import get_permission_codename
|
||||
from django.core import exceptions
|
||||
from django.core.management.base import CommandError
|
||||
from django.db import DEFAULT_DB_ALIAS, router
|
||||
from django.utils import six
|
||||
from django.utils.encoding import DEFAULT_LOCALE_ENCODING
|
||||
|
||||
|
||||
def _get_all_permissions(opts, ctype):
|
||||
def _get_all_permissions(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
|
||||
|
||||
|
||||
@@ -37,26 +35,6 @@ def _get_builtin_permissions(opts):
|
||||
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_config, verbosity=2, interactive=True, using=DEFAULT_DB_ALIAS, **kwargs):
|
||||
if not app_config.models_module:
|
||||
return
|
||||
@@ -71,9 +49,6 @@ def create_permissions(app_config, verbosity=2, interactive=True, using=DEFAULT_
|
||||
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
permission_name_max_length = Permission._meta.get_field('name').max_length
|
||||
verbose_name_max_length = permission_name_max_length - 11 # len('Can change ') prefix
|
||||
|
||||
# This will hold the permissions we're looking for as
|
||||
# (content_type, (codename, name))
|
||||
searched_perms = list()
|
||||
@@ -84,17 +59,8 @@ def create_permissions(app_config, verbosity=2, interactive=True, using=DEFAULT_
|
||||
# before creating foreign keys to them.
|
||||
ctype = ContentType.objects.db_manager(using).get_for_model(klass)
|
||||
|
||||
if len(klass._meta.verbose_name) > verbose_name_max_length:
|
||||
raise exceptions.ValidationError(
|
||||
"The verbose_name of %s.%s is longer than %s characters" % (
|
||||
ctype.app_label,
|
||||
ctype.model,
|
||||
verbose_name_max_length,
|
||||
)
|
||||
)
|
||||
|
||||
ctypes.add(ctype)
|
||||
for perm in _get_all_permissions(klass._meta, ctype):
|
||||
for perm in _get_all_permissions(klass._meta):
|
||||
searched_perms.append((ctype, perm))
|
||||
|
||||
# Find all the Permissions that have a content_type for a model we're
|
||||
@@ -111,18 +77,6 @@ def create_permissions(app_config, verbosity=2, interactive=True, using=DEFAULT_
|
||||
for ct, (codename, name) in searched_perms
|
||||
if (ct.pk, codename) not in all_perms
|
||||
]
|
||||
# Validate the permissions before bulk_creation to avoid cryptic database
|
||||
# error when the name is longer than 255 characters
|
||||
for perm in perms:
|
||||
if len(perm.name) > permission_name_max_length:
|
||||
raise exceptions.ValidationError(
|
||||
"The permission name %s of %s.%s is longer than %s characters" % (
|
||||
perm.name,
|
||||
perm.content_type.app_label,
|
||||
perm.content_type.model,
|
||||
permission_name_max_length,
|
||||
)
|
||||
)
|
||||
Permission.objects.using(using).bulk_create(perms)
|
||||
if verbosity >= 2:
|
||||
for perm in perms:
|
||||
|
||||
Reference in New Issue
Block a user