Исправлена критическая уязвимость безопасности, которая потенциально позволяла владельцам тенантов получить доступ к админ-панели Django. Изменения: - Добавлены явные setdefault для is_staff=False и is_superuser=False в CustomUserManager.create_user() - Добавлены явные флаги безопасности при создании владельца тенанта - Добавлены явные флаги безопасности при создании пользователей через систему ролей - Создан TenantAdminAccessMiddleware для защиты /admin/ на уровне middleware - Создана миграция данных для исправления флагов у существующих пользователей Реализована трёхуровневая защита (Defense-in-Depth): 1. Уровень модели: явные дефолты в create_user() 2. Уровень views: явные флаги при создании 3. Уровень middleware: runtime блокировка доступа Файлы: - accounts/models.py: явные флаги в create_user() - tenants/admin.py: явные флаги при создании владельца - user_roles/views.py: явные флаги при создании через роли - myproject/admin_access_middleware.py: новый middleware - myproject/settings.py: регистрация middleware - accounts/migrations/0002_fix_owner_staff_flags.py: миграция данных ВАЖНО: После применения этого коммита необходимо выполнить: 1. python manage.py migrate accounts 2. python manage.py migrate_schemas 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
125 lines
4.4 KiB
Python
125 lines
4.4 KiB
Python
from django.shortcuts import render, redirect, get_object_or_404
|
||
from django.contrib.auth.decorators import login_required
|
||
from django.contrib import messages
|
||
from django.contrib.auth import get_user_model
|
||
from user_roles.models import Role, UserRole
|
||
from user_roles.services import RoleService
|
||
from user_roles.decorators import owner_required
|
||
|
||
User = get_user_model()
|
||
|
||
|
||
@login_required
|
||
@owner_required
|
||
def user_role_list(request):
|
||
"""Список пользователей с их ролями"""
|
||
user_roles = UserRole.objects.select_related('user', 'role', 'created_by').all()
|
||
roles = Role.objects.all()
|
||
|
||
context = {
|
||
'user_roles': user_roles,
|
||
'roles': roles,
|
||
}
|
||
return render(request, 'user_roles/user_role_list.html', context)
|
||
|
||
|
||
@login_required
|
||
@owner_required
|
||
def user_role_create(request):
|
||
"""Создание нового пользователя с ролью"""
|
||
if request.method == 'POST':
|
||
email = request.POST.get('email')
|
||
name = request.POST.get('name')
|
||
role_code = request.POST.get('role')
|
||
password = request.POST.get('password', User.objects.make_random_password(12))
|
||
|
||
try:
|
||
# Создаем пользователя
|
||
user = User.objects.create_user(
|
||
email=email,
|
||
name=name,
|
||
password=password,
|
||
is_email_confirmed=True,
|
||
is_staff=False, # SECURITY: Пользователи ролей НЕ имеют доступа к админке
|
||
is_superuser=False # SECURITY: Пользователи ролей НЕ суперпользователи
|
||
)
|
||
|
||
# Назначаем роль
|
||
RoleService.assign_role_to_user(user, role_code, created_by=request.user)
|
||
|
||
messages.success(request, f'Пользователь {email} создан с ролью {role_code}. Пароль: {password}')
|
||
return redirect('user_roles:list')
|
||
|
||
except Exception as e:
|
||
messages.error(request, f'Ошибка при создании пользователя: {str(e)}')
|
||
|
||
roles = Role.objects.all()
|
||
context = {
|
||
'roles': roles,
|
||
}
|
||
return render(request, 'user_roles/user_role_create.html', context)
|
||
|
||
|
||
@login_required
|
||
@owner_required
|
||
def user_role_edit(request, pk):
|
||
"""Изменение роли пользователя"""
|
||
user_role = get_object_or_404(UserRole, pk=pk)
|
||
|
||
# Защита от самоблокировки
|
||
can_modify, error_message = RoleService.can_modify_user_role(request.user, user_role)
|
||
if not can_modify:
|
||
messages.error(request, error_message)
|
||
return redirect('user_roles:list')
|
||
|
||
if request.method == 'POST':
|
||
role_code = request.POST.get('role')
|
||
is_active = request.POST.get('is_active') == 'on'
|
||
|
||
try:
|
||
# Обновляем роль
|
||
role = RoleService.get_role_by_code(role_code)
|
||
if not role:
|
||
raise ValueError(f"Роль '{role_code}' не найдена")
|
||
|
||
user_role.role = role
|
||
user_role.is_active = is_active
|
||
user_role.save()
|
||
|
||
messages.success(request, f'Роль пользователя {user_role.user.email} обновлена')
|
||
return redirect('user_roles:list')
|
||
|
||
except Exception as e:
|
||
messages.error(request, f'Ошибка при обновлении роли: {str(e)}')
|
||
|
||
roles = Role.objects.all()
|
||
context = {
|
||
'user_role': user_role,
|
||
'roles': roles,
|
||
}
|
||
return render(request, 'user_roles/user_role_edit.html', context)
|
||
|
||
|
||
@login_required
|
||
@owner_required
|
||
def user_role_delete(request, pk):
|
||
"""Удаление роли пользователя (отключение доступа)"""
|
||
user_role = get_object_or_404(UserRole, pk=pk)
|
||
|
||
# Защита от самоблокировки
|
||
can_modify, error_message = RoleService.can_modify_user_role(request.user, user_role)
|
||
if not can_modify:
|
||
messages.error(request, error_message)
|
||
return redirect('user_roles:list')
|
||
|
||
if request.method == 'POST':
|
||
email = user_role.user.email
|
||
user_role.delete()
|
||
messages.success(request, f'Доступ пользователя {email} удален')
|
||
return redirect('user_roles:list')
|
||
|
||
context = {
|
||
'user_role': user_role,
|
||
}
|
||
return render(request, 'user_roles/user_role_delete.html', context)
|