Files
ACORE/core/access_preview.py
lokses ae54d93442 core: секции портала, контроль доступа и вью страниц
Section-based portal access, matrix прав по группам, аккаунтинг-портал,
управление группами пользователей, справочники, дашборд и общий каркас
навигации/стилей.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-09 18:03:20 +03:00

86 lines
3.3 KiB
Python

"""Dev-only: просмотр портала «как» роль/группа с её ограничениями.
Включается только при DEBUG. Состояние — в session['dev_access_preview'].
Реальные права пользователя не меняются; меняется только проверка
user_can_access / user_can_manage_hr_portal.
"""
from __future__ import annotations
from dataclasses import dataclass
from django.conf import settings
from django.contrib.auth.models import Group
SESSION_KEY = 'dev_access_preview'
@dataclass(frozen=True)
class AccessPreviewPreset:
id: str
label: str
group_names: tuple[str, ...] = ()
# Пустой id = без превью (полный доступ admin/superuser как обычно).
ACCESS_PREVIEW_PRESETS: tuple[AccessPreviewPreset, ...] = (
AccessPreviewPreset('', 'Как есть (мой доступ)'),
AccessPreviewPreset('pharmacist', 'Фармацевт', ('Фармацевт',)),
AccessPreviewPreset('pharmacy_manager', 'Заведующий аптекой', ('Заведующий аптекой',)),
AccessPreviewPreset('office', 'Офисный сотрудник', ('Офисный сотрудник',)),
AccessPreviewPreset('hr', 'HR', ('HR',)),
AccessPreviewPreset('it', 'IT', ('IT',)),
AccessPreviewPreset('network_admin', 'Администратор сети', ('Администратор сети',)),
AccessPreviewPreset('basic', 'Только базовые разделы', ()),
)
_PRESETS_BY_ID = {p.id: p for p in ACCESS_PREVIEW_PRESETS}
def access_preview_available(user) -> bool:
if not getattr(settings, 'DEBUG', False):
return False
if not user or not getattr(user, 'is_authenticated', False):
return False
return bool(getattr(user, 'is_staff', False) or getattr(user, 'is_superuser', False))
def get_preview_id(user) -> str:
return (getattr(user, '_access_preview', None) or '').strip()
def resolve_preset(preview_id: str | None) -> AccessPreviewPreset | None:
if preview_id is None:
return None
return _PRESETS_BY_ID.get(preview_id)
def preview_permission_strings(preview_id: str) -> frozenset[str] | None:
"""Набор 'app_label.codename' для пресета. None — превью выключено."""
preset = resolve_preset(preview_id or '')
if preset is None or not preset.id:
return None
if not preset.group_names:
return frozenset()
codes: set[str] = set()
groups = Group.objects.filter(name__in=preset.group_names).prefetch_related(
'permissions__content_type',
)
for group in groups:
for perm in group.permissions.all():
codes.add(f'{perm.content_type.app_label}.{perm.codename}')
return frozenset(codes)
def attach_preview_to_user(user, preview_id: str | None) -> None:
"""Вешает на user атрибуты для portal_access (на один request)."""
pid = (preview_id or '').strip()
if not pid or pid not in _PRESETS_BY_ID:
user._access_preview = ''
user._access_preview_perms = None
user._access_preview_groups = None
return
preset = _PRESETS_BY_ID[pid]
user._access_preview = pid
user._access_preview_perms = preview_permission_strings(pid)
user._access_preview_groups = frozenset(preset.group_names)