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.
5 from django.contrib.auth import authenticate
6 from django.contrib.auth.decorators import login_required
7 from django import forms
8 from django.http import HttpResponse
9 from django.http import Http404
10 from django.shortcuts import render
11 from django.views.generic.base import View
12 from oauthlib.common import urlencode, generate_token
13 from oauthlib.oauth1 import RequestTokenEndpoint, AccessTokenEndpoint
14 from oauthlib.oauth1 import AuthorizationEndpoint, OAuth1Error
15 from rest_framework.permissions import IsAuthenticated
16 from rest_framework.response import Response
17 from rest_framework.views import APIView
18 from rest_framework.generics import GenericAPIView, RetrieveAPIView, get_object_or_404
19 from catalogue.models import Book
20 from .models import BookUserData, KEY_SIZE, SECRET_SIZE, Token
21 from . import serializers
22 from .request_validator import PistonRequestValidator
23 from .utils import oauthlib_request, oauthlib_response, vary_on_auth
26 class OAuth1RequestTokenEndpoint(RequestTokenEndpoint):
27 def _create_request(self, *args, **kwargs):
28 r = super(OAuth1RequestTokenEndpoint, self)._create_request(*args, **kwargs)
29 r.redirect_uri = 'oob'
32 def create_request_token(self, request, credentials):
34 'oauth_token': self.token_generator()[:KEY_SIZE],
35 'oauth_token_secret': self.token_generator()[:SECRET_SIZE],
37 token.update(credentials)
38 self.request_validator.save_request_token(token, request)
39 return urlencode(token.items())
43 class OAuth1RequestTokenView(View):
45 self.endpoint = OAuth1RequestTokenEndpoint(PistonRequestValidator())
47 def dispatch(self, request):
48 return oauthlib_response(
49 self.endpoint.create_request_token_response(
50 **oauthlib_request(request)
55 class OAuthAuthenticationForm(forms.Form):
56 oauth_token = forms.CharField(widget=forms.HiddenInput)
57 oauth_callback = forms.CharField(widget=forms.HiddenInput) # changed from URLField - too strict
58 # removed authorize_access - redundant
61 class OAuth1AuthorizationEndpoint(AuthorizationEndpoint):
62 def create_verifier(self, request, credentials):
63 verifier = super(OAuth1AuthorizationEndpoint, self).create_verifier(request, credentials)
65 'oauth_token': verifier['oauth_token'],
70 def oauth_user_auth(request):
71 endpoint = OAuth1AuthorizationEndpoint(PistonRequestValidator())
73 if request.method == "GET":
74 # Why not just get oauth_token here?
75 # This is fairly straightforward, in't?
77 realms, credentials = endpoint.get_realms_and_credentials(
78 **oauthlib_request(request))
79 except OAuth1Error as e:
80 return HttpResponse(str(e), status=400)
81 callback = request.GET.get('oauth_callback')
83 form = OAuthAuthenticationForm(initial={
84 'oauth_token': credentials['resource_owner_key'],
85 'oauth_callback': callback,
88 return render(request, 'oauth/authorize_token.html', {'form': form})
90 if request.method == "POST":
92 response = oauthlib_response(
93 endpoint.create_authorization_response(
94 credentials={"user": request.user},
95 **oauthlib_request(request)
98 except OAuth1Error as e:
99 return HttpResponse(e.message, status=400)
104 class OAuth1AccessTokenEndpoint(AccessTokenEndpoint):
105 def _create_request(self, *args, **kwargs):
106 r = super(OAuth1AccessTokenEndpoint, self)._create_request(*args, **kwargs)
107 r.verifier = 'x' * 20
110 def create_access_token(self, request, credentials):
111 request.realms = self.request_validator.get_realms(
112 request.resource_owner_key, request)
114 'oauth_token': self.token_generator()[:KEY_SIZE],
115 'oauth_token_secret': self.token_generator()[:SECRET_SIZE],
116 'oauth_authorized_realms': ' '.join(request.realms)
118 token.update(credentials)
119 self.request_validator.save_access_token(token, request)
120 return urlencode(token.items())
124 class OAuth1AccessTokenView(View):
126 self.endpoint = OAuth1AccessTokenEndpoint(PistonRequestValidator())
128 def dispatch(self, request):
129 return oauthlib_response(
130 self.endpoint.create_access_token_response(
131 **oauthlib_request(request)
136 class LoginView(GenericAPIView):
137 serializer_class = serializers.LoginSerializer
139 def post(self, request):
140 serializer = self.get_serializer(data=request.data)
141 serializer.is_valid(raise_exception=True)
142 d = serializer.validated_data
143 user = authenticate(username=d['username'], password=d['password'])
145 return Response({"detail": "Invalid credentials."})
147 key = generate_token()[:KEY_SIZE]
148 Token.objects.create(
150 token_type=Token.ACCESS,
154 return Response({"access_token": key})
157 class Login2View(GenericAPIView):
158 serializer_class = serializers.LoginSerializer
160 def post(self, request):
161 serializer = self.get_serializer(data=request.data)
162 serializer.is_valid(raise_exception=True)
163 d = serializer.validated_data
164 user = authenticate(username=d['username'], password=d['password'])
166 return Response({"detail": "Invalid credentials."})
168 access_token = generate_token()[:KEY_SIZE]
169 Token.objects.create(
171 token_type=Token.ACCESS,
175 refresh_token = generate_token()[:KEY_SIZE]
176 Token.objects.create(
178 token_type=Token.REFRESH,
183 "access_token": access_token,
184 "refresh_token": refresh_token,
190 class UserView(RetrieveAPIView):
191 permission_classes = [IsAuthenticated]
192 serializer_class = serializers.UserSerializer
194 def get_object(self):
195 return self.request.user
199 class BookUserDataView(RetrieveAPIView):
200 permission_classes = [IsAuthenticated]
201 serializer_class = serializers.BookUserDataSerializer
202 lookup_field = 'book__slug'
203 lookup_url_kwarg = 'slug'
205 def get_queryset(self):
206 return BookUserData.objects.filter(user=self.request.user)
208 def get(self, *args, **kwargs):
210 return super(BookUserDataView, self).get(*args, **kwargs)
212 return Response({"state": "not_started"})
214 def post(self, request, slug, state):
215 if state not in ('reading', 'complete'):
218 book = get_object_or_404(Book, slug=slug)
219 instance = BookUserData.update(book, request.user, state)
220 serializer = self.get_serializer(instance)
221 return Response(serializer.data)
224 class BlogView(APIView):
225 def get(self, request):
230 class RegisterView(APIView):
231 serializer_class = serializers.RegisterSerializer
233 def get(self, request):
238 "html": "Chcę otrzymywać newsletter Wolnych Lektur",
243 'Administratorem danych osobowych jest Fundacja Wolne Lektury (ul. Marszałkowska 84/92 lok. 125, 00-514 Warszawa). Podanie danych osobowych jest dobrowolne. Dane są przetwarzane w zakresie niezbędnym do prowadzenia serwisu, a także w celach prowadzenia statystyk, ewaluacji i sprawozdawczości. W przypadku wyrażenia dodatkowej zgody adres e-mail zostanie wykorzystany także w celu przesyłania newslettera Wolnych Lektur. Osobom, których dane są zbierane, przysługuje prawo dostępu do treści swoich danych oraz ich poprawiania. Więcej informacji w <a href="https://fundacja.wolnelektury.pl/prywatnosc/">polityce prywatności</a>.'
247 def post(self, request):
251 class RefreshTokenView(APIView):
252 serializer_class = serializers.RefreshTokenSerializer
254 def post(self, request):
255 serializer = self.get_serializer(data=request.data)
256 serializer.is_valid(raise_exception=True)
257 d = serializer.validated_data
259 t = Token.objects.get(
260 key=d['refresh_token'],
261 token_type=Token.REFRESH
265 access_token = generate_token()[:KEY_SIZE]
266 Token.objects.create(
268 token_type=Token.ACCESS,
272 refresh_token = generate_token()[:KEY_SIZE]
273 Token.objects.create(
275 token_type=Token.REFRESH,
280 "access_token": access_token,
281 "refresh_token": refresh_token,
286 class RequestConfirmView(APIView):