""" Accounts app views for hospital management system with comprehensive CRUD operations. """ from allauth.account.views import LoginView from django.shortcuts import render, get_object_or_404, redirect from django.contrib.auth import authenticate, login, logout from django.contrib.auth.decorators import login_required from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin from django.views.generic import ( TemplateView, ListView, DetailView, CreateView, UpdateView, DeleteView ) from django.http import JsonResponse from django.contrib import messages from django.db.models import Q, Count from django.utils import timezone from django.urls import reverse_lazy, reverse from django.core.paginator import Paginator from datetime import timedelta from .models import User, TwoFactorDevice, SocialAccount, UserSession, PasswordHistory from .forms import ( UserForm, UserCreateForm, TwoFactorDeviceForm, SocialAccountForm, AccountsSearchForm, PasswordChangeForm ) from core.utils import AuditLogger # ============================================================================ # USER VIEWS (FULL CRUD - Master Data) # ============================================================================ession, PasswordHistory from .forms import ( UserForm, UserCreateForm, TwoFactorDeviceForm, SocialAccountForm, AccountsSearchForm, PasswordChangeForm ) from core.utils import AuditLogger # ============================================================================ # USER VIEWS (FULL CRUD - Master Data) # ============================================================================ class UserListView(LoginRequiredMixin, ListView): """ User listing view. """ model = User template_name = 'account/user_list.html' context_object_name = 'users' paginate_by = 25 def get_queryset(self): tenant = getattr(self.request, 'tenant', None) if not tenant: return User.objects.none() queryset = User.objects.filter(tenant=tenant) # Apply filters role = self.request.GET.get('role') if role: queryset = queryset.filter(role=role) department = self.request.GET.get('department') if department: queryset = queryset.filter(department=department) status = self.request.GET.get('status') if status == 'active': queryset = queryset.filter(is_active=True) elif status == 'inactive': queryset = queryset.filter(is_active=False) elif status == 'pending': queryset = queryset.filter(is_approved=False) search = self.request.GET.get('search') if search: queryset = queryset.filter( Q(username__icontains=search) | Q(email__icontains=search) | Q(first_name__icontains=search) | Q(last_name__icontains=search) | Q(employee_id__icontains=search) ) return queryset.order_by('last_name', 'first_name') def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) tenant = getattr(self.request, 'tenant', None) if tenant: # Get filter options context.update({ 'roles': User.objects.filter(tenant=tenant).values_list('role', flat=True).distinct(), 'departments': User.objects.filter(tenant=tenant).values_list('department', flat=True).distinct(), 'search_form': AccountsSearchForm(self.request.GET), }) return context class UserDetailView(LoginRequiredMixin, DetailView): """ User detail view. """ model = User template_name = 'account/user_detail.html' context_object_name = 'user_profile' def get_queryset(self): tenant = getattr(self.request, 'tenant', None) if not tenant: return User.objects.none() return User.objects.filter(tenant=tenant) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) user_profile = self.get_object() # Get user's sessions context['active_sessions'] = UserSession.objects.filter( user=user_profile, is_active=True ).order_by('-created_at') # Get user's two-factor devices context['two_factor_devices'] = TwoFactorDevice.objects.filter( user=user_profile, is_active=True ).order_by('-created_at') # Get user's social accounts context['social_accounts'] = SocialAccount.objects.filter( user=user_profile, is_active=True ).order_by('-created_at') # Get password history context['password_history'] = PasswordHistory.objects.filter( user=user_profile ).order_by('-created_at')[:5] # Get recent audit logs from core.models import AuditLogEntry context['recent_activity'] = AuditLogEntry.objects.filter( tenant=user_profile.tenant, user=user_profile ).order_by('-timestamp')[:10] return context class UserCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView): """ Create new user. """ model = User form_class = UserCreateForm template_name = 'account/user_create.html' permission_required = 'accounts.add_user' success_url = reverse_lazy('accounts:user_list') def form_valid(self, form): # Set tenant form.instance.tenant = getattr(self.request, 'tenant', None) response = super().form_valid(form) # Log user creation AuditLogger.log_event( tenant=form.instance.tenant, event_type='CREATE', event_category='USER_MANAGEMENT', action='Create User', description=f'Created new user: {self.object.username}', user=self.request.user, content_object=self.object, request=self.request ) messages.success(self.request, f'User "{self.object.username}" created successfully.') return response class UserUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView): """ Update user information. """ model = User form_class = UserForm template_name = 'account/user_form.html' permission_required = 'accounts.change_user' def get_queryset(self): tenant = getattr(self.request, 'tenant', None) if not tenant: return User.objects.none() return User.objects.filter(tenant=tenant) def get_success_url(self): return reverse('accounts:user_detail', kwargs={'pk': self.object.pk}) def form_valid(self, form): response = super().form_valid(form) # Log user update AuditLogger.log_event( tenant=self.object.tenant, event_type='UPDATE', event_category='USER_MANAGEMENT', action='Update User', description=f'Updated user: {self.object.username}', user=self.request.user, content_object=self.object, request=self.request ) messages.success(self.request, f'User "{self.object.username}" updated successfully.') return response class UserDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteView): """ Delete user (soft delete to inactive). """ model = User template_name = 'account/user_confirm_delete.html' permission_required = 'accounts.delete_user' success_url = reverse_lazy('accounts:user_list') def get_queryset(self): tenant = getattr(self.request, 'tenant', None) if not tenant: return User.objects.none() return User.objects.filter(tenant=tenant) def delete(self, request, *args, **kwargs): self.object = self.get_object() # Soft delete - set to inactive self.object.is_active = False self.object.save() # Log user deletion AuditLogger.log_event( tenant=self.object.tenant, event_type='DELETE', event_category='USER_MANAGEMENT', action='Deactivate User', description=f'Deactivated user: {self.object.username}', user=request.user, content_object=self.object, request=request ) messages.success(request, f'User "{self.object.username}" deactivated successfully.') return redirect(self.success_url) # ============================================================================ # TWO FACTOR DEVICE VIEWS (FULL CRUD - Security Data) # ============================================================================ class TwoFactorDeviceListView(LoginRequiredMixin, ListView): """ List two-factor devices. """ model = TwoFactorDevice template_name = 'account/two_factor_device_list.html' context_object_name = 'devices' paginate_by = 20 def get_queryset(self): tenant = getattr(self.request, 'tenant', None) if not tenant: return TwoFactorDevice.objects.none() queryset = TwoFactorDevice.objects.filter(user__tenant=tenant) # Apply filters user_id = self.request.GET.get('user_id') if user_id: queryset = queryset.filter(user_id=user_id) device_type = self.request.GET.get('device_type') if device_type: queryset = queryset.filter(device_type=device_type) status = self.request.GET.get('status') if status == 'active': queryset = queryset.filter(is_active=True) elif status == 'inactive': queryset = queryset.filter(is_active=False) return queryset.order_by('-created_at') def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) tenant = getattr(self.request, 'tenant', None) if tenant: context.update({ 'device_types': TwoFactorDevice.DEVICE_TYPE_CHOICES, 'users': User.objects.filter(tenant=tenant, is_active=True).order_by('last_name', 'first_name'), }) return context class TwoFactorDeviceDetailView(LoginRequiredMixin, DetailView): """ Display two-factor device details. """ model = TwoFactorDevice template_name = 'account/two_factor_device_detail.html' context_object_name = 'device' def get_queryset(self): tenant = getattr(self.request, 'tenant', None) if not tenant: return TwoFactorDevice.objects.none() return TwoFactorDevice.objects.filter(user__tenant=tenant) class TwoFactorDeviceCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView): """ Create new two-factor device. """ model = TwoFactorDevice form_class = TwoFactorDeviceForm template_name = 'account/two_factor_device_form.html' permission_required = 'accounts.add_twofactordevice' success_url = reverse_lazy('accounts:two_factor_device_list') def get_form_kwargs(self): kwargs = super().get_form_kwargs() kwargs['user'] = self.request.user return kwargs def form_valid(self, form): response = super().form_valid(form) # Generate secret key for TOTP devices if form.instance.device_type == 'TOTP': import secrets form.instance.secret_key = secrets.token_urlsafe(32) form.instance.save() # 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: {self.object.name}', user=self.request.user, content_object=self.object, request=self.request ) messages.success(self.request, f'Two-factor device "{self.object.name}" created successfully.') return response class TwoFactorDeviceUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView): """ Update two-factor device. """ model = TwoFactorDevice form_class = TwoFactorDeviceForm template_name = 'account/two_factor_device_form.html' permission_required = 'accounts.change_twofactordevice' def get_queryset(self): tenant = getattr(self.request, 'tenant', None) if not tenant: return TwoFactorDevice.objects.none() return TwoFactorDevice.objects.filter(user__tenant=tenant) def get_form_kwargs(self): kwargs = super().get_form_kwargs() kwargs['user'] = self.request.user return kwargs def get_success_url(self): return reverse('accounts:two_factor_device_detail', kwargs={'pk': self.object.pk}) def form_valid(self, form): response = super().form_valid(form) # Log device update AuditLogger.log_event( tenant=getattr(self.request, 'tenant', None), event_type='UPDATE', event_category='SECURITY', action='Update Two-Factor Device', description=f'Updated two-factor device: {self.object.name}', user=self.request.user, content_object=self.object, request=self.request ) messages.success(self.request, f'Two-factor device "{self.object.name}" updated successfully.') return response class TwoFactorDeviceDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteView): """ Delete two-factor device. """ model = TwoFactorDevice template_name = 'account/two_factor_device_confirm_delete.html' permission_required = 'accounts.delete_twofactordevice' success_url = reverse_lazy('accounts:two_factor_device_list') def get_queryset(self): tenant = getattr(self.request, 'tenant', None) if not tenant: return TwoFactorDevice.objects.none() return TwoFactorDevice.objects.filter(user__tenant=tenant) def delete(self, request, *args, **kwargs): self.object = self.get_object() device_name = self.object.name # Log device deletion AuditLogger.log_event( tenant=getattr(request, 'tenant', None), event_type='DELETE', event_category='SECURITY', action='Delete Two-Factor Device', description=f'Deleted two-factor device: {device_name}', user=request.user, content_object=self.object, request=request ) messages.success(request, f'Two-factor device "{device_name}" deleted successfully.') return super().delete(request, *args, **kwargs) # ============================================================================ # SOCIAL ACCOUNT VIEWS (FULL CRUD - Integration Data) # ============================================================================ class SocialAccountListView(LoginRequiredMixin, ListView): """ List social accounts. """ model = SocialAccount template_name = 'account/social_account_list.html' context_object_name = 'social_accounts' paginate_by = 20 def get_queryset(self): tenant = getattr(self.request, 'tenant', None) if not tenant: return SocialAccount.objects.none() queryset = SocialAccount.objects.filter(user__tenant=tenant) # Apply filters user_id = self.request.GET.get('user_id') if user_id: queryset = queryset.filter(user_id=user_id) provider = self.request.GET.get('provider') if provider: queryset = queryset.filter(provider=provider) status = self.request.GET.get('status') if status == 'active': queryset = queryset.filter(is_active=True) elif status == 'inactive': queryset = queryset.filter(is_active=False) return queryset.order_by('-created_at') def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) tenant = getattr(self.request, 'tenant', None) if tenant: context.update({ 'providers': SocialAccount.objects.filter( user__tenant=tenant ).values_list('provider', flat=True).distinct(), 'users': User.objects.filter(tenant=tenant, is_active=True).order_by('last_name', 'first_name'), }) return context class SocialAccountDetailView(LoginRequiredMixin, DetailView): """ Display social account details. """ model = SocialAccount template_name = 'account/social_account_detail.html' context_object_name = 'social_account' def get_queryset(self): tenant = getattr(self.request, 'tenant', None) if not tenant: return SocialAccount.objects.none() return SocialAccount.objects.filter(user__tenant=tenant) class SocialAccountCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView): """ Create new social account. """ model = SocialAccount form_class = SocialAccountForm template_name = 'account/social_account_form.html' permission_required = 'accounts.add_socialaccount' success_url = reverse_lazy('accounts:social_account_list') def get_form_kwargs(self): kwargs = super().get_form_kwargs() kwargs['user'] = self.request.user return kwargs def form_valid(self, form): response = super().form_valid(form) # Log social account creation AuditLogger.log_event( tenant=getattr(self.request, 'tenant', None), event_type='CREATE', event_category='INTEGRATION', action='Create Social Account', description=f'Created social account: {self.object.provider}', user=self.request.user, content_object=self.object, request=self.request ) messages.success(self.request, f'Social account "{self.object.provider}" created successfully.') return response class SocialAccountUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView): """ Update social account. """ model = SocialAccount form_class = SocialAccountForm template_name = 'account/social_account_form.html' permission_required = 'accounts.change_socialaccount' def get_queryset(self): tenant = getattr(self.request, 'tenant', None) if not tenant: return SocialAccount.objects.none() return SocialAccount.objects.filter(user__tenant=tenant) def get_form_kwargs(self): kwargs = super().get_form_kwargs() kwargs['user'] = self.request.user return kwargs def get_success_url(self): return reverse('accounts:social_account_detail', kwargs={'pk': self.object.pk}) def form_valid(self, form): response = super().form_valid(form) # Log social account update AuditLogger.log_event( tenant=getattr(self.request, 'tenant', None), event_type='UPDATE', event_category='INTEGRATION', action='Update Social Account', description=f'Updated social account: {self.object.provider}', user=self.request.user, content_object=self.object, request=self.request ) messages.success(self.request, f'Social account "{self.object.provider}" updated successfully.') return response class SocialAccountDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteView): """ Delete social account. """ model = SocialAccount template_name = 'account/social_account_confirm_delete.html' permission_required = 'accounts.delete_socialaccount' success_url = reverse_lazy('accounts:social_account_list') def get_queryset(self): tenant = getattr(self.request, 'tenant', None) if not tenant: return SocialAccount.objects.none() return SocialAccount.objects.filter(user__tenant=tenant) def delete(self, request, *args, **kwargs): self.object = self.get_object() provider = self.object.provider # Log social account deletion AuditLogger.log_event( tenant=getattr(request, 'tenant', None), event_type='DELETE', event_category='INTEGRATION', action='Delete Social Account', description=f'Deleted social account: {provider}', user=request.user, content_object=self.object, request=request ) messages.success(request, f'Social account "{provider}" deleted successfully.') return super().delete(request, *args, **kwargs) # ============================================================================ # USER SESSION VIEWS (READ-ONLY - System Generated) # ============================================================================ class UserSessionListView(LoginRequiredMixin, ListView): """ List user sessions. """ model = UserSession template_name = 'account/user_session_list.html' context_object_name = 'sessions' paginate_by = 50 def get_queryset(self): tenant = getattr(self.request, 'tenant', None) if not tenant: return UserSession.objects.none() queryset = UserSession.objects.filter(user__tenant=tenant) # Apply filters user_id = self.request.GET.get('user_id') if user_id: queryset = queryset.filter(user_id=user_id) status = self.request.GET.get('status') if status == 'active': queryset = queryset.filter(is_active=True) elif status == 'expired': queryset = queryset.filter(expires_at__lt=timezone.now()) device_type = self.request.GET.get('device_type') if device_type: queryset = queryset.filter(device_type=device_type) date_from = self.request.GET.get('date_from') if date_from: queryset = queryset.filter(created_at__gte=date_from) date_to = self.request.GET.get('date_to') if date_to: queryset = queryset.filter(created_at__lte=date_to) return queryset.order_by('-created_at') def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) tenant = getattr(self.request, 'tenant', None) if tenant: context.update({ 'device_types': UserSession.objects.filter( user__tenant=tenant ).values_list('device_type', flat=True).distinct(), 'users': User.objects.filter(tenant=tenant, is_active=True).order_by('last_name', 'first_name'), }) return context class UserSessionDetailView(LoginRequiredMixin, DetailView): """ Display user session details. """ model = UserSession template_name = 'account/user_session_detail.html' context_object_name = 'session' def get_queryset(self): tenant = getattr(self.request, 'tenant', None) if not tenant: return UserSession.objects.none() return UserSession.objects.filter(user__tenant=tenant) # ============================================================================ # PASSWORD HISTORY VIEWS (READ-ONLY - System Generated) # ============================================================================ class PasswordHistoryListView(LoginRequiredMixin, ListView): """ List password history. """ model = PasswordHistory template_name = 'account/password_history_list.html' context_object_name = 'password_history' paginate_by = 50 def get_queryset(self): tenant = getattr(self.request, 'tenant', None) if not tenant: return PasswordHistory.objects.none() queryset = PasswordHistory.objects.filter(user__tenant=tenant) # Apply filters user_id = self.request.GET.get('user_id') if user_id: queryset = queryset.filter(user_id=user_id) date_from = self.request.GET.get('date_from') if date_from: queryset = queryset.filter(created_at__gte=date_from) date_to = self.request.GET.get('date_to') if date_to: queryset = queryset.filter(created_at__lte=date_to) return queryset.order_by('-created_at') def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) tenant = getattr(self.request, 'tenant', None) if tenant: context.update({ 'users': User.objects.filter(tenant=tenant, is_active=True).order_by('last_name', 'first_name'), }) return context class PasswordHistoryDetailView(LoginRequiredMixin, DetailView): """ Display password history details. """ model = PasswordHistory template_name = 'account/password_history_detail.html' context_object_name = 'password_history' def get_queryset(self): tenant = getattr(self.request, 'tenant', None) if not tenant: return PasswordHistory.objects.none() return PasswordHistory.objects.filter(user__tenant=tenant) # ============================================================================ # PROFILE AND SESSION MANAGEMENT VIEWS # ============================================================================ class UserProfileView(LoginRequiredMixin, TemplateView): """ User profile management view. """ template_name = 'account/user_profile.html' def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) user = self.request.user # Get user's sessions context['active_sessions'] = UserSession.objects.filter( user=user, is_active=True ).order_by('-created_at') # Get user's two-factor devices context['two_factor_devices'] = TwoFactorDevice.objects.filter( user=user, is_active=True ).order_by('-created_at') # Get user's social accounts context['social_accounts'] = SocialAccount.objects.filter( user=user, is_active=True ).order_by('-created_at') # Get password history context['password_history'] = PasswordHistory.objects.filter( user=user ).order_by('-created_at')[:5] return context class SessionManagementView(LoginRequiredMixin, ListView): """ Session management view. """ model = UserSession template_name = 'account/session_management.html' context_object_name = 'sessions' paginate_by = 50 def get_queryset(self): tenant = getattr(self.request, 'tenant', None) if not tenant: return UserSession.objects.none() queryset = UserSession.objects.filter(user__tenant=tenant) # Apply filters user_id = self.request.GET.get('user_id') if user_id: queryset = queryset.filter(user_id=user_id) status = self.request.GET.get('status') if status == 'active': queryset = queryset.filter(is_active=True) elif status == 'expired': queryset = queryset.filter(expires_at__lt=timezone.now()) device_type = self.request.GET.get('device_type') if device_type: queryset = queryset.filter(device_type=device_type) return queryset.order_by('-created_at') # ============================================================================ # HTMX VIEWS FOR REAL-TIME UPDATES # ============================================================================ @login_required def user_search(request): """ HTMX view for user search. """ tenant = getattr(request, 'tenant', None) if not tenant: return JsonResponse({'error': 'No tenant found'}, status=400) search_query = request.GET.get('search', '') queryset = User.objects.filter(tenant=tenant) if search_query: queryset = queryset.filter( Q(username__icontains=search_query) | Q(email__icontains=search_query) | Q(first_name__icontains=search_query) | Q(last_name__icontains=search_query) | Q(employee_id__icontains=search_query) ) users = queryset.order_by('last_name', 'first_name')[:20] return render(request, 'account/partials/user_list.html', { 'users': users }) @login_required def user_stats(request): """ HTMX view for user statistics. """ tenant = getattr(request, 'tenant', None) if not tenant: return JsonResponse({'error': 'No tenant found'}, status=400) # Calculate user statistics total_users = User.objects.filter(tenant=tenant).count() active_users = User.objects.filter(tenant=tenant, is_active=True).count() pending_approval = User.objects.filter(tenant=tenant, is_approved=False).count() # Active sessions in last 24 hours yesterday = timezone.now() - timedelta(days=1) active_sessions = UserSession.objects.filter( user__tenant=tenant, last_activity_at__gte=yesterday, is_active=True ).count() # Users by role role_stats = User.objects.filter(tenant=tenant).values('role').annotate( count=Count('id') ).order_by('-count')[:5] # Two-factor adoption two_factor_users = User.objects.filter( tenant=tenant, twofactordevice__is_active=True ).distinct().count() stats = { 'total_users': total_users, 'active_users': active_users, 'pending_approval': pending_approval, 'active_sessions': active_sessions, 'two_factor_users': two_factor_users, 'role_stats': role_stats, } return render(request, 'account/partials/user_stats.html', {'stats': stats}) @login_required def session_list(request): """ HTMX view for session list. """ tenant = getattr(request, 'tenant', None) if not tenant: return JsonResponse({'error': 'No tenant found'}, status=400) user_id = request.GET.get('user_id') if user_id: sessions = UserSession.objects.filter( user_id=user_id, user__tenant=tenant ).order_by('-created_at')[:10] else: sessions = UserSession.objects.filter( user__tenant=tenant, is_active=True ).order_by('-created_at')[:20] return render(request, 'account/partials/session_list.html', { 'sessions': sessions }) @login_required def two_factor_setup(request): """ HTMX view for two-factor authentication setup. """ user = request.user if request.method == 'POST': device_type = request.POST.get('device_type') device_name = request.POST.get('device_name') if device_type and device_name: # Create new two-factor device device = TwoFactorDevice.objects.create( user=user, name=device_name, device_type=device_type, phone_number=request.POST.get('phone_number'), email_address=request.POST.get('email_address'), ) # Generate secret key for TOTP devices if device_type == 'TOTP': import secrets device.secret_key = secrets.token_urlsafe(32) device.save() # Log the setup AuditLogger.log_event( tenant=getattr(request, 'tenant', None), event_type='CREATE', event_category='SECURITY', action='Setup Two-Factor Device', description=f'User setup two-factor device: {device_name}', user=request.user, content_object=device, request=request ) return JsonResponse({'status': 'created', 'device_id': str(device.device_id)}) return render(request, 'account/partials/two_factor_setup.html') # ============================================================================ # ACTION VIEWS FOR WORKFLOW OPERATIONS # ============================================================================ @login_required def end_session(request, session_id): """ End a user session. """ tenant = getattr(request, 'tenant', None) if not tenant: return JsonResponse({'error': 'No tenant found'}, status=400) session = get_object_or_404( UserSession, session_id=session_id, user__tenant=tenant ) # End the session session.end_session() # Log the action AuditLogger.log_event( tenant=tenant, 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 ) messages.success(request, f'Session for {session.user.username} ended successfully.') return redirect('accounts:session_management') @login_required def user_profile_update(request): """ HTMX view for user profile update. """ if request.method == 'POST': user = request.user # Update basic information user.first_name = request.POST.get('first_name', user.first_name) user.last_name = request.POST.get('last_name', user.last_name) user.email = request.POST.get('email', user.email) user.phone_number = request.POST.get('phone_number', user.phone_number) user.mobile_number = request.POST.get('mobile_number', user.mobile_number) user.bio = request.POST.get('bio', user.bio) # Update preferences user.timezone = request.POST.get('timezone', user.timezone) user.language = request.POST.get('language', user.language) user.theme = request.POST.get('theme', user.theme) user.save() # Log the update AuditLogger.log_event( tenant=getattr(request, 'tenant', None), event_type='UPDATE', event_category='DATA_MODIFICATION', action='Update User Profile', description=f'User updated their profile: {user.username}', user=request.user, content_object=user, request=request ) messages.success(request, 'Profile updated successfully.') return JsonResponse({'status': 'success'}) return JsonResponse({'error': 'Invalid request'}, status=400) @login_required def remove_two_factor_device(request, device_id): """ Remove a two-factor device. """ device = get_object_or_404( TwoFactorDevice, device_id=device_id, user=request.user ) device_name = device.name device.delete() # Log the removal AuditLogger.log_event( tenant=getattr(request, 'tenant', None), event_type='DELETE', event_category='SECURITY', action='Remove Two-Factor Device', description=f'User removed two-factor device: {device_name}', user=request.user, additional_data={'device_name': device_name}, request=request ) messages.success(request, f'Two-factor device "{device_name}" removed successfully.') return redirect('accounts:user_profile') @login_required def user_activity_log(request, user_id): """ HTMX view for user activity log. """ tenant = getattr(request, 'tenant', None) if not tenant: return JsonResponse({'error': 'No tenant found'}, status=400) user_profile = get_object_or_404(User, id=user_id, tenant=tenant) # Get recent audit logs for this user from core.models import AuditLogEntry audit_logs = AuditLogEntry.objects.filter( tenant=tenant, user=user_profile ).order_by('-timestamp')[:20] return render(request, 'account/partials/user_activity_log.html', { 'audit_logs': audit_logs, 'user_profile': user_profile }) @login_required def approve_user(request, pk): """ Approve a user account. """ tenant = getattr(request, 'tenant', None) if not tenant: messages.error(request, 'No tenant found.') return redirect('accounts:user_list') user = get_object_or_404(User, pk=pk, tenant=tenant) user.is_approved = True user.save() # Log approval AuditLogger.log_event( tenant=tenant, event_type='UPDATE', event_category='USER_MANAGEMENT', action='Approve User', description=f'Approved user: {user.username}', user=request.user, content_object=user, request=request ) messages.success(request, f'User "{user.username}" approved successfully.') return redirect('accounts:user_detail', pk=pk) @login_required def activate_user(request, pk): """ Activate a user account. """ tenant = getattr(request, 'tenant', None) if not tenant: messages.error(request, 'No tenant found.') return redirect('accounts:user_list') user = get_object_or_404(User, pk=pk, tenant=tenant) user.is_active = True user.save() # Log activation AuditLogger.log_event( tenant=tenant, event_type='UPDATE', event_category='USER_MANAGEMENT', action='Activate User', description=f'Activated user: {user.username}', user=request.user, content_object=user, request=request ) messages.success(request, f'User "{user.username}" activated successfully.') return redirect('accounts:user_detail', pk=pk) @login_required def reset_user_password(request, pk): """ Reset user password. """ tenant = getattr(request, 'tenant', None) if not tenant: messages.error(request, 'No tenant found.') return redirect('accounts:user_list') user = get_object_or_404(User, pk=pk, tenant=tenant) # Generate temporary password import secrets import string temp_password = ''.join(secrets.choice(string.ascii_letters + string.digits) for _ in range(12)) # Set password and force change on next login user.set_password(temp_password) user.password_change_required = True user.save() # Log password reset AuditLogger.log_event( tenant=tenant, event_type='UPDATE', event_category='SECURITY', action='Reset User Password', description=f'Reset password for user: {user.username}', user=request.user, content_object=user, request=request ) messages.success(request, f'Password reset for "{user.username}". Temporary password: {temp_password}') return redirect('accounts:user_detail', pk=pk) # """ # Accounts app views for hospital management system with comprehensive CRUD operations. # """ # # from django.shortcuts import render, get_object_or_404, redirect # from django.contrib.auth import authenticate, login, logout # from django.contrib.auth.decorators import login_required # from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin # from django.views.generic import ( # TemplateView, ListView, DetailView, CreateView, UpdateView, DeleteView # ) # from django.http import JsonResponse # from django.contrib import messages # from django.db.models import Q, Count # from django.utils import timezone # from django.urls import reverse_lazy, reverse # from django.core.paginator import Paginator # from datetime import timedelta # from .models import * # from .forms import * # from core.utils import AuditLogger # # # ============================================================================ # # USER VIEWS (FULL CRUD - Master Data) # # ============================================================================ # # from core.utils import AuditLogger # # # # ============================================================================ # # USER VIEWS (FULL CRUD - Master Data) # # ============================================================================ # # class UserListView(LoginRequiredMixin, ListView): # """ # User listing view. # """ # model = User # template_name = 'accounts/user_list.html' # context_object_name = 'users' # paginate_by = 25 # # def get_queryset(self): # tenant = getattr(self.request, 'tenant', None) # if not tenant: # return User.objects.none() # # queryset = User.objects.filter(tenant=tenant) # # # Apply filters # role = self.request.GET.get('role') # if role: # queryset = queryset.filter(role=role) # # department = self.request.GET.get('department') # if department: # queryset = queryset.filter(department=department) # # status = self.request.GET.get('status') # if status == 'active': # queryset = queryset.filter(is_active=True) # elif status == 'inactive': # queryset = queryset.filter(is_active=False) # elif status == 'pending': # queryset = queryset.filter(is_approved=False) # # search = self.request.GET.get('search') # if search: # queryset = queryset.filter( # Q(username__icontains=search) | # Q(email__icontains=search) | # Q(first_name__icontains=search) | # Q(last_name__icontains=search) | # Q(employee_id__icontains=search) # ) # # return queryset.order_by('last_name', 'first_name') # # def get_context_data(self, **kwargs): # context = super().get_context_data(**kwargs) # tenant = getattr(self.request, 'tenant', None) # # if tenant: # # Get filter options # context.update({ # 'roles': User.objects.filter(tenant=tenant).values_list('role', flat=True).distinct(), # 'departments': User.objects.filter(tenant=tenant).values_list('department', flat=True).distinct(), # 'search_form': AccountsSearchForm(self.request.GET), # }) # # return context # # # class UserDetailView(LoginRequiredMixin, DetailView): # """ # User detail view. # """ # model = User # template_name = 'accounts/user_detail.html' # context_object_name = 'user_profile' # # def get_queryset(self): # tenant = getattr(self.request, 'tenant', None) # if not tenant: # return User.objects.none() # return User.objects.filter(tenant=tenant) # # def get_context_data(self, **kwargs): # context = super().get_context_data(**kwargs) # user_profile = self.get_object() # # # Get user's sessions # context['active_sessions'] = UserSession.objects.filter( # user=user_profile, # is_active=True # ).order_by('-created_at') # # # Get user's two-factor devices # context['two_factor_devices'] = TwoFactorDevice.objects.filter( # user=user_profile, # is_active=True # ).order_by('-created_at') # # # Get user's social accounts # context['social_accounts'] = SocialAccount.objects.filter( # user=user_profile, # is_active=True # ).order_by('-created_at') # # # Get password history # context['password_history'] = PasswordHistory.objects.filter( # user=user_profile # ).order_by('-created_at')[:5] # # # Get recent audit logs # from core.models import AuditLogEntry # context['recent_activity'] = AuditLogEntry.objects.filter( # tenant=user_profile.tenant, # user=user_profile # ).order_by('-timestamp')[:10] # # return context # # # class UserCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView): # """ # Create new user. # """ # model = User # form_class = UserCreateForm # template_name = 'accounts/user_create.html' # permission_required = 'accounts.add_user' # success_url = reverse_lazy('accounts:user_list') # # def form_valid(self, form): # # Set tenant # form.instance.tenant = getattr(self.request, 'tenant', None) # response = super().form_valid(form) # # # Log user creation # AuditLogger.log_event( # tenant=form.instance.tenant, # event_type='CREATE', # event_category='USER_MANAGEMENT', # action='Create User', # description=f'Created new user: {self.object.username}', # user=self.request.user, # content_object=self.object, # request=self.request # ) # # messages.success(self.request, f'User "{self.object.username}" created successfully.') # return response # # # class UserUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView): # """ # Update user information. # """ # model = User # form_class = UserForm # template_name = 'accounts/user_form.html' # permission_required = 'accounts.change_user' # # def get_queryset(self): # tenant = getattr(self.request, 'tenant', None) # if not tenant: # return User.objects.none() # return User.objects.filter(tenant=tenant) # # def get_success_url(self): # return reverse('accounts:user_detail', kwargs={'pk': self.object.pk}) # # def form_valid(self, form): # response = super().form_valid(form) # # # Log user update # AuditLogger.log_event( # tenant=self.object.tenant, # event_type='UPDATE', # event_category='USER_MANAGEMENT', # action='Update User', # description=f'Updated user: {self.object.username}', # user=self.request.user, # content_object=self.object, # request=self.request # ) # # messages.success(self.request, f'User "{self.object.username}" updated successfully.') # return response # # # class UserDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteView): # """ # Delete user (soft delete to inactive). # """ # model = User # template_name = 'accounts/user_confirm_delete.html' # permission_required = 'accounts.delete_user' # success_url = reverse_lazy('accounts:user_list') # # def get_queryset(self): # tenant = getattr(self.request, 'tenant', None) # if not tenant: # return User.objects.none() # return User.objects.filter(tenant=tenant) # # def delete(self, request, *args, **kwargs): # self.object = self.get_object() # # # Soft delete - set to inactive # self.object.is_active = False # self.object.save() # # # Log user deletion # AuditLogger.log_event( # tenant=self.object.tenant, # event_type='DELETE', # event_category='USER_MANAGEMENT', # action='Deactivate User', # description=f'Deactivated user: {self.object.username}', # user=request.user, # content_object=self.object, # request=request # ) # # messages.success(request, f'User "{self.object.username}" deactivated successfully.') # return redirect(self.success_url) # # # # ============================================================================ # # TWO FACTOR DEVICE VIEWS (FULL CRUD - Security Data) # # ============================================================================ # # class TwoFactorDeviceListView(LoginRequiredMixin, ListView): # """ # List two-factor devices. # """ # model = TwoFactorDevice # template_name = 'accounts/two_factor_device_list.html' # context_object_name = 'devices' # paginate_by = 20 # # def get_queryset(self): # tenant = getattr(self.request, 'tenant', None) # if not tenant: # return TwoFactorDevice.objects.none() # # queryset = TwoFactorDevice.objects.filter(user__tenant=tenant) # # # Apply filters # user_id = self.request.GET.get('user_id') # if user_id: # queryset = queryset.filter(user_id=user_id) # # device_type = self.request.GET.get('device_type') # if device_type: # queryset = queryset.filter(device_type=device_type) # # status = self.request.GET.get('status') # if status == 'active': # queryset = queryset.filter(is_active=True) # elif status == 'inactive': # queryset = queryset.filter(is_active=False) # # return queryset.order_by('-created_at') # # def get_context_data(self, **kwargs): # context = super().get_context_data(**kwargs) # tenant = getattr(self.request, 'tenant', None) # # if tenant: # context.update({ # 'device_types': TwoFactorDevice.DEVICE_TYPE_CHOICES, # 'users': User.objects.filter(tenant=tenant, is_active=True).order_by('last_name', 'first_name'), # }) # # return context # # # class TwoFactorDeviceDetailView(LoginRequiredMixin, DetailView): # """ # Display two-factor device details. # """ # model = TwoFactorDevice # template_name = 'accounts/two_factor_device_detail.html' # context_object_name = 'device' # # def get_queryset(self): # tenant = getattr(self.request, 'tenant', None) # if not tenant: # return TwoFactorDevice.objects.none() # return TwoFactorDevice.objects.filter(user__tenant=tenant) # # # class TwoFactorDeviceCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView): # """ # Create new two-factor device. # """ # model = TwoFactorDevice # form_class = TwoFactorDeviceForm # template_name = 'accounts/two_factor_device_form.html' # permission_required = 'accounts.add_twofactordevice' # success_url = reverse_lazy('accounts:two_factor_device_list') # # def get_form_kwargs(self): # kwargs = super().get_form_kwargs() # kwargs['user'] = self.request.user # return kwargs # # def form_valid(self, form): # response = super().form_valid(form) # # # Generate secret key for TOTP devices # if form.instance.device_type == 'TOTP': # import secrets # form.instance.secret_key = secrets.token_urlsafe(32) # form.instance.save() # # # 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: {self.object.name}', # user=self.request.user, # content_object=self.object, # request=self.request # ) # # messages.success(self.request, f'Two-factor device "{self.object.name}" created successfully.') # return response # # # class TwoFactorDeviceUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView): # """ # Update two-factor device. # """ # model = TwoFactorDevice # form_class = TwoFactorDeviceForm # template_name = 'accounts/two_factor_device_form.html' # permission_required = 'accounts.change_twofactordevice' # # def get_queryset(self): # tenant = getattr(self.request, 'tenant', None) # if not tenant: # return TwoFactorDevice.objects.none() # return TwoFactorDevice.objects.filter(user__tenant=tenant) # # def get_form_kwargs(self): # kwargs = super().get_form_kwargs() # kwargs['user'] = self.request.user # return kwargs # # def get_success_url(self): # return reverse('accounts:two_factor_device_detail', kwargs={'pk': self.object.pk}) # # def form_valid(self, form): # response = super().form_valid(form) # # # Log device update # AuditLogger.log_event( # tenant=getattr(self.request, 'tenant', None), # event_type='UPDATE', # event_category='SECURITY', # action='Update Two-Factor Device', # description=f'Updated two-factor device: {self.object.name}', # user=self.request.user, # content_object=self.object, # request=self.request # ) # # messages.success(self.request, f'Two-factor device "{self.object.name}" updated successfully.') # return response # # # class TwoFactorDeviceDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteView): # """ # Delete two-factor device. # """ # model = TwoFactorDevice # template_name = 'accounts/two_factor_device_confirm_delete.html' # permission_required = 'accounts.delete_twofactordevice' # success_url = reverse_lazy('accounts:two_factor_device_list') # # def get_queryset(self): # tenant = getattr(self.request, 'tenant', None) # if not tenant: # return TwoFactorDevice.objects.none() # return TwoFactorDevice.objects.filter(user__tenant=tenant) # # def delete(self, request, *args, **kwargs): # self.object = self.get_object() # device_name = self.object.name # # # Log device deletion # AuditLogger.log_event( # tenant=getattr(request, 'tenant', None), # event_type='DELETE', # event_category='SECURITY', # action='Delete Two-Factor Device', # description=f'Deleted two-factor device: {device_name}', # user=request.user, # content_object=self.object, # request=request # ) # # messages.success(request, f'Two-factor device "{device_name}" deleted successfully.') # return super().delete(request, *args, **kwargs) # # # # ============================================================================ # # SOCIAL ACCOUNT VIEWS (FULL CRUD - Integration Data) # # ============================================================================ # # class SocialAccountListView(LoginRequiredMixin, ListView): # """ # List social accounts. # """ # model = SocialAccount # template_name = 'accounts/social_account_list.html' # context_object_name = 'social_accounts' # paginate_by = 20 # # def get_queryset(self): # tenant = getattr(self.request, 'tenant', None) # if not tenant: # return SocialAccount.objects.none() # # queryset = SocialAccount.objects.filter(user__tenant=tenant) # # # Apply filters # user_id = self.request.GET.get('user_id') # if user_id: # queryset = queryset.filter(user_id=user_id) # # provider = self.request.GET.get('provider') # if provider: # queryset = queryset.filter(provider=provider) # # status = self.request.GET.get('status') # if status == 'active': # queryset = queryset.filter(is_active=True) # elif status == 'inactive': # queryset = queryset.filter(is_active=False) # # return queryset.order_by('-created_at') # # def get_context_data(self, **kwargs): # context = super().get_context_data(**kwargs) # tenant = getattr(self.request, 'tenant', None) # # if tenant: # context.update({ # 'providers': SocialAccount.objects.filter( # user__tenant=tenant # ).values_list('provider', flat=True).distinct(), # 'users': User.objects.filter(tenant=tenant, is_active=True).order_by('last_name', 'first_name'), # }) # # return context # # # class SocialAccountDetailView(LoginRequiredMixin, DetailView): # """ # Display social account details. # """ # model = SocialAccount # template_name = 'accounts/social_account_detail.html' # context_object_name = 'social_account' # # def get_queryset(self): # tenant = getattr(self.request, 'tenant', None) # if not tenant: # return SocialAccount.objects.none() # return SocialAccount.objects.filter(user__tenant=tenant) # # # class SocialAccountCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView): # """ # Create new social account. # """ # model = SocialAccount # form_class = SocialAccountForm # template_name = 'accounts/social_account_form.html' # permission_required = 'accounts.add_socialaccount' # success_url = reverse_lazy('accounts:social_account_list') # # def get_form_kwargs(self): # kwargs = super().get_form_kwargs() # kwargs['user'] = self.request.user # return kwargs # # def form_valid(self, form): # response = super().form_valid(form) # # # Log social account creation # AuditLogger.log_event( # tenant=getattr(self.request, 'tenant', None), # event_type='CREATE', # event_category='INTEGRATION', # action='Create Social Account', # description=f'Created social account: {self.object.provider}', # user=self.request.user, # content_object=self.object, # request=self.request # ) # # messages.success(self.request, f'Social account "{self.object.provider}" created successfully.') # return response # # # class SocialAccountUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView): # """ # Update social account. # """ # model = SocialAccount # form_class = SocialAccountForm # template_name = 'accounts/social_account_form.html' # permission_required = 'accounts.change_socialaccount' # # def get_queryset(self): # tenant = getattr(self.request, 'tenant', None) # if not tenant: # return SocialAccount.objects.none() # return SocialAccount.objects.filter(user__tenant=tenant) # # def get_form_kwargs(self): # kwargs = super().get_form_kwargs() # kwargs['user'] = self.request.user # return kwargs # # def get_success_url(self): # return reverse('accounts:social_account_detail', kwargs={'pk': self.object.pk}) # # def form_valid(self, form): # response = super().form_valid(form) # # # Log social account update # AuditLogger.log_event( # tenant=getattr(self.request, 'tenant', None), # event_type='UPDATE', # event_category='INTEGRATION', # action='Update Social Account', # description=f'Updated social account: {self.object.provider}', # user=self.request.user, # content_object=self.object, # request=self.request # ) # # messages.success(self.request, f'Social account "{self.object.provider}" updated successfully.') # return response # # # class SocialAccountDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteView): # """ # Delete social account. # """ # model = SocialAccount # template_name = 'accounts/social_account_confirm_delete.html' # permission_required = 'accounts.delete_socialaccount' # success_url = reverse_lazy('accounts:social_account_list') # # def get_queryset(self): # tenant = getattr(self.request, 'tenant', None) # if not tenant: # return SocialAccount.objects.none() # return SocialAccount.objects.filter(user__tenant=tenant) # # def delete(self, request, *args, **kwargs): # self.object = self.get_object() # provider = self.object.provider # # # Log social account deletion # AuditLogger.log_event( # tenant=getattr(request, 'tenant', None), # event_type='DELETE', # event_category='INTEGRATION', # action='Delete Social Account', # description=f'Deleted social account: {provider}', # user=request.user, # content_object=self.object, # request=request # ) # # messages.success(request, f'Social account "{provider}" deleted successfully.') # return super().delete(request, *args, **kwargs) # # # # ============================================================================ # # USER SESSION VIEWS (READ-ONLY - System Generated) # # ============================================================================ # # class UserSessionListView(LoginRequiredMixin, ListView): # """ # List user sessions. # """ # model = UserSession # template_name = 'accounts/user_session_list.html' # context_object_name = 'sessions' # paginate_by = 50 # # def get_queryset(self): # tenant = getattr(self.request, 'tenant', None) # if not tenant: # return UserSession.objects.none() # # queryset = UserSession.objects.filter(user__tenant=tenant) # # # Apply filters # user_id = self.request.GET.get('user_id') # if user_id: # queryset = queryset.filter(user_id=user_id) # # status = self.request.GET.get('status') # if status == 'active': # queryset = queryset.filter(is_active=True) # elif status == 'expired': # queryset = queryset.filter(expires_at__lt=timezone.now()) # # device_type = self.request.GET.get('device_type') # if device_type: # queryset = queryset.filter(device_type=device_type) # # date_from = self.request.GET.get('date_from') # if date_from: # queryset = queryset.filter(created_at__gte=date_from) # # date_to = self.request.GET.get('date_to') # if date_to: # queryset = queryset.filter(created_at__lte=date_to) # # return queryset.order_by('-created_at') # # def get_context_data(self, **kwargs): # context = super().get_context_data(**kwargs) # tenant = getattr(self.request, 'tenant', None) # # if tenant: # context.update({ # 'device_types': UserSession.objects.filter( # user__tenant=tenant # ).values_list('device_type', flat=True).distinct(), # 'users': User.objects.filter(tenant=tenant, is_active=True).order_by('last_name', 'first_name'), # }) # # return context # # # class UserSessionDetailView(LoginRequiredMixin, DetailView): # """ # Display user session details. # """ # model = UserSession # template_name = 'accounts/user_session_detail.html' # context_object_name = 'session' # # def get_queryset(self): # tenant = getattr(self.request, 'tenant', None) # if not tenant: # return UserSession.objects.none() # return UserSession.objects.filter(user__tenant=tenant) # # # # ============================================================================ # # PASSWORD HISTORY VIEWS (READ-ONLY - System Generated) # # ============================================================================ # # class PasswordHistoryListView(LoginRequiredMixin, ListView): # """ # List password history. # """ # model = PasswordHistory # template_name = 'accounts/password_history_list.html' # context_object_name = 'password_history' # paginate_by = 50 # # def get_queryset(self): # tenant = getattr(self.request, 'tenant', None) # if not tenant: # return PasswordHistory.objects.none() # # queryset = PasswordHistory.objects.filter(user__tenant=tenant) # # # Apply filters # user_id = self.request.GET.get('user_id') # if user_id: # queryset = queryset.filter(user_id=user_id) # # date_from = self.request.GET.get('date_from') # if date_from: # queryset = queryset.filter(created_at__gte=date_from) # # date_to = self.request.GET.get('date_to') # if date_to: # queryset = queryset.filter(created_at__lte=date_to) # # return queryset.order_by('-created_at') # # def get_context_data(self, **kwargs): # context = super().get_context_data(**kwargs) # tenant = getattr(self.request, 'tenant', None) # # if tenant: # context.update({ # 'users': User.objects.filter(tenant=tenant, is_active=True).order_by('last_name', 'first_name'), # }) # # return context # # # class PasswordHistoryDetailView(LoginRequiredMixin, DetailView): # """ # Display password history details. # """ # model = PasswordHistory # template_name = 'accounts/password_history_detail.html' # context_object_name = 'password_history' # # def get_queryset(self): # tenant = getattr(self.request, 'tenant', None) # if not tenant: # return PasswordHistory.objects.none() # return PasswordHistory.objects.filter(user__tenant=tenant) # # # # ============================================================================ # # PROFILE AND SESSION MANAGEMENT VIEWS # # ============================================================================ # # class UserProfileView(LoginRequiredMixin, TemplateView): # """ # User profile management view. # """ # template_name = 'accounts/user_profile.html' # # def get_context_data(self, **kwargs): # context = super().get_context_data(**kwargs) # user = self.request.user # # # Get user's sessions # context['active_sessions'] = UserSession.objects.filter( # user=user, # is_active=True # ).order_by('-created_at') # # # Get user's two-factor devices # context['two_factor_devices'] = TwoFactorDevice.objects.filter( # user=user, # is_active=True # ).order_by('-created_at') # # # Get user's social accounts # context['social_accounts'] = SocialAccount.objects.filter( # user=user, # is_active=True # ).order_by('-created_at') # # # Get password history # context['password_history'] = PasswordHistory.objects.filter( # user=user # ).order_by('-created_at')[:5] # # return context # # # class SessionManagementView(LoginRequiredMixin, ListView): # """ # Session management view. # """ # model = UserSession # template_name = 'accounts/session_management.html' # context_object_name = 'sessions' # paginate_by = 50 # # def get_queryset(self): # tenant = getattr(self.request, 'tenant', None) # if not tenant: # return UserSession.objects.none() # # queryset = UserSession.objects.filter(user__tenant=tenant) # # # Apply filters # user_id = self.request.GET.get('user_id') # if user_id: # queryset = queryset.filter(user_id=user_id) # # status = self.request.GET.get('status') # if status == 'active': # queryset = queryset.filter(is_active=True) # elif status == 'expired': # queryset = queryset.filter(expires_at__lt=timezone.now()) # # device_type = self.request.GET.get('device_type') # if device_type: # queryset = queryset.filter(device_type=device_type) # # return queryset.order_by('-created_at') # # # # ============================================================================ # # HTMX VIEWS FOR REAL-TIME UPDATES # # ============================================================================ # # @login_required # def user_search(request): # """ # HTMX view for user search. # """ # tenant = getattr(request, 'tenant', None) # if not tenant: # return JsonResponse({'error': 'No tenant found'}, status=400) # # search_query = request.GET.get('search', '') # queryset = User.objects.filter(tenant=tenant) # # if search_query: # queryset = queryset.filter( # Q(username__icontains=search_query) | # Q(email__icontains=search_query) | # Q(first_name__icontains=search_query) | # Q(last_name__icontains=search_query) | # Q(employee_id__icontains=search_query) # ) # # users = queryset.order_by('last_name', 'first_name')[:20] # # return render(request, 'accounts/partials/user_list.html', { # 'users': users # }) # # # @login_required # def user_stats(request): # """ # HTMX view for user statistics. # """ # tenant = getattr(request, 'tenant', None) # if not tenant: # return JsonResponse({'error': 'No tenant found'}, status=400) # # # Calculate user statistics # total_users = User.objects.filter(tenant=tenant).count() # active_users = User.objects.filter(tenant=tenant, is_active=True).count() # pending_approval = User.objects.filter(tenant=tenant, is_approved=False).count() # # # Active sessions in last 24 hours # yesterday = timezone.now() - timedelta(days=1) # active_sessions = UserSession.objects.filter( # user__tenant=tenant, # last_activity_at__gte=yesterday, # is_active=True # ).count() # # # Users by role # role_stats = User.objects.filter(tenant=tenant).values('role').annotate( # count=Count('id') # ).order_by('-count')[:5] # # # Two-factor adoption # two_factor_users = User.objects.filter( # tenant=tenant, # twofactordevice__is_active=True # ).distinct().count() # # stats = { # 'total_users': total_users, # 'active_users': active_users, # 'pending_approval': pending_approval, # 'active_sessions': active_sessions, # 'two_factor_users': two_factor_users, # 'role_stats': role_stats, # } # # return render(request, 'accounts/partials/user_stats.html', {'stats': stats}) # # # @login_required # def session_list(request): # """ # HTMX view for session list. # """ # tenant = getattr(request, 'tenant', None) # if not tenant: # return JsonResponse({'error': 'No tenant found'}, status=400) # # user_id = request.GET.get('user_id') # if user_id: # sessions = UserSession.objects.filter( # user_id=user_id, # user__tenant=tenant # ).order_by('-created_at')[:10] # else: # sessions = UserSession.objects.filter( # user__tenant=tenant, # is_active=True # ).order_by('-created_at')[:20] # # return render(request, 'accounts/partials/session_list.html', { # 'sessions': sessions # }) # # # @login_required # def two_factor_setup(request): # """ # HTMX view for two-factor authentication setup. # """ # user = request.user # # if request.method == 'POST': # device_type = request.POST.get('device_type') # device_name = request.POST.get('device_name') # # if device_type and device_name: # # Create new two-factor device # device = TwoFactorDevice.objects.create( # user=user, # name=device_name, # device_type=device_type, # phone_number=request.POST.get('phone_number'), # email_address=request.POST.get('email_address'), # ) # # # Generate secret key for TOTP devices # if device_type == 'TOTP': # import secrets # device.secret_key = secrets.token_urlsafe(32) # device.save() # # # Log the setup # AuditLogger.log_event( # tenant=getattr(request, 'tenant', None), # event_type='CREATE', # event_category='SECURITY', # action='Setup Two-Factor Device', # description=f'User setup two-factor device: {device_name}', # user=request.user, # content_object=device, # request=request # ) # # return JsonResponse({'status': 'created', 'device_id': str(device.device_id)}) # # return render(request, 'accounts/partials/two_factor_setup.html') # # # # ============================================================================ # # ACTION VIEWS FOR WORKFLOW OPERATIONS # # ============================================================================ # # @login_required # def end_session(request, session_id): # """ # End a user session. # """ # tenant = getattr(request, 'tenant', None) # if not tenant: # return JsonResponse({'error': 'No tenant found'}, status=400) # # session = get_object_or_404( # UserSession, # session_id=session_id, # user__tenant=tenant # ) # # # End the session # session.end_session() # # # Log the action # AuditLogger.log_event( # tenant=tenant, # 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 # ) # # messages.success(request, f'Session for {session.user.username} ended successfully.') # return redirect('accounts:session_management') # # # @login_required # def user_profile_update(request): # """ # HTMX view for user profile update. # """ # if request.method == 'POST': # user = request.user # # # Update basic information # user.first_name = request.POST.get('first_name', user.first_name) # user.last_name = request.POST.get('last_name', user.last_name) # user.email = request.POST.get('email', user.email) # user.phone_number = request.POST.get('phone_number', user.phone_number) # user.mobile_number = request.POST.get('mobile_number', user.mobile_number) # user.bio = request.POST.get('bio', user.bio) # # # Update preferences # user.timezone = request.POST.get('timezone', user.timezone) # user.language = request.POST.get('language', user.language) # user.theme = request.POST.get('theme', user.theme) # # user.save() # # # Log the update # AuditLogger.log_event( # tenant=getattr(request, 'tenant', None), # event_type='UPDATE', # event_category='DATA_MODIFICATION', # action='Update User Profile', # description=f'User updated their profile: {user.username}', # user=request.user, # content_object=user, # request=request # ) # # messages.success(request, 'Profile updated successfully.') # return JsonResponse({'status': 'success'}) # # return JsonResponse({'error': 'Invalid request'}, status=400) # # # @login_required # def remove_two_factor_device(request, device_id): # """ # Remove a two-factor device. # """ # device = get_object_or_404( # TwoFactorDevice, # device_id=device_id, # user=request.user # ) # # device_name = device.name # device.delete() # # # Log the removal # AuditLogger.log_event( # tenant=getattr(request, 'tenant', None), # event_type='DELETE', # event_category='SECURITY', # action='Remove Two-Factor Device', # description=f'User removed two-factor device: {device_name}', # user=request.user, # additional_data={'device_name': device_name}, # request=request # ) # # messages.success(request, f'Two-factor device "{device_name}" removed successfully.') # return redirect('accounts:user_profile') # # # @login_required # def user_activity_log(request, user_id): # """ # HTMX view for user activity log. # """ # tenant = getattr(request, 'tenant', None) # if not tenant: # return JsonResponse({'error': 'No tenant found'}, status=400) # # user_profile = get_object_or_404(User, id=user_id, tenant=tenant) # # # Get recent audit logs for this user # from core.models import AuditLogEntry # audit_logs = AuditLogEntry.objects.filter( # tenant=tenant, # user=user_profile # ).order_by('-timestamp')[:20] # # return render(request, 'accounts/partials/user_activity_log.html', { # 'audit_logs': audit_logs, # 'user_profile': user_profile # }) # # # @login_required # def approve_user(request, pk): # """ # Approve a user account. # """ # tenant = getattr(request, 'tenant', None) # if not tenant: # messages.error(request, 'No tenant found.') # return redirect('accounts:user_list') # # user = get_object_or_404(User, pk=pk, tenant=tenant) # # user.is_approved = True # user.save() # # # Log approval # AuditLogger.log_event( # tenant=tenant, # event_type='UPDATE', # event_category='USER_MANAGEMENT', # action='Approve User', # description=f'Approved user: {user.username}', # user=request.user, # content_object=user, # request=request # ) # # messages.success(request, f'User "{user.username}" approved successfully.') # return redirect('accounts:user_detail', pk=pk) # # # @login_required # def activate_user(request, pk): # """ # Activate a user account. # """ # tenant = getattr(request, 'tenant', None) # if not tenant: # messages.error(request, 'No tenant found.') # return redirect('accounts:user_list') # # user = get_object_or_404(User, pk=pk, tenant=tenant) # # user.is_active = True # user.save() # # # Log activation # AuditLogger.log_event( # tenant=tenant, # event_type='UPDATE', # event_category='USER_MANAGEMENT', # action='Activate User', # description=f'Activated user: {user.username}', # user=request.user, # content_object=user, # request=request # ) # # messages.success(request, f'User "{user.username}" activated successfully.') # return redirect('accounts:user_detail', pk=pk) # # # @login_required # def reset_user_password(request, pk): # """ # Reset user password. # """ # tenant = getattr(request, 'tenant', None) # if not tenant: # messages.error(request, 'No tenant found.') # return redirect('accounts:user_list') # # user = get_object_or_404(User, pk=pk, tenant=tenant) # # # Generate temporary password # import secrets # import string # temp_password = ''.join(secrets.choice(string.ascii_letters + string.digits) for _ in range(12)) # # # Set password and force change on next login # user.set_password(temp_password) # user.password_change_required = True # user.save() # # # Log password reset # AuditLogger.log_event( # tenant=tenant, # event_type='UPDATE', # event_category='SECURITY', # action='Reset User Password', # description=f'Reset password for user: {user.username}', # user=request.user, # content_object=user, # request=request # ) # # messages.success(request, f'Password reset for "{user.username}". Temporary password: {temp_password}') # return redirect('accounts:user_detail', pk=pk) # # # # #