From 5bb6301b7f53e3815ab1a81a5fa38721dc95b113 Mon Sep 17 00:00:00 2001 From: Sébastien Piquemal Date: Thu, 2 Feb 2012 18:19:44 +0200 Subject: Response as a subclass of HttpResponse - first draft, not quite there yet. --- djangorestframework/permissions.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'djangorestframework/permissions.py') diff --git a/djangorestframework/permissions.py b/djangorestframework/permissions.py index dfe55ce9..bce03cab 100644 --- a/djangorestframework/permissions.py +++ b/djangorestframework/permissions.py @@ -22,13 +22,13 @@ __all__ = ( _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.'}) + content={'detail': 'You do not have permission to access this resource. ' + + 'You may need to login or otherwise authenticate the request.'}, + status=status.HTTP_403_FORBIDDEN) _503_SERVICE_UNAVAILABLE = ErrorResponse( - status.HTTP_503_SERVICE_UNAVAILABLE, - {'detail': 'request was throttled'}) + content={'detail': 'request was throttled'}, + status=status.HTTP_503_SERVICE_UNAVAILABLE) class BasePermission(object): @@ -152,7 +152,7 @@ class BaseThrottle(BasePermission): self.history.insert(0, self.now) cache.set(self.key, self.history, self.duration) header = 'status=SUCCESS; next=%s sec' % self.next() - self.view.add_header('X-Throttle', header) + self.view.headers['X-Throttle'] = header def throttle_failure(self): """ @@ -160,7 +160,7 @@ class BaseThrottle(BasePermission): Raises a '503 service unavailable' response. """ header = 'status=FAILURE; next=%s sec' % self.next() - self.view.add_header('X-Throttle', header) + self.view.headers['X-Throttle'] = header raise _503_SERVICE_UNAVAILABLE def next(self): -- cgit v1.2.3 From ca96b4523b4c09489e4bfe726a894a5c6ada78aa Mon Sep 17 00:00:00 2001 From: Sébastien Piquemal Date: Tue, 7 Feb 2012 13:15:30 +0200 Subject: cleaned a bit Response/ResponseMixin code, added some documentation + renamed ErrorResponse to ImmediateResponse --- djangorestframework/permissions.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'djangorestframework/permissions.py') diff --git a/djangorestframework/permissions.py b/djangorestframework/permissions.py index bce03cab..4ddc35cb 100644 --- a/djangorestframework/permissions.py +++ b/djangorestframework/permissions.py @@ -6,7 +6,7 @@ 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 +from djangorestframework.response import ImmediateResponse import time __all__ = ( @@ -21,12 +21,12 @@ __all__ = ( ) -_403_FORBIDDEN_RESPONSE = ErrorResponse( +_403_FORBIDDEN_RESPONSE = ImmediateResponse( content={'detail': 'You do not have permission to access this resource. ' + 'You may need to login or otherwise authenticate the request.'}, status=status.HTTP_403_FORBIDDEN) -_503_SERVICE_UNAVAILABLE = ErrorResponse( +_503_SERVICE_UNAVAILABLE = ImmediateResponse( content={'detail': 'request was throttled'}, status=status.HTTP_503_SERVICE_UNAVAILABLE) @@ -43,7 +43,7 @@ class BasePermission(object): def check_permission(self, auth): """ - Should simply return, or raise an :exc:`response.ErrorResponse`. + Should simply return, or raise an :exc:`response.ImmediateResponse`. """ pass @@ -116,7 +116,7 @@ class BaseThrottle(BasePermission): def check_permission(self, auth): """ Check the throttling. - Return `None` or raise an :exc:`.ErrorResponse`. + Return `None` or raise an :exc:`.ImmediateResponse`. """ num, period = getattr(self.view, self.attr_name, self.default).split('/') self.num_requests = int(num) -- cgit v1.2.3 From db0b01037a95946938ccd44eae14d8779bfff1a9 Mon Sep 17 00:00:00 2001 From: Sébastien Piquemal Date: Fri, 10 Feb 2012 10:18:39 +0200 Subject: made suggested fixes --- djangorestframework/permissions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'djangorestframework/permissions.py') diff --git a/djangorestframework/permissions.py b/djangorestframework/permissions.py index 4ddc35cb..aa4cd631 100644 --- a/djangorestframework/permissions.py +++ b/djangorestframework/permissions.py @@ -22,12 +22,12 @@ __all__ = ( _403_FORBIDDEN_RESPONSE = ImmediateResponse( - content={'detail': 'You do not have permission to access this resource. ' + + {'detail': 'You do not have permission to access this resource. ' + 'You may need to login or otherwise authenticate the request.'}, status=status.HTTP_403_FORBIDDEN) _503_SERVICE_UNAVAILABLE = ImmediateResponse( - content={'detail': 'request was throttled'}, + {'detail': 'request was throttled'}, status=status.HTTP_503_SERVICE_UNAVAILABLE) -- cgit v1.2.3 From 1ff741d1ccc38f099a7159bdef787e5c04dc4f79 Mon Sep 17 00:00:00 2001 From: Sébastien Piquemal Date: Thu, 23 Feb 2012 23:34:20 +0200 Subject: updated docs --- djangorestframework/permissions.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'djangorestframework/permissions.py') diff --git a/djangorestframework/permissions.py b/djangorestframework/permissions.py index 335a7213..207a57b1 100644 --- a/djangorestframework/permissions.py +++ b/djangorestframework/permissions.py @@ -1,7 +1,8 @@ """ -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 permission -class to your view by setting your View's :attr:`permissions` class attribute. +The :mod:`permissions` module bundles a set of permission classes that are used +for checking if a request passes a certain set of constraints. + +Permission behavior is provided by mixing the :class:`mixins.PermissionsMixin` class into a :class:`View` class. """ from django.core.cache import cache -- cgit v1.2.3 From aed26b218ea39110489e85abc6f412399a1774a1 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 24 Aug 2012 22:11:00 +0100 Subject: Drop out resources & mixins --- djangorestframework/permissions.py | 14 -------------- 1 file changed, 14 deletions(-) (limited to 'djangorestframework/permissions.py') diff --git a/djangorestframework/permissions.py b/djangorestframework/permissions.py index 4a2b1b00..ec008bd9 100644 --- a/djangorestframework/permissions.py +++ b/djangorestframework/permissions.py @@ -18,7 +18,6 @@ __all__ = ( 'IsUserOrIsAnonReadOnly', 'PerUserThrottling', 'PerViewThrottling', - 'PerResourceThrottling' ) SAFE_METHODS = ['GET', 'HEAD', 'OPTIONS'] @@ -253,16 +252,3 @@ class PerViewThrottling(BaseThrottle): def get_cache_key(self): return 'throttle_view_%s' % self.view.__class__.__name__ - - -class PerResourceThrottling(BaseThrottle): - """ - Limits the rate of API calls that may be used against all views on - a given resource. - - The class name of the resource is used as a unique identifier to - throttle against. - """ - - def get_cache_key(self): - return 'throttle_resource_%s' % self.view.resource.__class__.__name__ -- cgit v1.2.3 From 1c28562397f168dc5e71fe1ccd61a8d7253b41e8 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Sat, 25 Aug 2012 13:43:28 +0100 Subject: Removing 403 immediate response --- djangorestframework/permissions.py | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) (limited to 'djangorestframework/permissions.py') diff --git a/djangorestframework/permissions.py b/djangorestframework/permissions.py index ec008bd9..b56d8a32 100644 --- a/djangorestframework/permissions.py +++ b/djangorestframework/permissions.py @@ -7,6 +7,7 @@ Permission behavior is provided by mixing the :class:`mixins.PermissionsMixin` c from django.core.cache import cache from djangorestframework import status +from djangorestframework.exceptions import PermissionDenied from djangorestframework.response import ImmediateResponse import time @@ -23,11 +24,6 @@ __all__ = ( SAFE_METHODS = ['GET', 'HEAD', 'OPTIONS'] -_403_FORBIDDEN_RESPONSE = ImmediateResponse( - {'detail': 'You do not have permission to access this resource. ' + - 'You may need to login or otherwise authenticate the request.'}, - status=status.HTTP_403_FORBIDDEN) - _503_SERVICE_UNAVAILABLE = ImmediateResponse( {'detail': 'request was throttled'}, status=status.HTTP_503_SERVICE_UNAVAILABLE) @@ -66,7 +62,7 @@ class IsAuthenticated(BasePermission): def check_permission(self, user): if not user.is_authenticated(): - raise _403_FORBIDDEN_RESPONSE + raise PermissionDenied() class IsAdminUser(BasePermission): @@ -76,7 +72,7 @@ class IsAdminUser(BasePermission): def check_permission(self, user): if not user.is_staff: - raise _403_FORBIDDEN_RESPONSE + raise PermissionDenied() class IsUserOrIsAnonReadOnly(BasePermission): @@ -87,7 +83,7 @@ class IsUserOrIsAnonReadOnly(BasePermission): def check_permission(self, user): if (not user.is_authenticated() and self.view.method not in SAFE_METHODS): - raise _403_FORBIDDEN_RESPONSE + raise PermissionDenied() class DjangoModelPermissions(BasePermission): @@ -123,10 +119,7 @@ class DjangoModelPermissions(BasePermission): 'app_label': model_cls._meta.app_label, 'model_name': model_cls._meta.module_name } - try: - return [perm % kwargs for perm in self.perms_map[method]] - except KeyError: - ImmediateResponse(status.HTTP_405_METHOD_NOT_ALLOWED) + return [perm % kwargs for perm in self.perms_map[method]] def check_permission(self, user): method = self.view.method @@ -134,7 +127,7 @@ class DjangoModelPermissions(BasePermission): perms = self.get_required_permissions(method, model_cls) if not user.is_authenticated or not user.has_perms(perms): - raise _403_FORBIDDEN_RESPONSE + raise PermissionDenied() class BaseThrottle(BasePermission): -- cgit v1.2.3 From 73cc77553ed5411f1959a51574b156a47ad5340d Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Sun, 26 Aug 2012 23:06:52 +0100 Subject: Drop ImmediateResponse --- djangorestframework/permissions.py | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) (limited to 'djangorestframework/permissions.py') diff --git a/djangorestframework/permissions.py b/djangorestframework/permissions.py index b56d8a32..bdda4def 100644 --- a/djangorestframework/permissions.py +++ b/djangorestframework/permissions.py @@ -6,9 +6,7 @@ Permission behavior is provided by mixing the :class:`mixins.PermissionsMixin` c """ from django.core.cache import cache -from djangorestframework import status -from djangorestframework.exceptions import PermissionDenied -from djangorestframework.response import ImmediateResponse +from djangorestframework.exceptions import PermissionDenied, Throttled import time __all__ = ( @@ -24,11 +22,6 @@ __all__ = ( SAFE_METHODS = ['GET', 'HEAD', 'OPTIONS'] -_503_SERVICE_UNAVAILABLE = ImmediateResponse( - {'detail': 'request was throttled'}, - status=status.HTTP_503_SERVICE_UNAVAILABLE) - - class BasePermission(object): """ A base class from which all permission classes should inherit. @@ -192,7 +185,7 @@ class BaseThrottle(BasePermission): """ self.history.insert(0, self.now) cache.set(self.key, self.history, self.duration) - header = 'status=SUCCESS; next=%s sec' % self.next() + header = 'status=SUCCESS; next=%.2f sec' % self.next() self.view.headers['X-Throttle'] = header def throttle_failure(self): @@ -200,9 +193,10 @@ class BaseThrottle(BasePermission): Called when a request to the API has failed due to throttling. Raises a '503 service unavailable' response. """ - header = 'status=FAILURE; next=%s sec' % self.next() + wait = self.next() + header = 'status=FAILURE; next=%.2f sec' % wait self.view.headers['X-Throttle'] = header - raise _503_SERVICE_UNAVAILABLE + raise Throttled(wait) def next(self): """ @@ -215,7 +209,7 @@ class BaseThrottle(BasePermission): available_requests = self.num_requests - len(self.history) + 1 - return '%.2f' % (remaining_duration / float(available_requests)) + return remaining_duration / float(available_requests) class PerUserThrottling(BaseThrottle): -- cgit v1.2.3 From c28b719333b16935e53c76fef79b096cb11322ed Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 4 Sep 2012 21:58:35 +0100 Subject: Refactored throttling --- djangorestframework/permissions.py | 172 +++++-------------------------------- 1 file changed, 22 insertions(+), 150 deletions(-) (limited to 'djangorestframework/permissions.py') diff --git a/djangorestframework/permissions.py b/djangorestframework/permissions.py index bdda4def..d6405a36 100644 --- a/djangorestframework/permissions.py +++ b/djangorestframework/permissions.py @@ -5,10 +5,6 @@ for checking if a request passes a certain set of constraints. Permission behavior is provided by mixing the :class:`mixins.PermissionsMixin` class into a :class:`View` class. """ -from django.core.cache import cache -from djangorestframework.exceptions import PermissionDenied, Throttled -import time - __all__ = ( 'BasePermission', 'FullAnonAccess', @@ -32,20 +28,11 @@ class BasePermission(object): """ self.view = view - def check_permission(self, auth): + def check_permission(self, request, obj=None): """ Should simply return, or raise an :exc:`response.ImmediateResponse`. """ - pass - - -class FullAnonAccess(BasePermission): - """ - Allows full access. - """ - - def check_permission(self, user): - pass + raise NotImplementedError(".check_permission() must be overridden.") class IsAuthenticated(BasePermission): @@ -53,9 +40,10 @@ class IsAuthenticated(BasePermission): Allows access only to authenticated users. """ - def check_permission(self, user): - if not user.is_authenticated(): - raise PermissionDenied() + def check_permission(self, request, obj=None): + if request.user.is_authenticated(): + return True + return False class IsAdminUser(BasePermission): @@ -63,20 +51,22 @@ class IsAdminUser(BasePermission): Allows access only to admin users. """ - def check_permission(self, user): - if not user.is_staff: - raise PermissionDenied() + def check_permission(self, request, obj=None): + if request.user.is_staff: + return True + return False -class IsUserOrIsAnonReadOnly(BasePermission): +class IsAuthenticatedOrReadOnly(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 not in SAFE_METHODS): - raise PermissionDenied() + def check_permission(self, request, obj=None): + if (request.user.is_authenticated() or + request.method in SAFE_METHODS): + return True + return False class DjangoModelPermissions(BasePermission): @@ -114,128 +104,10 @@ class DjangoModelPermissions(BasePermission): } return [perm % kwargs for perm in self.perms_map[method]] - def check_permission(self, user): - method = self.view.method - model_cls = self.view.resource.model - perms = self.get_required_permissions(method, model_cls) - - if not user.is_authenticated or not user.has_perms(perms): - raise PermissionDenied() - - -class BaseThrottle(BasePermission): - """ - Rate throttling of requests. - - The rate (requests / seconds) is set by a :attr:`throttle` attribute - on the :class:`.View` class. The attribute is a string of the form 'number of - requests/period'. - - Period should be one of: ('s', 'sec', 'm', 'min', 'h', 'hour', 'd', 'day') - - Previous request information used for throttling is stored in the cache. - """ - - attr_name = 'throttle' - default = '0/sec' - timer = time.time - - def get_cache_key(self): - """ - Should return a unique cache-key which can be used for throttling. - Must be overridden. - """ - pass - - def check_permission(self, auth): - """ - Check the throttling. - Return `None` or raise an :exc:`.ImmediateResponse`. - """ - num, period = getattr(self.view, self.attr_name, self.default).split('/') - self.num_requests = int(num) - self.duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]] - self.auth = auth - self.check_throttle() - - def check_throttle(self): - """ - Implement the check to see if the request should be throttled. - - On success calls :meth:`throttle_success`. - On failure calls :meth:`throttle_failure`. - """ - self.key = self.get_cache_key() - self.history = cache.get(self.key, []) - self.now = self.timer() - - # Drop any requests from the history which have now passed the - # throttle duration - while self.history and self.history[-1] <= self.now - self.duration: - self.history.pop() - if len(self.history) >= self.num_requests: - self.throttle_failure() - else: - self.throttle_success() - - def throttle_success(self): - """ - Inserts the current request's timestamp along with the key - into the cache. - """ - self.history.insert(0, self.now) - cache.set(self.key, self.history, self.duration) - header = 'status=SUCCESS; next=%.2f sec' % self.next() - self.view.headers['X-Throttle'] = header - - def throttle_failure(self): - """ - Called when a request to the API has failed due to throttling. - Raises a '503 service unavailable' response. - """ - wait = self.next() - header = 'status=FAILURE; next=%.2f sec' % wait - self.view.headers['X-Throttle'] = header - raise Throttled(wait) - - def next(self): - """ - Returns the recommended next request time in seconds. - """ - if self.history: - remaining_duration = self.duration - (self.now - self.history[-1]) - else: - remaining_duration = self.duration - - available_requests = self.num_requests - len(self.history) + 1 - - return remaining_duration / float(available_requests) - - -class PerUserThrottling(BaseThrottle): - """ - Limits the rate of API calls that may be made by a given user. - - 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. - """ - - def get_cache_key(self): - if self.auth.is_authenticated(): - ident = self.auth.id - else: - ident = self.view.request.META.get('REMOTE_ADDR', None) - return 'throttle_user_%s' % ident - - -class PerViewThrottling(BaseThrottle): - """ - Limits the rate of API calls that may be used on a given view. - - The class name of the view is used as a unique identifier to - throttle against. - """ + def check_permission(self, request, obj=None): + model_cls = self.view.model + perms = self.get_required_permissions(request.method, model_cls) - def get_cache_key(self): - return 'throttle_view_%s' % self.view.__class__.__name__ + if request.user.is_authenticated() and request.user.has_perms(perms, obj): + return True + return False -- cgit v1.2.3 From 367dd01a338e67c772362af62889a53a1302fb02 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 5 Sep 2012 13:04:07 +0100 Subject: Fix permission issues --- djangorestframework/permissions.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'djangorestframework/permissions.py') diff --git a/djangorestframework/permissions.py b/djangorestframework/permissions.py index d6405a36..eff2ed2b 100644 --- a/djangorestframework/permissions.py +++ b/djangorestframework/permissions.py @@ -41,7 +41,7 @@ class IsAuthenticated(BasePermission): """ def check_permission(self, request, obj=None): - if request.user.is_authenticated(): + if request.user and request.user.is_authenticated(): return True return False @@ -52,7 +52,7 @@ class IsAdminUser(BasePermission): """ def check_permission(self, request, obj=None): - if request.user.is_staff: + if request.user and request.user.is_staff(): return True return False @@ -63,8 +63,9 @@ class IsAuthenticatedOrReadOnly(BasePermission): """ def check_permission(self, request, obj=None): - if (request.user.is_authenticated() or - request.method in SAFE_METHODS): + if (request.method in SAFE_METHODS or + request.user and + request.user.is_authenticated()): return True return False @@ -108,6 +109,8 @@ class DjangoModelPermissions(BasePermission): model_cls = self.view.model perms = self.get_required_permissions(request.method, model_cls) - if request.user.is_authenticated() and request.user.has_perms(perms, obj): + if (request.user and + request.user.is_authenticated() and + request.user.has_perms(perms, obj)): return True return False -- cgit v1.2.3 From 1c78bf53dbc4f75cfdc240c72f4db9d2376cb9cb Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 6 Sep 2012 13:49:15 +0100 Subject: Refactoring some basics --- djangorestframework/permissions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'djangorestframework/permissions.py') diff --git a/djangorestframework/permissions.py b/djangorestframework/permissions.py index eff2ed2b..64e455f5 100644 --- a/djangorestframework/permissions.py +++ b/djangorestframework/permissions.py @@ -52,7 +52,7 @@ class IsAdminUser(BasePermission): """ def check_permission(self, request, obj=None): - if request.user and request.user.is_staff(): + if request.user and request.user.is_staff: return True return False @@ -82,7 +82,7 @@ class DjangoModelPermissions(BasePermission): """ # Map methods into required permission codes. - # Override this if you need to also provide 'read' permissions, + # Override this if you need to also provide 'view' permissions, # or if you want to provide custom permission codes. perms_map = { 'GET': [], -- cgit v1.2.3 From 6c109ac60f891955df367c61d4d7094039098076 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 13 Sep 2012 18:32:56 +0100 Subject: Improve throttles and docs --- djangorestframework/permissions.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'djangorestframework/permissions.py') diff --git a/djangorestframework/permissions.py b/djangorestframework/permissions.py index 64e455f5..3a669822 100644 --- a/djangorestframework/permissions.py +++ b/djangorestframework/permissions.py @@ -28,11 +28,11 @@ class BasePermission(object): """ self.view = view - def check_permission(self, request, obj=None): + def has_permission(self, request, obj=None): """ Should simply return, or raise an :exc:`response.ImmediateResponse`. """ - raise NotImplementedError(".check_permission() must be overridden.") + raise NotImplementedError(".has_permission() must be overridden.") class IsAuthenticated(BasePermission): @@ -40,7 +40,7 @@ class IsAuthenticated(BasePermission): Allows access only to authenticated users. """ - def check_permission(self, request, obj=None): + def has_permission(self, request, obj=None): if request.user and request.user.is_authenticated(): return True return False @@ -51,7 +51,7 @@ class IsAdminUser(BasePermission): Allows access only to admin users. """ - def check_permission(self, request, obj=None): + def has_permission(self, request, obj=None): if request.user and request.user.is_staff: return True return False @@ -62,7 +62,7 @@ class IsAuthenticatedOrReadOnly(BasePermission): The request is authenticated as a user, or is a read-only request. """ - def check_permission(self, request, obj=None): + def has_permission(self, request, obj=None): if (request.method in SAFE_METHODS or request.user and request.user.is_authenticated()): @@ -105,7 +105,7 @@ class DjangoModelPermissions(BasePermission): } return [perm % kwargs for perm in self.perms_map[method]] - def check_permission(self, request, obj=None): + def has_permission(self, request, obj=None): model_cls = self.view.model perms = self.get_required_permissions(request.method, model_cls) -- cgit v1.2.3 From 87dae4d8549c02fa9a57adb3bb876d249dae1f79 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 20 Sep 2012 13:19:43 +0100 Subject: Remove old 'djangorestframework directories --- djangorestframework/permissions.py | 116 ------------------------------------- 1 file changed, 116 deletions(-) delete mode 100644 djangorestframework/permissions.py (limited to 'djangorestframework/permissions.py') diff --git a/djangorestframework/permissions.py b/djangorestframework/permissions.py deleted file mode 100644 index 3a669822..00000000 --- a/djangorestframework/permissions.py +++ /dev/null @@ -1,116 +0,0 @@ -""" -The :mod:`permissions` module bundles a set of permission classes that are used -for checking if a request passes a certain set of constraints. - -Permission behavior is provided by mixing the :class:`mixins.PermissionsMixin` class into a :class:`View` class. -""" - -__all__ = ( - 'BasePermission', - 'FullAnonAccess', - 'IsAuthenticated', - 'IsAdminUser', - 'IsUserOrIsAnonReadOnly', - 'PerUserThrottling', - 'PerViewThrottling', -) - -SAFE_METHODS = ['GET', 'HEAD', 'OPTIONS'] - - -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 has_permission(self, request, obj=None): - """ - Should simply return, or raise an :exc:`response.ImmediateResponse`. - """ - raise NotImplementedError(".has_permission() must be overridden.") - - -class IsAuthenticated(BasePermission): - """ - Allows access only to authenticated users. - """ - - def has_permission(self, request, obj=None): - if request.user and request.user.is_authenticated(): - return True - return False - - -class IsAdminUser(BasePermission): - """ - Allows access only to admin users. - """ - - def has_permission(self, request, obj=None): - 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. - """ - - def has_permission(self, request, obj=None): - 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. - - This permission should only be used on views with a `ModelResource`. - """ - - # 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'], - } - - 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, - 'model_name': model_cls._meta.module_name - } - return [perm % kwargs for perm in self.perms_map[method]] - - def has_permission(self, request, obj=None): - model_cls = self.view.model - perms = self.get_required_permissions(request.method, model_cls) - - if (request.user and - request.user.is_authenticated() and - request.user.has_perms(perms, obj)): - return True - return False -- cgit v1.2.3