Skip to content

Commit aa6af6a

Browse files
committed
Add global role fixture and enhance test setup for permissions
1 parent 2637890 commit aa6af6a

2 files changed

Lines changed: 120 additions & 3 deletions

File tree

dojo/fixtures/dojo_testdata.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2871,6 +2871,14 @@
28712871
"role": 5
28722872
}
28732873
},
2874+
{
2875+
"pk": 4,
2876+
"model": "dojo.global_role",
2877+
"fields": {
2878+
"user": 4,
2879+
"role": 2
2880+
}
2881+
},
28742882
{
28752883
"model": "dojo.language_type",
28762884
"pk": 1,

unittests/test_rest_framework.py

Lines changed: 112 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,15 @@
1414

1515
from django.conf import settings
1616
from django.contrib.auth.models import Permission
17+
from django.core.files.uploadedfile import SimpleUploadedFile
1718
from django.test import tag as test_tag
1819
from django.test.utils import override_settings
1920
from django.urls import reverse
2021
from django.utils import timezone
2122
from drf_spectacular.drainage import GENERATOR_STATS
2223
from drf_spectacular.settings import spectacular_settings
2324
from drf_spectacular.validation import validate_schema
25+
from parameterized import parameterized
2426
from rest_framework import status
2527
from rest_framework.authtoken.models import Token
2628
from rest_framework.mixins import (
@@ -368,6 +370,11 @@ class TestType(Enum):
368370

369371
class BaseClass:
370372
class RESTEndpointTest(DojoAPITestCase):
373+
NOT_AUTHORIZED_USER_ID = 3
374+
GLOBAL_READER_USER_ID = 5
375+
GLOBAL_WRITER_USER_ID = 4
376+
GLOBAL_OWNER_USER_ID = 6
377+
371378
def __init__(self, *args, **kwargs):
372379
DojoAPITestCase.__init__(self, *args, **kwargs)
373380

@@ -380,19 +387,25 @@ def setUp(self):
380387
self.schema = get_open_api3_json_schema()
381388

382389
def setUp_not_authorized(self):
383-
testuser = User.objects.get(id=3)
390+
testuser = User.objects.get(id=self.NOT_AUTHORIZED_USER_ID)
384391
token = Token.objects.get(user=testuser)
385392
self.client = APIClient()
386393
self.client.credentials(HTTP_AUTHORIZATION="Token " + token.key)
387394

388395
def setUp_global_reader(self):
389-
testuser = User.objects.get(id=5)
396+
testuser = User.objects.get(id=self.GLOBAL_READER_USER_ID)
397+
token = Token.objects.get(user=testuser)
398+
self.client = APIClient()
399+
self.client.credentials(HTTP_AUTHORIZATION="Token " + token.key)
400+
401+
def setUp_global_writer(self):
402+
testuser = User.objects.get(id=self.GLOBAL_WRITER_USER_ID)
390403
token = Token.objects.get(user=testuser)
391404
self.client = APIClient()
392405
self.client.credentials(HTTP_AUTHORIZATION="Token " + token.key)
393406

394407
def setUp_global_owner(self):
395-
testuser = User.objects.get(id=6)
408+
testuser = User.objects.get(id=self.GLOBAL_OWNER_USER_ID)
396409
token = Token.objects.get(user=testuser)
397410
self.client = APIClient()
398411
self.client.credentials(HTTP_AUTHORIZATION="Token " + token.key)
@@ -1179,6 +1192,42 @@ def __init__(self, *args, **kwargs):
11791192
self.deleted_objects = 23
11801193
BaseClass.RESTEndpointTest.__init__(self, *args, **kwargs)
11811194

1195+
@parameterized.expand(
1196+
[
1197+
("files", {"title": "test", "file": b"empty"}),
1198+
("notes", {"entry": "string"}),
1199+
],
1200+
)
1201+
def test_related_objects(self, related_object_path, payload):
1202+
"""
1203+
Tests that BaseRelatedObjectPermission enforces the permissions not associated
1204+
with the base object. For example, even though a request to add a note to an
1205+
engagement is a POST, we do not need engagement add permissions, but rather
1206+
engagement edit permissions since that is what is defined in the
1207+
UserHasEngagementRelatedObjectPermission class
1208+
"""
1209+
self.setUp_global_reader()
1210+
# Get an engagement
1211+
response = self.client.get(self.url, format="json")
1212+
self.assertEqual(200, response.status_code, response.content[:1000])
1213+
engagement_id = response.data["results"][0]["id"]
1214+
# Attempt to add a related object
1215+
relative_url = f"{self.url}{engagement_id}/{related_object_path}/"
1216+
response = self.client.post(relative_url, payload)
1217+
self.assertEqual(403, response.status_code, response.content[:1000])
1218+
# Now switch to a user with edit permissions (but not create)
1219+
self.setUp_global_writer()
1220+
# Retry adding the related object
1221+
if related_object_path == "files":
1222+
# Convert bytes to a mock uploaded file
1223+
payload["file"] = SimpleUploadedFile(
1224+
name="test_file.txt",
1225+
content=payload["file"], # the b"empty"
1226+
content_type="text/plain",
1227+
)
1228+
response = self.client.post(relative_url, payload)
1229+
self.assertEqual(201, response.status_code, response.content[:1000])
1230+
11821231

11831232
class RiskAcceptanceTest(BaseClass.BaseClassTest):
11841233
fixtures = ["dojo_testdata.json"]
@@ -3440,6 +3489,66 @@ def test_delete(self):
34403489
response = self.client.delete(relative_url)
34413490
self.assertEqual(409, response.status_code, response.content[:1000])
34423491

3492+
def test_list_method_requires_no_authorization(self):
3493+
"""
3494+
Tests the use case of not supplying GET permissions for the BaseDjangoModelPermission
3495+
class used in the UserHasDevelopmentEnvironmentPermission class.
3496+
"""
3497+
self.setUp_not_authorized()
3498+
response = self.client.get(self.url, format="json")
3499+
self.assertEqual(200, response.status_code, response.content[:1000])
3500+
3501+
@parameterized.expand(
3502+
[
3503+
(
3504+
"add_development_environment",
3505+
"post",
3506+
201,
3507+
{
3508+
"name": "Test_1",
3509+
},
3510+
),
3511+
(
3512+
"change_development_environment",
3513+
"put",
3514+
200,
3515+
{"name": "Test_2"},
3516+
),
3517+
(
3518+
"change_development_environment",
3519+
"put",
3520+
200,
3521+
{"name": "Test_3"},
3522+
),
3523+
(
3524+
"delete_development_environment",
3525+
"delete",
3526+
409, # Deletion is blocked because of existing references, but it is better than 403 for this test
3527+
None,
3528+
),
3529+
],
3530+
)
3531+
def test_user_needs_configuration_permission(self, codename, method, expected_status, payload):
3532+
"""
3533+
Tests that BaseDjangoModelPermission enforces the django configuration permissions
3534+
through the class used in the UserHasDevelopmentEnvironmentPermission class.
3535+
"""
3536+
# Ensure we get a 403 first
3537+
self.setUp_not_authorized()
3538+
response = self.client.post(self.url, payload, format="json")
3539+
self.assertEqual(403, response.status_code, response.content[:1000])
3540+
# Now Get the same user as self.client is using, add the permission, and try again
3541+
testuser = User.objects.get(id=self.NOT_AUTHORIZED_USER_ID)
3542+
permission = Permission.objects.get(codename=codename)
3543+
testuser.user_permissions.add(permission)
3544+
if method in {"put", "patch", "delete"}:
3545+
current_objects = self.client.get(self.url, format="json").data
3546+
relative_url = self.url + "{}/".format(current_objects["results"][-1]["id"])
3547+
else:
3548+
relative_url = self.url
3549+
response = getattr(self.client, method)(relative_url, payload, format="json")
3550+
self.assertEqual(expected_status, response.status_code, response.content[:1000])
3551+
34433552

34443553
class TestTypeTest(BaseClass.AuthenticatedViewTest):
34453554
fixtures = ["dojo_testdata.json"]

0 commit comments

Comments
 (0)