mirror of
https://github.com/jazzband/django-axes.git
synced 2026-05-28 08:48:19 +00:00
The old architecture used exceptions in the signal handler which prevented transactions from running smoothly and signal handlers from running after Axes handlers. The new architecture changes the request approach to request flagging and moves the exception handling into the middleware call method. This allows users to more flexibly run their own signal handlers and optionally use the Axes middleware if they want to do so. Fixes #440 Fixes #442
62 lines
2.9 KiB
Python
62 lines
2.9 KiB
Python
from django.contrib.auth.backends import ModelBackend
|
|
|
|
from axes.exceptions import AxesBackendPermissionDenied, AxesBackendRequestParameterRequired
|
|
from axes.handlers.proxy import AxesProxyHandler
|
|
from axes.helpers import get_credentials, get_lockout_message, toggleable
|
|
|
|
|
|
class AxesBackend(ModelBackend):
|
|
"""
|
|
Authentication backend class that forbids login attempts for locked out users.
|
|
|
|
Use this class as the first item of ``AUTHENTICATION_BACKENDS`` to
|
|
prevent locked out users from being logged in by the Django authentication flow.
|
|
|
|
.. note:: This backend does not log your user in. It monitors login attempts.
|
|
Authentication is handled by the following backends that are configured in ``AUTHENTICATION_BACKENDS``.
|
|
"""
|
|
|
|
@toggleable
|
|
def authenticate(self, request, username: str = None, password: str = None, **kwargs: dict):
|
|
"""
|
|
Checks user lockout status and raises an exception if user is not allowed to log in.
|
|
|
|
This method interrupts the login flow and inserts error message directly to the
|
|
``response_context`` attribute that is supplied as a keyword argument.
|
|
|
|
:keyword response_context: kwarg that will be have its ``error`` attribute updated with context.
|
|
:raises AxesBackendRequestParameterRequired: if request parameter is not passed.
|
|
:raises AxesBackendPermissionDenied: if user is already locked out.
|
|
"""
|
|
|
|
if request is None:
|
|
raise AxesBackendRequestParameterRequired('AxesBackend requires a request as an argument to authenticate')
|
|
|
|
credentials = get_credentials(username=username, password=password, **kwargs)
|
|
|
|
if AxesProxyHandler.is_allowed(request, credentials):
|
|
return
|
|
|
|
# Locked out, don't try to authenticate, just update response_context and return.
|
|
# Its a bit weird to pass a context and expect a response value but its nice to get a "why" back.
|
|
|
|
error_msg = get_lockout_message()
|
|
response_context = kwargs.get('response_context', {})
|
|
response_context['error'] = error_msg
|
|
|
|
# Raise an error that stops the authentication flows at django.contrib.auth.authenticate.
|
|
# This error stops bubbling up at the authenticate call which catches backend PermissionDenied errors.
|
|
# After this error is caught by authenticate it emits a signal indicating user login failed,
|
|
# which is processed by axes.signals.log_user_login_failed which logs the attempt and raises
|
|
# a second exception which bubbles up the middleware stack and produces a HTTP 403 Forbidden reply
|
|
# in the axes.middleware.AxesMiddleware.process_exception middleware exception handler.
|
|
|
|
raise AxesBackendPermissionDenied('AxesBackend detected that the given user is locked out')
|
|
|
|
|
|
class AxesModelBackend(AxesBackend):
|
|
"""
|
|
Backwards compatibility class for version 4 to version 5 migration.
|
|
|
|
See the ``AxesBackend`` class documentation and implementation.
|
|
"""
|