Shifted epired_at filed to new model

This commit is contained in:
kuldeepkhatke 2025-06-07 18:43:14 +05:30
parent 21e163621b
commit d5e4268f5c
5 changed files with 95 additions and 39 deletions

View file

@ -7,8 +7,8 @@ from axes.models import AccessAttempt, AccessLog, AccessFailureLog
class AccessAttemptAdmin(admin.ModelAdmin):
if settings.AXES_INDIVIDUAL_ATTEMPT_EXPIRY:
list_display = (
if settings.AXES_USE_ATTEMPT_EXPIRATION:
list_display = (
"attempt_time",
"expires_at",
"ip_address",
@ -27,7 +27,6 @@ class AccessAttemptAdmin(admin.ModelAdmin):
"failures_since_start",
)
list_filter = ["attempt_time", "path_info"]
search_fields = ["ip_address", "username", "user_agent", "path_info"]
@ -50,11 +49,16 @@ class AccessAttemptAdmin(admin.ModelAdmin):
"get_data",
"post_data",
"failures_since_start",
"expires_at",
]
def has_add_permission(self, request: HttpRequest) -> bool:
return False
def expires_at(self, obj: AccessAttempt):
if hasattr(obj, "expiration") and obj.expiration.expires_at:
return obj.expiration.expires_at #.strftime("%Y-%m-%d %H:%M:%S")
return _("Not set")
class AccessLogAdmin(admin.ModelAdmin):
list_display = (

View file

@ -20,7 +20,7 @@ from axes.helpers import (
get_lockout_parameters,
get_query_str,
)
from axes.models import AccessAttempt, AccessFailureLog, AccessLog
from axes.models import AccessAttempt, AccessAttemptExpiration, AccessFailureLog, AccessLog
from axes.signals import user_locked_out
log = getLogger(__name__)
@ -184,13 +184,7 @@ class AxesDatabaseHandler(AbstractAxesHandler, AxesBaseHandler):
"http_accept": request.axes_http_accept,
"path_info": request.axes_path_info,
"failures_since_start": 1,
"attempt_time": request.axes_attempt_time,
# Set the expiry time for the attempt based on the cool off period.
"expires_at": (
get_individual_attempt_expiry(request)
if settings.AXES_USE_ATTEMPT_EXPIRATION
else None
),
"attempt_time": request.axes_attempt_time
},
)
@ -218,8 +212,6 @@ class AxesDatabaseHandler(AbstractAxesHandler, AxesBaseHandler):
attempt.path_info = request.axes_path_info
attempt.failures_since_start = F("failures_since_start") + 1
attempt.attempt_time = request.axes_attempt_time
if settings.AXES_USE_ATTEMPT_EXPIRATION:
attempt.expires_at = max(get_individual_attempt_expiry(request), attempt.expires_at)
attempt.save()
log.warning(
@ -227,6 +219,23 @@ class AxesDatabaseHandler(AbstractAxesHandler, AxesBaseHandler):
client_str,
)
# Set the expiration time for the attempt based on the cool off period.
if settings.AXES_USE_ATTEMPT_EXPIRATION:
if not hasattr(attempt, "expiration") or attempt.expiration is None:
log.debug(
"AXES: Creating new AccessAttemptExpiration for %s", client_str
)
# Create a new AccessAttemptExpiration if it does not exist
attempt.expiration = AccessAttemptExpiration(
access_attempt=attempt
)
attempt.expiration.expires_at = get_individual_attempt_expiry(request)
else:
attempt.expiration.expires_at = max(
get_individual_attempt_expiry(request), attempt.expiration.expires_at
)
attempt.expiration.save()
# 3. or 4. database query: Calculate the current maximum failure number from the existing attempts
failures_since_start = self.get_failures(request, credentials)
request.axes_failures_since_start = failures_since_start
@ -392,7 +401,7 @@ class AxesDatabaseHandler(AbstractAxesHandler, AxesBaseHandler):
if settings.AXES_USE_ATTEMPT_EXPIRATION:
threshold = timezone.now()
count, _ = AccessAttempt.objects.filter(expires_at__lt=threshold).delete()
count, _ = AccessAttempt.objects.filter(expiration__expires_at__lt=threshold).delete()
log.info(
"AXES: Cleaned up %s expired access attempts from database that expiry were older than %s",
count,

View file

@ -1,18 +0,0 @@
# Generated by Django 4.2.21 on 2025-05-27 18:03
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('axes', '0009_add_session_hash'),
]
operations = [
migrations.AddField(
model_name='accessattempt',
name='expires_at',
field=models.DateTimeField(blank=True, help_text='The time when this access attempt expires and is no longer valid.', null=True, verbose_name='Expires At'),
),
]

View file

@ -0,0 +1,50 @@
# Generated by Django 5.2.1 on 2025-06-07 17:37
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("axes", "0009_add_session_hash"),
]
operations = [
migrations.CreateModel(
name="AccessAttemptExpiration",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"expires_at",
models.DateTimeField(
blank=True,
help_text="The time when access attempt expires and is no longer valid.",
null=True,
verbose_name="Expires At",
),
),
(
"access_attempt",
models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
related_name="expiration",
to="axes.accessattempt",
verbose_name="Access Attempt",
),
),
],
options={
"verbose_name": "access attempt expiration",
"verbose_name_plural": "access attempt expirations",
},
),
]

View file

@ -42,13 +42,6 @@ class AccessAttempt(AccessBase):
failures_since_start = models.PositiveIntegerField(_("Failed Logins"))
expires_at = models.DateTimeField(
_("Expires At"),
null=True,
blank=True,
help_text=_("The time when this access attempt expires and is no longer valid."),
)
def __str__(self):
return f"Attempted Access: {self.attempt_time}"
@ -58,6 +51,24 @@ class AccessAttempt(AccessBase):
unique_together = [["username", "ip_address", "user_agent"]]
class AccessAttemptExpiration(models.Model):
access_attempt = models.OneToOneField(
AccessAttempt,
on_delete=models.CASCADE,
related_name="expiration",
verbose_name=_("Access Attempt"),
)
expires_at = models.DateTimeField(
_("Expires At"),
null=True,
blank=True,
help_text=_("The time when access attempt expires and is no longer valid."),
)
class Meta:
verbose_name = _("access attempt expiration")
verbose_name_plural = _("access attempt expirations")
class AccessLog(AccessBase):
logout_time = models.DateTimeField(_("Logout Time"), null=True, blank=True)
session_hash = models.CharField(_("Session key hash (sha256)"), default="", blank=True, max_length=64)