diff --git a/docs/api-guide/permissions.md b/docs/api-guide/permissions.md index 25baa4813..ac2924a83 100644 --- a/docs/api-guide/permissions.md +++ b/docs/api-guide/permissions.md @@ -231,7 +231,7 @@ If you need to test if a request is a read operation or a write operation, you s --- -Custom permissions will raise a `PermissionDenied` exception if the test fails. To change the error message associated with the exception, implement a `message` attribute directly on your custom permission. Otherwise the `default_detail` attribute from `PermissionDenied` will be used. +Custom permissions will raise a `PermissionDenied` exception if the test fails. To change the error message associated with the exception, implement a `message` attribute directly on your custom permission. Otherwise the `default_detail` attribute from `PermissionDenied` will be used. Similarly, to change the code identifier associated with the exception, implement a `code` attribute directly on your custom permission - otherwise the `default_code` attribute from `PermissionDenied` will be used. from rest_framework import permissions diff --git a/rest_framework/views.py b/rest_framework/views.py index 69db053d6..d1b5e4ed9 100644 --- a/rest_framework/views.py +++ b/rest_framework/views.py @@ -166,13 +166,13 @@ class APIView(View): """ raise exceptions.MethodNotAllowed(request.method) - def permission_denied(self, request, message=None): + def permission_denied(self, request, message=None, code=None): """ If request is not permitted, determine what kind of exception to raise. """ if request.authenticators and not request.successful_authenticator: raise exceptions.NotAuthenticated() - raise exceptions.PermissionDenied(detail=message) + raise exceptions.PermissionDenied(detail=message, code=code) def throttled(self, request, wait): """ @@ -331,7 +331,9 @@ class APIView(View): for permission in self.get_permissions(): if not permission.has_permission(request, self): self.permission_denied( - request, message=getattr(permission, 'message', None) + request, + message=getattr(permission, 'message', None), + code=getattr(permission, 'code', None) ) def check_object_permissions(self, request, obj): @@ -342,7 +344,9 @@ class APIView(View): for permission in self.get_permissions(): if not permission.has_object_permission(request, self, obj): self.permission_denied( - request, message=getattr(permission, 'message', None) + request, + message=getattr(permission, 'message', None), + code=getattr(permission, 'code', None) ) def check_throttles(self, request): diff --git a/tests/test_permissions.py b/tests/test_permissions.py index b6178c0bb..d445f271d 100644 --- a/tests/test_permissions.py +++ b/tests/test_permissions.py @@ -438,6 +438,7 @@ class BasicPerm(permissions.BasePermission): class BasicPermWithDetail(permissions.BasePermission): message = 'Custom: You cannot access this resource' + code = 'permission_denied_custom' def has_permission(self, request, view): return False @@ -450,6 +451,7 @@ class BasicObjectPerm(permissions.BasePermission): class BasicObjectPermWithDetail(permissions.BasePermission): message = 'Custom: You cannot access this resource' + code = 'permission_denied_custom' def has_object_permission(self, request, view, obj): return False @@ -492,30 +494,35 @@ class CustomPermissionsTests(TestCase): credentials = basic_auth_header('username', 'password') self.request = factory.get('/1', format='json', HTTP_AUTHORIZATION=credentials) self.custom_message = 'Custom: You cannot access this resource' + self.custom_code = 'permission_denied_custom' def test_permission_denied(self): response = denied_view(self.request, pk=1) detail = response.data.get('detail') self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) self.assertNotEqual(detail, self.custom_message) + self.assertNotEqual(detail.code, self.custom_code) def test_permission_denied_with_custom_detail(self): response = denied_view_with_detail(self.request, pk=1) detail = response.data.get('detail') self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(detail, self.custom_message) + self.assertEqual(detail.code, self.custom_code) def test_permission_denied_for_object(self): response = denied_object_view(self.request, pk=1) detail = response.data.get('detail') self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) self.assertNotEqual(detail, self.custom_message) + self.assertNotEqual(detail.code, self.custom_code) def test_permission_denied_for_object_with_custom_detail(self): response = denied_object_view_with_detail(self.request, pk=1) detail = response.data.get('detail') self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(detail, self.custom_message) + self.assertEqual(detail.code, self.custom_code) class PermissionsCompositionTests(TestCase):