@@ -856,6 +856,63 @@ 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" }),
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+ response = self .client .post (
902+ relative_url ,
903+ {
904+ "file" : SimpleUploadedFile (
905+ name = "test_file.txt" ,
906+ content = b"empty" ,
907+ content_type = "text/plain" ,
908+ ),
909+ ** payload ,
910+ },
911+ )
912+ else :
913+ response = self .client .post (relative_url , payload )
914+ self .assertIn (response .status_code , [200 , 201 ], response .content [:1000 ])
915+
859916
860917@versioned_fixtures
861918class AppAnalysisTest (BaseClass .BaseClassTest ):
@@ -1487,7 +1544,7 @@ def test_update_object_not_authorized(self):
14871544
14881545
14891546@versioned_fixtures
1490- class EngagementTest (BaseClass .BaseClassTest ):
1547+ class EngagementTest (BaseClass .RelatedObjectsTest , BaseClass . BaseClassTest ):
14911548 fixtures = ["dojo_testdata.json" ]
14921549
14931550 def __init__ (self , * args , ** kwargs ):
@@ -1517,42 +1574,6 @@ def __init__(self, *args, **kwargs):
15171574 self .deleted_objects = 23
15181575 BaseClass .RESTEndpointTest .__init__ (self , * args , ** kwargs )
15191576
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-
15561577
15571578@versioned_fixtures
15581579class RiskAcceptanceTest (BaseClass .BaseClassTest ):
@@ -1726,7 +1747,7 @@ def test_file_with_quoted_name(self):
17261747
17271748
17281749@versioned_fixtures
1729- class FindingsTest (BaseClass .BaseClassTest ):
1750+ class FindingsTest (BaseClass .RelatedObjectsTest , BaseClass . BaseClassTest ):
17301751 fixtures = ["dojo_testdata.json" ]
17311752
17321753 def __init__ (self , * args , ** kwargs ):
@@ -2415,7 +2436,7 @@ def test_severity_validation(self):
24152436
24162437
24172438@versioned_fixtures
2418- class TestsTest (BaseClass .BaseClassTest ):
2439+ class TestsTest (BaseClass .RelatedObjectsTest , BaseClass . BaseClassTest ):
24192440 fixtures = ["dojo_testdata.json" ]
24202441
24212442 def __init__ (self , * args , ** kwargs ):
0 commit comments