From dc35f9e4089ddecee8b3c51b0af3a610faf3932f Mon Sep 17 00:00:00 2001 From: James Meakin <12661555+jmsmkn@users.noreply.github.com> Date: Sun, 16 May 2021 17:22:21 +0200 Subject: [PATCH] Remove test dependency on django guardian An ObjectPermissionBackend is added which is similar to the one in guardian.backends.ObjectPermissionBackend for test support. The differences are that: - Permissions can only be assigned to groups - The object permissions are stored in a defaultdict - Permissions can only be assigned using app_label.codename This allows DRF to be released without waiting for django-guardian to update. --- requirements/requirements-optionals.txt | 1 - tests/authentication/backends.py | 35 +++++++++++++++++++++++++ tests/conftest.py | 19 +++----------- tests/test_permissions.py | 15 ++++------- 4 files changed, 44 insertions(+), 26 deletions(-) create mode 100644 tests/authentication/backends.py diff --git a/requirements/requirements-optionals.txt b/requirements/requirements-optionals.txt index 4cb0e54f4..83e17d1e1 100644 --- a/requirements/requirements-optionals.txt +++ b/requirements/requirements-optionals.txt @@ -2,7 +2,6 @@ coreapi==2.3.1 coreschema==0.0.4 django-filter>=2.4.0,<3.0 -django-guardian>=2.3.0,<2.4 markdown==3.3;python_version>="3.6" markdown==3.2.2;python_version=="3.5" psycopg2-binary>=2.8.5,<2.9 diff --git a/tests/authentication/backends.py b/tests/authentication/backends.py new file mode 100644 index 000000000..e228929fe --- /dev/null +++ b/tests/authentication/backends.py @@ -0,0 +1,35 @@ +from collections import defaultdict + +from django.contrib.auth.models import Permission + + +def assign_perm(perm, group, obj=None): + if "." not in perm: + raise ValueError("perm must be in the format 'app_label.codename'") + + if obj: + ObjectPermissionBackend.assign_perm(perm, group, obj) + else: + # Assign global permissions if there is no object + app_label, codename = perm.split(".", 1) + perm = Permission.objects.get( + content_type__app_label=app_label, codename=codename + ) + group.permissions.add(perm) + + +class ObjectPermissionBackend: + # Stores the set of groups that have the given permission for the given object + object_perms = defaultdict(lambda: defaultdict(set)) + + def authenticate(self, *_, **__): + return None + + @classmethod + def assign_perm(cls, perm, group, obj): + cls.object_perms[obj][perm].add(group) + + def has_perm(self, user_obj, perm, obj=None): + groups_with_obj_perm = self.object_perms[obj][perm] + users_groups = {*user_obj.groups.all()} + return bool(groups_with_obj_perm.intersection(users_groups)) diff --git a/tests/conftest.py b/tests/conftest.py index cc32cc637..c3b163701 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -19,6 +19,10 @@ def pytest_configure(config): from django.conf import settings settings.configure( + AUTHENTICATION_BACKENDS=( + 'django.contrib.auth.backends.ModelBackend', + 'tests.authentication.backends.ObjectPermissionBackend', + ), DEBUG_PROPAGATE_EXCEPTIONS=True, DATABASES={ 'default': { @@ -70,21 +74,6 @@ def pytest_configure(config): ), ) - # guardian is optional - try: - import guardian # NOQA - except ImportError: - pass - else: - settings.ANONYMOUS_USER_ID = -1 - settings.AUTHENTICATION_BACKENDS = ( - 'django.contrib.auth.backends.ModelBackend', - 'guardian.backends.ObjectPermissionBackend', - ) - settings.INSTALLED_APPS += ( - 'guardian', - ) - if config.getoption('--no-pkgroot'): sys.path.pop(0) diff --git a/tests/test_permissions.py b/tests/test_permissions.py index 4e6cae4b8..5caeeda24 100644 --- a/tests/test_permissions.py +++ b/tests/test_permissions.py @@ -1,8 +1,6 @@ import base64 -import unittest from unittest import mock -from django.conf import settings from django.contrib.auth.models import AnonymousUser, Group, Permission, User from django.db import models from django.test import TestCase @@ -14,6 +12,7 @@ from rest_framework import ( ) from rest_framework.routers import DefaultRouter from rest_framework.test import APIRequestFactory +from tests.authentication.backends import assign_perm from tests.models import BasicModel factory = APIRequestFactory() @@ -299,14 +298,11 @@ class GetQuerysetObjectPermissionInstanceView(generics.RetrieveUpdateDestroyAPIV get_queryset_object_permissions_view = GetQuerysetObjectPermissionInstanceView.as_view() -@unittest.skipUnless('guardian' in settings.INSTALLED_APPS, 'django-guardian not installed') class ObjectPermissionsIntegrationTests(TestCase): """ Integration tests for the object level permissions API. """ def setUp(self): - from guardian.shortcuts import assign_perm - # create users create = User.objects.create_user users = { @@ -320,14 +316,13 @@ class ObjectPermissionsIntegrationTests(TestCase): everyone = Group.objects.create(name='everyone') model_name = BasicPermModel._meta.model_name app_label = BasicPermModel._meta.app_label - f = '{}_{}'.format + f = '{}.{}_{}'.format perms = { - 'view': f('view', model_name), - 'change': f('change', model_name), - 'delete': f('delete', model_name) + 'view': f(app_label, 'view', model_name), + 'change': f(app_label, 'change', model_name), + 'delete': f(app_label, 'delete', model_name) } for perm in perms.values(): - perm = '{}.{}'.format(app_label, perm) assign_perm(perm, everyone) everyone.user_set.add(*users.values())