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
465 lines
18 KiB
Python
465 lines
18 KiB
Python
"""
|
||
Тесты прав доступа через должности (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")
|