simplify piston authorize form
authorJan Szejko <janek37@gmail.com>
Fri, 20 Apr 2018 13:03:58 +0000 (15:03 +0200)
committerJan Szejko <janek37@gmail.com>
Fri, 20 Apr 2018 13:03:58 +0000 (15:03 +0200)
src/api/piston_patch.py [new file with mode: 0644]
src/api/urls.py
src/wolnelektury/templates/piston/authorize_token.html

diff --git a/src/api/piston_patch.py b/src/api/piston_patch.py
new file mode 100644 (file)
index 0000000..4bf9b61
--- /dev/null
@@ -0,0 +1,112 @@
+# -*- coding: utf-8 -*-
+
+# modified from django-piston
+import base64
+import hmac
+
+from django import forms
+from django.conf import settings
+from django.contrib.auth.decorators import login_required
+from django.core.urlresolvers import get_callable
+from django.http import HttpResponseRedirect, HttpResponse
+from django.shortcuts import render_to_response
+from django.template.context import RequestContext
+from piston import oauth
+from piston.authentication import initialize_server_request, INVALID_PARAMS_RESPONSE, send_oauth_error
+
+
+class OAuthAuthenticationForm(forms.Form):
+    oauth_token = forms.CharField(widget=forms.HiddenInput)
+    oauth_callback = forms.CharField(widget=forms.HiddenInput)  # changed from URLField - too strict
+    # removed authorize_access - redundant
+    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...
+        import hashlib  # 2.5
+        hashed = hmac.new(key, token, hashlib.sha1)
+
+        # calculate the digest base 64
+        return base64.b64encode(hashed.digest())
+
+
+# The only thing changed in the views below is the form used
+
+
+def oauth_auth_view(request, token, callback, params):
+    form = 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):
+    oauth_server, oauth_request = initialize_server_request(request)
+
+    if oauth_request is None:
+        return INVALID_PARAMS_RESPONSE
+
+    try:
+        token = oauth_server.fetch_request_token(oauth_request)
+    except oauth.OAuthError, err:
+        return send_oauth_error(err)
+
+    try:
+        callback = oauth_server.get_callback(oauth_request)
+    except:
+        callback = None
+
+    if request.method == "GET":
+        params = oauth_request.get_normalized_parameters()
+
+        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":
+        try:
+            form = 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.'
+
+            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.')
+
+    return response
index 22c8249..29e7a75 100644 (file)
@@ -2,14 +2,16 @@
 # This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
-from django.conf.urls import patterns, url
+from django.conf.urls import url
 from django.views.decorators.csrf import csrf_exempt
 from django.views.generic import TemplateView
-from piston.authentication import OAuthAuthentication, oauth_access_token
+from piston.authentication import OAuthAuthentication, oauth_access_token, oauth_request_token
 from piston.resource import Resource
 from ssify import ssi_included
+import catalogue.views
 from api import handlers
 from api.helpers import CsrfExemptResource
+from api.piston_patch import oauth_user_auth
 
 auth = OAuthAuthentication(realm="Wolne Lektury")
 
@@ -50,21 +52,18 @@ def incl(request, model, pk, emitter_format):
     return resp
 
 
-urlpatterns = patterns(
-    'piston.authentication',
-    url(r'^oauth/request_token/$', 'oauth_request_token'),
-    url(r'^oauth/authorize/$', 'oauth_user_auth'),
+urlpatterns = [
+    url(r'^oauth/request_token/$', oauth_request_token),
+    url(r'^oauth/authorize/$', oauth_user_auth, name='oauth_user_auth'),
     url(r'^oauth/access_token/$', csrf_exempt(oauth_access_token)),
 
-) + patterns(
-    '',
     url(r'^$', TemplateView.as_view(template_name='api/main.html'), name='api'),
     url(r'^include/(?P<model>book|fragment|tag)/(?P<pk>\d+)\.(?P<lang>.+)\.(?P<emitter_format>xml|json)$',
         incl, name='api_include'),
 
     # info boxes (used by mobile app)
-    url(r'book/(?P<book_id>\d*?)/info\.html$', 'catalogue.views.book_info'),
-    url(r'tag/(?P<tag_id>\d*?)/info\.html$', 'catalogue.views.tag_info'),
+    url(r'book/(?P<book_id>\d*?)/info\.html$', catalogue.views.book_info),
+    url(r'tag/(?P<tag_id>\d*?)/info\.html$', catalogue.views.tag_info),
 
     # books by collections
     url(r'^collections/$', collection_list_resource, name="api_collections"),
@@ -103,4 +102,4 @@ urlpatterns = patterns(
 
     # tags by category
     url(r'^(?P<category>[a-z0-9-]+)/$', tag_list_resource, name='api_tag_list'),
-)
+]
index 03131c7..e54f701 100755 (executable)
@@ -11,7 +11,7 @@
       {% blocktrans %}Confirm to authorize access to Wolne Lektury as user <strong>{{ user}}</strong>.{% endblocktrans %}
     </p>
 
-    <form action="{% url 'piston.authentication.oauth_user_auth' %}" method="POST">
+    <form action="{% url 'oauth_user_auth' %}" method="POST">
       {% csrf_token %}
       {{ form.as_p }}
       <button type="submit">{% trans "Confirm" %}</button>