231 lines
7.6 KiB
Python
231 lines
7.6 KiB
Python
from rest_framework import status, generics, permissions
|
|
from rest_framework.decorators import api_view, permission_classes
|
|
from rest_framework.response import Response
|
|
from rest_framework.views import APIView
|
|
from rest_framework_simplejwt.tokens import RefreshToken
|
|
from django.contrib.auth import login
|
|
from django.utils import timezone
|
|
from django_ratelimit.decorators import ratelimit
|
|
from django.db import transaction
|
|
import uuid
|
|
|
|
from .models import User, UserProfile, LoginAttempt, EmailVerification
|
|
from .serializers import (
|
|
UserRegistrationSerializer, UserLoginSerializer, UserSerializer,
|
|
UserProfileSerializer, EmailSettingsSerializer, PasswordChangeSerializer,
|
|
EmailVerificationSerializer, LoginAttemptSerializer
|
|
)
|
|
|
|
|
|
class UserRegistrationView(APIView):
|
|
"""User registration endpoint."""
|
|
|
|
permission_classes = [permissions.AllowAny]
|
|
|
|
def post(self, request):
|
|
serializer = UserRegistrationSerializer(data=request.data)
|
|
if serializer.is_valid():
|
|
user = serializer.save()
|
|
|
|
# Generate tokens
|
|
refresh = RefreshToken.for_user(user)
|
|
access_token = refresh.access_token
|
|
|
|
return Response({
|
|
'message': 'User registered successfully. Please verify your email.',
|
|
'user': UserSerializer(user).data,
|
|
'tokens': {
|
|
'refresh': str(refresh),
|
|
'access': str(access_token),
|
|
}
|
|
}, status=status.HTTP_201_CREATED)
|
|
|
|
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
|
|
|
|
|
class UserLoginView(APIView):
|
|
"""User login endpoint."""
|
|
|
|
permission_classes = [permissions.AllowAny]
|
|
|
|
def post(self, request):
|
|
serializer = UserLoginSerializer(data=request.data)
|
|
if serializer.is_valid():
|
|
user = serializer.validated_data['user']
|
|
|
|
# Log login attempt
|
|
LoginAttempt.objects.create(
|
|
user=user,
|
|
email=user.email,
|
|
ip_address=self.get_client_ip(request),
|
|
user_agent=request.META.get('HTTP_USER_AGENT', ''),
|
|
success=True
|
|
)
|
|
|
|
# Generate tokens
|
|
refresh = RefreshToken.for_user(user)
|
|
access_token = refresh.access_token
|
|
|
|
# Update last login IP
|
|
user.last_login_ip = self.get_client_ip(request)
|
|
user.save()
|
|
|
|
return Response({
|
|
'message': 'Login successful',
|
|
'user': UserSerializer(user).data,
|
|
'tokens': {
|
|
'refresh': str(refresh),
|
|
'access': str(access_token),
|
|
}
|
|
}, status=status.HTTP_200_OK)
|
|
|
|
# Log failed login attempt
|
|
email = request.data.get('email', '')
|
|
LoginAttempt.objects.create(
|
|
email=email,
|
|
ip_address=self.get_client_ip(request),
|
|
user_agent=request.META.get('HTTP_USER_AGENT', ''),
|
|
success=False,
|
|
failure_reason='Invalid credentials'
|
|
)
|
|
|
|
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
|
|
|
def get_client_ip(self, request):
|
|
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
|
|
if x_forwarded_for:
|
|
ip = x_forwarded_for.split(',')[0]
|
|
else:
|
|
ip = request.META.get('REMOTE_ADDR')
|
|
return ip
|
|
|
|
|
|
class UserProfileView(generics.RetrieveUpdateAPIView):
|
|
"""User profile management."""
|
|
|
|
serializer_class = UserSerializer
|
|
permission_classes = [permissions.IsAuthenticated]
|
|
|
|
def get_object(self):
|
|
return self.request.user
|
|
|
|
|
|
class UserProfileSettingsView(generics.RetrieveUpdateAPIView):
|
|
"""User profile settings management."""
|
|
|
|
serializer_class = UserProfileSerializer
|
|
permission_classes = [permissions.IsAuthenticated]
|
|
|
|
def get_object(self):
|
|
profile, created = UserProfile.objects.get_or_create(user=self.request.user)
|
|
return profile
|
|
|
|
|
|
class EmailSettingsView(generics.RetrieveUpdateAPIView):
|
|
"""Email server settings management."""
|
|
|
|
serializer_class = EmailSettingsSerializer
|
|
permission_classes = [permissions.IsAuthenticated]
|
|
|
|
def get_object(self):
|
|
return self.request.user
|
|
|
|
|
|
class PasswordChangeView(APIView):
|
|
"""Password change endpoint."""
|
|
|
|
permission_classes = [permissions.IsAuthenticated]
|
|
|
|
@ratelimit(key='user', rate='5/m', method=['POST'])
|
|
def post(self, request):
|
|
serializer = PasswordChangeSerializer(data=request.data, context={'request': request})
|
|
if serializer.is_valid():
|
|
serializer.save()
|
|
return Response({'message': 'Password changed successfully'}, status=status.HTTP_200_OK)
|
|
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
|
|
|
|
|
class EmailVerificationView(APIView):
|
|
"""Email verification endpoint."""
|
|
|
|
permission_classes = [permissions.AllowAny]
|
|
|
|
def post(self, request):
|
|
serializer = EmailVerificationSerializer(data=request.data)
|
|
if serializer.is_valid():
|
|
user = serializer.save()
|
|
return Response({
|
|
'message': 'Email verified successfully',
|
|
'user': UserSerializer(user).data
|
|
}, status=status.HTTP_200_OK)
|
|
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
|
|
|
|
|
class ResendVerificationView(APIView):
|
|
"""Resend email verification."""
|
|
|
|
permission_classes = [permissions.IsAuthenticated]
|
|
|
|
@ratelimit(key='user', rate='3/m', method=['POST'])
|
|
def post(self, request):
|
|
user = request.user
|
|
|
|
if user.is_verified:
|
|
return Response({'message': 'Email is already verified'}, status=status.HTTP_400_BAD_REQUEST)
|
|
|
|
# Create new verification token
|
|
token = str(uuid.uuid4())
|
|
EmailVerification.objects.create(
|
|
user=user,
|
|
token=token,
|
|
expires_at=timezone.now() + timezone.timedelta(hours=24)
|
|
)
|
|
|
|
# TODO: Send verification email
|
|
|
|
return Response({'message': 'Verification email sent'}, status=status.HTTP_200_OK)
|
|
|
|
|
|
class LoginAttemptsView(generics.ListAPIView):
|
|
"""View login attempts for security monitoring."""
|
|
|
|
serializer_class = LoginAttemptSerializer
|
|
permission_classes = [permissions.IsAuthenticated]
|
|
|
|
def get_queryset(self):
|
|
return LoginAttempt.objects.filter(user=self.request.user).order_by('-timestamp')[:50]
|
|
|
|
|
|
class LogoutView(APIView):
|
|
"""Logout endpoint."""
|
|
|
|
permission_classes = [permissions.IsAuthenticated]
|
|
|
|
def post(self, request):
|
|
try:
|
|
refresh_token = request.data["refresh"]
|
|
token = RefreshToken(refresh_token)
|
|
token.blacklist()
|
|
return Response({'message': 'Logout successful'}, status=status.HTTP_200_OK)
|
|
except Exception as e:
|
|
return Response({'error': 'Invalid token'}, status=status.HTTP_400_BAD_REQUEST)
|
|
|
|
|
|
@api_view(['GET'])
|
|
@permission_classes([permissions.IsAuthenticated])
|
|
def user_stats(request):
|
|
"""Get user statistics."""
|
|
user = request.user
|
|
|
|
stats = {
|
|
'total_emails': 0, # Will be implemented with emails app
|
|
'unread_emails': 0, # Will be implemented with emails app
|
|
'sent_emails': 0, # Will be implemented with emails app
|
|
'draft_emails': 0, # Will be implemented with emails app
|
|
'last_login': user.last_login,
|
|
'is_verified': user.is_verified,
|
|
'account_created': user.date_joined,
|
|
}
|
|
|
|
return Response(stats)
|