1 # This file is part of Wolne Lektury, licensed under GNU Affero GPLv3 or later.
2 # Copyright © Fundacja Wolne Lektury. See NOTICE for more information.
4 from django.contrib.auth.decorators import login_required
5 from django import forms
6 from django.http import HttpResponse
7 from django.http import Http404
8 from django.shortcuts import render
9 from django.views.generic.base import View
10 from oauthlib.common import urlencode
11 from oauthlib.oauth1 import RequestTokenEndpoint, AccessTokenEndpoint
12 from oauthlib.oauth1 import AuthorizationEndpoint, OAuth1Error
13 from rest_framework.permissions import IsAuthenticated
14 from rest_framework.response import Response
15 from rest_framework.views import APIView
16 from rest_framework.generics import RetrieveAPIView, get_object_or_404
17 from catalogue.models import Book
18 from .models import BookUserData, KEY_SIZE, SECRET_SIZE
19 from . import serializers
20 from .request_validator import PistonRequestValidator
21 from .utils import oauthlib_request, oauthlib_response, vary_on_auth
24 class OAuth1RequestTokenEndpoint(RequestTokenEndpoint):
25 def _create_request(self, *args, **kwargs):
26 r = super(OAuth1RequestTokenEndpoint, self)._create_request(*args, **kwargs)
27 r.redirect_uri = 'oob'
30 def create_request_token(self, request, credentials):
32 'oauth_token': self.token_generator()[:KEY_SIZE],
33 'oauth_token_secret': self.token_generator()[:SECRET_SIZE],
35 token.update(credentials)
36 self.request_validator.save_request_token(token, request)
37 return urlencode(token.items())
41 class OAuth1RequestTokenView(View):
43 self.endpoint = OAuth1RequestTokenEndpoint(PistonRequestValidator())
45 def dispatch(self, request):
46 return oauthlib_response(
47 self.endpoint.create_request_token_response(
48 **oauthlib_request(request)
53 class OAuthAuthenticationForm(forms.Form):
54 oauth_token = forms.CharField(widget=forms.HiddenInput)
55 oauth_callback = forms.CharField(widget=forms.HiddenInput) # changed from URLField - too strict
56 # removed authorize_access - redundant
59 class OAuth1AuthorizationEndpoint(AuthorizationEndpoint):
60 def create_verifier(self, request, credentials):
61 verifier = super(OAuth1AuthorizationEndpoint, self).create_verifier(request, credentials)
63 'oauth_token': verifier['oauth_token'],
68 def oauth_user_auth(request):
69 endpoint = OAuth1AuthorizationEndpoint(PistonRequestValidator())
71 if request.method == "GET":
72 # Why not just get oauth_token here?
73 # This is fairly straightforward, in't?
75 realms, credentials = endpoint.get_realms_and_credentials(
76 **oauthlib_request(request))
77 except OAuth1Error as e:
78 return HttpResponse(str(e), status=400)
79 callback = request.GET.get('oauth_callback')
81 form = OAuthAuthenticationForm(initial={
82 'oauth_token': credentials['resource_owner_key'],
83 'oauth_callback': callback,
86 return render(request, 'oauth/authorize_token.html', {'form': form})
88 if request.method == "POST":
90 response = oauthlib_response(
91 endpoint.create_authorization_response(
92 credentials={"user": request.user},
93 **oauthlib_request(request)
96 except OAuth1Error as e:
97 return HttpResponse(e.message, status=400)
102 class OAuth1AccessTokenEndpoint(AccessTokenEndpoint):
103 def _create_request(self, *args, **kwargs):
104 r = super(OAuth1AccessTokenEndpoint, self)._create_request(*args, **kwargs)
105 r.verifier = 'x' * 20
108 def create_access_token(self, request, credentials):
109 request.realms = self.request_validator.get_realms(
110 request.resource_owner_key, request)
112 'oauth_token': self.token_generator()[:KEY_SIZE],
113 'oauth_token_secret': self.token_generator()[:SECRET_SIZE],
114 'oauth_authorized_realms': ' '.join(request.realms)
116 token.update(credentials)
117 self.request_validator.save_access_token(token, request)
118 return urlencode(token.items())
122 class OAuth1AccessTokenView(View):
124 self.endpoint = OAuth1AccessTokenEndpoint(PistonRequestValidator())
126 def dispatch(self, request):
127 return oauthlib_response(
128 self.endpoint.create_access_token_response(
129 **oauthlib_request(request)
135 class UserView(RetrieveAPIView):
136 permission_classes = [IsAuthenticated]
137 serializer_class = serializers.UserSerializer
139 def get_object(self):
140 return self.request.user
144 class BookUserDataView(RetrieveAPIView):
145 permission_classes = [IsAuthenticated]
146 serializer_class = serializers.BookUserDataSerializer
147 lookup_field = 'book__slug'
148 lookup_url_kwarg = 'slug'
150 def get_queryset(self):
151 return BookUserData.objects.filter(user=self.request.user)
153 def get(self, *args, **kwargs):
155 return super(BookUserDataView, self).get(*args, **kwargs)
157 return Response({"state": "not_started"})
159 def post(self, request, slug, state):
160 if state not in ('reading', 'complete'):
163 book = get_object_or_404(Book, slug=slug)
164 instance = BookUserData.update(book, request.user, state)
165 serializer = self.get_serializer(instance)
166 return Response(serializer.data)
169 class BlogView(APIView):
170 def get(self, request):