X-Git-Url: https://git.mdrn.pl/wolnelektury.git/blobdiff_plain/1434b3dfc0495e885921acb726df6cc06e00c5ed..4fb23ed3c3396d23a37a718fc857ecc8b36b0c7f:/src/api/views.py diff --git a/src/api/views.py b/src/api/views.py index 3cf957ed9..622332f2f 100644 --- a/src/api/views.py +++ b/src/api/views.py @@ -1,21 +1,26 @@ -# -*- coding: utf-8 -*- -# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later. -# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. +# This file is part of Wolne Lektury, licensed under GNU Affero GPLv3 or later. +# Copyright © Fundacja Wolne Lektury. See NOTICE for more information. # +from time import time +from django.contrib.auth import authenticate +from django.contrib.auth.decorators import login_required +from django import forms +from django.http import HttpResponse from django.http import Http404 -from oauthlib.common import urlencode -from oauthlib.oauth1 import RequestTokenEndpoint -from piston.models import KEY_SIZE, SECRET_SIZE +from django.shortcuts import render +from django.views.generic.base import View +from oauthlib.common import urlencode, generate_token +from oauthlib.oauth1 import RequestTokenEndpoint, AccessTokenEndpoint +from oauthlib.oauth1 import AuthorizationEndpoint, OAuth1Error from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from rest_framework.views import APIView -from rest_framework.generics import ListAPIView, RetrieveAPIView, get_object_or_404 -from migdal.models import Entry +from rest_framework.generics import GenericAPIView, RetrieveAPIView, get_object_or_404 from catalogue.models import Book -from .models import BookUserData +from .models import BookUserData, KEY_SIZE, SECRET_SIZE, Token from . import serializers from .request_validator import PistonRequestValidator -from .utils import oauthlib_request, oauthlib_response +from .utils import oauthlib_request, oauthlib_response, vary_on_auth class OAuth1RequestTokenEndpoint(RequestTokenEndpoint): @@ -34,7 +39,8 @@ class OAuth1RequestTokenEndpoint(RequestTokenEndpoint): return urlencode(token.items()) -class OAuth1RequestTokenView(APIView): +# Never Cache +class OAuth1RequestTokenView(View): def __init__(self): self.endpoint = OAuth1RequestTokenEndpoint(PistonRequestValidator()) @@ -46,6 +52,109 @@ class OAuth1RequestTokenView(APIView): ) +class OAuthAuthenticationForm(forms.Form): + oauth_token = forms.CharField(widget=forms.HiddenInput) + oauth_callback = forms.CharField(widget=forms.HiddenInput) # changed from URLField - too strict + # removed authorize_access - redundant + + +class OAuth1AuthorizationEndpoint(AuthorizationEndpoint): + def create_verifier(self, request, credentials): + verifier = super(OAuth1AuthorizationEndpoint, self).create_verifier(request, credentials) + return { + 'oauth_token': verifier['oauth_token'], + } + + +@login_required +def oauth_user_auth(request): + endpoint = OAuth1AuthorizationEndpoint(PistonRequestValidator()) + + if request.method == "GET": + # Why not just get oauth_token here? + # This is fairly straightforward, in't? + try: + realms, credentials = endpoint.get_realms_and_credentials( + **oauthlib_request(request)) + except OAuth1Error as e: + return HttpResponse(str(e), status=400) + callback = request.GET.get('oauth_callback') + + form = OAuthAuthenticationForm(initial={ + 'oauth_token': credentials['resource_owner_key'], + 'oauth_callback': callback, + }) + + return render(request, 'oauth/authorize_token.html', {'form': form}) + + if request.method == "POST": + try: + response = oauthlib_response( + endpoint.create_authorization_response( + credentials={"user": request.user}, + **oauthlib_request(request) + ) + ) + except OAuth1Error as e: + return HttpResponse(e.message, status=400) + else: + return response + + +class OAuth1AccessTokenEndpoint(AccessTokenEndpoint): + def _create_request(self, *args, **kwargs): + r = super(OAuth1AccessTokenEndpoint, self)._create_request(*args, **kwargs) + r.verifier = 'x' * 20 + return r + + def create_access_token(self, request, credentials): + request.realms = self.request_validator.get_realms( + request.resource_owner_key, request) + token = { + 'oauth_token': self.token_generator()[:KEY_SIZE], + 'oauth_token_secret': self.token_generator()[:SECRET_SIZE], + 'oauth_authorized_realms': ' '.join(request.realms) + } + token.update(credentials) + self.request_validator.save_access_token(token, request) + return urlencode(token.items()) + + +# Never cache +class OAuth1AccessTokenView(View): + def __init__(self): + self.endpoint = OAuth1AccessTokenEndpoint(PistonRequestValidator()) + + def dispatch(self, request): + return oauthlib_response( + self.endpoint.create_access_token_response( + **oauthlib_request(request) + ) + ) + + +class LoginView(GenericAPIView): + serializer_class = serializers.LoginSerializer + + def post(self, request): + serializer = self.get_serializer(data=request.data) + serializer.is_valid(raise_exception=True) + d = serializer.validated_data + user = authenticate(username=d['username'], password=d['password']) + if user is None: + return Response({"detail": "Invalid credentials."}) + + key = generate_token()[:KEY_SIZE] + Token.objects.create( + key=key, + token_type=Token.ACCESS, + timestamp=time(), + user=user, + ) + return Response({"access_token": key}) + + +@vary_on_auth class UserView(RetrieveAPIView): permission_classes = [IsAuthenticated] serializer_class = serializers.UserSerializer @@ -54,6 +163,7 @@ class UserView(RetrieveAPIView): return self.request.user +@vary_on_auth class BookUserDataView(RetrieveAPIView): permission_classes = [IsAuthenticated] serializer_class = serializers.BookUserDataSerializer @@ -79,15 +189,6 @@ class BookUserDataView(RetrieveAPIView): return Response(serializer.data) -class BlogView(ListAPIView): - serializer_class = serializers.BlogSerializer - - def get_queryset(self): - after = self.request.query_params.get('after') - count = int(self.request.query_params.get('count', 20)) - entries = Entry.published_objects.filter(in_stream=True).order_by('-first_published_at') - if after: - entries = entries.filter(first_published_at__lt=after) - if count: - entries = entries[:count] - return entries +class BlogView(APIView): + def get(self, request): + return Response([])