django-rest-framework/rest_framework/permissions.py

226 lines
7.2 KiB
Python
Raw Normal View History

"""
2012-10-15 16:27:50 +04:00
Provides a set of pluggable permission policies.
"""
from __future__ import unicode_literals
import inspect
import warnings
SAFE_METHODS = ['GET', 'HEAD', 'OPTIONS']
from django.http import Http404
from rest_framework.compat import oauth2_provider_scope, oauth2_constants, guardian
class BasePermission(object):
"""
A base class from which all permission classes should inherit.
"""
def has_permission(self, request, view):
"""
2012-10-15 16:27:50 +04:00
Return `True` if permission is granted, `False` otherwise.
"""
return True
def has_object_permission(self, request, view, obj):
"""
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
2012-10-27 23:17:49 +04:00
class AllowAny(BasePermission):
"""
Allow any access.
This isn't strictly required, since you could use an empty
permission_classes list, but it's useful because it makes the intention
more explicit.
"""
def has_permission(self, request, view):
2012-10-27 23:17:49 +04:00
return True
class IsAuthenticated(BasePermission):
"""
Allows access only to authenticated users.
"""
def has_permission(self, request, view):
if request.user and request.user.is_authenticated():
return True
return False
class IsAdminUser(BasePermission):
"""
Allows access only to admin users.
"""
def has_permission(self, request, view):
if request.user and request.user.is_staff:
return True
return False
class IsAuthenticatedOrReadOnly(BasePermission):
"""
The request is authenticated as a user, or is a read-only request.
"""
def has_permission(self, request, view):
if (request.method in SAFE_METHODS or
request.user and
request.user.is_authenticated()):
return True
return False
class DjangoModelPermissions(BasePermission):
"""
The request is authenticated using `django.contrib.auth` permissions.
See: https://docs.djangoproject.com/en/dev/topics/auth/#permissions
It ensures that the user is authenticated, and has the appropriate
`add`/`change`/`delete` permissions on the model.
This permission can only be applied against view classes that
provide a `.model` or `.queryset` attribute.
"""
# Map methods into required permission codes.
# Override this if you need to also provide 'view' permissions,
# or if you want to provide custom permission codes.
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'],
}
authenticated_users_only = True
def get_required_permissions(self, method, model_cls):
"""
Given a model and an HTTP method, return the list of permission
codes that the user is required to have.
"""
kwargs = {
'app_label': model_cls._meta.app_label,
2012-10-27 13:32:49 +04:00
'model_name': model_cls._meta.module_name
}
return [perm % kwargs for perm in self.perms_map[method]]
def has_permission(self, request, view):
2012-10-15 16:27:50 +04:00
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
2013-05-16 18:08:12 +04:00
# Workaround to ensure DjangoModelPermissions are not applied
# to the root view when using DefaultRouter.
if model_cls is None and getattr(view, '_ignore_model_permissions', False):
2013-05-16 18:08:12 +04:00
return True
assert model_cls, ('Cannot apply DjangoModelPermissions on a view that'
' does not have `.model` or `.queryset` property.')
2012-10-15 16:27:50 +04:00
perms = self.get_required_permissions(request.method, model_cls)
if (request.user and
(request.user.is_authenticated() or not self.authenticated_users_only) and
request.user.has_perms(perms)):
return True
return False
class DjangoModelPermissionsOrAnonReadOnly(DjangoModelPermissions):
"""
Similar to DjangoModelPermissions, except that anonymous users are
allowed read-only access.
"""
authenticated_users_only = False
class DjangoObjectLevelModelPermissions(DjangoModelPermissions):
"""
Basic object level permissions utilizing django-guardian.
"""
def __init__(self):
assert guardian, 'Using DjangoObjectLevelModelPermissions, but django-guardian is not installed'
action_perm_map = {
'list': 'read',
'retrieve': 'read',
'create': 'add',
'partial_update': 'change',
'update': 'change',
'destroy': 'delete',
}
def _get_names(self, view):
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
if not model_cls: # no model, no model based permissions
return None
model_name = model_cls._meta.module_name
return model_name
def has_permission(self, request, view):
if view.action == 'list':
user = request.user
queryset = view.get_queryset()
model_name = self._get_names(view)
view.queryset = guardian.shortcuts.get_objects_for_user(user, 'read_' + model_name, queryset) #TODO: move to filter
return super(DjangoObjectLevelModelPermissions, self).has_permission(request, view)
def has_object_permission(self, request, view, obj):
user = request.user
model_name = self._get_names(view)
action = self.action_perm_map.get(view.action)
assert action, "Tried to determine object permissions but no action specified in view"
perm = "{action}_{model_name}".format(action=action, model_name=model_name)
check = user.has_perm(perm, obj)
if not check:
raise Http404
return user.has_perm(perm, obj)
class TokenHasReadWriteScope(BasePermission):
"""
The request is authenticated as a user and the token used has the right scope
"""
def has_permission(self, request, view):
token = request.auth
read_only = request.method in SAFE_METHODS
if not token:
return False
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)
2013-03-13 00:12:28 +04:00
assert False, ('TokenHasReadWriteScope requires either the'
'`OAuthAuthentication` or `OAuth2Authentication` authentication '
'class to be used.')