izaac-2/backend/users/views.py
Jakub Kaniecki 12c76e3e5a init
2025-05-18 16:23:03 +02:00

174 lines
5.9 KiB
Python

from django.shortcuts import render
from rest_framework import generics, status
from rest_framework.response import Response
from rest_framework.permissions import AllowAny, IsAdminUser, IsAuthenticated
from rest_framework.views import APIView
from django.contrib.auth import get_user_model
from django.contrib.auth.password_validation import validate_password
from django.core.exceptions import ValidationError
from django.utils import timezone
from datetime import timedelta
from django.core.mail import send_mail
from django.conf import settings
from .serializers import UserRegistrationSerializer, UserSerializer, ResetPasswordSerializer, ResetPasswordConfirmSerializer
from .models import User, PasswordResetToken
import secrets
import string
User = get_user_model()
# Create your views here.
class RegisterView(generics.CreateAPIView):
"""
API endpoint for user registration.
create:
Register a new user with the following fields:
- username (required)
- password (required)
- password2 (required, must match password)
- email (required)
- first_name (required)
- last_name (required)
- role (optional, defaults to 'user')
"""
permission_classes = (AllowAny,)
serializer_class = UserRegistrationSerializer
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(
{"message": "User registered successfully. Please login to continue."},
status=status.HTTP_201_CREATED,
headers=headers
)
class UserViewSet(generics.ListAPIView):
"""
API endpoint for viewing and editing users.
"""
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = [IsAdminUser]
class UserDetailView(generics.RetrieveUpdateAPIView):
"""
API endpoint for retrieving and updating the current user's data.
"""
serializer_class = UserSerializer
permission_classes = [IsAuthenticated]
def get_object(self):
return self.request.user
def update(self, request, *args, **kwargs):
partial = kwargs.pop('partial', False)
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=partial)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
return Response(serializer.data)
class ChangePasswordView(APIView):
"""
API endpoint for changing user password.
"""
permission_classes = [IsAuthenticated]
def post(self, request):
user = request.user
old_password = request.data.get('old_password')
new_password = request.data.get('new_password')
if not old_password or not new_password:
return Response(
{"detail": "Both old and new password are required."},
status=status.HTTP_400_BAD_REQUEST
)
if not user.check_password(old_password):
return Response(
{"detail": "Current password is incorrect."},
status=status.HTTP_400_BAD_REQUEST
)
try:
validate_password(new_password, user)
except ValidationError as e:
return Response(
{"detail": list(e.messages)},
status=status.HTTP_400_BAD_REQUEST
)
user.set_password(new_password)
user.save()
return Response(
{"detail": "Password successfully updated."},
status=status.HTTP_200_OK
)
class ResetPasswordView(generics.GenericAPIView):
permission_classes = [AllowAny]
serializer_class = ResetPasswordSerializer
def post(self, request):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
email = serializer.validated_data['email']
try:
user = User.objects.get(email=email)
# Create a new reset token
token = PasswordResetToken.objects.create(
user=user,
expires_at=timezone.now() + timedelta(hours=24)
)
# Send reset email
reset_url = f"{settings.FRONTEND_URL}/reset-password/{token.token}"
send_mail(
'Password Reset Request',
f'Click the following link to reset your password: {reset_url}',
settings.DEFAULT_FROM_EMAIL,
[email],
fail_silently=False,
)
return Response({
'detail': 'If an account exists with this email, you will receive password reset instructions.'
}, status=status.HTTP_200_OK)
except User.DoesNotExist:
# Don't reveal that the email doesn't exist
return Response({
'detail': 'If an account exists with this email, you will receive password reset instructions.'
}, status=status.HTTP_200_OK)
class ResetPasswordConfirmView(generics.GenericAPIView):
permission_classes = [AllowAny]
serializer_class = ResetPasswordConfirmSerializer
def post(self, request):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
token = serializer.validated_data['token']
new_password = serializer.validated_data['new_password']
reset_token = PasswordResetToken.objects.get(token=token)
user = reset_token.user
# Update password
user.set_password(new_password)
user.save()
# Mark token as used
reset_token.used = True
reset_token.save()
return Response({
'detail': 'Password has been reset successfully.'
}, status=status.HTTP_200_OK)