diff options
Diffstat (limited to 'rest_framework/permissions.py')
| -rw-r--r-- | rest_framework/permissions.py | 60 |
1 files changed, 60 insertions, 0 deletions
diff --git a/rest_framework/permissions.py b/rest_framework/permissions.py index 0c7b02ff..14bec42c 100644 --- a/rest_framework/permissions.py +++ b/rest_framework/permissions.py @@ -2,6 +2,7 @@ Provides a set of pluggable permission policies. """ from __future__ import unicode_literals +from django.http import Http404 from rest_framework.compat import oauth2_provider_scope, oauth2_constants SAFE_METHODS = ['GET', 'HEAD', 'OPTIONS'] @@ -141,6 +142,65 @@ class DjangoModelPermissionsOrAnonReadOnly(DjangoModelPermissions): authenticated_users_only = False +class DjangoObjectPermissions(DjangoModelPermissions): + """ + 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. + + This permission can only be applied against view classes that + provide a `.model` or `.queryset` attribute. + """ + + 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.perms_map[method]] + + def has_object_permission(self, request, view, obj): + model_cls = getattr(view, 'model', None) + queryset = getattr(view, 'queryset', None) + + if model_cls is None and queryset is not None: + model_cls = queryset.model + + perms = self.get_required_object_permissions(request.method, model_cls) + user = request.user + + 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): """ The request is authenticated as a user and the token used has the right scope |
