From cb9fb6ef2f9ac38c4f1c3946252a542b1f3f15d7 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 11 Apr 2011 13:45:38 +0100 Subject: [PATCH] Refactoring of authentication/permissions --- djangorestframework/authenticators.py | 20 ------------- djangorestframework/request.py | 41 ++++++++++++++++++++++++++- djangorestframework/resource.py | 10 +++++-- djangorestframework/response.py | 6 ++-- 4 files changed, 50 insertions(+), 27 deletions(-) diff --git a/djangorestframework/authenticators.py b/djangorestframework/authenticators.py index 0d267b648..e382de10a 100644 --- a/djangorestframework/authenticators.py +++ b/djangorestframework/authenticators.py @@ -10,26 +10,6 @@ from djangorestframework.utils import as_tuple import base64 -class AuthenticatorMixin(object): - """Adds pluggable authentication behaviour.""" - - """The set of authenticators to use.""" - authenticators = None - - def authenticate(self, request): - """Attempt to authenticate the request, returning an authentication context or None. - An authentication context may be any object, although in many cases it will simply be a :class:`User` instance.""" - - # Attempt authentication against each authenticator in turn, - # and return None if no authenticators succeed in authenticating the request. - for authenticator in as_tuple(self.authenticators): - auth_context = authenticator(self).authenticate(request) - if auth_context: - return auth_context - - return None - - class BaseAuthenticator(object): """All authenticators should extend BaseAuthenticator.""" diff --git a/djangorestframework/request.py b/djangorestframework/request.py index 71ff8c0b7..8a4330b44 100644 --- a/djangorestframework/request.py +++ b/djangorestframework/request.py @@ -9,7 +9,7 @@ from django.http.multipartparser import LimitBytes from StringIO import StringIO class RequestMixin(object): - """Mixin behaviour to deal with requests.""" + """Mixin class to provide request parsing behaviour.""" USE_FORM_OVERLOADING = True METHOD_PARAM = "_method" @@ -214,3 +214,42 @@ class RequestMixin(object): +class AuthMixin(object): + """Mixin class to provide authentication and permissions.""" + authenticators = () + permitters = () + + @property + def auth(self): + if not hasattr(self, '_auth'): + self._auth = self._authenticate() + return self._auth + + # TODO? + #@property + #def user(self): + # if not has_attr(self, '_user'): + # auth = self.auth + # if isinstance(auth, User...): + # self._user = auth + # else: + # self._user = getattr(auth, 'user', None) + # return self._user + + def check_permissions(self): + if not self.permissions: + return + + auth = self.auth + for permitter_cls in self.permitters: + permitter = permission_cls(self) + permitter.permit(auth) + + def _authenticate(self): + for authenticator_cls in self.authenticators: + authenticator = authenticator_cls(self) + auth = authenticator.authenticate(self.request) + if auth: + return auth + return None + diff --git a/djangorestframework/resource.py b/djangorestframework/resource.py index 6ec220734..e62903635 100644 --- a/djangorestframework/resource.py +++ b/djangorestframework/resource.py @@ -6,7 +6,7 @@ from djangorestframework.emitters import EmitterMixin from djangorestframework.authenticators import AuthenticatorMixin from djangorestframework.validators import FormValidatorMixin from djangorestframework.response import Response, ResponseException -from djangorestframework.request import RequestMixin +from djangorestframework.request import RequestMixin, AuthMixin from djangorestframework import emitters, parsers, authenticators, status @@ -18,7 +18,7 @@ from djangorestframework import emitters, parsers, authenticators, status __all__ = ['Resource'] -class Resource(EmitterMixin, AuthenticatorMixin, FormValidatorMixin, RequestMixin, View): +class Resource(EmitterMixin, AuthMixin, FormValidatorMixin, RequestMixin, View): """Handles incoming requests and maps them to REST operations, performing authentication, input deserialization, input validation, output serialization.""" @@ -139,7 +139,7 @@ class Resource(EmitterMixin, AuthenticatorMixin, FormValidatorMixin, RequestMixi # Typically the context will be a user, or None if this is an anonymous request, # but it could potentially be more complex (eg the context of a request key which # has been signed against a particular set of permissions) - auth_context = self.authenticate(request) + auth_context = self.auth # If using a form POST with '_method'/'_content'/'_content_type' overrides, then alter # self.method, self.content_type, self.CONTENT appropriately. @@ -173,6 +173,10 @@ class Resource(EmitterMixin, AuthenticatorMixin, FormValidatorMixin, RequestMixi except ResponseException, exc: response = exc.response + + except: + import traceback + traceback.print_exc() # Always add these headers. # diff --git a/djangorestframework/response.py b/djangorestframework/response.py index fb2e14a28..809e17546 100644 --- a/djangorestframework/response.py +++ b/djangorestframework/response.py @@ -8,7 +8,7 @@ class NoContent(object): """Used to indicate no body in http response. (We cannot just use None, as that is a valid, serializable response object.) - TODO: On relflection I'm going to get rid of this and just not support serailized 'None' responses. + TODO: On reflection I'm going to get rid of this and just not support serialized 'None' responses. """ pass @@ -23,8 +23,8 @@ class Response(object): @property def status_text(self): - """Return reason text corrosponding to our HTTP response status code. - Provided for convienience.""" + """Return reason text corresponding to our HTTP response status code. + Provided for convenience.""" return STATUS_CODE_TEXT.get(self.status, '')