diff --git a/.hgignore b/.hgignore index 3aa7626..8387da3 100644 --- a/.hgignore +++ b/.hgignore @@ -11,3 +11,4 @@ downloads develop-eggs .DS_Store .svn +*.swp diff --git a/piston/authentication.py b/piston/authentication.py index fe4a69c..2f325fa 100644 --- a/piston/authentication.py +++ b/piston/authentication.py @@ -6,9 +6,12 @@ from django.conf import settings from django.core.urlresolvers import get_callable from django.core.exceptions import ImproperlyConfigured +from django.shortcuts import render_to_response +from django.template import RequestContext from django.utils.importlib import import_module import oauth +from piston import forms class NoAuthentication(object): """ @@ -136,7 +139,12 @@ def oauth_request_token(request): return response def oauth_auth_view(request, token, callback, params): - return HttpResponse("Just a fake view for auth. %s, %s, %s" % (token, callback, params)) + form = forms.OAuthAuthenticationForm(initial={ + 'oauth_token': token.key, + 'oauth_callback': callback, + }) + return render_to_response('piston/authorize_token.html', + { 'form': form }, RequestContext(request)) @login_required def oauth_user_auth(request): @@ -156,35 +164,34 @@ def oauth_user_auth(request): callback = None if request.method == "GET": - request.session['oauth'] = token.key params = oauth_request.get_normalized_parameters() - oauth_view = getattr(settings, 'OAUTH_AUTH_VIEW', 'oauth_auth_view') - - return get_callable(oauth_view)(request, token, callback, params) + oauth_view = getattr(settings, 'OAUTH_AUTH_VIEW', None) + if oauth_view is None: + return oauth_auth_view(request, token, callback, params) + else: + return get_callable(oauth_view)(request, token, callback, params) elif request.method == "POST": - if request.session.get('oauth', '') == token.key: - request.session['oauth'] = '' + try: + form = forms.OAuthAuthenticationForm(request.POST) + if form.is_valid(): + token = oauth_server.authorize_token(token, request.user) + args = '?'+token.to_string(only_key=True) + else: + args = '?error=%s' % 'Access not granted by user.' - try: - if int(request.POST.get('authorize_access', '0')): - token = oauth_server.authorize_token(token, request.user) - args = '?'+token.to_string(only_key=True) - else: - args = '?error=%s' % 'Access not granted by user.' + if not callback: + callback = getattr(settings, 'OAUTH_CALLBACK_VIEW') + return get_callable(callback)(request, token) - if not callback: - callback = getattr(settings, 'OAUTH_CALLBACK_VIEW') - return get_callable(callback)(request, token) - - response = HttpResponseRedirect(callback+args) - - except oauth.OAuthError, err: - response = send_oauth_error(err) - else: - response = HttpResponse('Action not allowed.') + response = HttpResponseRedirect(callback+args) + + except oauth.OAuthError, err: + response = send_oauth_error(err) + else: + response = HttpResponse('Action not allowed.') - return response + return response def oauth_access_token(request): oauth_server, oauth_request = initialize_server_request(request) @@ -278,4 +285,4 @@ def is_valid_request(request): def validate_token(request, check_timestamp=True, check_nonce=True): oauth_server, oauth_request = initialize_server_request(request) return oauth_server.verify_request(oauth_request) - + diff --git a/piston/forms.py b/piston/forms.py index 727f997..8f1f1d7 100644 --- a/piston/forms.py +++ b/piston/forms.py @@ -1,4 +1,8 @@ +import hmac +import base64 + from django import forms +from django.conf import settings class Form(forms.Form): pass @@ -17,3 +21,43 @@ def merge_from_initial(self): for field in filter(filt, getattr(self.Meta, 'fields', ())): self.data[field] = self.initial.get(field, None) + +class OAuthAuthenticationForm(forms.Form): + oauth_token = forms.CharField(widget=forms.HiddenInput) + oauth_callback = forms.URLField(widget=forms.HiddenInput) + authorize_access = forms.BooleanField(required=True) + csrf_signature = forms.CharField(widget=forms.HiddenInput) + + def __init__(self, *args, **kwargs): + forms.Form.__init__(self, *args, **kwargs) + + self.fields['csrf_signature'].initial = self.initial_csrf_signature + + def clean_csrf_signature(self): + sig = self.cleaned_data['csrf_signature'] + token = self.cleaned_data['oauth_token'] + + sig1 = OAuthAuthenticationForm.get_csrf_signature(settings.SECRET_KEY, token) + + if sig != sig1: + raise forms.ValidationError("CSRF signature is not valid") + + return sig + + def initial_csrf_signature(self): + token = self.initial['oauth_token'] + return OAuthAuthenticationForm.get_csrf_signature(settings.SECRET_KEY, token) + + @staticmethod + def get_csrf_signature(key, token): + # Check signature... + try: + import hashlib # 2.5 + hashed = hmac.new(key, token, hashlib.sha1) + except: + import sha # deprecated + hashed = hmac.new(key, token, sha) + + # calculate the digest base 64 + return base64.b64encode(hashed.digest()) + diff --git a/piston/templates/piston/authorize_token.html b/piston/templates/piston/authorize_token.html new file mode 100644 index 0000000..dae840e --- /dev/null +++ b/piston/templates/piston/authorize_token.html @@ -0,0 +1,15 @@ + + + + Authorize Token + + +

Authorize Token

+ +
+ {{ form.as_table }} +
+ + + diff --git a/tests/test_project/apps/testapp/tests.py b/tests/test_project/apps/testapp/tests.py index 22a85d1..ef801f8 100644 --- a/tests/test_project/apps/testapp/tests.py +++ b/tests/test_project/apps/testapp/tests.py @@ -71,13 +71,19 @@ def test_handshake(self): http_url='http://testserver/api/oauth/authorize') request.sign_request(self.signature_method, oaconsumer, oatoken) - # Place the token into the client session... - session = self.client.session - session['oauth'] = oatoken.key - session.save() + # Request the login page +# TODO: Parse the response to make sure all the fields exist +# response = self.client.get('/api/oauth/authorize', { +# 'oauth_token': oatoken.key, +# 'oauth_callback': 'http://printer.example.com/request_token_ready', +# }) + + from piston.forms import OAuthAuthenticationForm + from django.conf import settings response = self.client.post('/api/oauth/authorize', { 'oauth_token': oatoken.key, 'oauth_callback': 'http://printer.example.com/request_token_ready', + 'csrf_signature': OAuthAuthenticationForm.get_csrf_signature(settings.SECRET_KEY, oatoken.key), 'authorize_access': 1, })