diff options
Diffstat (limited to 'djangorestframework/permissions.py')
| -rw-r--r-- | djangorestframework/permissions.py | 124 |
1 files changed, 124 insertions, 0 deletions
diff --git a/djangorestframework/permissions.py b/djangorestframework/permissions.py new file mode 100644 index 00000000..1f6151f8 --- /dev/null +++ b/djangorestframework/permissions.py @@ -0,0 +1,124 @@ +""" +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 +from djangorestframework.response import ErrorResponse +import time + +__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): + """ + A base class from which all permission classes should inherit. + """ + def __init__(self, view): + """ + Permission classes are always passed the current view on creation. + """ + self.view = view + + def check_permission(self, auth): + """ + Should simply return, or raise an :exc:`response.ErrorResponse`. + """ + pass + + +class FullAnonAccess(BasePermission): + """ + Allows full access. + """ + + def check_permission(self, user): + pass + + +class IsAuthenticated(BasePermission): + """ + Allows access only to authenticated users. + """ + + def check_permission(self, user): + if not user.is_authenticated(): + raise _403_FORBIDDEN_RESPONSE + + +class IsAdminUser(BasePermission): + """ + 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). + + 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. + """ + + def check_permission(self, user): + (num_requests, duration) = getattr(self.view, 'throttle', (0, 0)) + + if user.is_authenticated(): + 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: + raise _503_THROTTLED_RESPONSE + + history.insert(0, now) + cache.set(key, history, duration) |
