feat: implement user roles system with tenant isolation

Добавлена система ролей пользователей для управления доступом в multi-tenant приложении.

Новые роли:
- Владелец (Owner): полный доступ, управление пользователями
- Менеджер (Manager): управление заказами, клиентами, товарами, складом
- Флорист (Florist): работа с заказами и складскими операциями
- Курьер (Courier): роль создана, права будут определены позже

Архитектура:
- Роли автоматически изолируются по тенантам через django-tenants (TENANT_APPS)
- Не требуется FK на Client/Tenant - изоляция через PostgreSQL schemas
- Роли автоматически создаются при создании нового тенанта

Компоненты:
- user_roles/models.py: модели Role и UserRole
- user_roles/services.py: RoleService для управления ролями
- user_roles/decorators.py: @role_required, @owner_required
- user_roles/mixins.py: RoleBasedAdminMixin, OwnerOnlyAdminMixin
- user_roles/admin.py: админка для управления ролями
- user_roles/management/commands/init_roles.py: команда для инициализации

Изменения:
- accounts/models.py: добавлены helper методы (is_owner, has_role, etc)
- settings.py: добавлен user_roles в TENANT_APPS
- tenants/admin.py: автосоздание ролей при создании тенанта

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-12-01 18:06:47 +03:00
parent eef2cb820f
commit f4e7ad0aac
17 changed files with 471 additions and 0 deletions

View File

@@ -0,0 +1,40 @@
from functools import wraps
from django.http import HttpResponseForbidden
from django.shortcuts import redirect
from django.contrib import messages
from user_roles.services import RoleService
def role_required(*role_codes):
"""
Декоратор для проверки роли пользователя.
Использование:
@role_required('owner', 'manager')
def my_view(request):
...
"""
def decorator(view_func):
@wraps(view_func)
def wrapper(request, *args, **kwargs):
if not request.user.is_authenticated:
return redirect('login')
if RoleService.user_has_role(request.user, *role_codes):
return view_func(request, *args, **kwargs)
messages.error(request, 'У вас нет прав для выполнения этого действия.')
return HttpResponseForbidden('Access denied')
return wrapper
return decorator
def owner_required(view_func):
"""Декоратор для проверки роли Владелец"""
return role_required('owner')(view_func)
def manager_or_owner_required(view_func):
"""Декоратор для проверки роли Менеджер или Владелец"""
return role_required('owner', 'manager')(view_func)