""" Accounts app views for hospital management system with comprehensive CRUD operations. """ import os 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 hr.models import Employee 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) # ============================================================================ 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': employee = Employee.objects.get(user=request.user) # Update basic information employee.first_name = request.POST.get('first_name', employee.first_name) employee.last_name = request.POST.get('last_name', employee.last_name) employee.email = request.POST.get('email', employee.email) employee.phone = request.POST.get('phone', employee.phone) employee.mobile_phone = request.POST.get('mobile_phone', employee.mobile_phone) employee.bio = request.POST.get('bio', employee.bio) # Update preferences employee.user_timezone = request.POST.get('timezone', employee.user_timezone) employee.language = request.POST.get('language', employee.language) employee.theme = request.POST.get('theme', employee.theme) employee.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: {employee.user.username}', user=request.user, content_object=employee, request=request ) messages.success(request, 'Profile updated successfully.') return redirect('accounts:user_profile') 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) def upload_avatar(request, pk): 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) if request.method == 'POST': if 'profile_picture' in request.FILES: # Delete old profile picture if it exists if user.profile_picture: old_picture_path = user.profile_picture.path if os.path.exists(old_picture_path): os.remove(old_picture_path) # Save new profile picture user.profile_picture = request.FILES['profile_picture'] user.save() # Log the upload AuditLogger.log_event( tenant=tenant, event_type='UPDATE', event_category='USER_MANAGEMENT', action='Upload Profile Picture', description=f'Uploaded profile picture for user: {user.username}', user=request.user, content_object=user, request=request ) messages.success(request, f'Profile picture updated successfully for "{user.username}".') if request.headers.get('HX-Request'): # Return HTMX response with updated image return render(request, 'account/partials/profile_picture.html', { 'user_profile': user }) return redirect('accounts:user_detail', pk=pk) else: messages.error(request, 'No image file was uploaded.') # For GET requests, return the upload form if request.headers.get('HX-Request'): return render(request, 'account/partials/upload_avatar_form.html', { 'user_profile': user }) 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) # # # # # # from django.shortcuts import render, redirect, get_object_or_404 # from django.contrib.auth import login, logout, authenticate # from django.contrib.auth.decorators import login_required, permission_required # from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin # from django.contrib import messages # from django.views.generic import ( # CreateView, UpdateView, DeleteView, DetailView, ListView, FormView # ) # from django.urls import reverse_lazy, reverse # from django.http import JsonResponse, HttpResponse # from django.utils import timezone # from django.db import transaction, models # from django.core.mail import send_mail # from django.conf import settings # from django.contrib.auth.models import Group # from viewflow.workflow.flow.views import CreateProcessView, UpdateProcessView # # from .models import User, TwoFactorDevice, SocialAccount, UserSession, PasswordHistory # from .forms import ( # UserRegistrationForm, AccountActivationForm, TwoFactorSetupForm, # PermissionManagementForm, SecurityAuditForm, SessionManagementForm, # ComplianceCheckForm, AccountDeactivationForm, PasswordResetForm, # PasswordChangeForm # ) # from .flows import UserOnboardingFlow, SecurityManagementFlow, AccountDeactivationFlow # # # class UserRegistrationView(LoginRequiredMixin, PermissionRequiredMixin, CreateProcessView): # """ # View for user registration in onboarding workflow # """ # model = User # form_class = UserRegistrationForm # template_name = 'accounts/user_registration.html' # permission_required = 'accounts.can_register_users' # flow_class = UserOnboardingFlow # # def get_form_kwargs(self): # kwargs = super().get_form_kwargs() # kwargs['tenant'] = self.request.user.tenant # return kwargs # # def form_valid(self, form): # with transaction.atomic(): # # Create user # user = form.save(commit=False) # user.tenant = self.request.user.tenant # user.is_active = False # Will be activated in workflow # user.created_by = self.request.user # user.save() # # # Start onboarding workflow # process = self.flow_class.start.run( # user=user, # created_by=self.request.user # ) # # messages.success( # self.request, # f'User registration initiated for {user.get_full_name()}. ' # f'Onboarding workflow started.' # ) # # return redirect('accounts:user_detail', pk=user.pk) # # def get_context_data(self, **kwargs): # context = super().get_context_data(**kwargs) # context['title'] = 'Register New User' # context['breadcrumbs'] = [ # {'name': 'Home', 'url': reverse('core:dashboard')}, # {'name': 'Users', 'url': reverse('accounts:user_list')}, # {'name': 'Register User', 'url': ''} # ] # return context # # # class AccountActivationView(LoginRequiredMixin, PermissionRequiredMixin, FormView): # """ # View for account activation in onboarding workflow # """ # form_class = AccountActivationForm # template_name = 'accounts/account_activation.html' # permission_required = 'accounts.can_activate_accounts' # # def get_success_url(self): # return reverse('accounts:user_detail', kwargs={'pk': self.kwargs['pk']}) # # def form_valid(self, form): # user = get_object_or_404(User, pk=self.kwargs['pk']) # activation_code = form.cleaned_data['activation_code'] # # # Verify activation code (implement your verification logic) # if self.verify_activation_code(user, activation_code): # user.is_active = True # user.email_verified = True # user.activated_at = timezone.now() # user.save() # # # Send welcome email # self.send_welcome_email(user) # # messages.success( # self.request, # f'Account activated successfully for {user.get_full_name()}.' # ) # else: # messages.error(self.request, 'Invalid activation code.') # return self.form_invalid(form) # # return super().form_valid(form) # # def verify_activation_code(self, user, code): # """Verify activation code (implement your logic)""" # # This would implement actual verification logic # return True # # def send_welcome_email(self, user): # """Send welcome email to activated user""" # if user.email: # send_mail( # subject='Welcome to Hospital Management System', # message=f'Welcome {user.get_full_name()}! Your account has been activated.', # from_email=settings.DEFAULT_FROM_EMAIL, # recipient_list=[user.email], # fail_silently=True # ) # # def get_context_data(self, **kwargs): # context = super().get_context_data(**kwargs) # context['user'] = get_object_or_404(User, pk=self.kwargs['pk']) # context['title'] = 'Activate Account' # return context # # # class TwoFactorSetupView(LoginRequiredMixin, PermissionRequiredMixin, CreateView): # """ # View for two-factor authentication setup # """ # model = TwoFactorDevice # form_class = TwoFactorSetupForm # template_name = 'accounts/two_factor_setup.html' # permission_required = 'accounts.can_setup_two_factor' # # def get_success_url(self): # return reverse('accounts:security_settings') # # def form_valid(self, form): # device = form.save(commit=False) # device.user = self.request.user # device.is_active = True # device.created_at = timezone.now() # # # Generate device-specific configuration # device.configuration = self.generate_device_config(device.device_type) # device.save() # # messages.success( # self.request, # 'Two-factor authentication has been set up successfully.' # ) # # return super().form_valid(form) # # def generate_device_config(self, device_type): # """Generate device-specific configuration""" # if device_type == 'totp': # # Generate TOTP secret # import pyotp # return {'secret': pyotp.random_base32()} # elif device_type == 'backup_codes': # # Generate backup codes # import secrets # codes = [secrets.token_hex(4) for _ in range(10)] # return {'codes': codes} # return {} # # def get_context_data(self, **kwargs): # context = super().get_context_data(**kwargs) # context['title'] = 'Setup Two-Factor Authentication' # return context # # # class PermissionManagementView(LoginRequiredMixin, PermissionRequiredMixin, FormView): # """ # View for managing user permissions # """ # form_class = PermissionManagementForm # template_name = 'accounts/permission_management.html' # permission_required = 'accounts.can_manage_permissions' # # def get_success_url(self): # return reverse('accounts:user_detail', kwargs={'pk': self.kwargs['pk']}) # # def get_form_kwargs(self): # kwargs = super().get_form_kwargs() # kwargs['tenant'] = self.request.user.tenant # return kwargs # # def get_initial(self): # user = get_object_or_404(User, pk=self.kwargs['pk']) # return { # 'groups': user.groups.all(), # 'is_staff': user.is_staff, # 'is_superuser': user.is_superuser, # 'access_level': getattr(user, 'access_level', 'basic'), # 'department_access': getattr(user, 'department_access', []) # } # # def form_valid(self, form): # user = get_object_or_404(User, pk=self.kwargs['pk']) # # with transaction.atomic(): # # Update user permissions # user.groups.set(form.cleaned_data['groups']) # user.is_staff = form.cleaned_data['is_staff'] # user.is_superuser = form.cleaned_data['is_superuser'] # user.access_level = form.cleaned_data['access_level'] # user.save() # # # Log permission changes # self.log_permission_changes(user, form.cleaned_data) # # messages.success( # self.request, # f'Permissions updated successfully for {user.get_full_name()}.' # ) # # return super().form_valid(form) # # def log_permission_changes(self, user, changes): # """Log permission changes for audit""" # from core.models import AuditLogEntry # AuditLogEntry.objects.create( # tenant=user.tenant, # user=self.request.user, # event_type='PERMISSION_CHANGE', # action='UPDATE', # object_type='User', # object_id=str(user.id), # details=changes, # ip_address=self.request.META.get('REMOTE_ADDR'), # user_agent=self.request.META.get('HTTP_USER_AGENT', '') # ) # # def get_context_data(self, **kwargs): # context = super().get_context_data(**kwargs) # context['user'] = get_object_or_404(User, pk=self.kwargs['pk']) # context['title'] = 'Manage Permissions' # return context # # # class SecurityAuditView(LoginRequiredMixin, PermissionRequiredMixin, FormView): # """ # View for security audit configuration # """ # form_class = SecurityAuditForm # template_name = 'accounts/security_audit.html' # permission_required = 'accounts.can_audit_security' # # def get_success_url(self): # return reverse('accounts:security_dashboard') # # def form_valid(self, form): # audit_config = form.cleaned_data # # # Start security audit workflow # process = SecurityManagementFlow.start.run( # user=self.request.user, # audit_config=audit_config, # created_by=self.request.user # ) # # messages.success( # self.request, # 'Security audit has been initiated. You will receive a notification when complete.' # ) # # return super().form_valid(form) # # def get_context_data(self, **kwargs): # context = super().get_context_data(**kwargs) # context['title'] = 'Security Audit' # context['recent_audits'] = self.get_recent_audits() # return context # # def get_recent_audits(self): # """Get recent security audits""" # # This would return recent audit records # return [] # # # class SessionManagementView(LoginRequiredMixin, PermissionRequiredMixin, FormView): # """ # View for session management configuration # """ # form_class = SessionManagementForm # template_name = 'accounts/session_management.html' # permission_required = 'accounts.can_manage_sessions' # # def get_success_url(self): # return reverse('accounts:security_settings') # # def get_initial(self): # # Get current session settings # return { # 'session_timeout': getattr(settings, 'SESSION_COOKIE_AGE', 1800) // 60, # 'max_concurrent_sessions': 3, # 'require_secure_cookies': getattr(settings, 'SESSION_COOKIE_SECURE', True), # 'enable_session_monitoring': True, # 'log_session_events': True, # 'auto_logout_inactive': True # } # # def form_valid(self, form): # config = form.cleaned_data # # # Update session configuration # self.update_session_config(config) # # messages.success( # self.request, # 'Session management settings updated successfully.' # ) # # return super().form_valid(form) # # def update_session_config(self, config): # """Update session configuration""" # # This would update session configuration # pass # # def get_context_data(self, **kwargs): # context = super().get_context_data(**kwargs) # context['title'] = 'Session Management' # context['active_sessions'] = self.get_active_sessions() # return context # # def get_active_sessions(self): # """Get active user sessions""" # return UserSession.objects.filter( # user=self.request.user, # is_active=True # ).order_by('-created_at') # # # class ComplianceCheckView(LoginRequiredMixin, PermissionRequiredMixin, FormView): # """ # View for compliance verification # """ # form_class = ComplianceCheckForm # template_name = 'accounts/compliance_check.html' # permission_required = 'accounts.can_verify_compliance' # # def get_success_url(self): # return reverse('accounts:compliance_dashboard') # # def form_valid(self, form): # compliance_config = form.cleaned_data # # # Start compliance check # self.start_compliance_check(compliance_config) # # messages.success( # self.request, # 'Compliance check has been initiated. Results will be available shortly.' # ) # # return super().form_valid(form) # # def start_compliance_check(self, config): # """Start compliance verification process""" # # This would start compliance checking # pass # # def get_context_data(self, **kwargs): # context = super().get_context_data(**kwargs) # context['title'] = 'Compliance Check' # context['compliance_status'] = self.get_compliance_status() # return context # # def get_compliance_status(self): # """Get current compliance status""" # return { # 'hipaa': 'compliant', # 'gdpr': 'compliant', # 'sox': 'pending', # 'pci_dss': 'non_compliant', # 'iso27001': 'compliant' # } # # # class AccountDeactivationView(LoginRequiredMixin, PermissionRequiredMixin, FormView): # """ # View for account deactivation # """ # form_class = AccountDeactivationForm # template_name = 'accounts/account_deactivation.html' # permission_required = 'accounts.can_deactivate_accounts' # # def get_success_url(self): # return reverse('accounts:user_list') # # def get_form_kwargs(self): # kwargs = super().get_form_kwargs() # kwargs['tenant'] = self.request.user.tenant # return kwargs # # def form_valid(self, form): # user = get_object_or_404(User, pk=self.kwargs['pk']) # deactivation_config = form.cleaned_data # # # Start account deactivation workflow # process = AccountDeactivationFlow.start.run( # user=user, # deactivation_config=deactivation_config, # created_by=self.request.user # ) # # messages.success( # self.request, # f'Account deactivation initiated for {user.get_full_name()}.' # ) # # return super().form_valid(form) # # def get_context_data(self, **kwargs): # context = super().get_context_data(**kwargs) # context['user'] = get_object_or_404(User, pk=self.kwargs['pk']) # context['title'] = 'Deactivate Account' # return context # # # class UserListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): # """ # View for listing users # """ # model = User # template_name = 'accounts/user_list.html' # context_object_name = 'users' # permission_required = 'accounts.view_user' # paginate_by = 25 # # def get_queryset(self): # queryset = User.objects.filter(tenant=self.request.user.tenant) # # # Apply filters # search = self.request.GET.get('search') # if search: # queryset = queryset.filter( # models.Q(first_name__icontains=search) | # models.Q(last_name__icontains=search) | # models.Q(email__icontains=search) | # models.Q(username__icontains=search) # ) # # department = self.request.GET.get('department') # if department: # queryset = queryset.filter(department_id=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) # # return queryset.order_by('last_name', 'first_name') # # def get_context_data(self, **kwargs): # context = super().get_context_data(**kwargs) # context['title'] = 'Users' # context['departments'] = self.get_departments() # context['search'] = self.request.GET.get('search', '') # context['selected_department'] = self.request.GET.get('department', '') # context['selected_status'] = self.request.GET.get('status', '') # return context # # def get_departments(self): # """Get departments for filter""" # from core.models import Department # return Department.objects.filter(tenant=self.request.user.tenant) # # # class UserDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView): # """ # View for user details # """ # model = User # template_name = 'accounts/user_detail.html' # context_object_name = 'user' # permission_required = 'accounts.view_user' # # def get_queryset(self): # return User.objects.filter(tenant=self.request.user.tenant) # # def get_context_data(self, **kwargs): # context = super().get_context_data(**kwargs) # user = self.object # context['title'] = f'{user.get_full_name()}' # context['two_factor_devices'] = user.two_factor_devices.filter(is_active=True) # context['recent_sessions'] = user.user_sessions.order_by('-created_at')[:5] # context['password_history'] = user.password_history.order_by('-created_at')[:5] # return context # # # class SecurityDashboardView(LoginRequiredMixin, PermissionRequiredMixin, ListView): # """ # View for security dashboard # """ # template_name = 'accounts/security_dashboard.html' # permission_required = 'accounts.view_security_dashboard' # # def get_queryset(self): # return None # # def get_context_data(self, **kwargs): # context = super().get_context_data(**kwargs) # context['title'] = 'Security Dashboard' # context['security_metrics'] = self.get_security_metrics() # context['recent_alerts'] = self.get_recent_security_alerts() # context['compliance_status'] = self.get_compliance_status() # return context # # def get_security_metrics(self): # """Get security metrics""" # tenant = self.request.user.tenant # return { # 'total_users': User.objects.filter(tenant=tenant).count(), # 'active_users': User.objects.filter(tenant=tenant, is_active=True).count(), # 'users_with_2fa': User.objects.filter( # tenant=tenant, # two_factor_devices__is_active=True # ).distinct().count(), # 'failed_logins_today': 0, # Would be calculated from audit logs # 'password_expiring_soon': 0 # Would be calculated from password history # } # # def get_recent_security_alerts(self): # """Get recent security alerts""" # # This would return recent security alerts # return [] # # def get_compliance_status(self): # """Get compliance status""" # return { # 'hipaa': 'compliant', # 'gdpr': 'compliant', # 'sox': 'pending', # 'pci_dss': 'non_compliant', # 'iso27001': 'compliant' # } # # # # AJAX Views # @login_required # @permission_required('accounts.can_manage_sessions') # def terminate_session_ajax(request, session_id): # """AJAX view to terminate user session""" # if request.method == 'POST': # try: # session = UserSession.objects.get( # id=session_id, # user__tenant=request.user.tenant # ) # session.end_session() # # return JsonResponse({ # 'success': True, # 'message': 'Session terminated successfully.' # }) # except UserSession.DoesNotExist: # return JsonResponse({ # 'success': False, # 'message': 'Session not found.' # }) # # return JsonResponse({'success': False, 'message': 'Invalid request.'}) # # # @login_required # @permission_required('accounts.can_reset_passwords') # def reset_password_ajax(request, user_id): # """AJAX view to reset user password""" # if request.method == 'POST': # try: # user = User.objects.get( # id=user_id, # tenant=request.user.tenant # ) # # # Generate temporary password # import secrets # temp_password = secrets.token_urlsafe(12) # user.set_password(temp_password) # user.must_change_password = True # user.save() # # # Send password reset email # send_mail( # subject='Password Reset', # message=f'Your temporary password is: {temp_password}', # from_email=settings.DEFAULT_FROM_EMAIL, # recipient_list=[user.email], # fail_silently=True # ) # # return JsonResponse({ # 'success': True, # 'message': 'Password reset successfully. User will receive email with temporary password.' # }) # except User.DoesNotExist: # return JsonResponse({ # 'success': False, # 'message': 'User not found.' # }) # # return JsonResponse({'success': False, 'message': 'Invalid request.'}) # # # @login_required # def user_search_ajax(request): # """AJAX view for user search""" # query = request.GET.get('q', '') # if len(query) < 2: # return JsonResponse({'users': []}) # # users = User.objects.filter( # tenant=request.user.tenant, # is_active=True # ).filter( # models.Q(first_name__icontains=query) | # models.Q(last_name__icontains=query) | # models.Q(email__icontains=query) | # models.Q(username__icontains=query) # )[:10] # # user_data = [ # { # 'id': user.id, # 'name': user.get_full_name(), # 'email': user.email, # 'department': user.department.name if user.department else '' # } # for user in users # ] # # return JsonResponse({'users': user_data}) #