mirror of
https://github.com/jazzband/django-axes.git
synced 2026-03-16 22:30:23 +00:00
Shifted epired_at filed to new model
This commit is contained in:
parent
21e163621b
commit
d5e4268f5c
5 changed files with 95 additions and 39 deletions
|
|
@ -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 = (
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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'),
|
||||
),
|
||||
]
|
||||
50
axes/migrations/0010_accessattemptexpiration.py
Normal file
50
axes/migrations/0010_accessattemptexpiration.py
Normal 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",
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in a new issue