Refactor user account system and remove moderation integration

- Remove first_name and last_name fields from User model
- Add user deletion and social provider services
- Restructure auth serializers into separate directory
- Update avatar upload functionality and API endpoints
- Remove django-moderation integration documentation
- Add mandatory compliance enforcement rules
- Update frontend documentation with API usage examples
This commit is contained in:
pacnpal
2025-08-30 07:31:58 -04:00
parent bb7da85516
commit 04394b9976
31 changed files with 7200 additions and 1297 deletions

View File

@@ -6,23 +6,6 @@ user deletion while preserving submissions, profile management, settings,
preferences, privacy, notifications, and security.
"""
from rest_framework import status
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated, IsAdminUser
from rest_framework.response import Response
from drf_spectacular.utils import extend_schema, OpenApiParameter
from drf_spectacular.types import OpenApiTypes
from django.shortcuts import get_object_or_404
from rest_framework.permissions import AllowAny
from django.utils import timezone
from apps.accounts.models import (
User,
UserProfile,
TopList,
UserNotification,
NotificationPreference,
)
from apps.accounts.services import UserDeletionService
from apps.api.v1.serializers.accounts import (
CompleteUserSerializer,
UserPreferencesSerializer,
@@ -39,6 +22,27 @@ from apps.api.v1.serializers.accounts import (
MarkNotificationsReadSerializer,
AvatarUploadSerializer,
)
from apps.accounts.services import UserDeletionService
from apps.accounts.models import (
User,
UserProfile,
TopList,
UserNotification,
NotificationPreference,
)
import logging
from rest_framework import status
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated, IsAdminUser
from rest_framework.response import Response
from drf_spectacular.utils import extend_schema, OpenApiParameter
from drf_spectacular.types import OpenApiTypes
from django.shortcuts import get_object_or_404
from rest_framework.permissions import AllowAny
from django.utils import timezone
# Set up logging
logger = logging.getLogger(__name__)
@extend_schema(
@@ -106,7 +110,7 @@ def delete_user_preserve_submissions(request, user_id):
Delete a user while preserving all their submissions.
This endpoint allows administrators to delete user accounts while
preserving all user-generated content (reviews, photos, top lists, etc.).
preserving all user - generated content(reviews, photos, top lists, etc.).
All submissions are transferred to a system "deleted_user" placeholder.
**Admin Only**: This endpoint requires admin permissions.
@@ -119,14 +123,71 @@ def delete_user_preserve_submissions(request, user_id):
# Check if user can be deleted
can_delete, reason = UserDeletionService.can_delete_user(user)
if not can_delete:
# Log the attempt for security monitoring
logger.warning(
f"Admin user {request.user.username} attempted to delete protected user {user.username} (ID: {user_id}). Reason: {reason}",
extra={
"admin_user": request.user.username,
"target_user": user.username,
"target_user_id": user_id,
"is_superuser": user.is_superuser,
"user_role": user.role,
"rejection_reason": reason,
}
)
# Determine error code based on reason
error_code = "DELETION_FORBIDDEN"
if "superuser" in reason.lower():
error_code = "SUPERUSER_DELETION_FORBIDDEN"
elif "admin" in reason.lower():
error_code = "ADMIN_DELETION_FORBIDDEN"
elif "system" in reason.lower():
error_code = "SYSTEM_USER_DELETION_FORBIDDEN"
return Response(
{"success": False, "error": f"Cannot delete user: {reason}"},
{
"success": False,
"error": f"Cannot delete user: {reason}",
"error_code": error_code,
"user_info": {
"username": user.username,
"user_id": user.user_id,
"role": user.role,
"is_superuser": user.is_superuser,
"is_staff": user.is_staff,
},
"help_text": "Contact system administrator if you need to delete this account type.",
},
status=status.HTTP_400_BAD_REQUEST,
)
# Log the successful deletion attempt
logger.info(
f"Admin user {request.user.username} is deleting user {user.username} (ID: {user_id})",
extra={
"admin_user": request.user.username,
"target_user": user.username,
"target_user_id": user_id,
"action": "user_deletion",
}
)
# Perform the deletion
result = UserDeletionService.delete_user_preserve_submissions(user)
# Log successful deletion
logger.info(
f"Successfully deleted user {result['deleted_user']['username']} (ID: {user_id}) by admin {request.user.username}",
extra={
"admin_user": request.user.username,
"deleted_user": result['deleted_user']['username'],
"deleted_user_id": user_id,
"preserved_submissions": result['preserved_submissions'],
"action": "user_deletion_completed",
}
)
return Response(
{
"success": True,
@@ -137,8 +198,25 @@ def delete_user_preserve_submissions(request, user_id):
)
except Exception as e:
# Log the error for debugging
logger.error(
f"Error deleting user {user_id} by admin {request.user.username}: {str(e)}",
extra={
"admin_user": request.user.username,
"target_user_id": user_id,
"error": str(e),
"action": "user_deletion_error",
},
exc_info=True
)
return Response(
{"success": False, "error": f"Error deleting user: {str(e)}"},
{
"success": False,
"error": f"Error deleting user: {str(e)}",
"error_code": "DELETION_ERROR",
"help_text": "Please try again or contact system administrator if the problem persists.",
},
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
)
@@ -185,7 +263,7 @@ def request_account_deletion(request):
account. A verification code will be sent to their email address, and the
account will only be deleted after they provide the correct code.
**Authentication Required**: User must be logged in.
**Authentication Required**: User must be logged in .
**Email Verification**: A verification code is sent to the user's email.
@@ -197,6 +275,17 @@ def request_account_deletion(request):
# Create deletion request and send email
deletion_request = UserDeletionService.request_user_deletion(user)
# Log the self-service deletion request
logger.info(
f"User {user.username} (ID: {user.user_id}) requested account deletion",
extra={
"user": user.username,
"user_id": user.user_id,
"email": user.email,
"action": "self_deletion_request",
}
)
return Response(
{
"success": True,
@@ -208,12 +297,65 @@ def request_account_deletion(request):
)
except ValueError as e:
# Log the rejection for security monitoring
logger.warning(
f"User {request.user.username} (ID: {request.user.user_id}) attempted self-deletion but was rejected: {str(e)}",
extra={
"user": request.user.username,
"user_id": request.user.user_id,
"is_superuser": request.user.is_superuser,
"user_role": request.user.role,
"rejection_reason": str(e),
"action": "self_deletion_rejected",
}
)
# Determine error code based on reason
error_message = str(e)
error_code = "DELETION_FORBIDDEN"
if "superuser" in error_message.lower():
error_code = "SUPERUSER_DELETION_FORBIDDEN"
elif "admin" in error_message.lower():
error_code = "ADMIN_DELETION_FORBIDDEN"
elif "system" in error_message.lower():
error_code = "SYSTEM_USER_DELETION_FORBIDDEN"
return Response(
{"success": False, "error": str(e)}, status=status.HTTP_400_BAD_REQUEST
{
"success": False,
"error": error_message,
"error_code": error_code,
"user_info": {
"username": request.user.username,
"user_id": request.user.user_id,
"role": request.user.role,
"is_superuser": request.user.is_superuser,
"is_staff": request.user.is_staff,
},
"help_text": "Superuser and admin accounts cannot be self-deleted for security reasons. Contact system administrator if you need to delete this account.",
},
status=status.HTTP_400_BAD_REQUEST,
)
except Exception as e:
# Log the error for debugging
logger.error(
f"Error creating deletion request for user {request.user.username} (ID: {request.user.user_id}): {str(e)}",
extra={
"user": request.user.username,
"user_id": request.user.user_id,
"error": str(e),
"action": "self_deletion_error",
},
exc_info=True
)
return Response(
{"success": False, "error": f"Error creating deletion request: {str(e)}"},
{
"success": False,
"error": f"Error creating deletion request: {str(e)}",
"error_code": "DELETION_REQUEST_ERROR",
"help_text": "Please try again or contact support if the problem persists.",
},
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
)
@@ -1279,7 +1421,7 @@ def get_user_notifications(request):
unread_count = UserNotification.objects.filter(user=user, is_read=False).count()
# Apply pagination
notifications = queryset[offset : offset + limit]
notifications = queryset[offset: offset + limit]
# Build pagination URLs
request_url = request.build_absolute_uri().split("?")[0]
@@ -1517,11 +1659,13 @@ def upload_avatar(request):
)
except Exception as e:
print(f"Upload avatar - Error saving to profile: {e}")
return Response(
{"success": False, "error": f"Failed to upload avatar: {str(e)}"},
status=status.HTTP_400_BAD_REQUEST,
)
print(f"Upload avatar - Serializer errors: {serializer.errors}")
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)