aboutsummaryrefslogtreecommitdiffstats
path: root/rest_framework/permissions.py
diff options
context:
space:
mode:
authorTom Christie2013-09-10 21:00:13 +0100
committerTom Christie2013-09-10 21:00:13 +0100
commit5970baa20112921217ae4f2c2a9f175df25922db (patch)
tree40f4ae8e6d2ffd841c31b6a55f98d9823516d61c /rest_framework/permissions.py
parent75fb4b02b40da04f16c6c288bbe20ea0bc0b4154 (diff)
downloaddjango-rest-framework-5970baa20112921217ae4f2c2a9f175df25922db.tar.bz2
Tweaks and docs to object-level model permissions.
Diffstat (limited to 'rest_framework/permissions.py')
-rw-r--r--rest_framework/permissions.py47
1 files changed, 31 insertions, 16 deletions
diff --git a/rest_framework/permissions.py b/rest_framework/permissions.py
index 70bf9c61..53184798 100644
--- a/rest_framework/permissions.py
+++ b/rest_framework/permissions.py
@@ -152,10 +152,10 @@ class DjangoModelPermissionsOrAnonReadOnly(DjangoModelPermissions):
authenticated_users_only = False
-class DjangoObjectLevelModelPermissions(DjangoModelPermissions):
+class DjangoObjectPermissions(DjangoModelPermissions):
"""
- The request is authenticated using `django.contrib.auth` permissions.
- See: https://docs.djangoproject.com/en/dev/topics/auth/#permissions
+ The request is authenticated using Django's object-level permissions.
+ It requires an object-permissions-enabled backend, such as Django Guardian.
It ensures that the user is authenticated, and has the appropriate
`add`/`change`/`delete` permissions on the object using .has_perms.
@@ -164,21 +164,22 @@ class DjangoObjectLevelModelPermissions(DjangoModelPermissions):
provide a `.model` or `.queryset` attribute.
"""
- actions_map = {
- 'GET': ['read_%(model_name)s'],
- 'OPTIONS': ['read_%(model_name)s'],
- 'HEAD': ['read_%(model_name)s'],
- 'POST': ['add_%(model_name)s'],
- 'PUT': ['change_%(model_name)s'],
- 'PATCH': ['change_%(model_name)s'],
- 'DELETE': ['delete_%(model_name)s'],
+ perms_map = {
+ 'GET': [],
+ 'OPTIONS': [],
+ 'HEAD': [],
+ 'POST': ['%(app_label)s.add_%(model_name)s'],
+ 'PUT': ['%(app_label)s.change_%(model_name)s'],
+ 'PATCH': ['%(app_label)s.change_%(model_name)s'],
+ 'DELETE': ['%(app_label)s.delete_%(model_name)s'],
}
def get_required_object_permissions(self, method, model_cls):
kwargs = {
+ 'app_label': model_cls._meta.app_label,
'model_name': model_cls._meta.module_name
}
- return [perm % kwargs for perm in self.actions_map[method]]
+ return [perm % kwargs for perm in self.perms_map[method]]
def has_object_permission(self, request, view, obj):
model_cls = getattr(view, 'model', None)
@@ -190,10 +191,24 @@ class DjangoObjectLevelModelPermissions(DjangoModelPermissions):
perms = self.get_required_object_permissions(request.method, model_cls)
user = request.user
- check = user.has_perms(perms, obj)
- if not check:
- raise Http404
- return user.has_perms(perms, obj)
+ if not user.has_perms(perms, obj):
+ # If the user does not have permissions we need to determine if
+ # they have read permissions to see 403, or not, and simply see
+ # a 404 reponse.
+
+ if request.method in ('GET', 'OPTIONS', 'HEAD'):
+ # Read permissions already checked and failed, no need
+ # to make another lookup.
+ raise Http404
+
+ read_perms = self.get_required_object_permissions('GET', model_cls)
+ if not user.has_perms(read_perms, obj):
+ raise Http404
+
+ # Has read permissions.
+ return False
+
+ return True
class TokenHasReadWriteScope(BasePermission):