123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455 |
- from django.contrib.auth.checks import (
- check_middleware,
- check_models_permissions,
- check_user_model,
- )
- from django.contrib.auth.middleware import (
- AuthenticationMiddleware,
- LoginRequiredMiddleware,
- )
- from django.contrib.auth.models import AbstractBaseUser
- from django.contrib.sessions.middleware import SessionMiddleware
- from django.core import checks
- from django.db import models
- from django.db.models import Q, UniqueConstraint
- from django.test import SimpleTestCase, override_settings, override_system_checks
- from django.test.utils import isolate_apps
- from .models import CustomUserNonUniqueUsername
- @isolate_apps("auth_tests", attr_name="apps")
- @override_system_checks([check_user_model])
- class UserModelChecksTests(SimpleTestCase):
- @override_settings(AUTH_USER_MODEL="auth_tests.CustomUserNonListRequiredFields")
- def test_required_fields_is_list(self):
- """REQUIRED_FIELDS should be a list."""
- class CustomUserNonListRequiredFields(AbstractBaseUser):
- username = models.CharField(max_length=30, unique=True)
- date_of_birth = models.DateField()
- USERNAME_FIELD = "username"
- REQUIRED_FIELDS = "date_of_birth"
- errors = checks.run_checks(app_configs=self.apps.get_app_configs())
- self.assertEqual(
- errors,
- [
- checks.Error(
- "'REQUIRED_FIELDS' must be a list or tuple.",
- obj=CustomUserNonListRequiredFields,
- id="auth.E001",
- ),
- ],
- )
- @override_settings(AUTH_USER_MODEL="auth_tests.CustomUserBadRequiredFields")
- def test_username_not_in_required_fields(self):
- """USERNAME_FIELD should not appear in REQUIRED_FIELDS."""
- class CustomUserBadRequiredFields(AbstractBaseUser):
- username = models.CharField(max_length=30, unique=True)
- date_of_birth = models.DateField()
- USERNAME_FIELD = "username"
- REQUIRED_FIELDS = ["username", "date_of_birth"]
- errors = checks.run_checks(self.apps.get_app_configs())
- self.assertEqual(
- errors,
- [
- 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 'username', you "
- "should remove 'username' from the 'REQUIRED_FIELDS'."
- ),
- obj=CustomUserBadRequiredFields,
- id="auth.E002",
- ),
- ],
- )
- @override_settings(AUTH_USER_MODEL="auth_tests.CustomUserNonUniqueUsername")
- def test_username_non_unique(self):
- """
- A non-unique USERNAME_FIELD raises an error only if the default
- authentication backend is used. Otherwise, a warning is raised.
- """
- errors = checks.run_checks()
- self.assertEqual(
- errors,
- [
- checks.Error(
- "'CustomUserNonUniqueUsername.username' must be "
- "unique because it is named as the 'USERNAME_FIELD'.",
- obj=CustomUserNonUniqueUsername,
- id="auth.E003",
- ),
- ],
- )
- with self.settings(AUTHENTICATION_BACKENDS=["my.custom.backend"]):
- errors = checks.run_checks()
- self.assertEqual(
- errors,
- [
- checks.Warning(
- "'CustomUserNonUniqueUsername.username' is named as "
- "the 'USERNAME_FIELD', but it is not unique.",
- hint=(
- "Ensure that your authentication backend(s) can handle "
- "non-unique usernames."
- ),
- obj=CustomUserNonUniqueUsername,
- id="auth.W004",
- ),
- ],
- )
- @override_settings(AUTH_USER_MODEL="auth_tests.CustomUserPartiallyUnique")
- def test_username_partially_unique(self):
- class CustomUserPartiallyUnique(AbstractBaseUser):
- username = models.CharField(max_length=30)
- USERNAME_FIELD = "username"
- class Meta:
- constraints = [
- UniqueConstraint(
- fields=["username"],
- name="partial_username_unique",
- condition=Q(password__isnull=False),
- ),
- ]
- errors = checks.run_checks(app_configs=self.apps.get_app_configs())
- self.assertEqual(
- errors,
- [
- checks.Error(
- "'CustomUserPartiallyUnique.username' must be unique because "
- "it is named as the 'USERNAME_FIELD'.",
- obj=CustomUserPartiallyUnique,
- id="auth.E003",
- ),
- ],
- )
- with self.settings(AUTHENTICATION_BACKENDS=["my.custom.backend"]):
- errors = checks.run_checks(app_configs=self.apps.get_app_configs())
- self.assertEqual(
- errors,
- [
- checks.Warning(
- "'CustomUserPartiallyUnique.username' is named as the "
- "'USERNAME_FIELD', but it is not unique.",
- hint=(
- "Ensure that your authentication backend(s) can "
- "handle non-unique usernames."
- ),
- obj=CustomUserPartiallyUnique,
- id="auth.W004",
- ),
- ],
- )
- @override_settings(AUTH_USER_MODEL="auth_tests.CustomUserUniqueConstraint")
- def test_username_unique_with_model_constraint(self):
- class CustomUserUniqueConstraint(AbstractBaseUser):
- username = models.CharField(max_length=30)
- USERNAME_FIELD = "username"
- class Meta:
- constraints = [
- UniqueConstraint(fields=["username"], name="username_unique"),
- ]
- self.assertEqual(checks.run_checks(app_configs=self.apps.get_app_configs()), [])
- with self.settings(AUTHENTICATION_BACKENDS=["my.custom.backend"]):
- errors = checks.run_checks(app_configs=self.apps.get_app_configs())
- self.assertEqual(errors, [])
- @override_settings(AUTH_USER_MODEL="auth_tests.BadUser")
- def test_is_anonymous_authenticated_methods(self):
- """
- <User Model>.is_anonymous/is_authenticated must not be methods.
- """
- class BadUser(AbstractBaseUser):
- username = models.CharField(max_length=30, unique=True)
- USERNAME_FIELD = "username"
- def is_anonymous(self):
- return True
- def is_authenticated(self):
- return True
- errors = checks.run_checks(app_configs=self.apps.get_app_configs())
- self.assertEqual(
- errors,
- [
- 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!" % BadUser,
- obj=BadUser,
- id="auth.C009",
- ),
- 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!" % BadUser,
- obj=BadUser,
- id="auth.C010",
- ),
- ],
- )
- @isolate_apps("auth_tests", attr_name="apps")
- @override_system_checks([check_models_permissions])
- class ModelsPermissionsChecksTests(SimpleTestCase):
- def test_clashing_default_permissions(self):
- class Checked(models.Model):
- class Meta:
- permissions = [("change_checked", "Can edit permission (duplicate)")]
- errors = checks.run_checks(self.apps.get_app_configs())
- self.assertEqual(
- errors,
- [
- checks.Error(
- "The permission codenamed 'change_checked' clashes with a builtin "
- "permission for model 'auth_tests.Checked'.",
- obj=Checked,
- id="auth.E005",
- ),
- ],
- )
- def test_non_clashing_custom_permissions(self):
- class Checked(models.Model):
- class Meta:
- permissions = [
- ("my_custom_permission", "Some permission"),
- ("other_one", "Some other permission"),
- ]
- errors = checks.run_checks(self.apps.get_app_configs())
- self.assertEqual(errors, [])
- def test_clashing_custom_permissions(self):
- class Checked(models.Model):
- class Meta:
- permissions = [
- ("my_custom_permission", "Some permission"),
- ("other_one", "Some other permission"),
- (
- "my_custom_permission",
- "Some permission with duplicate permission code",
- ),
- ]
- errors = checks.run_checks(self.apps.get_app_configs())
- self.assertEqual(
- errors,
- [
- checks.Error(
- "The permission codenamed 'my_custom_permission' is duplicated for "
- "model 'auth_tests.Checked'.",
- obj=Checked,
- id="auth.E006",
- ),
- ],
- )
- def test_verbose_name_max_length(self):
- class Checked(models.Model):
- class Meta:
- verbose_name = (
- "some ridiculously long verbose name that is out of control" * 5
- )
- errors = checks.run_checks(self.apps.get_app_configs())
- self.assertEqual(
- errors,
- [
- checks.Error(
- "The verbose_name of model 'auth_tests.Checked' must be at most "
- "244 characters for its builtin permission names to be at most 255 "
- "characters.",
- obj=Checked,
- id="auth.E007",
- ),
- ],
- )
- 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
- )
- class Checked(models.Model):
- class Meta:
- permissions = [
- ("my_custom_permission", custom_permission_name),
- ]
- errors = checks.run_checks(self.apps.get_app_configs())
- self.assertEqual(
- errors,
- [
- checks.Error(
- "The permission named '%s' of model 'auth_tests.Checked' is longer "
- "than 255 characters." % custom_permission_name,
- obj=Checked,
- id="auth.E008",
- ),
- ],
- )
- 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:
- default_permissions = ()
- self.assertEqual(checks.run_checks(self.apps.get_app_configs()), [])
- class LoginRequiredMiddlewareSubclass(LoginRequiredMiddleware):
- redirect_field_name = "redirect_to"
- class AuthenticationMiddlewareSubclass(AuthenticationMiddleware):
- pass
- class SessionMiddlewareSubclass(SessionMiddleware):
- pass
- @override_system_checks([check_middleware])
- class MiddlewareChecksTests(SimpleTestCase):
- @override_settings(
- MIDDLEWARE=[
- "auth_tests.test_checks.SessionMiddlewareSubclass",
- "auth_tests.test_checks.AuthenticationMiddlewareSubclass",
- "auth_tests.test_checks.LoginRequiredMiddlewareSubclass",
- ]
- )
- def test_middleware_subclasses(self):
- errors = checks.run_checks()
- self.assertEqual(errors, [])
- @override_settings(
- MIDDLEWARE=[
- "auth_tests.test_checks",
- "auth_tests.test_checks.NotExist",
- ]
- )
- def test_invalid_middleware_skipped(self):
- errors = checks.run_checks()
- self.assertEqual(errors, [])
- @override_settings(
- MIDDLEWARE=[
- "django.contrib.does.not.Exist",
- "django.contrib.sessions.middleware.SessionMiddleware",
- "django.contrib.auth.middleware.AuthenticationMiddleware",
- "django.contrib.auth.middleware.LoginRequiredMiddleware",
- ]
- )
- def test_check_ignores_import_error_in_middleware(self):
- errors = checks.run_checks()
- self.assertEqual(errors, [])
- @override_settings(
- MIDDLEWARE=[
- "django.contrib.sessions.middleware.SessionMiddleware",
- "django.contrib.auth.middleware.AuthenticationMiddleware",
- "django.contrib.auth.middleware.LoginRequiredMiddleware",
- ]
- )
- def test_correct_order_with_login_required_middleware(self):
- errors = checks.run_checks()
- self.assertEqual(errors, [])
- @override_settings(
- MIDDLEWARE=[
- "django.contrib.auth.middleware.LoginRequiredMiddleware",
- "django.contrib.auth.middleware.AuthenticationMiddleware",
- "django.contrib.sessions.middleware.SessionMiddleware",
- ]
- )
- def test_incorrect_order_with_login_required_middleware(self):
- errors = checks.run_checks()
- self.assertEqual(
- errors,
- [
- checks.Error(
- "In order to use django.contrib.auth.middleware."
- "LoginRequiredMiddleware, django.contrib.auth.middleware."
- "AuthenticationMiddleware must be defined before it in MIDDLEWARE.",
- id="auth.E013",
- )
- ],
- )
- @override_settings(
- MIDDLEWARE=[
- "django.contrib.auth.middleware.LoginRequiredMiddleware",
- ]
- )
- def test_missing_authentication_with_login_required_middleware(self):
- errors = checks.run_checks()
- self.assertEqual(
- errors,
- [
- checks.Error(
- "In order to use django.contrib.auth.middleware."
- "LoginRequiredMiddleware, django.contrib.auth.middleware."
- "AuthenticationMiddleware must be defined before it in MIDDLEWARE.",
- id="auth.E013",
- )
- ],
- )
|