a62f023a90
На карточке Employee — вкладка «Доступ (сводка)»: по каждому ограниченному разделу портала показывает итог (есть/нет) и источник (какая группа дала право, individual grant/revoke override, личное право). Считается теми же правилами, что Employee.has_perm (override имеет приоритет над группами), поэтому сводка 1-в-1 совпадает с реальной проверкой доступа. Править по-прежнему: поле «Группы» + инлайн «Доп. права на разделы портала» (grant/revoke). - core/access_summary.py: employee_access_summary() - EmployeeAdmin: readonly effective_access_summary + вкладка - core/tests_access_summary.py: 6 тестов (в т.ч. совпадение с has_perm) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
102 lines
4.0 KiB
Python
102 lines
4.0 KiB
Python
from datetime import date
|
|
|
|
from django.contrib.auth.models import Group, Permission
|
|
from django.contrib.contenttypes.models import ContentType
|
|
from django.test import TestCase
|
|
|
|
from apps.employees.models import Employee, EmployeePermissionOverride
|
|
from core.access_summary import employee_access_summary
|
|
from core.models import PortalAccess
|
|
|
|
|
|
def portal_perm(codename: str) -> Permission:
|
|
ct = ContentType.objects.get_for_model(PortalAccess, for_concrete_model=False)
|
|
return Permission.objects.get(content_type=ct, codename=codename)
|
|
|
|
|
|
def make_employee(**overrides):
|
|
n = Employee.objects.count() + 1
|
|
defaults = {
|
|
'username': f'sum.user{n}',
|
|
'email': f'sum.user{n}@company.local',
|
|
'first_name': 'Тест',
|
|
'last_name': 'Сводка',
|
|
'hire_date': date(2026, 1, 1),
|
|
'user_location_type': 'office',
|
|
'mattermost_sync_enabled': False,
|
|
'ad_sync_enabled': False,
|
|
'is_staff': False,
|
|
}
|
|
defaults.update(overrides)
|
|
return Employee.objects.create(**defaults)
|
|
|
|
|
|
def row_for(rows, key):
|
|
return next(r for r in rows if r.key == key)
|
|
|
|
|
|
class AccessSummaryTests(TestCase):
|
|
def test_rows_cover_all_restricted_sections(self):
|
|
from core.portal_sections import RESTRICTED_KEYS
|
|
rows = employee_access_summary(make_employee())
|
|
self.assertEqual({r.key for r in rows}, set(RESTRICTED_KEYS))
|
|
|
|
def test_no_access_by_default(self):
|
|
rows = employee_access_summary(make_employee())
|
|
self.assertFalse(row_for(rows, 'reports').allowed)
|
|
self.assertEqual(row_for(rows, 'reports').source_text(), '—')
|
|
|
|
def test_group_grant_shows_source(self):
|
|
emp = make_employee()
|
|
group = Group.objects.create(name='Тест-отчёты')
|
|
group.permissions.add(portal_perm('access_reports'))
|
|
emp.groups.add(group)
|
|
rows = employee_access_summary(emp)
|
|
reports = row_for(rows, 'reports')
|
|
self.assertTrue(reports.allowed)
|
|
self.assertIn('Тест-отчёты', reports.source_text())
|
|
|
|
def test_override_revoke_wins_over_group(self):
|
|
emp = make_employee()
|
|
group = Group.objects.create(name='Тест-отчёты2')
|
|
group.permissions.add(portal_perm('access_reports'))
|
|
emp.groups.add(group)
|
|
EmployeePermissionOverride.objects.create(
|
|
employee=emp, permission=portal_perm('access_reports'),
|
|
override_type='revoke', is_active=True,
|
|
)
|
|
reports = row_for(employee_access_summary(emp), 'reports')
|
|
self.assertFalse(reports.allowed)
|
|
self.assertIn('отозвано', reports.source_text())
|
|
|
|
def test_override_grant_without_group(self):
|
|
emp = make_employee()
|
|
EmployeePermissionOverride.objects.create(
|
|
employee=emp, permission=portal_perm('access_rro'),
|
|
override_type='grant', is_active=True,
|
|
)
|
|
rro = row_for(employee_access_summary(emp), 'rro')
|
|
self.assertTrue(rro.allowed)
|
|
self.assertIn('выдано', rro.source_text())
|
|
|
|
def test_summary_matches_has_perm(self):
|
|
"""Сводка обязана совпадать с фактической проверкой Employee.has_perm."""
|
|
emp = make_employee()
|
|
group = Group.objects.create(name='Тест-микс')
|
|
group.permissions.add(portal_perm('access_reports'))
|
|
emp.groups.add(group)
|
|
EmployeePermissionOverride.objects.create(
|
|
employee=emp, permission=portal_perm('access_rro'),
|
|
override_type='grant', is_active=True,
|
|
)
|
|
EmployeePermissionOverride.objects.create(
|
|
employee=emp, permission=portal_perm('access_reports'),
|
|
override_type='revoke', is_active=True,
|
|
)
|
|
emp = Employee.objects.get(pk=emp.pk) # сброс кэша прав
|
|
for r in employee_access_summary(emp):
|
|
self.assertEqual(
|
|
r.allowed, emp.has_perm(f'core.access_{r.key}'),
|
|
msg=f'Расхождение по разделу {r.key}',
|
|
)
|