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.
This commit is contained in:
James Meakin 2021-05-16 17:22:21 +02:00
parent 3875d3284e
commit dc35f9e408
4 changed files with 44 additions and 26 deletions

View File

@ -2,7 +2,6 @@
coreapi==2.3.1 coreapi==2.3.1
coreschema==0.0.4 coreschema==0.0.4
django-filter>=2.4.0,<3.0 django-filter>=2.4.0,<3.0
django-guardian>=2.3.0,<2.4
markdown==3.3;python_version>="3.6" markdown==3.3;python_version>="3.6"
markdown==3.2.2;python_version=="3.5" markdown==3.2.2;python_version=="3.5"
psycopg2-binary>=2.8.5,<2.9 psycopg2-binary>=2.8.5,<2.9

View File

@ -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))

View File

@ -19,6 +19,10 @@ def pytest_configure(config):
from django.conf import settings from django.conf import settings
settings.configure( settings.configure(
AUTHENTICATION_BACKENDS=(
'django.contrib.auth.backends.ModelBackend',
'tests.authentication.backends.ObjectPermissionBackend',
),
DEBUG_PROPAGATE_EXCEPTIONS=True, DEBUG_PROPAGATE_EXCEPTIONS=True,
DATABASES={ DATABASES={
'default': { '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'): if config.getoption('--no-pkgroot'):
sys.path.pop(0) sys.path.pop(0)

View File

@ -1,8 +1,6 @@
import base64 import base64
import unittest
from unittest import mock from unittest import mock
from django.conf import settings
from django.contrib.auth.models import AnonymousUser, Group, Permission, User from django.contrib.auth.models import AnonymousUser, Group, Permission, User
from django.db import models from django.db import models
from django.test import TestCase from django.test import TestCase
@ -14,6 +12,7 @@ from rest_framework import (
) )
from rest_framework.routers import DefaultRouter from rest_framework.routers import DefaultRouter
from rest_framework.test import APIRequestFactory from rest_framework.test import APIRequestFactory
from tests.authentication.backends import assign_perm
from tests.models import BasicModel from tests.models import BasicModel
factory = APIRequestFactory() factory = APIRequestFactory()
@ -299,14 +298,11 @@ class GetQuerysetObjectPermissionInstanceView(generics.RetrieveUpdateDestroyAPIV
get_queryset_object_permissions_view = GetQuerysetObjectPermissionInstanceView.as_view() get_queryset_object_permissions_view = GetQuerysetObjectPermissionInstanceView.as_view()
@unittest.skipUnless('guardian' in settings.INSTALLED_APPS, 'django-guardian not installed')
class ObjectPermissionsIntegrationTests(TestCase): class ObjectPermissionsIntegrationTests(TestCase):
""" """
Integration tests for the object level permissions API. Integration tests for the object level permissions API.
""" """
def setUp(self): def setUp(self):
from guardian.shortcuts import assign_perm
# create users # create users
create = User.objects.create_user create = User.objects.create_user
users = { users = {
@ -320,14 +316,13 @@ class ObjectPermissionsIntegrationTests(TestCase):
everyone = Group.objects.create(name='everyone') everyone = Group.objects.create(name='everyone')
model_name = BasicPermModel._meta.model_name model_name = BasicPermModel._meta.model_name
app_label = BasicPermModel._meta.app_label app_label = BasicPermModel._meta.app_label
f = '{}_{}'.format f = '{}.{}_{}'.format
perms = { perms = {
'view': f('view', model_name), 'view': f(app_label, 'view', model_name),
'change': f('change', model_name), 'change': f(app_label, 'change', model_name),
'delete': f('delete', model_name) 'delete': f(app_label, 'delete', model_name)
} }
for perm in perms.values(): for perm in perms.values():
perm = '{}.{}'.format(app_label, perm)
assign_perm(perm, everyone) assign_perm(perm, everyone)
everyone.user_set.add(*users.values()) everyone.user_set.add(*users.values())