|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220 |
- from itertools import chain
- from types import MethodType
-
- 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:
- cls = apps.get_model(settings.AUTH_USER_MODEL)
- else:
- app_label, model_name = settings.AUTH_USER_MODEL.split(".")
- for app_config in app_configs:
- if app_config.label == app_label:
- cls = app_config.get_model(model_name)
- break
- else:
- # Checks might be run against a set of app configs that don't
- # include the specified user model. In this case we simply don't
- # perform the checks defined below.
- return []
-
- errors = []
-
- # Check that REQUIRED_FIELDS is a list
- if not isinstance(cls.REQUIRED_FIELDS, (list, tuple)):
- errors.append(
- checks.Error(
- "'REQUIRED_FIELDS' must be a list or tuple.",
- obj=cls,
- id="auth.E001",
- )
- )
-
- # Check that the USERNAME FIELD isn't included in REQUIRED_FIELDS.
- if cls.USERNAME_FIELD in cls.REQUIRED_FIELDS:
- errors.append(
- checks.Error(
- "The field named as the 'USERNAME_FIELD' "
- "for a custom user model must not be included in 'REQUIRED_FIELDS'.",
- hint=(
- "The 'USERNAME_FIELD' is currently set to '%s', you "
- "should remove '%s' from the 'REQUIRED_FIELDS'."
- % (cls.USERNAME_FIELD, cls.USERNAME_FIELD)
- ),
- obj=cls,
- id="auth.E002",
- )
- )
-
- # Check that the username field is unique
- if not cls._meta.get_field(cls.USERNAME_FIELD).unique and not any(
- constraint.fields == (cls.USERNAME_FIELD,)
- for constraint in cls._meta.total_unique_constraints
- ):
- if settings.AUTHENTICATION_BACKENDS == [
- "django.contrib.auth.backends.ModelBackend"
- ]:
- errors.append(
- checks.Error(
- "'%s.%s' must be unique because it is named as the "
- "'USERNAME_FIELD'." % (cls._meta.object_name, cls.USERNAME_FIELD),
- obj=cls,
- id="auth.E003",
- )
- )
- else:
- errors.append(
- checks.Warning(
- "'%s.%s' is named as the 'USERNAME_FIELD', but it is not unique."
- % (cls._meta.object_name, cls.USERNAME_FIELD),
- hint=(
- "Ensure that your authentication backend(s) can handle "
- "non-unique usernames."
- ),
- obj=cls,
- id="auth.W004",
- )
- )
-
- if isinstance(cls().is_anonymous, MethodType):
- errors.append(
- checks.Critical(
- "%s.is_anonymous must be an attribute or property rather than "
- "a method. Ignoring this is a security issue as anonymous "
- "users will be treated as authenticated!" % cls,
- obj=cls,
- id="auth.C009",
- )
- )
- if isinstance(cls().is_authenticated, MethodType):
- errors.append(
- checks.Critical(
- "%s.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!" % cls,
- obj=cls,
- id="auth.C010",
- )
- )
- 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
- permission_codename_max_length = Permission._meta.get_field("codename").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 builtin_permissions
- else 0
- )
- 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' must be at most %d "
- "characters for its builtin permission names to be at "
- "most %d characters."
- % (opts.label, verbose_name_max_length, permission_name_max_length),
- obj=model,
- 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' must be at most %d characters "
- "for its builtin permission codenames to be at most %d "
- "characters."
- % (
- opts.label,
- 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.
- if len(name) > permission_name_max_length:
- errors.append(
- checks.Error(
- "The permission named '%s' of model '%s' is longer "
- "than %d characters."
- % (
- name,
- opts.label,
- permission_name_max_length,
- ),
- obj=model,
- 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' is "
- "longer than %d characters."
- % (
- codename,
- opts.label,
- permission_codename_max_length,
- ),
- obj=model,
- id="auth.E012",
- )
- )
- # 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'." % (codename, opts.label),
- obj=model,
- id="auth.E005",
- )
- )
- elif codename in codenames:
- errors.append(
- checks.Error(
- "The permission codenamed '%s' is duplicated for "
- "model '%s'." % (codename, opts.label),
- obj=model,
- id="auth.E006",
- )
- )
- codenames.add(codename)
-
- return errors
|