Fixed #30226 -- Added BaseBackend for authentication.
This commit is contained in:
parent
4b6dfe1622
commit
75337a6050
|
@ -8,7 +8,24 @@ from django.utils.deprecation import RemovedInDjango31Warning
|
||||||
UserModel = get_user_model()
|
UserModel = get_user_model()
|
||||||
|
|
||||||
|
|
||||||
class ModelBackend:
|
class BaseBackend:
|
||||||
|
def authenticate(self, request, **kwargs):
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_user(self, user_id):
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_group_permissions(self, user_obj, obj=None):
|
||||||
|
return set()
|
||||||
|
|
||||||
|
def get_all_permissions(self, user_obj, obj=None):
|
||||||
|
return self.get_group_permissions(user_obj, obj=obj)
|
||||||
|
|
||||||
|
def has_perm(self, user_obj, perm, obj=None):
|
||||||
|
return perm in self.get_all_permissions(user_obj, obj=obj)
|
||||||
|
|
||||||
|
|
||||||
|
class ModelBackend(BaseBackend):
|
||||||
"""
|
"""
|
||||||
Authenticates against settings.AUTH_USER_MODEL.
|
Authenticates against settings.AUTH_USER_MODEL.
|
||||||
"""
|
"""
|
||||||
|
@ -86,7 +103,7 @@ class ModelBackend:
|
||||||
return user_obj._perm_cache
|
return user_obj._perm_cache
|
||||||
|
|
||||||
def has_perm(self, user_obj, perm, obj=None):
|
def has_perm(self, user_obj, perm, obj=None):
|
||||||
return user_obj.is_active and perm in self.get_all_permissions(user_obj, obj)
|
return user_obj.is_active and super().has_perm(user_obj, perm, obj=obj)
|
||||||
|
|
||||||
def has_module_perms(self, user_obj, app_label):
|
def has_module_perms(self, user_obj, app_label):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -460,6 +460,27 @@ Available authentication backends
|
||||||
|
|
||||||
The following backends are available in :mod:`django.contrib.auth.backends`:
|
The following backends are available in :mod:`django.contrib.auth.backends`:
|
||||||
|
|
||||||
|
.. class:: BaseBackend
|
||||||
|
|
||||||
|
.. versionadded:: 3.0
|
||||||
|
|
||||||
|
A base class that provides default implementations for all required
|
||||||
|
methods. By default, it will reject any user and provide no permissions.
|
||||||
|
|
||||||
|
.. method:: get_group_permissions(user_obj, obj=None)
|
||||||
|
|
||||||
|
Returns an empty set.
|
||||||
|
|
||||||
|
.. method:: get_all_permissions(user_obj, obj=None)
|
||||||
|
|
||||||
|
Uses :meth:`get_group_permissions` to get the set of permission strings
|
||||||
|
the ``user_obj`` has.
|
||||||
|
|
||||||
|
.. method:: has_perm(user_obj, perm, obj=None)
|
||||||
|
|
||||||
|
Uses :meth:`get_all_permissions` to check if ``user_obj`` has the
|
||||||
|
permission string ``perm``.
|
||||||
|
|
||||||
.. class:: ModelBackend
|
.. class:: ModelBackend
|
||||||
|
|
||||||
This is the default authentication backend used by Django. It
|
This is the default authentication backend used by Django. It
|
||||||
|
|
|
@ -69,6 +69,9 @@ Minor features
|
||||||
:class:`~django.contrib.auth.views.PasswordResetConfirmView` allows specifying
|
:class:`~django.contrib.auth.views.PasswordResetConfirmView` allows specifying
|
||||||
a token parameter displayed as a component of password reset URLs.
|
a token parameter displayed as a component of password reset URLs.
|
||||||
|
|
||||||
|
* Added :class:`~django.contrib.auth.backends.BaseBackend` class to ease
|
||||||
|
customization of authentication backends.
|
||||||
|
|
||||||
:mod:`django.contrib.contenttypes`
|
:mod:`django.contrib.contenttypes`
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
|
@ -100,14 +100,18 @@ and returns a user object or ``None``.
|
||||||
The ``authenticate`` method takes a ``request`` argument and credentials as
|
The ``authenticate`` method takes a ``request`` argument and credentials as
|
||||||
keyword arguments. Most of the time, it'll just look like this::
|
keyword arguments. Most of the time, it'll just look like this::
|
||||||
|
|
||||||
class MyBackend:
|
from django.contrib.auth.backends import BaseBackend
|
||||||
|
|
||||||
|
class MyBackend(BaseBackend):
|
||||||
def authenticate(self, request, username=None, password=None):
|
def authenticate(self, request, username=None, password=None):
|
||||||
# Check the username/password and return a user.
|
# Check the username/password and return a user.
|
||||||
...
|
...
|
||||||
|
|
||||||
But it could also authenticate a token, like so::
|
But it could also authenticate a token, like so::
|
||||||
|
|
||||||
class MyBackend:
|
from django.contrib.auth.backends import BaseBackend
|
||||||
|
|
||||||
|
class MyBackend(BaseBackend):
|
||||||
def authenticate(self, request, token=None):
|
def authenticate(self, request, token=None):
|
||||||
# Check the token and return a user.
|
# Check the token and return a user.
|
||||||
...
|
...
|
||||||
|
@ -132,10 +136,11 @@ variable defined in your ``settings.py`` file and creates a Django ``User``
|
||||||
object the first time a user authenticates::
|
object the first time a user authenticates::
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.contrib.auth.backends import BaseBackend
|
||||||
from django.contrib.auth.hashers import check_password
|
from django.contrib.auth.hashers import check_password
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
|
||||||
class SettingsBackend:
|
class SettingsBackend(BaseBackend):
|
||||||
"""
|
"""
|
||||||
Authenticate against the settings ADMIN_LOGIN and ADMIN_PASSWORD.
|
Authenticate against the settings ADMIN_LOGIN and ADMIN_PASSWORD.
|
||||||
|
|
||||||
|
@ -190,11 +195,11 @@ exception in :meth:`~django.contrib.auth.models.User.has_perm()` or
|
||||||
:meth:`~django.contrib.auth.models.User.has_module_perms()`, the authorization
|
:meth:`~django.contrib.auth.models.User.has_module_perms()`, the authorization
|
||||||
will immediately fail and Django won't check the backends that follow.
|
will immediately fail and Django won't check the backends that follow.
|
||||||
|
|
||||||
The simple backend above could implement permissions for the magic admin
|
A backend could implement permissions for the magic admin fairly simply::
|
||||||
fairly simply::
|
|
||||||
|
|
||||||
class SettingsBackend:
|
from django.contrib.auth.backends import BaseBackend
|
||||||
...
|
|
||||||
|
class MagicAdminBackend(BaseBackend):
|
||||||
def has_perm(self, user_obj, perm, obj=None):
|
def has_perm(self, user_obj, perm, obj=None):
|
||||||
return user_obj.username == settings.ADMIN_LOGIN
|
return user_obj.username == settings.ADMIN_LOGIN
|
||||||
|
|
||||||
|
@ -205,10 +210,7 @@ all take the user object, which may be an anonymous user, as an argument.
|
||||||
|
|
||||||
A full authorization implementation can be found in the ``ModelBackend`` class
|
A full authorization implementation can be found in the ``ModelBackend`` class
|
||||||
in :source:`django/contrib/auth/backends.py`, which is the default backend and
|
in :source:`django/contrib/auth/backends.py`, which is the default backend and
|
||||||
queries the ``auth_permission`` table most of the time. If you wish to provide
|
queries the ``auth_permission`` table most of the time.
|
||||||
custom behavior for only part of the backend API, you can take advantage of
|
|
||||||
Python inheritance and subclass ``ModelBackend`` instead of implementing the
|
|
||||||
complete API in a custom backend.
|
|
||||||
|
|
||||||
.. _anonymous_auth:
|
.. _anonymous_auth:
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ from unittest import mock
|
||||||
from django.contrib.auth import (
|
from django.contrib.auth import (
|
||||||
BACKEND_SESSION_KEY, SESSION_KEY, authenticate, get_user, signals,
|
BACKEND_SESSION_KEY, SESSION_KEY, authenticate, get_user, signals,
|
||||||
)
|
)
|
||||||
from django.contrib.auth.backends import ModelBackend
|
from django.contrib.auth.backends import BaseBackend, ModelBackend
|
||||||
from django.contrib.auth.hashers import MD5PasswordHasher
|
from django.contrib.auth.hashers import MD5PasswordHasher
|
||||||
from django.contrib.auth.models import AnonymousUser, Group, Permission, User
|
from django.contrib.auth.models import AnonymousUser, Group, Permission, User
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
@ -20,6 +20,28 @@ from .models import (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class SimpleBackend(BaseBackend):
|
||||||
|
def get_group_permissions(self, user_obj, obj=None):
|
||||||
|
return ['group_perm']
|
||||||
|
|
||||||
|
|
||||||
|
@override_settings(AUTHENTICATION_BACKENDS=['auth_tests.test_auth_backends.SimpleBackend'])
|
||||||
|
class BaseBackendTest(TestCase):
|
||||||
|
@classmethod
|
||||||
|
def setUpTestData(cls):
|
||||||
|
cls.user = User.objects.create_user('test', 'test@example.com', 'test')
|
||||||
|
|
||||||
|
def test_get_group_permissions(self):
|
||||||
|
self.assertEqual(self.user.get_group_permissions(), {'group_perm'})
|
||||||
|
|
||||||
|
def test_get_all_permissions(self):
|
||||||
|
self.assertEqual(self.user.get_all_permissions(), {'group_perm'})
|
||||||
|
|
||||||
|
def test_has_perm(self):
|
||||||
|
self.assertIs(self.user.has_perm('group_perm'), True)
|
||||||
|
self.assertIs(self.user.has_perm('other_perm', TestObj()), False)
|
||||||
|
|
||||||
|
|
||||||
class CountingMD5PasswordHasher(MD5PasswordHasher):
|
class CountingMD5PasswordHasher(MD5PasswordHasher):
|
||||||
"""Hasher that counts how many times it computes a hash."""
|
"""Hasher that counts how many times it computes a hash."""
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue