django-rest-framework/djangorestframework/permissions.py

125 lines
3.4 KiB
Python
Raw Normal View History

"""
The :mod:`permissions` module bundles a set of permission classes that are used
for checking if a request passes a certain set of constraints. You can assign a permision
class to your view by setting your View's :attr:`permissions` class attribute.
"""
from django.core.cache import cache
from djangorestframework import status
2011-05-10 13:49:28 +04:00
from djangorestframework.response import ErrorResponse
import time
2011-05-10 13:49:28 +04:00
__all__ = (
'BasePermission',
'FullAnonAccess',
'IsAuthenticated',
'IsAdminUser',
'IsUserOrIsAnonReadOnly',
'PerUserThrottling'
)
_403_FORBIDDEN_RESPONSE = ErrorResponse(
status.HTTP_403_FORBIDDEN,
{'detail': 'You do not have permission to access this resource. ' +
'You may need to login or otherwise authenticate the request.'})
_503_THROTTLED_RESPONSE = ErrorResponse(
status.HTTP_503_SERVICE_UNAVAILABLE,
{'detail': 'request was throttled'})
class BasePermission(object):
2011-05-10 13:49:28 +04:00
"""
A base class from which all permission classes should inherit.
"""
def __init__(self, view):
2011-05-10 13:49:28 +04:00
"""
Permission classes are always passed the current view on creation.
"""
self.view = view
2011-05-10 13:49:28 +04:00
def check_permission(self, auth):
"""
Should simply return, or raise an :exc:`response.ErrorResponse`.
2011-05-10 13:49:28 +04:00
"""
pass
2011-05-04 12:21:17 +04:00
class FullAnonAccess(BasePermission):
2011-05-10 13:49:28 +04:00
"""
Allows full access.
"""
def check_permission(self, user):
pass
2011-05-04 12:21:17 +04:00
class IsAuthenticated(BasePermission):
2011-05-10 13:49:28 +04:00
"""
Allows access only to authenticated users.
"""
2011-05-10 13:49:28 +04:00
def check_permission(self, user):
if not user.is_authenticated():
raise _403_FORBIDDEN_RESPONSE
class IsAdminUser(BasePermission):
2011-05-10 13:49:28 +04:00
"""
Allows access only to admin users.
"""
def check_permission(self, user):
if not user.is_admin():
raise _403_FORBIDDEN_RESPONSE
class IsUserOrIsAnonReadOnly(BasePermission):
"""
The request is authenticated as a user, or is a read-only request.
"""
def check_permission(self, user):
if (not user.is_authenticated() and
self.view.method != 'GET' and
self.view.method != 'HEAD'):
raise _403_FORBIDDEN_RESPONSE
class PerUserThrottling(BasePermission):
"""
Rate throttling of requests on a per-user basis.
The rate (requests / seconds) is set by a :attr:`throttle` attribute on the ``View`` class.
The attribute is a two tuple of the form (number of requests, duration in seconds).
2011-05-10 13:49:28 +04:00
The user id will be used as a unique identifier if the user is authenticated.
For anonymous requests, the IP address of the client will be used.
Previous request information used for throttling is stored in the cache.
"""
2011-05-10 13:49:28 +04:00
def check_permission(self, user):
(num_requests, duration) = getattr(self.view, 'throttle', (0, 0))
2011-05-10 13:49:28 +04:00
if user.is_authenticated():
ident = str(user)
else:
ident = self.view.request.META.get('REMOTE_ADDR', None)
key = 'throttle_%s' % ident
history = cache.get(key, [])
now = time.time()
# Drop any requests from the history which have now passed the throttle duration
while history and history[0] < now - duration:
history.pop()
if len(history) >= num_requests:
2011-05-10 13:49:28 +04:00
raise _503_THROTTLED_RESPONSE
history.insert(0, now)
2011-05-10 13:49:28 +04:00
cache.set(key, history, duration)