4 from django.http import HttpResponse, HttpResponseRedirect
5 from django.contrib.auth.models import User, AnonymousUser
6 from django.contrib.auth.decorators import login_required
7 from django.template import loader
8 from django.contrib.auth import authenticate
9 from django.conf import settings
10 from django.core.urlresolvers import get_callable
11 from django.core.exceptions import ImproperlyConfigured
12 from django.shortcuts import render_to_response
13 from django.template import RequestContext
15 from piston import forms
17 class NoAuthentication(object):
19 Authentication handler that always returns
20 True, so no authentication is needed, nor
21 initiated (`challenge` is missing.)
23 def is_authenticated(self, request):
26 class HttpBasicAuthentication(object):
28 Basic HTTP authenticater. Synopsis:
30 Authentication handlers must implement two methods:
31 - `is_authenticated`: Will be called when checking for
32 authentication. Receives a `request` object, please
33 set your `User` object on `request.user`, otherwise
34 return False (or something that evaluates to False.)
35 - `challenge`: In cases where `is_authenticated` returns
36 False, the result of this method will be returned.
37 This will usually be a `HttpResponse` object with
38 some kind of challenge headers and 401 code on it.
40 def __init__(self, auth_func=authenticate, realm='API'):
41 self.auth_func = auth_func
44 def is_authenticated(self, request):
45 auth_string = request.META.get('HTTP_AUTHORIZATION', None)
51 (authmeth, auth) = auth_string.split(" ", 1)
53 if not authmeth.lower() == 'basic':
56 auth = auth.strip().decode('base64')
57 (username, password) = auth.split(':', 1)
58 except (ValueError, binascii.Error):
61 request.user = self.auth_func(username=username, password=password) \
64 return not request.user in (False, None, AnonymousUser())
67 resp = HttpResponse("Authorization Required")
68 resp['WWW-Authenticate'] = 'Basic realm="%s"' % self.realm
69 resp.status_code = 401
73 return u'<HTTPBasic: realm=%s>' % self.realm
75 class HttpBasicSimple(HttpBasicAuthentication):
76 def __init__(self, realm, username, password):
77 self.user = User.objects.get(username=username)
78 self.password = password
80 super(HttpBasicSimple, self).__init__(auth_func=self.hash, realm=realm)
82 def hash(self, username, password):
83 if username == self.user.username and password == self.password:
86 def load_data_store():
87 '''Load data store for OAuth Consumers, Tokens, Nonces and Resources
89 path = getattr(settings, 'OAUTH_DATA_STORE', 'piston.store.DataStore')
91 # stolen from django.contrib.auth.load_backend
93 module, attr = path[:i], path[i+1:]
96 mod = __import__(module, {}, {}, attr)
97 except ImportError, e:
98 raise ImproperlyConfigured, 'Error importing OAuth data store %s: "%s"' % (module, e)
101 cls = getattr(mod, attr)
102 except AttributeError:
103 raise ImproperlyConfigured, 'Module %s does not define a "%s" OAuth data store' % (module, attr)
107 # Set the datastore here.
108 oauth_datastore = load_data_store()
110 def initialize_server_request(request):
112 Shortcut for initialization.
114 if request.method == "POST": #and \
115 # request.META['CONTENT_TYPE'] == "application/x-www-form-urlencoded":
116 params = dict(request.REQUEST.items())
120 # Seems that we want to put HTTP_AUTHORIZATION into 'Authorization'
121 # for oauth.py to understand. Lovely.
122 request.META['Authorization'] = request.META.get('HTTP_AUTHORIZATION', '')
124 oauth_request = oauth.OAuthRequest.from_request(
125 request.method, request.build_absolute_uri(),
126 headers=request.META, parameters=params,
127 query_string=request.environ.get('QUERY_STRING', ''))
130 oauth_server = oauth.OAuthServer(oauth_datastore(oauth_request))
131 oauth_server.add_signature_method(oauth.OAuthSignatureMethod_PLAINTEXT())
132 oauth_server.add_signature_method(oauth.OAuthSignatureMethod_HMAC_SHA1())
136 return oauth_server, oauth_request
138 def send_oauth_error(err=None):
140 Shortcut for sending an error.
142 response = HttpResponse(err.message.encode('utf-8'))
143 response.status_code = 401
146 header = oauth.build_authenticate_header(realm=realm)
148 for k, v in header.iteritems():
153 def oauth_request_token(request):
154 oauth_server, oauth_request = initialize_server_request(request)
156 if oauth_server is None:
157 return INVALID_PARAMS_RESPONSE
159 token = oauth_server.fetch_request_token(oauth_request)
161 response = HttpResponse(token.to_string())
162 except oauth.OAuthError, err:
163 response = send_oauth_error(err)
167 def oauth_auth_view(request, token, callback, params):
168 form = forms.OAuthAuthenticationForm(initial={
169 'oauth_token': token.key,
170 'oauth_callback': token.get_callback_url() or callback,
173 return render_to_response('piston/authorize_token.html',
174 { 'form': form }, RequestContext(request))
177 def oauth_user_auth(request):
178 oauth_server, oauth_request = initialize_server_request(request)
180 if oauth_request is None:
181 return INVALID_PARAMS_RESPONSE
184 token = oauth_server.fetch_request_token(oauth_request)
185 except oauth.OAuthError, err:
186 return send_oauth_error(err)
189 callback = oauth_server.get_callback(oauth_request)
193 if request.method == "GET":
194 params = oauth_request.get_normalized_parameters()
196 oauth_view = getattr(settings, 'OAUTH_AUTH_VIEW', None)
197 if oauth_view is None:
198 return oauth_auth_view(request, token, callback, params)
200 return get_callable(oauth_view)(request, token, callback, params)
201 elif request.method == "POST":
203 form = forms.OAuthAuthenticationForm(request.POST)
205 token = oauth_server.authorize_token(token, request.user)
206 args = '?'+token.to_string(only_key=True)
208 args = '?error=%s' % 'Access not granted by user.'
209 print "FORM ERROR", form.errors
212 callback = getattr(settings, 'OAUTH_CALLBACK_VIEW')
213 return get_callable(callback)(request, token)
215 response = HttpResponseRedirect(callback+args)
217 except oauth.OAuthError, err:
218 response = send_oauth_error(err)
220 response = HttpResponse('Action not allowed.')
224 def oauth_access_token(request):
225 oauth_server, oauth_request = initialize_server_request(request)
227 if oauth_request is None:
228 return INVALID_PARAMS_RESPONSE
231 token = oauth_server.fetch_access_token(oauth_request)
232 return HttpResponse(token.to_string())
233 except oauth.OAuthError, err:
234 return send_oauth_error(err)
236 INVALID_PARAMS_RESPONSE = send_oauth_error(oauth.OAuthError('Invalid request parameters.'))
238 class OAuthAuthentication(object):
240 OAuth authentication. Based on work by Leah Culver.
242 def __init__(self, realm='API'):
244 self.builder = oauth.build_authenticate_header
246 def is_authenticated(self, request):
248 Checks whether a means of specifying authentication
249 is provided, and if so, if it is a valid token.
251 Read the documentation on `HttpBasicAuthentication`
252 for more information about what goes on here.
254 if self.is_valid_request(request):
256 consumer, token, parameters = self.validate_token(request)
257 except oauth.OAuthError, err:
258 print send_oauth_error(err)
261 if consumer and token:
262 request.user = token.user
263 request.consumer = consumer
264 request.throttle_extra = token.consumer.id
271 Returns a 401 response with a small bit on
272 what OAuth is, and where to learn more about it.
274 When this was written, browsers did not understand
275 OAuth authentication on the browser side, and hence
276 the helpful template we render. Maybe some day in the
277 future, browsers will take care of this stuff for us
278 and understand the 401 with the realm we give it.
280 response = HttpResponse()
281 response.status_code = 401
284 for k, v in self.builder(realm=realm).iteritems():
287 tmpl = loader.render_to_string('oauth/challenge.html',
288 { 'MEDIA_URL': settings.MEDIA_URL })
290 response.content = tmpl
295 def is_valid_request(request):
297 Checks whether the required parameters are either in
298 the http-authorization header sent by some clients,
299 which is by the way the preferred method according to
300 OAuth spec, but otherwise fall back to `GET` and `POST`.
302 must_have = [ 'oauth_'+s for s in [
303 'consumer_key', 'token', 'signature',
304 'signature_method', 'timestamp', 'nonce' ] ]
306 is_in = lambda l: all([ (p in l) for p in must_have ])
308 auth_params = request.META.get("HTTP_AUTHORIZATION", "")
309 req_params = request.REQUEST
311 return is_in(auth_params) or is_in(req_params)
314 def validate_token(request, check_timestamp=True, check_nonce=True):
315 oauth_server, oauth_request = initialize_server_request(request)
316 return oauth_server.verify_request(oauth_request)