Files
octopus/myproject/user_roles/services.py
Andrey Smakotin f4e7ad0aac 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>
2025-12-01 18:06:47 +03:00

101 lines
4.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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