feat: Add analytics, incident, and alert models and APIs, along with user permissions and bulk profile lookups.

This commit is contained in:
pacnpal
2026-01-07 11:07:36 -05:00
parent 4da7e52fb0
commit 3ec5a4857d
46 changed files with 4012 additions and 199 deletions

View File

@@ -166,7 +166,7 @@ def setup_totp(request):
def activate_totp(request):
"""Verify TOTP code and activate MFA."""
from allauth.mfa.models import Authenticator
from allauth.mfa.recovery_codes.internal import auth as recovery_auth
from allauth.mfa.recovery_codes.internal.auth import RecoveryCodes
from allauth.mfa.totp.internal import auth as totp_auth
user = request.user
@@ -178,8 +178,9 @@ def activate_totp(request):
status=status.HTTP_400_BAD_REQUEST,
)
# Get pending secret from session
secret = request.session.get("pending_totp_secret")
# Get pending secret from session OR from request body
# (request body is used as fallback for JWT auth where sessions may not persist)
secret = request.session.get("pending_totp_secret") or request.data.get("secret", "").strip()
if not secret:
return Response(
{"detail": "No pending TOTP setup. Please start setup again."},
@@ -207,16 +208,13 @@ def activate_totp(request):
data={"secret": secret},
)
# Generate recovery codes
codes = recovery_auth.generate_recovery_codes()
Authenticator.objects.create(
user=user,
type=Authenticator.Type.RECOVERY_CODES,
data={"codes": codes},
)
# Generate recovery codes using allauth's RecoveryCodes API
recovery_instance = RecoveryCodes.activate(user)
codes = recovery_instance.get_unused_codes()
# Clear session
del request.session["pending_totp_secret"]
# Clear session (only if it exists - won't exist with JWT auth + secret from body)
if "pending_totp_secret" in request.session:
del request.session["pending_totp_secret"]
return Response(
{
@@ -361,7 +359,7 @@ def verify_totp(request):
def regenerate_recovery_codes(request):
"""Regenerate recovery codes."""
from allauth.mfa.models import Authenticator
from allauth.mfa.recovery_codes.internal import auth as recovery_auth
from allauth.mfa.recovery_codes.internal.auth import RecoveryCodes
user = request.user
password = request.data.get("password", "")
@@ -380,15 +378,14 @@ def regenerate_recovery_codes(request):
status=status.HTTP_400_BAD_REQUEST,
)
# Generate new codes
codes = recovery_auth.generate_recovery_codes()
# Delete existing recovery codes first (so activate creates new ones)
Authenticator.objects.filter(
user=user, type=Authenticator.Type.RECOVERY_CODES
).delete()
# Update or create recovery codes authenticator
authenticator, created = Authenticator.objects.update_or_create(
user=user,
type=Authenticator.Type.RECOVERY_CODES,
defaults={"data": {"codes": codes}},
)
# Generate new recovery codes using allauth's RecoveryCodes API
recovery_instance = RecoveryCodes.activate(user)
codes = recovery_instance.get_unused_codes()
return Response(
{

View File

@@ -377,7 +377,7 @@ class MFALoginVerifyAPIView(APIView):
"""Verify TOTP code against user's authenticator."""
try:
from allauth.mfa.models import Authenticator
from allauth.mfa.totp import TOTP
from allauth.mfa.totp.internal import auth as totp_auth
try:
authenticator = Authenticator.objects.get(
@@ -387,9 +387,12 @@ class MFALoginVerifyAPIView(APIView):
except Authenticator.DoesNotExist:
return False
# Get the TOTP instance and verify
totp = TOTP(authenticator)
return totp.validate_code(code)
# Get the secret from authenticator data and verify
secret = authenticator.data.get("secret")
if not secret:
return False
return totp_auth.validate_totp_code(secret, code)
except ImportError:
logger.error("allauth.mfa not available for TOTP verification")