2422 lines
79 KiB
Python
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)
|
|
#
|
|
#
|
|
#
|
|
#
|
|
#
|