django-axes/axes/admin.py
2026-02-11 22:14:31 +02:00

176 lines
4.9 KiB
Python

from django.contrib import admin
from django.http import HttpRequest
from django.utils.translation import gettext_lazy as _
from axes.conf import settings
from axes.models import AccessAttempt, AccessLog, AccessFailureLog
from axes.handlers.database import AxesDatabaseHandler
class IsLockedOutFilter(admin.SimpleListFilter):
title = _("Locked Out")
parameter_name = "locked_out"
def lookups(self, request, model_admin):
return (
("yes", _("Yes")),
("no", _("No")),
)
def queryset(self, request, queryset):
if self.value() == "yes":
return queryset.filter(
failures_since_start__gte=settings.AXES_FAILURE_LIMIT
)
if self.value() == "no":
return queryset.filter(failures_since_start__lt=settings.AXES_FAILURE_LIMIT)
return queryset
class AccessAttemptAdmin(admin.ModelAdmin):
list_display = [
"attempt_time",
"ip_address",
"user_agent",
"username",
"path_info",
"failures_since_start",
]
if settings.AXES_USE_ATTEMPT_EXPIRATION:
list_display.append("expiration")
list_filter = ["attempt_time", "path_info"]
if isinstance(settings.AXES_FAILURE_LIMIT, int) and settings.AXES_FAILURE_LIMIT > 0:
# This will only add the status field if AXES_FAILURE_LIMIT is set to a positive integer
# Because callable failure limit requires scope of request object
list_display.append("status")
list_filter.append(IsLockedOutFilter) # type: ignore[arg-type]
search_fields = ["ip_address", "username", "user_agent", "path_info"]
date_hierarchy = "attempt_time"
fieldsets = (
(
None,
{"fields": ("username", "path_info", "failures_since_start", "expiration")},
),
(_("Form Data"), {"fields": ("get_data", "post_data")}),
(_("Meta Data"), {"fields": ("user_agent", "ip_address", "http_accept")}),
)
readonly_fields = [
"user_agent",
"ip_address",
"username",
"http_accept",
"path_info",
"attempt_time",
"get_data",
"post_data",
"failures_since_start",
"expiration",
]
actions = ["cleanup_expired_attempts"]
@admin.action(description=_("Clean up expired attempts"))
def cleanup_expired_attempts(self, request, queryset): # noqa
count = self.handler.clean_expired_user_attempts(request=request)
self.message_user(request, _(f"Cleaned up {count} expired access attempts."))
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.handler = AxesDatabaseHandler()
def has_add_permission(self, request: HttpRequest) -> bool:
return False
def expiration(self, obj: AccessAttempt):
return obj.expiration.expires_at if hasattr(obj, "expiration") else _("Not set")
def status(self, obj: AccessAttempt):
return (
f"{settings.AXES_FAILURE_LIMIT - obj.failures_since_start} "
+ _("Attempt Remaining")
if obj.failures_since_start < settings.AXES_FAILURE_LIMIT
else _("Locked Out")
)
class AccessLogAdmin(admin.ModelAdmin):
list_display = (
"attempt_time",
"logout_time",
"ip_address",
"username",
"user_agent",
"path_info",
)
list_filter = ["attempt_time", "logout_time", "path_info"]
search_fields = ["ip_address", "user_agent", "username", "path_info"]
date_hierarchy = "attempt_time"
fieldsets = (
(None, {"fields": ("username", "path_info")}),
(_("Meta Data"), {"fields": ("user_agent", "ip_address", "http_accept")}),
)
readonly_fields = [
"user_agent",
"ip_address",
"username",
"http_accept",
"path_info",
"attempt_time",
"logout_time",
]
def has_add_permission(self, request: HttpRequest) -> bool:
return False
class AccessFailureLogAdmin(admin.ModelAdmin):
list_display = (
"attempt_time",
"ip_address",
"username",
"user_agent",
"path_info",
"locked_out",
)
list_filter = ["attempt_time", "locked_out", "path_info"]
search_fields = ["ip_address", "user_agent", "username", "path_info"]
date_hierarchy = "attempt_time"
fieldsets = (
(None, {"fields": ("username", "path_info")}),
(_("Meta Data"), {"fields": ("user_agent", "ip_address", "http_accept")}),
)
readonly_fields = [
"user_agent",
"ip_address",
"username",
"http_accept",
"path_info",
"attempt_time",
"locked_out",
]
def has_add_permission(self, request: HttpRequest) -> bool:
return False
if settings.AXES_ENABLE_ADMIN:
admin.site.register(AccessAttempt, AccessAttemptAdmin)
admin.site.register(AccessLog, AccessLogAdmin)
admin.site.register(AccessFailureLog, AccessFailureLogAdmin)