2012-09-20 16:06:27 +04:00
|
|
|
"""
|
2012-10-15 16:27:50 +04:00
|
|
|
Provides a set of pluggable permission policies.
|
2012-09-20 16:06:27 +04:00
|
|
|
"""
|
2013-02-05 00:55:35 +04:00
|
|
|
from __future__ import unicode_literals
|
2013-02-11 16:47:56 +04:00
|
|
|
import inspect
|
|
|
|
import warnings
|
2012-09-20 16:06:27 +04:00
|
|
|
|
|
|
|
SAFE_METHODS = ['GET', 'HEAD', 'OPTIONS']
|
|
|
|
|
2013-09-08 08:18:52 +04:00
|
|
|
from django.http import Http404
|
2013-09-09 19:39:09 +04:00
|
|
|
from rest_framework.compat import oauth2_provider_scope, oauth2_constants
|
2013-03-10 17:08:29 +04:00
|
|
|
|
2012-09-20 16:06:27 +04:00
|
|
|
|
|
|
|
class BasePermission(object):
|
|
|
|
"""
|
|
|
|
A base class from which all permission classes should inherit.
|
|
|
|
"""
|
|
|
|
|
2013-02-11 16:47:56 +04:00
|
|
|
def has_permission(self, request, view):
|
2012-09-20 16:06:27 +04:00
|
|
|
"""
|
2012-10-15 16:27:50 +04:00
|
|
|
Return `True` if permission is granted, `False` otherwise.
|
2012-09-20 16:06:27 +04:00
|
|
|
"""
|
2013-02-11 16:47:56 +04:00
|
|
|
return True
|
|
|
|
|
|
|
|
def has_object_permission(self, request, view, obj):
|
|
|
|
"""
|
|
|
|
Return `True` if permission is granted, `False` otherwise.
|
|
|
|
"""
|
2013-04-23 14:31:38 +04:00
|
|
|
if len(inspect.getargspec(self.has_permission).args) == 4:
|
2013-04-29 15:45:00 +04:00
|
|
|
warnings.warn(
|
|
|
|
'The `obj` argument in `has_permission` is deprecated. '
|
|
|
|
'Use `has_object_permission()` instead for object permissions.',
|
|
|
|
DeprecationWarning, stacklevel=2
|
|
|
|
)
|
2013-02-11 16:47:56 +04:00
|
|
|
return self.has_permission(request, view, obj)
|
|
|
|
return True
|
2012-09-20 16:06:27 +04:00
|
|
|
|
|
|
|
|
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.
|
|
|
|
"""
|
2013-02-11 16:47:56 +04:00
|
|
|
def has_permission(self, request, view):
|
2012-10-27 23:17:49 +04:00
|
|
|
return True
|
|
|
|
|
|
|
|
|
2012-09-20 16:06:27 +04:00
|
|
|
class IsAuthenticated(BasePermission):
|
|
|
|
"""
|
|
|
|
Allows access only to authenticated users.
|
|
|
|
"""
|
|
|
|
|
2013-02-11 16:47:56 +04:00
|
|
|
def has_permission(self, request, view):
|
2012-09-20 16:06:27 +04:00
|
|
|
if request.user and request.user.is_authenticated():
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
class IsAdminUser(BasePermission):
|
|
|
|
"""
|
|
|
|
Allows access only to admin users.
|
|
|
|
"""
|
|
|
|
|
2013-02-11 16:47:56 +04:00
|
|
|
def has_permission(self, request, view):
|
2012-09-20 16:06:27 +04:00
|
|
|
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.
|
|
|
|
"""
|
|
|
|
|
2013-02-11 16:47:56 +04:00
|
|
|
def has_permission(self, request, view):
|
2012-09-20 16:06:27 +04:00
|
|
|
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.
|
|
|
|
|
2013-04-30 17:34:28 +04:00
|
|
|
This permission can only be applied against view classes that
|
|
|
|
provide a `.model` or `.queryset` attribute.
|
2012-09-20 16:06:27 +04:00
|
|
|
"""
|
|
|
|
|
|
|
|
# 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'],
|
|
|
|
}
|
|
|
|
|
2013-03-09 03:42:20 +04:00
|
|
|
authenticated_users_only = True
|
|
|
|
|
2012-09-20 16:06:27 +04:00
|
|
|
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
|
2012-09-20 16:06:27 +04:00
|
|
|
}
|
|
|
|
return [perm % kwargs for perm in self.perms_map[method]]
|
|
|
|
|
2013-02-11 16:47:56 +04:00
|
|
|
def has_permission(self, request, view):
|
2012-10-15 16:27:50 +04:00
|
|
|
model_cls = getattr(view, 'model', None)
|
2013-03-09 03:42:20 +04:00
|
|
|
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.
|
2013-06-18 14:10:56 +04:00
|
|
|
if model_cls is None and getattr(view, '_ignore_model_permissions', False):
|
2013-05-16 18:08:12 +04:00
|
|
|
return True
|
|
|
|
|
2013-03-09 03:42:20 +04:00
|
|
|
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
|
|
|
|
2012-09-20 16:06:27 +04:00
|
|
|
perms = self.get_required_permissions(request.method, model_cls)
|
|
|
|
|
|
|
|
if (request.user and
|
2013-03-09 03:42:20 +04:00
|
|
|
(request.user.is_authenticated() or not self.authenticated_users_only) and
|
2013-02-10 20:43:52 +04:00
|
|
|
request.user.has_perms(perms)):
|
2012-09-20 16:06:27 +04:00
|
|
|
return True
|
|
|
|
return False
|
2013-03-10 17:08:29 +04:00
|
|
|
|
|
|
|
|
2013-04-30 17:34:28 +04:00
|
|
|
class DjangoModelPermissionsOrAnonReadOnly(DjangoModelPermissions):
|
|
|
|
"""
|
|
|
|
Similar to DjangoModelPermissions, except that anonymous users are
|
|
|
|
allowed read-only access.
|
|
|
|
"""
|
|
|
|
authenticated_users_only = False
|
|
|
|
|
|
|
|
|
2013-09-06 20:01:31 +04:00
|
|
|
class DjangoObjectLevelModelPermissions(DjangoModelPermissions):
|
2013-09-08 08:18:52 +04:00
|
|
|
"""
|
2013-09-09 20:32:29 +04:00
|
|
|
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 object using .has_perms.
|
|
|
|
|
|
|
|
This permission can only be applied against view classes that
|
|
|
|
provide a `.model` or `.queryset` attribute.
|
2013-09-08 08:18:52 +04:00
|
|
|
"""
|
|
|
|
|
2013-09-09 19:39:09 +04:00
|
|
|
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'],
|
2013-09-08 08:18:52 +04:00
|
|
|
}
|
|
|
|
|
2013-09-09 19:39:09 +04:00
|
|
|
def get_required_object_permissions(self, method, model_cls):
|
|
|
|
kwargs = {
|
|
|
|
'model_name': model_cls._meta.module_name
|
|
|
|
}
|
|
|
|
return [perm % kwargs for perm in self.actions_map[method]]
|
2013-09-08 08:18:52 +04:00
|
|
|
|
|
|
|
def has_object_permission(self, request, view, obj):
|
2013-09-09 19:39:09 +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-09-08 08:18:52 +04:00
|
|
|
|
2013-09-09 19:39:09 +04:00
|
|
|
perms = self.get_required_object_permissions(request.method, model_cls)
|
2013-09-08 08:48:03 +04:00
|
|
|
user = request.user
|
|
|
|
|
2013-09-09 19:39:09 +04:00
|
|
|
check = user.has_perms(perms, obj)
|
2013-09-08 08:18:52 +04:00
|
|
|
if not check:
|
|
|
|
raise Http404
|
2013-09-09 19:39:09 +04:00
|
|
|
return user.has_perms(perms, obj)
|
2013-09-08 08:18:52 +04:00
|
|
|
|
2013-09-06 20:01:31 +04:00
|
|
|
|
2013-03-10 17:08:29 +04:00
|
|
|
class TokenHasReadWriteScope(BasePermission):
|
|
|
|
"""
|
|
|
|
The request is authenticated as a user and the token used has the right scope
|
|
|
|
"""
|
|
|
|
|
|
|
|
def has_permission(self, request, view):
|
2013-03-12 23:07:30 +04:00
|
|
|
token = request.auth
|
2013-03-10 17:08:29 +04:00
|
|
|
read_only = request.method in SAFE_METHODS
|
2013-03-12 23:07:30 +04:00
|
|
|
|
|
|
|
if not token:
|
2013-03-10 17:08:29 +04:00
|
|
|
return False
|
2013-03-12 23:07:30 +04:00
|
|
|
|
|
|
|
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.')
|