mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-13 05:06:53 +03:00
Added authenicators. Awesome.
This commit is contained in:
parent
e95198a1c0
commit
eff54c00d5
|
@ -3,9 +3,15 @@ FlyWheel Documentation
|
||||||
|
|
||||||
This is the online documentation for FlyWheel - A REST framework for Django.
|
This is the online documentation for FlyWheel - A REST framework for Django.
|
||||||
|
|
||||||
|
Some of FlyWheel's features:
|
||||||
|
|
||||||
* Clean, simple, class-based views for Resources.
|
* Clean, simple, class-based views for Resources.
|
||||||
* Easy input validation using Forms and ModelForms.
|
* Support for ModelResources with nice default implementations and input validation.
|
||||||
* Self describing APIs, with HTML and Plain Text outputs.
|
* Automatically provides a browse-able self-documenting API.
|
||||||
|
* Pluggable Emitters, Parsers and Authenticators - Easy to customise.
|
||||||
|
* Content type negotiation using Accept headers.
|
||||||
|
* Optional support for forms as input validation.
|
||||||
|
* Modular architecture - Easy to extend and modify.
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 1
|
:maxdepth: 1
|
||||||
|
|
44
flywheel/authenticators.py
Normal file
44
flywheel/authenticators.py
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
from django.contrib.auth import authenticate
|
||||||
|
import base64
|
||||||
|
|
||||||
|
class BaseAuthenticator(object):
|
||||||
|
"""All authenticators should extend BaseAuthenticator."""
|
||||||
|
|
||||||
|
def __init__(self, resource):
|
||||||
|
"""Initialise the authenticator with the Resource instance as state,
|
||||||
|
in case the authenticator needs to access any metadata on the Resource object."""
|
||||||
|
self.resource = resource
|
||||||
|
|
||||||
|
def authenticate(self, request):
|
||||||
|
"""Authenticate the request and return the authentication context or None.
|
||||||
|
|
||||||
|
The default permission checking on Resource will use the allowed_methods attribute
|
||||||
|
for permissions if the authentication context is not None, and use anon_allowed_methods otherwise.
|
||||||
|
|
||||||
|
The authentication context is passed to the method calls eg Resource.get(request, auth) in order to
|
||||||
|
allow them to apply any more fine grained permission checking at the point the response is being generated.
|
||||||
|
|
||||||
|
This function must be overridden to be implemented."""
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class BasicAuthenticator(BaseAuthenticator):
|
||||||
|
"""Use HTTP Basic authentication"""
|
||||||
|
def authenticate(self, request):
|
||||||
|
if 'HTTP_AUTHORIZATION' in request.META:
|
||||||
|
auth = request.META['HTTP_AUTHORIZATION'].split()
|
||||||
|
if len(auth) == 2 and auth[0].lower() == "basic":
|
||||||
|
uname, passwd = base64.b64decode(auth[1]).split(':')
|
||||||
|
user = authenticate(username=uname, password=passwd)
|
||||||
|
if user is not None and user.is_active:
|
||||||
|
return user
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class UserLoggedInAuthenticator(BaseAuthenticator):
|
||||||
|
"""Use Djagno's built-in request session for authentication."""
|
||||||
|
def authenticate(self, request):
|
||||||
|
if request.user and request.user.is_active:
|
||||||
|
return request.user
|
||||||
|
return None
|
||||||
|
|
|
@ -2,7 +2,7 @@ from django.contrib.sites.models import Site
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
|
|
||||||
from flywheel import emitters, parsers
|
from flywheel import emitters, parsers, authenticators
|
||||||
from flywheel.response import status, Response, ResponseException
|
from flywheel.response import status, Response, ResponseException
|
||||||
|
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
@ -49,6 +49,10 @@ class Resource(object):
|
||||||
parsers.XMLParser,
|
parsers.XMLParser,
|
||||||
parsers.FormParser )
|
parsers.FormParser )
|
||||||
|
|
||||||
|
# List of all authenticating methods to attempt
|
||||||
|
authenticators = ( authenticators.UserLoggedInAuthenticator,
|
||||||
|
authenticators.BasicAuthenticator )
|
||||||
|
|
||||||
# Optional form for input validation and presentation of HTML formatted responses.
|
# Optional form for input validation and presentation of HTML formatted responses.
|
||||||
form = None
|
form = None
|
||||||
|
|
||||||
|
@ -81,7 +85,6 @@ class Resource(object):
|
||||||
""""""
|
""""""
|
||||||
# Setup the resource context
|
# Setup the resource context
|
||||||
self.request = request
|
self.request = request
|
||||||
self.auth_context = None
|
|
||||||
self.response = None
|
self.response = None
|
||||||
self.form_instance = None
|
self.form_instance = None
|
||||||
|
|
||||||
|
@ -123,7 +126,7 @@ class Resource(object):
|
||||||
# """Return an list of all the media types that this resource can emit."""
|
# """Return an list of all the media types that this resource can emit."""
|
||||||
# return [parser.media_type for parser in self.parsers]
|
# return [parser.media_type for parser in self.parsers]
|
||||||
|
|
||||||
#def deafult_parser(self):
|
#def default_parser(self):
|
||||||
# return self.parsers[0]
|
# return self.parsers[0]
|
||||||
|
|
||||||
|
|
||||||
|
@ -133,31 +136,22 @@ class Resource(object):
|
||||||
return self.add_domain(reverse(view, args=args, kwargs=kwargs))
|
return self.add_domain(reverse(view, args=args, kwargs=kwargs))
|
||||||
|
|
||||||
|
|
||||||
def authenticate(self, request):
|
def get(self, request, auth, *args, **kwargs):
|
||||||
"""TODO"""
|
|
||||||
return None
|
|
||||||
# user = ...
|
|
||||||
# if DEBUG and request is from localhost
|
|
||||||
# if anon_user and not anon_allowed_methods raise PermissionDenied
|
|
||||||
# return auth_context
|
|
||||||
|
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
|
||||||
"""Must be subclassed to be implemented."""
|
"""Must be subclassed to be implemented."""
|
||||||
self.not_implemented('GET')
|
self.not_implemented('GET')
|
||||||
|
|
||||||
|
|
||||||
def post(self, request, content, *args, **kwargs):
|
def post(self, request, auth, content, *args, **kwargs):
|
||||||
"""Must be subclassed to be implemented."""
|
"""Must be subclassed to be implemented."""
|
||||||
self.not_implemented('POST')
|
self.not_implemented('POST')
|
||||||
|
|
||||||
|
|
||||||
def put(self, request, content, *args, **kwargs):
|
def put(self, request, auth, content, *args, **kwargs):
|
||||||
"""Must be subclassed to be implemented."""
|
"""Must be subclassed to be implemented."""
|
||||||
self.not_implemented('PUT')
|
self.not_implemented('PUT')
|
||||||
|
|
||||||
|
|
||||||
def delete(self, request, *args, **kwargs):
|
def delete(self, request, auth, *args, **kwargs):
|
||||||
"""Must be subclassed to be implemented."""
|
"""Must be subclassed to be implemented."""
|
||||||
self.not_implemented('DELETE')
|
self.not_implemented('DELETE')
|
||||||
|
|
||||||
|
@ -196,8 +190,24 @@ class Resource(object):
|
||||||
return method
|
return method
|
||||||
|
|
||||||
|
|
||||||
def check_method_allowed(self, method):
|
def authenticate(self, request):
|
||||||
|
"""Attempt to authenticate the request, returning an authentication context or None"""
|
||||||
|
for authenticator in self.authenticators:
|
||||||
|
auth_context = authenticator(self).authenticate(request)
|
||||||
|
if auth_context:
|
||||||
|
return auth_context
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def check_method_allowed(self, method, auth):
|
||||||
"""Ensure the request method is acceptable for this resource."""
|
"""Ensure the request method is acceptable for this resource."""
|
||||||
|
|
||||||
|
# If anonoymous check permissions and bail with no further info if disallowed
|
||||||
|
if auth is None and not method in self.anon_allowed_methods:
|
||||||
|
raise ResponseException(status.HTTP_403_FORBIDDEN,
|
||||||
|
{'detail': 'You do not have permission to access this resource. ' +
|
||||||
|
'You may need to login or otherwise authenticate the request.'})
|
||||||
|
|
||||||
if not method in self.callmap.keys():
|
if not method in self.callmap.keys():
|
||||||
raise ResponseException(status.HTTP_501_NOT_IMPLEMENTED,
|
raise ResponseException(status.HTTP_501_NOT_IMPLEMENTED,
|
||||||
{'detail': 'Unknown or unsupported method \'%s\'' % method})
|
{'detail': 'Unknown or unsupported method \'%s\'' % method})
|
||||||
|
@ -376,10 +386,10 @@ class Resource(object):
|
||||||
# Typically the context will be a user, or None if this is an anonymous request,
|
# 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
|
# but it could potentially be more complex (eg the context of a request key which
|
||||||
# has been signed against a particular set of permissions)
|
# has been signed against a particular set of permissions)
|
||||||
self.auth_context = self.authenticate(request)
|
auth_context = self.authenticate(request)
|
||||||
|
|
||||||
# Ensure the requested operation is permitted on this resource
|
# Ensure the requested operation is permitted on this resource
|
||||||
self.check_method_allowed(method)
|
self.check_method_allowed(method, auth_context)
|
||||||
|
|
||||||
# Get the appropriate create/read/update/delete function
|
# Get the appropriate create/read/update/delete function
|
||||||
func = getattr(self, self.callmap.get(method, None))
|
func = getattr(self, self.callmap.get(method, None))
|
||||||
|
@ -391,10 +401,10 @@ class Resource(object):
|
||||||
data = parser(self).parse(request.raw_post_data)
|
data = parser(self).parse(request.raw_post_data)
|
||||||
self.form_instance = self.get_form(data)
|
self.form_instance = self.get_form(data)
|
||||||
data = self.cleanup_request(data, self.form_instance)
|
data = self.cleanup_request(data, self.form_instance)
|
||||||
response = func(request, data, *args, **kwargs)
|
response = func(request, auth_context, data, *args, **kwargs)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
response = func(request, *args, **kwargs)
|
response = func(request, auth_context, *args, **kwargs)
|
||||||
|
|
||||||
# Allow return value to be either Response, or an object, or None
|
# Allow return value to be either Response, or an object, or None
|
||||||
if isinstance(response, Response):
|
if isinstance(response, Response):
|
||||||
|
|
Loading…
Reference in New Issue
Block a user