Commit 4692374e by Tom Christie

Generic permissions added, allowed_methods and anon_allowed_methods now defunct,…

Generic permissions added, allowed_methods and anon_allowed_methods now defunct, dispatch now mirrors View.dispatch more nicely
parent cb4b4f6b
...@@ -13,10 +13,10 @@ import base64 ...@@ -13,10 +13,10 @@ import base64
class BaseAuthenticator(object): class BaseAuthenticator(object):
"""All authenticators should extend BaseAuthenticator.""" """All authenticators should extend BaseAuthenticator."""
def __init__(self, mixin): def __init__(self, view):
"""Initialise the authenticator with the mixin instance as state, """Initialise the authenticator with the mixin instance as state,
in case the authenticator needs to access any metadata on the mixin object.""" in case the authenticator needs to access any metadata on the mixin object."""
self.mixin = mixin self.view = view
def authenticate(self, request): def authenticate(self, request):
"""Authenticate the request and return the authentication context or None. """Authenticate the request and return the authentication context or None.
...@@ -61,11 +61,13 @@ class BasicAuthenticator(BaseAuthenticator): ...@@ -61,11 +61,13 @@ class BasicAuthenticator(BaseAuthenticator):
class UserLoggedInAuthenticator(BaseAuthenticator): class UserLoggedInAuthenticator(BaseAuthenticator):
"""Use Djagno's built-in request session for authentication.""" """Use Django's built-in request session for authentication."""
def authenticate(self, request): def authenticate(self, request):
if getattr(request, 'user', None) and request.user.is_active: if getattr(request, 'user', None) and request.user.is_active:
# Temporarily request.POST with .RAW_CONTENT, so that we use our more generic request parsing # Temporarily set request.POST to view.RAW_CONTENT,
request._post = self.mixin.RAW_CONTENT # so that we use our more generic request parsing,
# in preference to Django's form-only request parsing.
request._post = self.view.RAW_CONTENT
resp = CsrfViewMiddleware().process_view(request, None, (), {}) resp = CsrfViewMiddleware().process_view(request, None, (), {})
del(request._post) del(request._post)
if resp is None: # csrf passed if resp is None: # csrf passed
......
...@@ -396,9 +396,9 @@ class ResponseMixin(object): ...@@ -396,9 +396,9 @@ class ResponseMixin(object):
########## Auth Mixin ########## ########## Auth Mixin ##########
class AuthMixin(object): class AuthMixin(object):
"""Mixin class to provide authentication and permissions.""" """Mixin class to provide authentication and permission checking."""
authenticators = () authenticators = ()
permitters = () permissions = ()
@property @property
def auth(self): def auth(self):
...@@ -406,6 +406,14 @@ class AuthMixin(object): ...@@ -406,6 +406,14 @@ class AuthMixin(object):
self._auth = self._authenticate() self._auth = self._authenticate()
return self._auth return self._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
# TODO? # TODO?
#@property #@property
#def user(self): #def user(self):
...@@ -421,15 +429,11 @@ class AuthMixin(object): ...@@ -421,15 +429,11 @@ class AuthMixin(object):
if not self.permissions: if not self.permissions:
return return
auth = self.auth for permission_cls in self.permissions:
for permitter_cls in self.permitters: permission = permission_cls(self)
permitter = permission_cls(self) if not permission.has_permission(self.auth):
permitter.permit(auth) raise 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.'})
def _authenticate(self):
for authenticator_cls in self.authenticators:
authenticator = authenticator_cls(self)
auth = authenticator.authenticate(self.request)
if auth:
return auth
return None
...@@ -410,13 +410,13 @@ class ModelResource(Resource): ...@@ -410,13 +410,13 @@ class ModelResource(Resource):
class RootModelResource(ModelResource): class RootModelResource(ModelResource):
"""A Resource which provides default operations for list and create.""" """A Resource which provides default operations for list and create."""
allowed_methods = ('GET', 'POST')
queryset = None queryset = None
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
queryset = self.queryset if self.queryset else self.model.objects.all() queryset = self.queryset if self.queryset else self.model.objects.all()
return queryset.filter(**kwargs) return queryset.filter(**kwargs)
put = delete = http_method_not_allowed
class QueryModelResource(ModelResource): class QueryModelResource(ModelResource):
"""Resource with default operations for list. """Resource with default operations for list.
...@@ -424,10 +424,8 @@ class QueryModelResource(ModelResource): ...@@ -424,10 +424,8 @@ class QueryModelResource(ModelResource):
allowed_methods = ('GET',) allowed_methods = ('GET',)
queryset = None queryset = None
def get_form(self, data=None):
return None
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
queryset = self.queryset if self.queryset else self.model.objects.all() queryset = self.queryset if self.queryset else self.model.objects.all()
return queryset.filer(**kwargs) return queryset.filer(**kwargs)
post = put = delete = http_method_not_allowed
\ No newline at end of file
...@@ -18,10 +18,13 @@ class UserAgentMungingTest(TestCase): ...@@ -18,10 +18,13 @@ class UserAgentMungingTest(TestCase):
http://www.gethifi.com/blog/browser-rest-http-accept-headers""" http://www.gethifi.com/blog/browser-rest-http-accept-headers"""
def setUp(self): def setUp(self):
class MockResource(Resource): class MockResource(Resource):
anon_allowed_methods = allowed_methods = ('GET',) permissions = ()
def get(self, request): def get(self, request):
return {'a':1, 'b':2, 'c':3} return {'a':1, 'b':2, 'c':3}
self.req = RequestFactory() self.req = RequestFactory()
self.MockResource = MockResource self.MockResource = MockResource
self.view = MockResource.as_view() self.view = MockResource.as_view()
......
...@@ -13,8 +13,6 @@ except ImportError: ...@@ -13,8 +13,6 @@ except ImportError:
import simplejson as json import simplejson as json
class MockResource(Resource): class MockResource(Resource):
allowed_methods = ('POST',)
def post(self, request): def post(self, request):
return {'a':1, 'b':2, 'c':3} return {'a':1, 'b':2, 'c':3}
......
...@@ -16,7 +16,7 @@ class UploadFilesTests(TestCase): ...@@ -16,7 +16,7 @@ class UploadFilesTests(TestCase):
file = forms.FileField file = forms.FileField
class MockResource(Resource): class MockResource(Resource):
allowed_methods = anon_allowed_methods = ('POST',) permissions = ()
form = FileForm form = FileForm
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
......
...@@ -12,7 +12,7 @@ except ImportError: ...@@ -12,7 +12,7 @@ except ImportError:
class MockResource(Resource): class MockResource(Resource):
"""Mock resource which simply returns a URL, so that we can ensure that reversed URLs are fully qualified""" """Mock resource which simply returns a URL, so that we can ensure that reversed URLs are fully qualified"""
anon_allowed_methods = ('GET',) permissions = ()
def get(self, request): def get(self, request):
return reverse('another') return reverse('another')
...@@ -28,5 +28,9 @@ class ReverseTests(TestCase): ...@@ -28,5 +28,9 @@ class ReverseTests(TestCase):
urls = 'djangorestframework.tests.reverse' urls = 'djangorestframework.tests.reverse'
def test_reversed_urls_are_fully_qualified(self): def test_reversed_urls_are_fully_qualified(self):
response = self.client.get('/') try:
response = self.client.get('/')
except:
import traceback
traceback.print_exc()
self.assertEqual(json.loads(response.content), 'http://testserver/another') self.assertEqual(json.loads(response.content), 'http://testserver/another')
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment