Allow uploading audiobooks via API.
[wolnelektury.git] / src / api / piston_patch.py
index fb3987b..6a80e15 100644 (file)
@@ -1,18 +1,14 @@
 # -*- coding: utf-8 -*-
-
-# modified from django-piston
-import base64
-import hmac
-
-from django import forms
-from django.conf import settings
+# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
+from oauthlib.oauth1 import AuthorizationEndpoint, OAuth1Error
 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
+from django import forms
+from django.http import HttpResponse, HttpResponseRedirect
+from django.shortcuts import render
+from .request_validator import PistonRequestValidator
+from .utils import oauthlib_request, oauthlib_response
 
 
 class HttpResponseAppRedirect(HttpResponseRedirect):
@@ -23,95 +19,46 @@ 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
-# and also the Http Redirect class
-
-
-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))
+class OAuth1AuthorizationEndpoint(AuthorizationEndpoint):
+    def create_verifier(self, request, credentials):
+        verifier = super(OAuth1AuthorizationEndpoint, self).create_verifier(request, credentials)
+        return {
+            'oauth_token': verifier['oauth_token'],
+        }
 
 
 @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
+    endpoint = OAuth1AuthorizationEndpoint(PistonRequestValidator())
 
     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":
+        # Why not just get oauth_token here?
+        # This is fairly straightforward, in't?
         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)
+            realms, credentials = endpoint.get_realms_and_credentials(
+                **oauthlib_request(request))
+        except OAuth1Error as e:
+            return HttpResponse(e.message, status=400)
+        callback = request.GET.get('oauth_callback')
 
-            response = HttpResponseAppRedirect(callback + args)
+        form = OAuthAuthenticationForm(initial={
+            'oauth_token': credentials['resource_owner_key'],
+            'oauth_callback': callback,
+        })
 
-        except oauth.OAuthError, err:
-            response = send_oauth_error(err)
-    else:
-        response = HttpResponse('Action not allowed.')
+        return render(request, 'piston/authorize_token.html', {'form': form})
 
-    return response
+    elif request.method == "POST":
+        try:
+            response = oauthlib_response(
+                endpoint.create_authorization_response(
+                    credentials={"user": request.user},
+                    **oauthlib_request(request)
+                )
+            )
+        except OAuth1Error as e:
+            return HttpResponse(e.message, status=400)
+        else:
+            return response