@@ -731,9 +731,34 @@ class AuthExampleViewSet(ExampleViewSet):
731731 class SimpleSerializer (serializers .Serializer ):
732732 name = serializers .CharField ()
733733
734+ class CrashOnObjectPermission (permissions .BasePermission ):
735+ def has_permission (self , request , view ):
736+ return True
737+
738+ def has_object_permission (self , request , view , obj ):
739+ return obj .user .is_staff
740+
741+ class Issue6855Serializer (serializers .Serializer ):
742+ name = serializers .CharField ()
743+
744+ class Issue6855Object :
745+ def __init__ (self , name ):
746+ self .name = name
747+
748+ class Issue6855ViewSet (ViewSet ):
749+ @action (detail = True )
750+ def extra_action (self , request , pk = None ):
751+ serializer = BrowsableAPIRendererTests .Issue6855Serializer (
752+ BrowsableAPIRendererTests .Issue6855Object (name = 'demo' )
753+ )
754+ return Response (serializer .data )
755+
756+ Issue6855ViewSet .permission_classes = [CrashOnObjectPermission ]
757+
734758 router = SimpleRouter ()
735759 router .register ('examples' , ExampleViewSet , basename = 'example' )
736760 router .register ('auth-examples' , AuthExampleViewSet , basename = 'auth-example' )
761+ router .register ('issue-6855' , Issue6855ViewSet , basename = 'issue-6855' )
737762 urlpatterns = [path ('api/' , include (router .urls ))]
738763
739764 def setUp (self ):
@@ -820,6 +845,34 @@ def test_extra_actions_dropdown_not_authed(self):
820845 assert '/api/examples/list_action/' not in resp .content .decode ()
821846 assert '>Extra list action<' not in resp .content .decode ()
822847
848+ def test_options_form_does_not_check_object_permissions_for_extra_action (self ):
849+ resp = self .client .get ('/api/issue-6855/1/extra_action/' , HTTP_ACCEPT = 'text/html' )
850+ assert resp .status_code == status .HTTP_200_OK
851+
852+ def test_delete_form_still_checks_object_permissions (self ):
853+ class ObjectPermissionDenied (permissions .BasePermission ):
854+ def has_permission (self , request , view ):
855+ return True
856+
857+ def has_object_permission (self , request , view , obj ):
858+ return False
859+
860+ class DummyObject :
861+ name = 'Name'
862+
863+ class DummyDeleteView (APIView ):
864+ permission_classes = [ObjectPermissionDenied ]
865+
866+ def delete (self , request ):
867+ return Response ()
868+
869+ request = Request (APIRequestFactory ().get ('/' ))
870+ serializer = BrowsableAPIRendererTests .SimpleSerializer (instance = DummyObject ())
871+ delete_form = self .renderer .get_rendered_html_form (
872+ serializer .data , DummyDeleteView (), 'DELETE' , request
873+ )
874+ assert delete_form is None
875+
823876
824877class AdminRendererTests (TestCase ):
825878
0 commit comments