@@ -856,6 +856,56 @@ def test_detail_configuration_not_authorized(self):
856856 response = self .client .get (relative_url )
857857 self .assertEqual (200 , response .status_code , response .content [:1000 ])
858858
859+ class RelatedObjectsTest (BaseClassTest ):
860+ def test_notes_can_be_added_by_users_with_read_only_permissions (self ):
861+ self .setUp_global_reader ()
862+ response = self .client .get (self .url , format = "json" )
863+ self .assertEqual (200 , response .status_code , response .content [:1000 ])
864+ engagement_id = response .data ["results" ][0 ]["id" ]
865+ # Attempt to add a note with reader permissions
866+ relative_url = f"{ self .url } { engagement_id } /notes/"
867+ response = self .client .post (relative_url , {"entry" : "string" })
868+ self .assertEqual (201 , response .status_code , response .content [:1000 ])
869+
870+ @parameterized .expand (
871+ [
872+ ("files" , {"title" : "test" , "file" : b"empty" }),
873+ ("tags" , {"tags" : ["apple" , "banana" , "cherry" ]}),
874+ ],
875+ )
876+ def test_related_objects (self , related_object_path , payload ):
877+ """
878+ Tests that BaseRelatedObjectPermission enforces the permissions not associated
879+ with the base object. For example, even though a request to add a tag to an
880+ engagement is a POST, we do not need engagement add permissions, but rather
881+ engagement edit permissions since that is what is defined in the
882+ UserHasEngagementRelatedObjectPermission class
883+ """
884+ self .setUp_global_reader ()
885+ # Skip tags for engagement and tests
886+ if related_object_path == "tags" and self .endpoint_model in {Engagement , Test }:
887+ return
888+ # Get an object
889+ response = self .client .get (self .url , format = "json" )
890+ self .assertEqual (200 , response .status_code , response .content [:1000 ])
891+ object_id = response .data ["results" ][0 ]["id" ]
892+ # Attempt to add a related object
893+ relative_url = f"{ self .url } { object_id } /{ related_object_path } /"
894+ response = self .client .post (relative_url , payload )
895+ self .assertEqual (403 , response .status_code , response .content [:1000 ])
896+ # Now switch to a user with edit permissions (but not create)
897+ self .setUp_global_writer ()
898+ # Retry adding the related object
899+ if related_object_path == "files" :
900+ # Convert bytes to a mock uploaded file
901+ payload ["file" ] = SimpleUploadedFile (
902+ name = "test_file.txt" ,
903+ content = payload ["file" ], # the b"empty"
904+ content_type = "text/plain" ,
905+ )
906+ response = self .client .post (relative_url , payload )
907+ self .assertIn (response .status_code , [200 , 201 ], response .content [:1000 ])
908+
859909
860910@versioned_fixtures
861911class AppAnalysisTest (BaseClass .BaseClassTest ):
@@ -1487,7 +1537,7 @@ def test_update_object_not_authorized(self):
14871537
14881538
14891539@versioned_fixtures
1490- class EngagementTest (BaseClass .BaseClassTest ):
1540+ class EngagementTest (BaseClass .RelatedObjectsTest , BaseClass . BaseClassTest ):
14911541 fixtures = ["dojo_testdata.json" ]
14921542
14931543 def __init__ (self , * args , ** kwargs ):
@@ -1517,42 +1567,6 @@ def __init__(self, *args, **kwargs):
15171567 self .deleted_objects = 23
15181568 BaseClass .RESTEndpointTest .__init__ (self , * args , ** kwargs )
15191569
1520- @parameterized .expand (
1521- [
1522- ("files" , {"title" : "test" , "file" : b"empty" }),
1523- ("notes" , {"entry" : "string" }),
1524- ],
1525- )
1526- def test_related_objects (self , related_object_path , payload ):
1527- """
1528- Tests that BaseRelatedObjectPermission enforces the permissions not associated
1529- with the base object. For example, even though a request to add a note to an
1530- engagement is a POST, we do not need engagement add permissions, but rather
1531- engagement edit permissions since that is what is defined in the
1532- UserHasEngagementRelatedObjectPermission class
1533- """
1534- self .setUp_global_reader ()
1535- # Get an engagement
1536- response = self .client .get (self .url , format = "json" )
1537- self .assertEqual (200 , response .status_code , response .content [:1000 ])
1538- engagement_id = response .data ["results" ][0 ]["id" ]
1539- # Attempt to add a related object
1540- relative_url = f"{ self .url } { engagement_id } /{ related_object_path } /"
1541- response = self .client .post (relative_url , payload )
1542- self .assertEqual (403 , response .status_code , response .content [:1000 ])
1543- # Now switch to a user with edit permissions (but not create)
1544- self .setUp_global_writer ()
1545- # Retry adding the related object
1546- if related_object_path == "files" :
1547- # Convert bytes to a mock uploaded file
1548- payload ["file" ] = SimpleUploadedFile (
1549- name = "test_file.txt" ,
1550- content = payload ["file" ], # the b"empty"
1551- content_type = "text/plain" ,
1552- )
1553- response = self .client .post (relative_url , payload )
1554- self .assertEqual (201 , response .status_code , response .content [:1000 ])
1555-
15561570
15571571@versioned_fixtures
15581572class RiskAcceptanceTest (BaseClass .BaseClassTest ):
@@ -1726,7 +1740,7 @@ def test_file_with_quoted_name(self):
17261740
17271741
17281742@versioned_fixtures
1729- class FindingsTest (BaseClass .BaseClassTest ):
1743+ class FindingsTest (BaseClass .RelatedObjectsTest , BaseClass . BaseClassTest ):
17301744 fixtures = ["dojo_testdata.json" ]
17311745
17321746 def __init__ (self , * args , ** kwargs ):
@@ -2415,7 +2429,7 @@ def test_severity_validation(self):
24152429
24162430
24172431@versioned_fixtures
2418- class TestsTest (BaseClass .BaseClassTest ):
2432+ class TestsTest (BaseClass .RelatedObjectsTest , BaseClass . BaseClassTest ):
24192433 fixtures = ["dojo_testdata.json" ]
24202434
24212435 def __init__ (self , * args , ** kwargs ):
0 commit comments