diff options
Diffstat (limited to 'rest_framework/permissions.py')
| -rw-r--r-- | rest_framework/permissions.py | 108 | 
1 files changed, 67 insertions, 41 deletions
| diff --git a/rest_framework/permissions.py b/rest_framework/permissions.py index 1036663e..9069d315 100644 --- a/rest_framework/permissions.py +++ b/rest_framework/permissions.py @@ -2,13 +2,11 @@  Provides a set of pluggable permission policies.  """  from __future__ import unicode_literals -import inspect -import warnings +from django.http import Http404 +from rest_framework.compat import get_model_name  SAFE_METHODS = ['GET', 'HEAD', 'OPTIONS'] -from rest_framework.compat import oauth2_provider_scope, oauth2_constants -  class BasePermission(object):      """ @@ -25,13 +23,6 @@ class BasePermission(object):          """          Return `True` if permission is granted, `False` otherwise.          """ -        if len(inspect.getargspec(self.has_permission).args) == 4: -            warnings.warn( -                'The `obj` argument in `has_permission` is deprecated. ' -                'Use `has_object_permission()` instead for object permissions.', -                DeprecationWarning, stacklevel=2 -            ) -            return self.has_permission(request, view, obj)          return True @@ -52,9 +43,7 @@ class IsAuthenticated(BasePermission):      """      def has_permission(self, request, view): -        if request.user and request.user.is_authenticated(): -            return True -        return False +        return request.user and request.user.is_authenticated()  class IsAdminUser(BasePermission): @@ -63,9 +52,7 @@ class IsAdminUser(BasePermission):      """      def has_permission(self, request, view): -        if request.user and request.user.is_staff: -            return True -        return False +        return request.user and request.user.is_staff  class IsAuthenticatedOrReadOnly(BasePermission): @@ -74,11 +61,11 @@ class IsAuthenticatedOrReadOnly(BasePermission):      """      def has_permission(self, request, view): -        if (request.method in SAFE_METHODS or +        return ( +            request.method in SAFE_METHODS or              request.user and -            request.user.is_authenticated()): -            return True -        return False +            request.user.is_authenticated() +        )  class DjangoModelPermissions(BasePermission): @@ -115,11 +102,14 @@ class DjangoModelPermissions(BasePermission):          """          kwargs = {              'app_label': model_cls._meta.app_label, -            'model_name': model_cls._meta.module_name +            'model_name': get_model_name(model_cls)          }          return [perm % kwargs for perm in self.perms_map[method]]      def has_permission(self, request, view): +        # Note that `.model` attribute on views is deprecated, although we +        # enforce the deprecation on the view `get_serializer_class()` and +        # `get_queryset()` methods, rather than here.          model_cls = getattr(view, 'model', None)          queryset = getattr(view, 'queryset', None) @@ -136,11 +126,11 @@ class DjangoModelPermissions(BasePermission):          perms = self.get_required_permissions(request.method, model_cls) -        if (request.user and +        return ( +            request.user and              (request.user.is_authenticated() or not self.authenticated_users_only) and -            request.user.has_perms(perms)): -            return True -        return False +            request.user.has_perms(perms) +        )  class DjangoModelPermissionsOrAnonReadOnly(DjangoModelPermissions): @@ -151,24 +141,60 @@ class DjangoModelPermissionsOrAnonReadOnly(DjangoModelPermissions):      authenticated_users_only = False -class TokenHasReadWriteScope(BasePermission): +class DjangoObjectPermissions(DjangoModelPermissions):      """ -    The request is authenticated as a user and the token used has the right scope +    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.      """ -    def has_permission(self, request, view): -        token = request.auth -        read_only = request.method in SAFE_METHODS +    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'], +    } -        if not token: -            return False +    def get_required_object_permissions(self, method, model_cls): +        kwargs = { +            'app_label': model_cls._meta.app_label, +            'model_name': get_model_name(model_cls) +        } +        return [perm % kwargs for perm in self.perms_map[method]] -        if hasattr(token, 'resource'):  # OAuth 1 -            return read_only or not request.auth.resource.is_readonly -        elif hasattr(token, 'scope'):  # OAuth 2 -            required = oauth2_constants.READ if read_only else oauth2_constants.WRITE -            return oauth2_provider_scope.check(required, request.auth.scope) +    def has_object_permission(self, request, view, obj): +        model_cls = getattr(view, 'model', None) +        queryset = getattr(view, 'queryset', None) -        assert False, ('TokenHasReadWriteScope requires either the' -        '`OAuthAuthentication` or `OAuth2Authentication` authentication ' -        'class to be used.') +        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 response. + +            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 | 
