""" Accounts API views. """ from rest_framework import viewsets, filters, status from rest_framework.decorators import action from rest_framework.response import Response from rest_framework.permissions import IsAuthenticated, AllowAny from django_filters.rest_framework import DjangoFilterBackend from django.db.models import Q, Count from django.utils import timezone from django.contrib.auth import login, logout from ..models import User, TwoFactorDevice, SocialAccount, UserSession, PasswordHistory from .serializers import ( UserSerializer, UserProfileSerializer, TwoFactorDeviceSerializer, SocialAccountSerializer, UserSessionSerializer, PasswordHistorySerializer, LoginSerializer, PasswordChangeSerializer, PasswordResetSerializer ) from core.utils import AuditLogger class UserViewSet(viewsets.ModelViewSet): """ User API viewset. """ serializer_class = UserSerializer permission_classes = [IsAuthenticated] filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter] filterset_fields = [ 'role', 'department', 'is_active', 'is_verified', 'is_approved', 'two_factor_enabled' ] search_fields = [ 'username', 'email', 'first_name', 'last_name', 'employee_id', 'license_number', 'npi_number' ] ordering_fields = ['username', 'email', 'last_name', 'created_at', 'last_login'] ordering = ['last_name', 'first_name'] def get_queryset(self): if hasattr(self.request, 'tenant') and self.request.tenant: return User.objects.filter(tenant=self.request.tenant) return User.objects.none() def perform_create(self, serializer): user = serializer.save(tenant=getattr(self.request, 'tenant', None)) # Log user creation AuditLogger.log_event( tenant=getattr(self.request, 'tenant', None), event_type='CREATE', event_category='DATA_MODIFICATION', action='Create User', description=f'Created user: {user.username}', user=self.request.user, content_object=user, request=self.request ) def perform_update(self, serializer): user = serializer.save() # Log user update AuditLogger.log_event( tenant=getattr(self.request, 'tenant', None), event_type='UPDATE', event_category='DATA_MODIFICATION', action='Update User', description=f'Updated user: {user.username}', user=self.request.user, content_object=user, request=self.request ) @action(detail=False, methods=['get']) def me(self, request): """ Get current user profile. """ serializer = UserProfileSerializer(request.user) return Response(serializer.data) @action(detail=False, methods=['put', 'patch']) def update_profile(self, request): """ Update current user profile. """ serializer = UserProfileSerializer( request.user, data=request.data, partial=request.method == 'PATCH' ) if serializer.is_valid(): serializer.save() # Log profile update AuditLogger.log_event( tenant=getattr(request, 'tenant', None), event_type='UPDATE', event_category='DATA_MODIFICATION', action='Update Profile', description=f'User updated their profile: {request.user.username}', user=request.user, content_object=request.user, request=request ) return Response(serializer.data) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) @action(detail=False, methods=['post']) def change_password(self, request): """ Change user password. """ serializer = PasswordChangeSerializer( data=request.data, context={'request': request} ) if serializer.is_valid(): serializer.save() # Log password change AuditLogger.log_event( tenant=getattr(request, 'tenant', None), event_type='UPDATE', event_category='SECURITY', action='Change Password', description=f'User changed their password: {request.user.username}', user=request.user, content_object=request.user, request=request ) return Response({'message': 'Password changed successfully'}) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) @action(detail=True, methods=['post']) def lock_account(self, request, pk=None): """ Lock user account. """ user = self.get_object() duration = request.data.get('duration_minutes', 15) user.lock_account(duration) # Log account lock AuditLogger.log_event( tenant=getattr(request, 'tenant', None), event_type='UPDATE', event_category='SECURITY', action='Lock User Account', description=f'Locked user account: {user.username}', user=request.user, content_object=user, additional_data={'duration_minutes': duration}, request=request ) return Response({'message': 'Account locked successfully'}) @action(detail=True, methods=['post']) def unlock_account(self, request, pk=None): """ Unlock user account. """ user = self.get_object() user.unlock_account() # Log account unlock AuditLogger.log_event( tenant=getattr(request, 'tenant', None), event_type='UPDATE', event_category='SECURITY', action='Unlock User Account', description=f'Unlocked user account: {user.username}', user=request.user, content_object=user, request=request ) return Response({'message': 'Account unlocked successfully'}) @action(detail=False, methods=['get']) def statistics(self, request): """ Get user statistics. """ queryset = self.get_queryset() stats = { 'total_users': queryset.count(), 'active_users': queryset.filter(is_active=True).count(), 'pending_approval': queryset.filter(is_approved=False).count(), 'locked_accounts': queryset.filter(locked_until__gt=timezone.now()).count(), 'two_factor_enabled': queryset.filter(two_factor_enabled=True).count(), 'users_by_role': list( queryset.values('role').annotate(count=Count('id')).order_by('-count') ), 'users_by_department': list( queryset.exclude(department__isnull=True).exclude(department='') .values('department').annotate(count=Count('id')).order_by('-count')[:10] ), } return Response(stats) class TwoFactorDeviceViewSet(viewsets.ModelViewSet): """ Two-factor device API viewset. """ serializer_class = TwoFactorDeviceSerializer permission_classes = [IsAuthenticated] filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter] filterset_fields = ['device_type', 'is_active', 'is_verified'] search_fields = ['name'] ordering_fields = ['name', 'created_at', 'last_used_at'] ordering = ['-created_at'] def get_queryset(self): # Users can only see their own devices return TwoFactorDevice.objects.filter(user=self.request.user) def perform_create(self, serializer): device = serializer.save(user=self.request.user) # Log device creation AuditLogger.log_event( tenant=getattr(self.request, 'tenant', None), event_type='CREATE', event_category='SECURITY', action='Create Two-Factor Device', description=f'Created two-factor device: {device.name}', user=self.request.user, content_object=device, request=self.request ) def perform_destroy(self, instance): device_name = instance.name instance.delete() # Log device deletion AuditLogger.log_event( tenant=getattr(self.request, 'tenant', None), event_type='DELETE', event_category='SECURITY', action='Delete Two-Factor Device', description=f'Deleted two-factor device: {device_name}', user=self.request.user, additional_data={'device_name': device_name}, request=self.request ) class SocialAccountViewSet(viewsets.ReadOnlyModelViewSet): """ Social account API viewset. """ serializer_class = SocialAccountSerializer permission_classes = [IsAuthenticated] filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter] filterset_fields = ['provider', 'is_active'] search_fields = ['provider_email', 'display_name'] ordering_fields = ['provider', 'created_at', 'last_login_at'] ordering = ['-created_at'] def get_queryset(self): # Users can only see their own social accounts return SocialAccount.objects.filter(user=self.request.user) class UserSessionViewSet(viewsets.ReadOnlyModelViewSet): """ User session API viewset. """ serializer_class = UserSessionSerializer permission_classes = [IsAuthenticated] filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter] filterset_fields = ['device_type', 'is_active', 'login_method', 'country'] search_fields = ['ip_address', 'browser', 'operating_system'] ordering_fields = ['created_at', 'last_activity_at', 'expires_at'] ordering = ['-created_at'] def get_queryset(self): if self.request.user.is_staff: # Staff can see all sessions in their tenant if hasattr(self.request, 'tenant') and self.request.tenant: return UserSession.objects.filter(user__tenant=self.request.tenant) else: # Regular users can only see their own sessions return UserSession.objects.filter(user=self.request.user) return UserSession.objects.none() @action(detail=True, methods=['post']) def end_session(self, request, pk=None): """ End a user session. """ session = self.get_object() # Check if user can end this session if not request.user.is_staff and session.user != request.user: return Response( {'error': 'You can only end your own sessions'}, status=status.HTTP_403_FORBIDDEN ) session.end_session() # Log session termination AuditLogger.log_event( tenant=getattr(request, 'tenant', None), event_type='UPDATE', event_category='AUTHENTICATION', action='End User Session', description=f'Ended session for user: {session.user.username}', user=request.user, content_object=session, additional_data={ 'target_user': session.user.username, 'session_ip': session.ip_address, }, request=request ) return Response({'message': 'Session ended successfully'}) @action(detail=False, methods=['get']) def active(self, request): """ Get active sessions. """ queryset = self.get_queryset().filter(is_active=True) serializer = self.get_serializer(queryset, many=True) return Response(serializer.data) class AuthViewSet(viewsets.ViewSet): """ Authentication API viewset. """ @action(detail=False, methods=['post'], permission_classes=[AllowAny]) def login(self, request): """ User login. """ serializer = LoginSerializer(data=request.data) if serializer.is_valid(): user = serializer.validated_data['user'] login(request, user) # Log successful login AuditLogger.log_event( tenant=getattr(request, 'tenant', None), event_type='LOGIN', event_category='AUTHENTICATION', action='User Login', description=f'User logged in: {user.username}', user=user, content_object=user, request=request ) # Reset failed login attempts user.reset_failed_login() return Response({ 'message': 'Login successful', 'user': UserSerializer(user).data }) else: # Log failed login attempt username = request.data.get('username') if username: try: user = User.objects.get(username=username) user.increment_failed_login() AuditLogger.log_event( tenant=getattr(request, 'tenant', None), event_type='LOGIN', event_category='SECURITY', action='Failed Login Attempt', description=f'Failed login attempt for user: {username}', user=None, is_successful=False, additional_data={'username': username}, request=request ) except User.DoesNotExist: pass return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) @action(detail=False, methods=['post']) def logout(self, request): """ User logout. """ user = request.user logout(request) # Log logout AuditLogger.log_event( tenant=getattr(request, 'tenant', None), event_type='LOGOUT', event_category='AUTHENTICATION', action='User Logout', description=f'User logged out: {user.username}', user=user, content_object=user, request=request ) return Response({'message': 'Logout successful'}) @action(detail=False, methods=['post'], permission_classes=[AllowAny]) def password_reset(self, request): """ Password reset request. """ serializer = PasswordResetSerializer(data=request.data) if serializer.is_valid(): email = serializer.validated_data['email'] # Log password reset request AuditLogger.log_event( tenant=getattr(request, 'tenant', None), event_type='UPDATE', event_category='SECURITY', action='Password Reset Request', description=f'Password reset requested for email: {email}', user=None, additional_data={'email': email}, request=request ) # In a real implementation, send password reset email here return Response({'message': 'Password reset email sent'}) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)