diff options
| author | Tom Christie | 2011-06-02 12:58:10 +0100 |
|---|---|---|
| committer | Tom Christie | 2011-06-02 12:58:10 +0100 |
| commit | b50492853f537a2473bb0a9eea86c8b0ed6b8824 (patch) | |
| tree | d289d39aacf187a8a0696a4c1c863aabe1472c3a /djangorestframework/permissions.py | |
| parent | 7ee9adbe5c03c29cd4a894dd476548f7fe73b5e4 (diff) | |
| parent | fc1640de75511006e89f033c9270ec91a9f1e4d4 (diff) | |
| download | django-rest-framework-b50492853f537a2473bb0a9eea86c8b0ed6b8824.tar.bz2 | |
pull in -dev as 0.2.0
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) |
