2025-08-12 13:33:25 +03:00

2422 lines
79 KiB
Python

"""
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)
#
#
#
#
#