1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-08 11:16:47 +00:00
webvirtcloud/accounts/auth.py
catborise 0b5eb990ed fix(admin): a forced password change cannot trap a user or be skipped with a token
- The administrator's password form took "password-based
  authentication: Disabled" with "must change it at the next login":
  the change form asks for the old password, which the user then never
  had, so they could not get past it. That combination is refused, and
  an administrator cannot disable their own password.
- A user who must change the password could still use the API with
  their tokens. A token is now refused until they change it, as the
  pages are.
2026-10-26 10:12:53 +03:00

31 lines
1.3 KiB
Python

"""API token authentication: knox's, noting when each token was last used."""
from datetime import timedelta
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from drf_spectacular.contrib.knox_auth_token import KnoxTokenScheme
from knox.auth import TokenAuthentication as KnoxTokenAuthentication
from rest_framework import exceptions
from accounts.models import UserAttributes
# a token's last use is written at most once a minute, not on every request
LAST_USED_EVERY = timedelta(minutes=1)
class TokenAuthentication(KnoxTokenAuthentication):
def validate_user(self, auth_token):
user, token = super().validate_user(auth_token)
# as the pages do (ForcePasswordChangeMiddleware), until the user changes the password
if UserAttributes.objects.filter(user=user, must_change_password=True).exists():
raise exceptions.AuthenticationFailed(_("Change your password before using the API."))
now = timezone.now()
if token.last_used is None or now - token.last_used >= LAST_USED_EVERY:
type(token).objects.filter(pk=token.pk).update(last_used=now)
return user, token
class TokenScheme(KnoxTokenScheme):
"""The API schema's description of these tokens, as of knox's."""
target_class = "accounts.auth.TokenAuthentication"