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.contrib.auth.models import User
8 from django import forms
9 from django.http import HttpResponse
10 from django.http import Http404
11 from django.shortcuts import render
12 from django.views.generic.base import View
13 from oauthlib.common import urlencode, generate_token
14 from oauthlib.oauth1 import RequestTokenEndpoint, AccessTokenEndpoint
15 from oauthlib.oauth1 import AuthorizationEndpoint, OAuth1Error
16 from rest_framework.permissions import IsAuthenticated
17 from rest_framework.response import Response
18 from rest_framework.views import APIView
19 from rest_framework.generics import GenericAPIView, RetrieveAPIView, get_object_or_404
20 from catalogue.models import Book
21 from .models import BookUserData, KEY_SIZE, SECRET_SIZE, Token
22 from social.models import UserConfirmation
23 from . import serializers
24 from .request_validator import PistonRequestValidator
25 from .utils import oauthlib_request, oauthlib_response, vary_on_auth
28 class OAuth1RequestTokenEndpoint(RequestTokenEndpoint):
29 def _create_request(self, *args, **kwargs):
30 r = super(OAuth1RequestTokenEndpoint, self)._create_request(*args, **kwargs)
31 r.redirect_uri = 'oob'
34 def create_request_token(self, request, credentials):
36 'oauth_token': self.token_generator()[:KEY_SIZE],
37 'oauth_token_secret': self.token_generator()[:SECRET_SIZE],
39 token.update(credentials)
40 self.request_validator.save_request_token(token, request)
41 return urlencode(token.items())
45 class OAuth1RequestTokenView(View):
47 self.endpoint = OAuth1RequestTokenEndpoint(PistonRequestValidator())
49 def dispatch(self, request):
50 return oauthlib_response(
51 self.endpoint.create_request_token_response(
52 **oauthlib_request(request)
57 class OAuthAuthenticationForm(forms.Form):
58 oauth_token = forms.CharField(widget=forms.HiddenInput)
59 oauth_callback = forms.CharField(widget=forms.HiddenInput) # changed from URLField - too strict
60 # removed authorize_access - redundant
63 class OAuth1AuthorizationEndpoint(AuthorizationEndpoint):
64 def create_verifier(self, request, credentials):
65 verifier = super(OAuth1AuthorizationEndpoint, self).create_verifier(request, credentials)
67 'oauth_token': verifier['oauth_token'],
72 def oauth_user_auth(request):
73 endpoint = OAuth1AuthorizationEndpoint(PistonRequestValidator())
75 if request.method == "GET":
76 # Why not just get oauth_token here?
77 # This is fairly straightforward, in't?
79 realms, credentials = endpoint.get_realms_and_credentials(
80 **oauthlib_request(request))
81 except OAuth1Error as e:
82 return HttpResponse(str(e), status=400)
83 callback = request.GET.get('oauth_callback')
85 form = OAuthAuthenticationForm(initial={
86 'oauth_token': credentials['resource_owner_key'],
87 'oauth_callback': callback,
90 return render(request, 'oauth/authorize_token.html', {'form': form})
92 if request.method == "POST":
94 response = oauthlib_response(
95 endpoint.create_authorization_response(
96 credentials={"user": request.user},
97 **oauthlib_request(request)
100 except OAuth1Error as e:
101 return HttpResponse(e.message, status=400)
106 class OAuth1AccessTokenEndpoint(AccessTokenEndpoint):
107 def _create_request(self, *args, **kwargs):
108 r = super(OAuth1AccessTokenEndpoint, self)._create_request(*args, **kwargs)
109 r.verifier = 'x' * 20
112 def create_access_token(self, request, credentials):
113 request.realms = self.request_validator.get_realms(
114 request.resource_owner_key, request)
116 'oauth_token': self.token_generator()[:KEY_SIZE],
117 'oauth_token_secret': self.token_generator()[:SECRET_SIZE],
118 'oauth_authorized_realms': ' '.join(request.realms)
120 token.update(credentials)
121 self.request_validator.save_access_token(token, request)
122 return urlencode(token.items())
126 class OAuth1AccessTokenView(View):
128 self.endpoint = OAuth1AccessTokenEndpoint(PistonRequestValidator())
130 def dispatch(self, request):
131 return oauthlib_response(
132 self.endpoint.create_access_token_response(
133 **oauthlib_request(request)
138 class LoginView(GenericAPIView):
139 serializer_class = serializers.LoginSerializer
141 def post(self, request):
142 serializer = self.get_serializer(data=request.data)
143 serializer.is_valid(raise_exception=True)
144 d = serializer.validated_data
145 user = authenticate(username=d['username'], password=d['password'])
147 return Response({"detail": "Invalid credentials."})
149 key = generate_token()[:KEY_SIZE]
150 Token.objects.create(
152 token_type=Token.ACCESS,
156 return Response({"access_token": key})
159 class Login2View(GenericAPIView):
160 serializer_class = serializers.LoginSerializer
162 def post(self, request):
163 serializer = self.get_serializer(data=request.data)
164 serializer.is_valid(raise_exception=True)
165 d = serializer.validated_data
166 user = authenticate(username=d['username'], password=d['password'])
168 return Response({"detail": "Invalid credentials."})
170 access_token = generate_token()[:KEY_SIZE]
171 Token.objects.create(
173 token_type=Token.ACCESS,
177 refresh_token = generate_token()[:KEY_SIZE]
178 Token.objects.create(
180 token_type=Token.REFRESH,
185 "access_token": access_token,
186 "refresh_token": refresh_token,
192 class UserView(RetrieveAPIView):
193 permission_classes = [IsAuthenticated]
194 serializer_class = serializers.UserSerializer
196 def get_object(self):
197 return self.request.user
201 class BookUserDataView(RetrieveAPIView):
202 permission_classes = [IsAuthenticated]
203 serializer_class = serializers.BookUserDataSerializer
204 lookup_field = 'book__slug'
205 lookup_url_kwarg = 'slug'
207 def get_queryset(self):
208 return BookUserData.objects.filter(user=self.request.user)
210 def get(self, *args, **kwargs):
212 return super(BookUserDataView, self).get(*args, **kwargs)
214 return Response({"state": "not_started"})
216 def post(self, request, slug, state):
217 if state not in ('reading', 'complete'):
220 book = get_object_or_404(Book, slug=slug)
221 instance = BookUserData.update(book, request.user, state)
222 serializer = self.get_serializer(instance)
223 return Response(serializer.data)
226 class BlogView(APIView):
227 def get(self, request):
232 class RegisterView(GenericAPIView):
233 serializer_class = serializers.RegisterSerializer
235 def get(self, request):
240 "html": "Chcę otrzymywać newsletter Wolnych Lektur",
245 '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>.'
249 def post(self, request):
250 serializer = self.get_serializer(data=request.data)
251 serializer.is_valid(raise_exception=True)
252 d = serializer.validated_data
259 user.set_password(d['password'])
266 "detail": "Nie można utworzyć konta.",
271 UserConfirmation.request(user)
275 class RefreshTokenView(APIView):
276 serializer_class = serializers.RefreshTokenSerializer
278 def post(self, request):
279 serializer = self.get_serializer(data=request.data)
280 serializer.is_valid(raise_exception=True)
281 d = serializer.validated_data
283 t = Token.objects.get(
284 key=d['refresh_token'],
285 token_type=Token.REFRESH
289 access_token = generate_token()[:KEY_SIZE]
290 Token.objects.create(
292 token_type=Token.ACCESS,
296 refresh_token = generate_token()[:KEY_SIZE]
297 Token.objects.create(
299 token_type=Token.REFRESH,
304 "access_token": access_token,
305 "refresh_token": refresh_token,
310 class RequestConfirmView(APIView):
311 serializer_class = serializers.RequestConfirmSerializer
313 def post(self, request):
314 serializer = self.get_serializer(data=request.data)
315 serializer.is_valid(raise_exception=True)
316 d = serializer.validated_data
319 user = User.objects.get(
323 except User.DoesNotExist:
326 UserConfirmation.request(user)