Files
ivatar/ivatar/ivataraccount/auth_views.py

121 lines
4.0 KiB
Python

# -*- coding: utf-8 -*-
"""
External authentication views for ivatar
"""
import secrets
from typing import Optional
from django.views.generic.base import View
from django.http import JsonResponse, HttpRequest
from ipware import get_client_ip
from .auth_models import AuthToken, BruteForceAttempt
class ExternalAuthView(View):
"""
View for external authentication with optional group checking
"""
def get(self, request: HttpRequest, *args, **kwargs) -> JsonResponse:
"""
Handle GET request for external authentication
"""
group: Optional[str] = kwargs.get("group", None)
client_ip = get_client_ip(request)[0]
user_agent = request.META.get("HTTP_USER_AGENT", "")
# Determine identifier for brute force tracking
if request.user.is_authenticated:
# For authenticated users, track by user
brute_force_attempt, created = BruteForceAttempt.objects.get_or_create(
user=request.user,
defaults={
"attempt_count": 0,
"ip_address": client_ip,
"user_agent": user_agent,
},
)
else:
# For unauthenticated users, track by IP and username if available
username = request.GET.get("username", "")
brute_force_attempt, created = BruteForceAttempt.objects.get_or_create(
username=username or None,
ip_address=client_ip,
user_agent=user_agent,
defaults={"attempt_count": 0},
)
# Reset attempts if time window has expired
brute_force_attempt.reset_if_expired()
if brute_force_attempt.should_block():
return JsonResponse(
{
"authenticated": False,
"reason": "too_many_attempts",
"message": "Too many authentication attempts. Please try again later.",
},
status=429,
)
# Check if user is authenticated
if not request.user.is_authenticated:
brute_force_attempt.increment_attempt()
return JsonResponse(
{
"authenticated": False,
"reason": "not_authenticated",
"message": "User is not authenticated",
},
status=401,
)
# If group is specified, check if user is in that group
if group:
# Special handling for 'admin' group: also check if user is superuser
if group == "admin":
is_in_group = (
request.user.groups.filter(name=group).exists()
or request.user.is_superuser
)
else:
is_in_group = request.user.groups.filter(name=group).exists()
if not is_in_group:
brute_force_attempt.increment_attempt()
return JsonResponse(
{
"authenticated": False,
"reason": "not_in_group",
"message": f"User is not in group: {group}",
},
status=403,
)
# Generate a new token
token = secrets.token_urlsafe(48)
# Create auth token
auth_token = AuthToken.objects.create(
token=token, user=request.user, ip_address=client_ip
)
# Reset brute force attempts on successful authentication
brute_force_attempt.attempt_count = 0
brute_force_attempt.is_blocked = False
brute_force_attempt.save()
return JsonResponse(
{
"authenticated": True,
"token": token,
"expires_at": auth_token.expires_at.isoformat(),
"user": {
"id": request.user.id,
"username": request.user.username,
"email": request.user.email,
},
}
)