1 # -*- coding: utf-8 -*-
2 # This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
3 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
5 from django.contrib.auth.decorators import login_required
6 from django import forms
7 from django.http import HttpResponse
8 from django.http import Http404
9 from django.shortcuts import render
10 from django.views.generic.base import View
11 from oauthlib.common import urlencode
12 from oauthlib.oauth1 import RequestTokenEndpoint, AccessTokenEndpoint
13 from oauthlib.oauth1 import AuthorizationEndpoint, OAuth1Error
14 from api.models import KEY_SIZE, SECRET_SIZE
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 ListAPIView, RetrieveAPIView, get_object_or_404
19 from migdal.models import Entry
20 from catalogue.models import Book
21 from .models import BookUserData
22 from . import serializers
23 from .request_validator import PistonRequestValidator
24 from .utils import oauthlib_request, oauthlib_response, vary_on_auth
27 class OAuth1RequestTokenEndpoint(RequestTokenEndpoint):
28 def _create_request(self, *args, **kwargs):
29 r = super(OAuth1RequestTokenEndpoint, self)._create_request(*args, **kwargs)
30 r.redirect_uri = 'oob'
33 def create_request_token(self, request, credentials):
35 'oauth_token': self.token_generator()[:KEY_SIZE],
36 'oauth_token_secret': self.token_generator()[:SECRET_SIZE],
38 token.update(credentials)
39 self.request_validator.save_request_token(token, request)
40 return urlencode(token.items())
44 class OAuth1RequestTokenView(View):
46 self.endpoint = OAuth1RequestTokenEndpoint(PistonRequestValidator())
48 def dispatch(self, request):
49 return oauthlib_response(
50 self.endpoint.create_request_token_response(
51 **oauthlib_request(request)
56 class OAuthAuthenticationForm(forms.Form):
57 oauth_token = forms.CharField(widget=forms.HiddenInput)
58 oauth_callback = forms.CharField(widget=forms.HiddenInput) # changed from URLField - too strict
59 # removed authorize_access - redundant
62 class OAuth1AuthorizationEndpoint(AuthorizationEndpoint):
63 def create_verifier(self, request, credentials):
64 verifier = super(OAuth1AuthorizationEndpoint, self).create_verifier(request, credentials)
66 'oauth_token': verifier['oauth_token'],
71 def oauth_user_auth(request):
72 endpoint = OAuth1AuthorizationEndpoint(PistonRequestValidator())
74 if request.method == "GET":
75 # Why not just get oauth_token here?
76 # This is fairly straightforward, in't?
78 realms, credentials = endpoint.get_realms_and_credentials(
79 **oauthlib_request(request))
80 except OAuth1Error as e:
81 return HttpResponse(e.message, status=400)
82 callback = request.GET.get('oauth_callback')
84 form = OAuthAuthenticationForm(initial={
85 'oauth_token': credentials['resource_owner_key'],
86 'oauth_callback': callback,
89 return render(request, 'oauth/authorize_token.html', {'form': form})
91 elif request.method == "POST":
93 response = oauthlib_response(
94 endpoint.create_authorization_response(
95 credentials={"user": request.user},
96 **oauthlib_request(request)
99 except OAuth1Error as e:
100 return HttpResponse(e.message, status=400)
105 class OAuth1AccessTokenEndpoint(AccessTokenEndpoint):
106 def _create_request(self, *args, **kwargs):
107 r = super(OAuth1AccessTokenEndpoint, self)._create_request(*args, **kwargs)
108 r.verifier = 'x' * 20
111 def create_access_token(self, request, credentials):
112 request.realms = self.request_validator.get_realms(
113 request.resource_owner_key, request)
115 'oauth_token': self.token_generator()[:KEY_SIZE],
116 'oauth_token_secret': self.token_generator()[:SECRET_SIZE],
117 'oauth_authorized_realms': ' '.join(request.realms)
119 token.update(credentials)
120 self.request_validator.save_access_token(token, request)
121 return urlencode(token.items())
125 class OAuth1AccessTokenView(View):
127 self.endpoint = OAuth1AccessTokenEndpoint(PistonRequestValidator())
129 def dispatch(self, request):
130 return oauthlib_response(
131 self.endpoint.create_access_token_response(
132 **oauthlib_request(request)
138 class UserView(RetrieveAPIView):
139 permission_classes = [IsAuthenticated]
140 serializer_class = serializers.UserSerializer
142 def get_object(self):
143 return self.request.user
147 class BookUserDataView(RetrieveAPIView):
148 permission_classes = [IsAuthenticated]
149 serializer_class = serializers.BookUserDataSerializer
150 lookup_field = 'book__slug'
151 lookup_url_kwarg = 'slug'
153 def get_queryset(self):
154 return BookUserData.objects.filter(user=self.request.user)
156 def get(self, *args, **kwargs):
158 return super(BookUserDataView, self).get(*args, **kwargs)
160 return Response({"state": "not_started"})
162 def post(self, request, slug, state):
163 if state not in ('reading', 'complete'):
166 book = get_object_or_404(Book, slug=slug)
167 instance = BookUserData.update(book, request.user, state)
168 serializer = self.get_serializer(instance)
169 return Response(serializer.data)
172 class BlogView(ListAPIView):
173 serializer_class = serializers.BlogSerializer
175 def get_queryset(self):
176 after = self.request.query_params.get('after')
177 count = int(self.request.query_params.get('count', 20))
178 entries = Entry.published_objects.filter(in_stream=True).order_by('-first_published_at')
180 entries = entries.filter(first_published_at__lt=after)
182 entries = entries[:count]