from __future__ import annotations from typing import Iterable ROLE_PRIORITY = [ 'admin', 'prod_head', 'technologist', 'master', 'clerk', 'supply', 'manager', 'operator', 'observer', 'director', ] def get_user_group_roles(user) -> set[str]: """Возвращает роли пользователя только из Django Groups. Используется для экранов, где включён строгий доступ "только по группам". EmployeeProfile.role здесь намеренно не учитывается. Правило: superuser получает роль admin. """ roles: set[str] = set() if not user or not getattr(user, 'is_authenticated', False): return roles if getattr(user, 'is_superuser', False): roles.add('admin') try: roles |= set(user.groups.values_list('name', flat=True)) except Exception: pass return roles def get_user_roles(user) -> set[str]: """Возвращает множество ролей пользователя. Источник ролей (вариант A, плавная миграция): - Django Groups: позволяет назначать несколько ролей одному пользователю. - Fallback на EmployeeProfile.role: чтобы при деплое и до раздачи групп система продолжала работать по старой модели (одна роль). Правило: superuser всегда получает роль admin независимо от групп/профиля. """ roles: set[str] = set() # Изначально множество пустое # 1. Проверяем, что пользователь авторизован if not user or not getattr(user, 'is_authenticated', False): return roles # 2. Проверяем, что пользователь не superuser if getattr(user, 'is_superuser', False): roles.add('admin') # 3. Проверяем, что у пользователя есть хотя бы одна группа try: roles |= set(user.groups.values_list('name', flat=True)) except Exception: pass # 4. Проверяем, что у пользователя есть роль в EmployeeProfile profile = getattr(user, 'profile', None) if profile and getattr(profile, 'role', None): roles.add(str(profile.role)) return roles def primary_role(roles: Iterable[str]) -> str: """Выбирает "основную" роль для отображения в UI. Примечание: права доступа должны проверяться по всем ролям (has_any_role). primary_role используется только для: - подписи/лейбла "роль пользователя" в шаблонах - дефолтного поведения, где требуется один статус (например, оформление UI) """ s = set(roles or []) for r in ROLE_PRIORITY: if r in s: return r return 'operator' def has_any_role(roles: Iterable[str], required: Iterable[str]) -> bool: """Проверяет, что у пользователя есть хотя бы одна роль из required. Используется во вьюхах для разрешений вида: roles = get_user_roles(request.user) if not has_any_role(roles, ['admin', 'master']): return redirect('registry') """ s = set(roles or []) for r in required or []: if r in s: return True return False