# Path: axes/conf.py

# 
# 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/helpers.py
# def get_client_ip_address(request) -> str:
#     """
#     Get client IP address as configured by the user.
# 
#     The django-ipware package is used for address resolution
#     and parameters can be configured in the Axes package.
#     """
# 
#     client_ip_address, _ = ipware.ip.get_client_ip(
#         request,
#         proxy_order=settings.AXES_PROXY_ORDER,
#         proxy_count=settings.AXES_PROXY_COUNT,
#         proxy_trusted_ips=settings.AXES_PROXY_TRUSTED_IPS,
#         request_header_order=settings.AXES_META_PRECEDENCE_ORDER,
#     )
# 
#     return client_ip_address


from logging import getLogger
from typing import Optional
from django.http import HttpRequest
from axes.conf import settings
from axes.handlers.proxy import AxesProxyHandler
from axes.helpers import get_client_ip_address




log = getLogger(__name__)


def reset(ip: str = None, username: str = None, ip_or_username=False) -> int:
    """
    Reset records that match IP or username, and return the count of removed attempts.

    This utility method is meant to be used from the CLI or via Python API.
    """

    return AxesProxyHandler.reset_attempts(
        ip_address=ip, username=username, ip_or_username=ip_or_username
    )


def reset_request(request: HttpRequest) -> int:
    """
    Reset records that match IP or username, and return the count of removed attempts.

    This utility method is meant to be used from the CLI or via Python API.
    """

    ip: Optional[str] = get_client_ip_address(request)
    username = request.GET.get("username", None)

    ip_or_username = settings.AXES_LOCK_OUT_BY_USER_OR_IP

# Path: axes/conf.py

# 
# 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/helpers.py
# def get_client_ip_address(request) -> str:
#     """
#     Get client IP address as configured by the user.
# 
#     The django-ipware package is used for address resolution
#     and parameters can be configured in the Axes package.
#     """
# 
#     client_ip_address, _ = ipware.ip.get_client_ip(
#         request,
#         proxy_order=settings.AXES_PROXY_ORDER,
#         proxy_count=settings.AXES_PROXY_COUNT,
#         proxy_trusted_ips=settings.AXES_PROXY_TRUSTED_IPS,
#         request_header_order=settings.AXES_META_PRECEDENCE_ORDER,
#     )
# 
#     return client_ip_address


# Path: axes/utils.py
from logging import getLogger
from typing import Optional
from django.http import HttpRequest
from axes.conf import settings
from axes.handlers.proxy import AxesProxyHandler
from axes.helpers import get_client_ip_address





log = getLogger(__name__)


def reset(ip: str = None, username: str = None, ip_or_username=False) -> int:
    """
    Reset records that match IP or username, and return the count of removed attempts.

    This utility method is meant to be used from the CLI or via Python API.
    """

    return AxesProxyHandler.reset_attempts(
        ip_address=ip, username=username, ip_or_username=ip_or_username
    )


def reset_request(request: HttpRequest) -> int:
    """
    Reset records that match IP or username, and return the count of removed attempts.

    This utility method is meant to be used from the CLI or via Python API.
    """

    ip: Optional[str] = get_client_ip_address(request)
    username = request.GET.get("username", None)

    ip_or_username = settings.AXES_LOCK_OUT_BY_USER_OR_IP

    if settings.AXES_ONLY_USER_FAILURES: