mirror of
https://git.linux-kernel.at/oliver/ivatar.git
synced 2025-11-19 22:48:01 +00:00
121 lines
4.0 KiB
Python
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,
|
|
},
|
|
}
|
|
)
|