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 allauth.account.forms import ResetPasswordForm
6 from django.conf import settings
7 from django.contrib.auth import authenticate
8 from django.contrib.auth.decorators import login_required
9 from django.contrib.auth.models import User
10 from django import forms
11 from django.http import HttpResponse
12 from django.http import Http404
13 from django.shortcuts import render
14 from django.views.generic.base import View
15 from oauthlib.common import urlencode, generate_token
16 from oauthlib.oauth1 import RequestTokenEndpoint, AccessTokenEndpoint
17 from oauthlib.oauth1 import AuthorizationEndpoint, OAuth1Error
18 from rest_framework.permissions import IsAuthenticated
19 from rest_framework.response import Response
20 from rest_framework.views import APIView
21 from rest_framework.generics import GenericAPIView, RetrieveAPIView, get_object_or_404
22 from catalogue.models import Book
23 from .models import BookUserData, KEY_SIZE, SECRET_SIZE, Token
24 from social.models import UserConfirmation
25 from . import serializers
26 from .request_validator import PistonRequestValidator
27 from .utils import oauthlib_request, oauthlib_response, vary_on_auth
30 class OAuth1RequestTokenEndpoint(RequestTokenEndpoint):
31 def _create_request(self, *args, **kwargs):
32 r = super(OAuth1RequestTokenEndpoint, self)._create_request(*args, **kwargs)
33 r.redirect_uri = 'oob'
36 def create_request_token(self, request, credentials):
38 'oauth_token': self.token_generator()[:KEY_SIZE],
39 'oauth_token_secret': self.token_generator()[:SECRET_SIZE],
41 token.update(credentials)
42 self.request_validator.save_request_token(token, request)
43 return urlencode(token.items())
47 class OAuth1RequestTokenView(View):
49 self.endpoint = OAuth1RequestTokenEndpoint(PistonRequestValidator())
51 def dispatch(self, request):
52 return oauthlib_response(
53 self.endpoint.create_request_token_response(
54 **oauthlib_request(request)
59 class OAuthAuthenticationForm(forms.Form):
60 oauth_token = forms.CharField(widget=forms.HiddenInput)
61 oauth_callback = forms.CharField(widget=forms.HiddenInput) # changed from URLField - too strict
62 # removed authorize_access - redundant
65 class OAuth1AuthorizationEndpoint(AuthorizationEndpoint):
66 def create_verifier(self, request, credentials):
67 verifier = super(OAuth1AuthorizationEndpoint, self).create_verifier(request, credentials)
69 'oauth_token': verifier['oauth_token'],
74 def oauth_user_auth(request):
75 endpoint = OAuth1AuthorizationEndpoint(PistonRequestValidator())
77 if request.method == "GET":
78 # Why not just get oauth_token here?
79 # This is fairly straightforward, in't?
81 realms, credentials = endpoint.get_realms_and_credentials(
82 **oauthlib_request(request))
83 except OAuth1Error as e:
84 return HttpResponse(str(e), status=400)
85 callback = request.GET.get('oauth_callback')
87 form = OAuthAuthenticationForm(initial={
88 'oauth_token': credentials['resource_owner_key'],
89 'oauth_callback': callback,
92 return render(request, 'oauth/authorize_token.html', {'form': form})
94 if request.method == "POST":
96 response = oauthlib_response(
97 endpoint.create_authorization_response(
98 credentials={"user": request.user},
99 **oauthlib_request(request)
102 except OAuth1Error as e:
103 return HttpResponse(e.message, status=400)
108 class OAuth1AccessTokenEndpoint(AccessTokenEndpoint):
109 def _create_request(self, *args, **kwargs):
110 r = super(OAuth1AccessTokenEndpoint, self)._create_request(*args, **kwargs)
111 r.verifier = 'x' * 20
114 def create_access_token(self, request, credentials):
115 request.realms = self.request_validator.get_realms(
116 request.resource_owner_key, request)
118 'oauth_token': self.token_generator()[:KEY_SIZE],
119 'oauth_token_secret': self.token_generator()[:SECRET_SIZE],
120 'oauth_authorized_realms': ' '.join(request.realms)
122 token.update(credentials)
123 self.request_validator.save_access_token(token, request)
124 return urlencode(token.items())
128 class OAuth1AccessTokenView(View):
130 self.endpoint = OAuth1AccessTokenEndpoint(PistonRequestValidator())
132 def dispatch(self, request):
133 return oauthlib_response(
134 self.endpoint.create_access_token_response(
135 **oauthlib_request(request)
140 class LoginView(GenericAPIView):
141 serializer_class = serializers.LoginSerializer
143 def post(self, request):
144 serializer = self.get_serializer(data=request.data)
145 serializer.is_valid(raise_exception=True)
146 d = serializer.validated_data
147 user = authenticate(username=d['username'], password=d['password'])
149 return Response({"detail": "Invalid credentials."})
151 key = generate_token()[:KEY_SIZE]
152 Token.objects.create(
154 token_type=Token.ACCESS,
158 return Response({"access_token": key})
161 class Login2View(GenericAPIView):
162 serializer_class = serializers.LoginSerializer
164 def post(self, request):
165 serializer = self.get_serializer(data=request.data)
166 serializer.is_valid(raise_exception=True)
167 d = serializer.validated_data
168 user = authenticate(username=d['username'], password=d['password'])
170 return Response({"detail": "Invalid credentials."})
172 access_token = generate_token()[:KEY_SIZE]
173 Token.objects.create(
175 token_type=Token.ACCESS,
179 refresh_token = generate_token()[:KEY_SIZE]
180 Token.objects.create(
182 token_type=Token.REFRESH,
187 "access_token": access_token,
188 "refresh_token": refresh_token,
194 class UserView(RetrieveAPIView):
195 permission_classes = [IsAuthenticated]
196 serializer_class = serializers.UserSerializer
198 def get_object(self):
199 return self.request.user
203 class BookUserDataView(RetrieveAPIView):
204 permission_classes = [IsAuthenticated]
205 serializer_class = serializers.BookUserDataSerializer
206 lookup_field = 'book__slug'
207 lookup_url_kwarg = 'slug'
209 def get_queryset(self):
210 return BookUserData.objects.filter(user=self.request.user)
212 def get(self, *args, **kwargs):
214 return super(BookUserDataView, self).get(*args, **kwargs)
216 return Response({"state": "not_started"})
218 def post(self, request, slug, state):
219 if state not in ('reading', 'complete'):
222 book = get_object_or_404(Book, slug=slug)
223 instance = BookUserData.update(book, request.user, state)
224 serializer = self.get_serializer(instance)
225 return Response(serializer.data)
228 class BlogView(APIView):
229 def get(self, request):
234 class RegisterView(GenericAPIView):
235 serializer_class = serializers.RegisterSerializer
237 def get(self, request):
242 "html": "Chcę otrzymywać newsletter Wolnych Lektur",
247 '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>.'
251 def post(self, request):
252 if not settings.FEATURE_API_REGISTER:
255 "detail": "Rejestracja aktualnie niedostępna."
259 serializer = self.get_serializer(data=request.data)
260 serializer.is_valid(raise_exception=True)
261 d = serializer.validated_data
268 user.set_password(d['password'])
275 "detail": "Nie można utworzyć konta.",
280 UserConfirmation.request(user)
284 class RefreshTokenView(APIView):
285 serializer_class = serializers.RefreshTokenSerializer
287 def post(self, request):
288 serializer = self.get_serializer(data=request.data)
289 serializer.is_valid(raise_exception=True)
290 d = serializer.validated_data
292 t = Token.objects.get(
293 key=d['refresh_token'],
294 token_type=Token.REFRESH
298 access_token = generate_token()[:KEY_SIZE]
299 Token.objects.create(
301 token_type=Token.ACCESS,
305 refresh_token = generate_token()[:KEY_SIZE]
306 Token.objects.create(
308 token_type=Token.REFRESH,
313 "access_token": access_token,
314 "refresh_token": refresh_token,
319 class RequestConfirmView(APIView):
320 serializer_class = serializers.RequestConfirmSerializer
322 def post(self, request):
323 serializer = self.get_serializer(data=request.data)
324 serializer.is_valid(raise_exception=True)
325 d = serializer.validated_data
328 user = User.objects.get(
332 except User.DoesNotExist:
335 UserConfirmation.request(user)
339 class DeleteAccountView(GenericAPIView):
340 permission_classes = [IsAuthenticated]
341 serializer_class = serializers.DeleteAccountSerializer
343 def post(self, request):
345 serializer = self.get_serializer(
349 serializer.is_valid(raise_exception=True)
350 d = serializer.validated_data
356 class PasswordView(GenericAPIView):
357 permission_classes = [IsAuthenticated]
358 serializer_class = serializers.PasswordSerializer
360 def post(self, request):
362 serializer = self.get_serializer(
366 serializer.is_valid(raise_exception=True)
367 d = serializer.validated_data
368 u.set_password(d['new_password'])
373 class ResetPasswordView(GenericAPIView):
374 serializer_class = serializers.ResetPasswordSerializer
376 def post(self, request):
377 serializer = serializers.ResetPasswordSerializer(data=request.data)
378 serializer.is_valid(raise_exception=True)
379 form = ResetPasswordForm({"email": serializer.validated_data['email']})