Конкретно пересмотрел логику работы. Легаси вынесена в архив
All checks were successful
Deploy MES Core / deploy (push) Successful in 13s
All checks were successful
Deploy MES Core / deploy (push) Successful in 13s
This commit is contained in:
106
shiftflow/authz.py
Normal file
106
shiftflow/authz.py
Normal file
@@ -0,0 +1,106 @@
|
||||
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
|
||||
Reference in New Issue
Block a user