2011-04-27 21:08:32 +04:00
|
|
|
from django.core.cache import cache
|
|
|
|
from djangorestframework import status
|
2011-05-10 13:49:28 +04:00
|
|
|
from djangorestframework.response import ErrorResponse
|
2011-04-27 21:08:32 +04:00
|
|
|
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'})
|
|
|
|
|
|
|
|
|
2011-04-27 21:08:32 +04:00
|
|
|
|
|
|
|
class BasePermission(object):
|
2011-05-10 13:49:28 +04:00
|
|
|
"""
|
|
|
|
A base class from which all permission classes should inherit.
|
|
|
|
"""
|
2011-04-27 21:08:32 +04:00
|
|
|
def __init__(self, view):
|
2011-05-10 13:49:28 +04:00
|
|
|
"""
|
|
|
|
Permission classes are always passed the current view on creation.
|
|
|
|
"""
|
2011-04-27 21:08:32 +04:00
|
|
|
self.view = view
|
|
|
|
|
2011-05-10 13:49:28 +04:00
|
|
|
def check_permission(self, auth):
|
|
|
|
"""
|
2011-05-19 00:13:48 +04:00
|
|
|
Should simply return, or raise an :class:`response.ErrorResponse`.
|
2011-05-10 13:49:28 +04:00
|
|
|
"""
|
|
|
|
pass
|
2011-04-27 21:08:32 +04:00
|
|
|
|
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
|
|
|
|
2011-04-27 21:08:32 +04:00
|
|
|
class IsAuthenticated(BasePermission):
|
2011-05-10 13:49:28 +04:00
|
|
|
"""
|
|
|
|
Allows access only to authenticated users.
|
|
|
|
"""
|
2011-04-27 21:08:32 +04:00
|
|
|
|
2011-05-10 13:49:28 +04:00
|
|
|
def check_permission(self, user):
|
|
|
|
if not user.is_authenticated():
|
|
|
|
raise _403_FORBIDDEN_RESPONSE
|
2011-04-27 21:08:32 +04:00
|
|
|
|
2011-05-19 11:36:55 +04:00
|
|
|
|
2011-05-19 00:13:48 +04:00
|
|
|
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.
|
|
|
|
|
2011-05-19 00:13:48 +04:00
|
|
|
The rate (requests / seconds) is set by a :attr:`throttle` attribute on the ``View`` class.
|
2011-04-27 21:08:32 +04:00
|
|
|
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.
|
2011-04-27 21:08:32 +04:00
|
|
|
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):
|
2011-04-27 21:08:32 +04:00
|
|
|
(num_requests, duration) = getattr(self.view, 'throttle', (0, 0))
|
|
|
|
|
2011-05-10 13:49:28 +04:00
|
|
|
if user.is_authenticated():
|
2011-04-27 21:08:32 +04:00
|
|
|
ident = str(auth)
|
|
|
|
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
|
2011-04-27 21:08:32 +04:00
|
|
|
|
|
|
|
history.insert(0, now)
|
2011-05-10 13:49:28 +04:00
|
|
|
cache.set(key, history, duration)
|