Corp/backend/tests/employees/test_position_permissions.py
Игорь Надеин 81cdf7fb24 fix: add is_active check to PositionRoleBackend and fix tests
Changes:
1. auth_backends.py - Added is_active check in get_all_permissions()
   Inactive users should not receive any permissions from their position

2. test_position_permissions.py - Multiple test fixes:
   - Moved fixtures to module level (accessible to all test classes)
   - Changed Permission creation to filter().first() pattern (no IntegrityError)
   - Fixed permission cache issues (use fresh User.objects.get() after changes)
   - Changed test_staff_user_keeps_all_permissions → test_superuser_has_all_permissions
     (is_staff does NOT grant all perms, only is_superuser does)

Test Results:
✅ 13/13 tests PASSED
✅ All permission inheritance scenarios verified
✅ PositionRoleBackend implementation confirmed working

Key Findings:
- Position → Groups → Permissions inheritance works correctly
- Permission caching requires fresh instance after position changes
- is_active check was missing (now fixed)
- is_staff does NOT grant all permissions (only is_superuser)

Next Steps:
- Check production environment for permission caching issues
- Verify LDAP group sync is working
- Test UI button visibility with real user accounts
2025-12-08 14:02:09 +03:00

465 lines
18 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.

"""
Тесты прав доступа через должности (Position + Groups).
Проверяют, что:
1. Пользователь с должностью получает права из групп должности
2. PositionRoleBackend корректно добавляет права через get_all_permissions
3. has_perm работает для прав из должностей
4. Права наследуются при назначении/изменении должности
"""
import pytest
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Group, Permission
from django.contrib.contenttypes.models import ContentType
from employees.models import Position
User = get_user_model()
pytestmark = pytest.mark.django_db
# ============================================================================
# Фикстуры (на уровне модуля, доступны всем тестам)
# ============================================================================
@pytest.fixture
def feed_content_type():
"""ContentType для приложения feed (для прав публикаций)."""
ct, _ = ContentType.objects.get_or_create(
app_label="feed", model="post"
)
return ct
@pytest.fixture
def documents_content_type():
"""ContentType для приложения documents."""
ct, _ = ContentType.objects.get_or_create(
app_label="documents", model="document"
)
return ct
@pytest.fixture
def group_with_publish_perm(feed_content_type):
"""Группа с правом публикации в feed."""
group = Group.objects.create(name="publishers")
# Ищем существующее право или создаём новое
perm = Permission.objects.filter(
codename="publish_company_post",
content_type=feed_content_type,
).first()
if not perm:
perm = Permission.objects.create(
codename="publish_company_post",
name="Can publish company post",
content_type=feed_content_type,
)
group.permissions.add(perm)
return group
@pytest.fixture
def group_with_document_perm(documents_content_type):
"""Группа с правом добавления документов."""
group = Group.objects.create(name="doc_creators")
# Ищем существующее право или используем defaults
perm = Permission.objects.filter(
codename="add_document",
content_type=documents_content_type,
).first()
if not perm:
perm = Permission.objects.create(
codename="add_document",
name="Can add document",
content_type=documents_content_type,
)
group.permissions.add(perm)
return group
@pytest.fixture
def engineer_position(group_with_publish_perm, group_with_document_perm):
"""Должность 'Инженер' с двумя группами прав."""
position = Position.objects.create(
name="Инженер",
description="Технический специалист",
)
position.groups.add(
group_with_publish_perm, group_with_document_perm
)
return position
@pytest.fixture
def manager_position(group_with_publish_perm):
"""Должность 'Менеджер' только с правом публикации."""
position = Position.objects.create(
name="Менеджер",
description="Управленец",
)
position.groups.add(group_with_publish_perm)
return position
@pytest.fixture
def basic_user(user_factory):
"""Обычный пользователь без должности и прав."""
return user_factory(
email="user@example.com",
staff=False,
superuser=False,
verified=True,
active=True,
)
# ============================================================================
# Тесты
# ============================================================================
class TestPositionPermissions:
"""Тесты прав доступа через должности."""
def test_user_without_position_has_no_permissions(self, basic_user):
"""Пользователь без должности не имеет прав."""
assert not basic_user.has_perm("feed.publish_company_post")
assert not basic_user.has_perm("documents.add_document")
# Проверяем через get_all_permissions
all_perms = basic_user.get_all_permissions()
assert "feed.publish_company_post" not in all_perms
assert "documents.add_document" not in all_perms
def test_user_with_position_gets_permissions(
self, basic_user, engineer_position
):
"""Пользователь с должностью получает права из групп должности."""
# Назначаем должность
basic_user.position = engineer_position
basic_user.save()
# Обновляем пользователя из БД (чтобы подтянуть связи)
basic_user.refresh_from_db()
# Проверяем has_perm
assert basic_user.has_perm("feed.publish_company_post"), \
"Должен быть доступ к публикации (из группы publishers)"
assert basic_user.has_perm("documents.add_document"), \
"Должен быть доступ к созданию документов (из группы doc_creators)"
# Проверяем через get_all_permissions
all_perms = basic_user.get_all_permissions()
assert "feed.publish_company_post" in all_perms, \
f"Право должно быть в all_perms. Текущие права: {all_perms}"
assert "documents.add_document" in all_perms, \
f"Право должно быть в all_perms. Текущие права: {all_perms}"
def test_position_change_updates_permissions(
self, basic_user, engineer_position, manager_position
):
"""При смене должности права обновляются."""
# Назначаем инженера (2 права)
basic_user.position = engineer_position
basic_user.save()
# Получаем fresh instance чтобы сбросить кэш прав
user_id = basic_user.id
basic_user = User.objects.get(id=user_id)
assert basic_user.has_perm("feed.publish_company_post")
assert basic_user.has_perm("documents.add_document")
# Меняем на менеджера (только 1 право)
basic_user.position = manager_position
basic_user.save()
# Снова получаем fresh instance
basic_user = User.objects.get(id=user_id)
assert basic_user.has_perm("feed.publish_company_post"), \
"Право на публикацию должно остаться"
assert not basic_user.has_perm("documents.add_document"), \
"Право на документы должно исчезнуть"
def test_remove_position_removes_permissions(
self, basic_user, engineer_position
):
"""При снятии должности права удаляются."""
# Назначаем должность
basic_user.position = engineer_position
basic_user.save()
# Получаем fresh instance
user_id = basic_user.id
basic_user = User.objects.get(id=user_id)
assert basic_user.has_perm("feed.publish_company_post")
assert basic_user.has_perm("documents.add_document")
# Снимаем должность
basic_user.position = None
basic_user.save()
# Снова fresh instance для сброса кэша
basic_user = User.objects.get(id=user_id)
assert not basic_user.has_perm("feed.publish_company_post"), \
"Права должны исчезнуть после снятия должности"
assert not basic_user.has_perm("documents.add_document"), \
"Права должны исчезнуть после снятия должности"
def test_multiple_users_with_same_position(
self, user_factory, engineer_position
):
"""Несколько пользователей с одной должностью получают
одинаковые права.
"""
user1 = user_factory(email="user1@example.com")
user2 = user_factory(email="user2@example.com")
user1.position = engineer_position
user2.position = engineer_position
user1.save()
user2.save()
user1.refresh_from_db()
user2.refresh_from_db()
# Оба должны иметь одинаковые права
for user in [user1, user2]:
assert user.has_perm("feed.publish_company_post")
assert user.has_perm("documents.add_document")
def test_superuser_has_all_permissions(
self, user_factory, engineer_position
):
"""is_superuser имеет все права независимо от должности."""
superuser = user_factory(email="admin@example.com", superuser=True)
# Без должности superuser имеет все права
assert superuser.has_perm("feed.publish_company_post")
assert superuser.has_perm("documents.add_document")
assert superuser.has_perm("any.random_permission")
# С должностью тоже все права
superuser.position = engineer_position
superuser.save()
superuser = User.objects.get(id=superuser.id)
assert superuser.has_perm("feed.publish_company_post")
assert superuser.has_perm("documents.add_document")
assert superuser.has_perm("any.random_permission")
def test_position_group_permissions_are_additive(
self, basic_user, feed_content_type
):
"""Если у должности несколько групп, права суммируются."""
# Создаём две группы с разными правами
group1 = Group.objects.create(name="group1")
perm1 = Permission.objects.filter(
codename="perm1",
content_type=feed_content_type,
).first()
if not perm1:
perm1 = Permission.objects.create(
codename="perm1",
name="Permission 1",
content_type=feed_content_type,
)
group1.permissions.add(perm1)
group2 = Group.objects.create(name="group2")
perm2 = Permission.objects.filter(
codename="perm2",
content_type=feed_content_type,
).first()
if not perm2:
perm2 = Permission.objects.create(
codename="perm2",
name="Permission 2",
content_type=feed_content_type,
)
group2.permissions.add(perm2)
# Создаём должность с обеими группами
position = Position.objects.create(name="Multi-group Position")
position.groups.add(group1, group2)
basic_user.position = position
basic_user.save()
basic_user.refresh_from_db()
# Пользователь должен иметь права из обеих групп
assert basic_user.has_perm("feed.perm1")
assert basic_user.has_perm("feed.perm2")
def test_get_all_permissions_includes_position_perms(
self, basic_user, engineer_position
):
"""get_all_permissions() включает права из должности."""
basic_user.position = engineer_position
basic_user.save()
basic_user.refresh_from_db()
all_perms = basic_user.get_all_permissions()
# Проверяем, что это set
assert isinstance(all_perms, set)
# Проверяем содержимое
assert "feed.publish_company_post" in all_perms
assert "documents.add_document" in all_perms
# Проверяем, что нет дубликатов (set гарантирует это)
perms_list = list(all_perms)
assert len(perms_list) == len(set(perms_list))
class TestPositionPermissionsEdgeCases:
"""Тесты граничных случаев и edge cases."""
@pytest.fixture
def empty_position(self):
"""Должность без групп."""
return Position.objects.create(
name="Empty Position",
description="Должность без прав",
)
def test_position_without_groups_grants_no_permissions(
self, user_factory, empty_position
):
"""Должность без групп не даёт прав."""
user = user_factory(email="user@example.com")
user.position = empty_position
user.save()
user.refresh_from_db()
all_perms = user.get_all_permissions()
assert len(all_perms) == 0, "Должность без групп не должна давать прав"
def test_inactive_user_has_no_permissions(
self, user_factory, engineer_position
):
"""Неактивный пользователь не имеет прав даже с должностью."""
user = user_factory(email="inactive@example.com", active=False)
user.position = engineer_position
user.save()
# Получаем fresh instance
user = User.objects.get(id=user.id)
# is_active=False блокирует все права
assert not user.has_perm("feed.publish_company_post")
assert not user.has_perm("documents.add_document")
def test_position_permissions_with_direct_user_permissions(
self, user_factory, engineer_position, feed_content_type
):
"""Права из должности + прямые права пользователя объединяются."""
user = user_factory(email="user@example.com")
# Даём пользователю прямое право
direct_perm, _ = Permission.objects.get_or_create(
codename="direct_perm",
name="Direct Permission",
content_type=feed_content_type,
)
user.user_permissions.add(direct_perm)
# Назначаем должность
user.position = engineer_position
user.save()
user.refresh_from_db()
# Должен иметь и прямые права, и права из должности
assert user.has_perm("feed.direct_perm"), \
"Прямое право должно работать"
assert user.has_perm("feed.publish_company_post"), \
"Право из должности должно работать"
assert user.has_perm("documents.add_document"), \
"Право из должности должно работать"
class TestPositionPermissionsIntegration:
"""Интеграционные тесты с реальными моделями приложения."""
def test_feed_permissions_through_position(
self, user_factory, feed_content_type
):
"""Проверка прав на публикацию постов через должность."""
# Создаём группу с правами публикации
publishers = Group.objects.create(name="publishers")
for codename in ["publish_company_post", "publish_department_post"]:
perm, _ = Permission.objects.get_or_create(
codename=codename,
name=f"Can {codename.replace('_', ' ')}",
content_type=feed_content_type,
)
publishers.permissions.add(perm)
# Создаём должность редактора
editor_position = Position.objects.create(name="Редактор")
editor_position.groups.add(publishers)
# Назначаем пользователю
user = user_factory(email="editor@example.com")
user.position = editor_position
user.save()
user.refresh_from_db()
# Проверяем права
assert user.has_perm("feed.publish_company_post")
assert user.has_perm("feed.publish_department_post")
def test_documents_permissions_through_position(
self, user_factory, documents_content_type
):
"""Проверка прав на документы через должность."""
# Создаём группу с правами на документы
doc_managers = Group.objects.create(name="doc_managers")
for codename in [
"add_document", "change_document",
"delete_document", "view_document"
]:
# Используем filter().first() вместо get_or_create
perm = Permission.objects.filter(
codename=codename,
content_type=documents_content_type,
).first()
if not perm:
perm = Permission.objects.create(
codename=codename,
name=f"Can {codename.replace('_', ' ')}",
content_type=documents_content_type,
)
doc_managers.permissions.add(perm)
# Создаём должность документоведа
doc_position = Position.objects.create(name="Документовед")
doc_position.groups.add(doc_managers)
# Назначаем пользователю
user = user_factory(email="docs@example.com")
user.position = doc_position
user.save()
user.refresh_from_db()
# Проверяем все CRUD права
assert user.has_perm("documents.add_document")
assert user.has_perm("documents.change_document")
assert user.has_perm("documents.delete_document")
assert user.has_perm("documents.view_document")