# Path: axes/handlers/proxy.py
# class AxesProxyHandler(AbstractAxesHandler, AxesBaseHandler):
#     """
#     Proxy interface for configurable Axes signal handler class.
# 
#     If you wish to implement a custom version of this handler,
#     you can override the settings.AXES_HANDLER configuration string
#     with a class that implements a compatible interface and methods.
# 
#     Defaults to using axes.handlers.proxy.AxesProxyHandler if not overridden.
#     Refer to axes.handlers.proxy.AxesProxyHandler for default implementation.
#     """
# 
#     implementation = None  # type: AxesHandler
# 
#     @classmethod
#     def get_implementation(cls, force: bool = False) -> AxesHandler:
#         """
#         Fetch and initialize configured handler implementation and memoize it to avoid reinitialization.
# 
#         This method is re-entrant and can be called multiple times from e.g. Django application loader.
#         """
# 
#         if force or not cls.implementation:
#             cls.implementation = import_string(settings.AXES_HANDLER)()
#         return cls.implementation
# 
#     @classmethod
#     def reset_attempts(
#         cls,
#         *,
#         ip_address: str = None,
#         username: str = None,
#         ip_or_username: bool = False,
#     ) -> int:
#         return cls.get_implementation().reset_attempts(
#             ip_address=ip_address, username=username, ip_or_username=ip_or_username
#         )
# 
#     @classmethod
#     def reset_logs(cls, *, age_days: int = None) -> int:
#         return cls.get_implementation().reset_logs(age_days=age_days)
# 
#     @staticmethod
#     def update_request(request):
#         """
#         Update request attributes before passing them into the selected handler class.
#         """
# 
#         if request is None:
#             log.error(
#                 "AXES: AxesProxyHandler.update_request can not set request attributes to a None request"
#             )
#             return
#         if not hasattr(request, "axes_updated"):
#             request.axes_locked_out = False
#             request.axes_attempt_time = now()
#             request.axes_ip_address = get_client_ip_address(request)
#             request.axes_user_agent = get_client_user_agent(request)
#             request.axes_path_info = get_client_path_info(request)
#             request.axes_http_accept = get_client_http_accept(request)
#             request.axes_failures_since_start = None
#             request.axes_updated = True
#             request.axes_credentials = None
# 
#     @classmethod
#     def is_locked(cls, request, credentials: dict = None) -> bool:
#         cls.update_request(request)
#         return cls.get_implementation().is_locked(request, credentials)
# 
#     @classmethod
#     def is_allowed(cls, request, credentials: dict = None) -> bool:
#         cls.update_request(request)
#         return cls.get_implementation().is_allowed(request, credentials)
# 
#     @classmethod
#     def get_failures(cls, request, credentials: dict = None) -> int:
#         cls.update_request(request)
#         return cls.get_implementation().get_failures(request, credentials)
# 
#     @classmethod
#     @toggleable
#     def user_login_failed(cls, sender, credentials: dict, request=None, **kwargs):
#         cls.update_request(request)
#         return cls.get_implementation().user_login_failed(
#             sender, credentials, request, **kwargs
#         )
# 
#     @classmethod
#     @toggleable
#     def user_logged_in(cls, sender, request, user, **kwargs):
#         cls.update_request(request)
#         return cls.get_implementation().user_logged_in(sender, request, user, **kwargs)
# 
#     @classmethod
#     @toggleable
#     def user_logged_out(cls, sender, request, user, **kwargs):
#         cls.update_request(request)
#         return cls.get_implementation().user_logged_out(sender, request, user, **kwargs)
# 
#     @classmethod
#     @toggleable
#     def post_save_access_attempt(cls, instance, **kwargs):
#         return cls.get_implementation().post_save_access_attempt(instance, **kwargs)
# 
#     @classmethod
#     @toggleable
#     def post_delete_access_attempt(cls, instance, **kwargs):
#         return cls.get_implementation().post_delete_access_attempt(instance, **kwargs)
# 
# Path: axes/models.py
# class AccessAttempt(AccessBase):
#     get_data = models.TextField(_("GET Data"))
# 
#     post_data = models.TextField(_("POST Data"))
# 
#     failures_since_start = models.PositiveIntegerField(_("Failed Logins"))
# 
#     def __str__(self):
#         return f"Attempted Access: {self.attempt_time}"
# 
#     class Meta:
#         verbose_name = _("access attempt")
#         verbose_name_plural = _("access attempts")
#         unique_together = [["username", "ip_address", "user_agent"]]


from logging import getLogger
from django.contrib.auth.signals import (
    user_logged_in,
    user_logged_out,
    user_login_failed,
)
from django.core.signals import setting_changed
from django.db.models.signals import post_save, post_delete
from django.dispatch import Signal
from django.dispatch import receiver
from axes.handlers.proxy import AxesProxyHandler
from axes.models import AccessAttempt



log = getLogger(__name__)


# This signal provides the following arguments to any listeners:
# request - The current Request object.
# username - The username of the User who has been locked out.
# ip_address - The IP of the user who has been locked out.
user_locked_out = Signal()


@receiver(user_login_failed)
def handle_user_login_failed(*args, **kwargs):
    AxesProxyHandler.user_login_failed(*args, **kwargs)


@receiver(user_logged_in)
def handle_user_logged_in(*args, **kwargs):
    AxesProxyHandler.user_logged_in(*args, **kwargs)


@receiver(user_logged_out)
def handle_user_logged_out(*args, **kwargs):
    AxesProxyHandler.user_logged_out(*args, **kwargs)


@receiver(post_save, sender=AccessAttempt)
def handle_post_save_access_attempt(*args, **kwargs):

# Path: axes/handlers/proxy.py
# class AxesProxyHandler(AbstractAxesHandler, AxesBaseHandler):
#     """
#     Proxy interface for configurable Axes signal handler class.
# 
#     If you wish to implement a custom version of this handler,
#     you can override the settings.AXES_HANDLER configuration string
#     with a class that implements a compatible interface and methods.
# 
#     Defaults to using axes.handlers.proxy.AxesProxyHandler if not overridden.
#     Refer to axes.handlers.proxy.AxesProxyHandler for default implementation.
#     """
# 
#     implementation = None  # type: AxesHandler
# 
#     @classmethod
#     def get_implementation(cls, force: bool = False) -> AxesHandler:
#         """
#         Fetch and initialize configured handler implementation and memoize it to avoid reinitialization.
# 
#         This method is re-entrant and can be called multiple times from e.g. Django application loader.
#         """
# 
#         if force or not cls.implementation:
#             cls.implementation = import_string(settings.AXES_HANDLER)()
#         return cls.implementation
# 
#     @classmethod
#     def reset_attempts(
#         cls,
#         *,
#         ip_address: str = None,
#         username: str = None,
#         ip_or_username: bool = False,
#     ) -> int:
#         return cls.get_implementation().reset_attempts(
#             ip_address=ip_address, username=username, ip_or_username=ip_or_username
#         )
# 
#     @classmethod
#     def reset_logs(cls, *, age_days: int = None) -> int:
#         return cls.get_implementation().reset_logs(age_days=age_days)
# 
#     @staticmethod
#     def update_request(request):
#         """
#         Update request attributes before passing them into the selected handler class.
#         """
# 
#         if request is None:
#             log.error(
#                 "AXES: AxesProxyHandler.update_request can not set request attributes to a None request"
#             )
#             return
#         if not hasattr(request, "axes_updated"):
#             request.axes_locked_out = False
#             request.axes_attempt_time = now()
#             request.axes_ip_address = get_client_ip_address(request)
#             request.axes_user_agent = get_client_user_agent(request)
#             request.axes_path_info = get_client_path_info(request)
#             request.axes_http_accept = get_client_http_accept(request)
#             request.axes_failures_since_start = None
#             request.axes_updated = True
#             request.axes_credentials = None
# 
#     @classmethod
#     def is_locked(cls, request, credentials: dict = None) -> bool:
#         cls.update_request(request)
#         return cls.get_implementation().is_locked(request, credentials)
# 
#     @classmethod
#     def is_allowed(cls, request, credentials: dict = None) -> bool:
#         cls.update_request(request)
#         return cls.get_implementation().is_allowed(request, credentials)
# 
#     @classmethod
#     def get_failures(cls, request, credentials: dict = None) -> int:
#         cls.update_request(request)
#         return cls.get_implementation().get_failures(request, credentials)
# 
#     @classmethod
#     @toggleable
#     def user_login_failed(cls, sender, credentials: dict, request=None, **kwargs):
#         cls.update_request(request)
#         return cls.get_implementation().user_login_failed(
#             sender, credentials, request, **kwargs
#         )
# 
#     @classmethod
#     @toggleable
#     def user_logged_in(cls, sender, request, user, **kwargs):
#         cls.update_request(request)
#         return cls.get_implementation().user_logged_in(sender, request, user, **kwargs)
# 
#     @classmethod
#     @toggleable
#     def user_logged_out(cls, sender, request, user, **kwargs):
#         cls.update_request(request)
#         return cls.get_implementation().user_logged_out(sender, request, user, **kwargs)
# 
#     @classmethod
#     @toggleable
#     def post_save_access_attempt(cls, instance, **kwargs):
#         return cls.get_implementation().post_save_access_attempt(instance, **kwargs)
# 
#     @classmethod
#     @toggleable
#     def post_delete_access_attempt(cls, instance, **kwargs):
#         return cls.get_implementation().post_delete_access_attempt(instance, **kwargs)
# 
# Path: axes/models.py
# class AccessAttempt(AccessBase):
#     get_data = models.TextField(_("GET Data"))
# 
#     post_data = models.TextField(_("POST Data"))
# 
#     failures_since_start = models.PositiveIntegerField(_("Failed Logins"))
# 
#     def __str__(self):
#         return f"Attempted Access: {self.attempt_time}"
# 
#     class Meta:
#         verbose_name = _("access attempt")
#         verbose_name_plural = _("access attempts")
#         unique_together = [["username", "ip_address", "user_agent"]]


# Path: axes/signals.py
from logging import getLogger
from django.contrib.auth.signals import (
    user_logged_in,
    user_logged_out,
    user_login_failed,
)
from django.core.signals import setting_changed
from django.db.models.signals import post_save, post_delete
from django.dispatch import Signal
from django.dispatch import receiver
from axes.handlers.proxy import AxesProxyHandler
from axes.models import AccessAttempt




log = getLogger(__name__)


# This signal provides the following arguments to any listeners:
# request - The current Request object.
# username - The username of the User who has been locked out.
# ip_address - The IP of the user who has been locked out.
user_locked_out = Signal()


@receiver(user_login_failed)
def handle_user_login_failed(*args, **kwargs):
    AxesProxyHandler.user_login_failed(*args, **kwargs)


@receiver(user_logged_in)
def handle_user_logged_in(*args, **kwargs):
    AxesProxyHandler.user_logged_in(*args, **kwargs)


@receiver(user_logged_out)
def handle_user_logged_out(*args, **kwargs):
    AxesProxyHandler.user_logged_out(*args, **kwargs)


@receiver(post_save, sender=AccessAttempt)
def handle_post_save_access_attempt(*args, **kwargs):

    AxesProxyHandler.post_save_access_attempt(*args, **kwargs)