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.conf import settings
6 from django.contrib.auth import authenticate
7 from django.contrib.auth.decorators import login_required
8 from django.contrib.auth.models import User
9 from django import forms
10 from django.http import HttpResponse
11 from django.http import Http404
12 from django.shortcuts import render
13 from django.views.generic.base import View
14 from oauthlib.common import urlencode, generate_token
15 from oauthlib.oauth1 import RequestTokenEndpoint, AccessTokenEndpoint
16 from oauthlib.oauth1 import AuthorizationEndpoint, OAuth1Error
17 from rest_framework.permissions import IsAuthenticated
18 from rest_framework.response import Response
19 from rest_framework.views import APIView
20 from rest_framework.generics import GenericAPIView, RetrieveAPIView, get_object_or_404
21 from catalogue.models import Book
22 from .models import BookUserData, KEY_SIZE, SECRET_SIZE, Token
23 from social.models import UserConfirmation
24 from . import serializers
25 from .request_validator import PistonRequestValidator
26 from .utils import oauthlib_request, oauthlib_response, vary_on_auth
29 class OAuth1RequestTokenEndpoint(RequestTokenEndpoint):
30 def _create_request(self, *args, **kwargs):
31 r = super(OAuth1RequestTokenEndpoint, self)._create_request(*args, **kwargs)
32 r.redirect_uri = 'oob'
35 def create_request_token(self, request, credentials):
37 'oauth_token': self.token_generator()[:KEY_SIZE],
38 'oauth_token_secret': self.token_generator()[:SECRET_SIZE],
40 token.update(credentials)
41 self.request_validator.save_request_token(token, request)
42 return urlencode(token.items())
46 class OAuth1RequestTokenView(View):
48 self.endpoint = OAuth1RequestTokenEndpoint(PistonRequestValidator())
50 def dispatch(self, request):
51 return oauthlib_response(
52 self.endpoint.create_request_token_response(
53 **oauthlib_request(request)
58 class OAuthAuthenticationForm(forms.Form):
59 oauth_token = forms.CharField(widget=forms.HiddenInput)
60 oauth_callback = forms.CharField(widget=forms.HiddenInput) # changed from URLField - too strict
61 # removed authorize_access - redundant
64 class OAuth1AuthorizationEndpoint(AuthorizationEndpoint):
65 def create_verifier(self, request, credentials):
66 verifier = super(OAuth1AuthorizationEndpoint, self).create_verifier(request, credentials)
68 'oauth_token': verifier['oauth_token'],
73 def oauth_user_auth(request):
74 endpoint = OAuth1AuthorizationEndpoint(PistonRequestValidator())
76 if request.method == "GET":
77 # Why not just get oauth_token here?
78 # This is fairly straightforward, in't?
80 realms, credentials = endpoint.get_realms_and_credentials(
81 **oauthlib_request(request))
82 except OAuth1Error as e:
83 return HttpResponse(str(e), status=400)
84 callback = request.GET.get('oauth_callback')
86 form = OAuthAuthenticationForm(initial={
87 'oauth_token': credentials['resource_owner_key'],
88 'oauth_callback': callback,
91 return render(request, 'oauth/authorize_token.html', {'form': form})
93 if request.method == "POST":
95 response = oauthlib_response(
96 endpoint.create_authorization_response(
97 credentials={"user": request.user},
98 **oauthlib_request(request)
101 except OAuth1Error as e:
102 return HttpResponse(e.message, status=400)
107 class OAuth1AccessTokenEndpoint(AccessTokenEndpoint):
108 def _create_request(self, *args, **kwargs):
109 r = super(OAuth1AccessTokenEndpoint, self)._create_request(*args, **kwargs)
110 r.verifier = 'x' * 20
113 def create_access_token(self, request, credentials):
114 request.realms = self.request_validator.get_realms(
115 request.resource_owner_key, request)
117 'oauth_token': self.token_generator()[:KEY_SIZE],
118 'oauth_token_secret': self.token_generator()[:SECRET_SIZE],
119 'oauth_authorized_realms': ' '.join(request.realms)
121 token.update(credentials)
122 self.request_validator.save_access_token(token, request)
123 return urlencode(token.items())
127 class OAuth1AccessTokenView(View):
129 self.endpoint = OAuth1AccessTokenEndpoint(PistonRequestValidator())
131 def dispatch(self, request):
132 return oauthlib_response(
133 self.endpoint.create_access_token_response(
134 **oauthlib_request(request)
139 class LoginView(GenericAPIView):
140 serializer_class = serializers.LoginSerializer
142 def post(self, request):
143 serializer = self.get_serializer(data=request.data)
144 serializer.is_valid(raise_exception=True)
145 d = serializer.validated_data
146 user = authenticate(username=d['username'], password=d['password'])
148 return Response({"detail": "Invalid credentials."})
150 key = generate_token()[:KEY_SIZE]
151 Token.objects.create(
153 token_type=Token.ACCESS,
157 return Response({"access_token": key})
160 class Login2View(GenericAPIView):
161 serializer_class = serializers.LoginSerializer
163 def post(self, request):
164 serializer = self.get_serializer(data=request.data)
165 serializer.is_valid(raise_exception=True)
166 d = serializer.validated_data
167 user = authenticate(username=d['username'], password=d['password'])
169 return Response({"detail": "Invalid credentials."})
171 access_token = generate_token()[:KEY_SIZE]
172 Token.objects.create(
174 token_type=Token.ACCESS,
178 refresh_token = generate_token()[:KEY_SIZE]
179 Token.objects.create(
181 token_type=Token.REFRESH,
186 "access_token": access_token,
187 "refresh_token": refresh_token,
193 class UserView(RetrieveAPIView):
194 permission_classes = [IsAuthenticated]
195 serializer_class = serializers.UserSerializer
197 def get_object(self):
198 return self.request.user
202 class BookUserDataView(RetrieveAPIView):
203 permission_classes = [IsAuthenticated]
204 serializer_class = serializers.BookUserDataSerializer
205 lookup_field = 'book__slug'
206 lookup_url_kwarg = 'slug'
208 def get_queryset(self):
209 return BookUserData.objects.filter(user=self.request.user)
211 def get(self, *args, **kwargs):
213 return super(BookUserDataView, self).get(*args, **kwargs)
215 return Response({"state": "not_started"})
217 def post(self, request, slug, state):
218 if state not in ('reading', 'complete'):
221 book = get_object_or_404(Book, slug=slug)
222 instance = BookUserData.update(book, request.user, state)
223 serializer = self.get_serializer(instance)
224 return Response(serializer.data)
227 class BlogView(APIView):
228 def get(self, request):
233 class RegisterView(GenericAPIView):
234 serializer_class = serializers.RegisterSerializer
236 def get(self, request):
241 "html": "Chcę otrzymywać newsletter Wolnych Lektur",
246 '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>.'
250 def post(self, request):
251 if not settings.FEATURE_API_REGISTER:
254 "detail": "Rejestracja aktualnie niedostępna."
258 serializer = self.get_serializer(data=request.data)
259 serializer.is_valid(raise_exception=True)
260 d = serializer.validated_data
267 user.set_password(d['password'])
274 "detail": "Nie można utworzyć konta.",
279 UserConfirmation.request(user)
283 class RefreshTokenView(APIView):
284 serializer_class = serializers.RefreshTokenSerializer
286 def post(self, request):
287 serializer = self.get_serializer(data=request.data)
288 serializer.is_valid(raise_exception=True)
289 d = serializer.validated_data
291 t = Token.objects.get(
292 key=d['refresh_token'],
293 token_type=Token.REFRESH
297 access_token = generate_token()[:KEY_SIZE]
298 Token.objects.create(
300 token_type=Token.ACCESS,
304 refresh_token = generate_token()[:KEY_SIZE]
305 Token.objects.create(
307 token_type=Token.REFRESH,
312 "access_token": access_token,
313 "refresh_token": refresh_token,
318 class RequestConfirmView(APIView):
319 serializer_class = serializers.RequestConfirmSerializer
321 def post(self, request):
322 serializer = self.get_serializer(data=request.data)
323 serializer.is_valid(raise_exception=True)
324 d = serializer.validated_data
327 user = User.objects.get(
331 except User.DoesNotExist:
334 UserConfirmation.request(user)