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:
100
myproject/user_roles/services.py
Normal file
100
myproject/user_roles/services.py
Normal file
@@ -0,0 +1,100 @@
|
||||
from django.db import transaction
|
||||
from user_roles.models import Role, UserRole
|
||||
from django.contrib.auth import get_user_model
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
|
||||
class RoleService:
|
||||
"""Сервис для управления ролями"""
|
||||
|
||||
@staticmethod
|
||||
def create_default_roles():
|
||||
"""
|
||||
Создает системные роли для тенанта.
|
||||
Вызывается при создании тенанта (автоматически через signals или admin).
|
||||
|
||||
ВАЖНО: Эта функция вызывается в контексте schema_context(tenant.schema_name),
|
||||
поэтому роли создаются в schema конкретного тенанта автоматически!
|
||||
"""
|
||||
default_roles = [
|
||||
{
|
||||
'code': Role.OWNER,
|
||||
'name': 'Владелец',
|
||||
'description': 'Полный доступ ко всем функциям системы. Управление пользователями и настройками.',
|
||||
'is_system': True,
|
||||
},
|
||||
{
|
||||
'code': Role.MANAGER,
|
||||
'name': 'Менеджер',
|
||||
'description': 'Управление заказами, клиентами, товарами и складом. Без доступа к настройкам.',
|
||||
'is_system': True,
|
||||
},
|
||||
{
|
||||
'code': Role.FLORIST,
|
||||
'name': 'Флорист',
|
||||
'description': 'Работа с заказами и складскими операциями для заказов.',
|
||||
'is_system': True,
|
||||
},
|
||||
{
|
||||
'code': Role.COURIER,
|
||||
'name': 'Курьер',
|
||||
'description': 'Доставка заказов (права будут определены позже).',
|
||||
'is_system': True,
|
||||
},
|
||||
]
|
||||
|
||||
for role_data in default_roles:
|
||||
Role.objects.get_or_create(
|
||||
code=role_data['code'],
|
||||
defaults=role_data
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def get_role_by_code(code):
|
||||
"""Получить роль по коду"""
|
||||
try:
|
||||
return Role.objects.get(code=code, is_system=True)
|
||||
except Role.DoesNotExist:
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
@transaction.atomic
|
||||
def assign_role_to_user(user, role_code, created_by=None):
|
||||
"""
|
||||
Назначить роль пользователю в текущем тенанте.
|
||||
Если у пользователя уже есть роль - обновляет её.
|
||||
|
||||
ВАЖНО: Вызывается в контексте текущего тенанта (через middleware),
|
||||
поэтому UserRole создается в schema текущего тенанта автоматически!
|
||||
"""
|
||||
role = RoleService.get_role_by_code(role_code)
|
||||
if not role:
|
||||
raise ValueError(f"Роль с кодом '{role_code}' не найдена")
|
||||
|
||||
user_role, created = UserRole.objects.update_or_create(
|
||||
user=user,
|
||||
defaults={
|
||||
'role': role,
|
||||
'created_by': created_by,
|
||||
'is_active': True,
|
||||
}
|
||||
)
|
||||
return user_role
|
||||
|
||||
@staticmethod
|
||||
def get_user_role(user):
|
||||
"""Получить роль пользователя в текущем тенанте"""
|
||||
try:
|
||||
user_role = UserRole.objects.get(user=user, is_active=True)
|
||||
return user_role.role
|
||||
except UserRole.DoesNotExist:
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def user_has_role(user, *role_codes):
|
||||
"""Проверить, имеет ли пользователь одну из указанных ролей"""
|
||||
user_role = RoleService.get_user_role(user)
|
||||
if not user_role:
|
||||
return False
|
||||
return user_role.code in role_codes
|
||||
Reference in New Issue
Block a user