--- /dev/null
+"""
+Transitional code: bridge between Piston's OAuth implementation
+and DRF views.
+"""
+from piston.authentication import OAuthAuthentication
+from rest_framework.authentication import BaseAuthentication
+
+
+class PistonOAuthAuthentication(BaseAuthentication):
+ def __init__(self):
+ self.piston_auth = OAuthAuthentication()
+
+ def authenticate_header(self, request):
+ return 'OAuth realm="API"'
+
+ def authenticate(self, request):
+ if self.piston_auth.is_valid_request(request):
+ consumer, token, parameters = self.piston_auth.validate_token(request)
+ if consumer and token:
+ return token.user, token
+++ /dev/null
-# -*- 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.
-#
-"""
-Wrappers for piston Emitter classes.
-"""
-from piston.emitters import Emitter
-
-
-# hack
-class EpubEmitter(Emitter):
- def render(self, request):
- return self.data
-
-Emitter.register('epub', EpubEmitter, 'application/epub+zip')
from rest_framework import serializers
from django.core.urlresolvers import reverse
+from paypal.rest import user_is_subscribed
class AbsoluteURLField(serializers.ReadOnlyField):
if field in value and value[field] is None:
value[field] = ''
return value
+
+
+class UserPremiumField(serializers.ReadOnlyField):
+ def __init__(self, *args, **kwargs):
+ super(UserPremiumField, self).__init__(*args, source='*', **kwargs)
+
+ def to_representation(self, value):
+ return user_is_subscribed(value)
from stats.utils import piwik_track
from wolnelektury.utils import re_escape
-from . import emitters # Register our emitters
API_BASE = WL_BASE = MEDIA_BASE = lazy(
lambda: u'https://' + Site.objects.get_current().domain, unicode)()
return rc.NOT_FOUND
-class EpubHandler(BookDetailHandler):
- def read(self, request, slug):
- if not user_is_subscribed(request.user):
- return rc.FORBIDDEN
- try:
- book = Book.objects.get(slug=slug)
- except Book.DoesNotExist:
- return rc.NOT_FOUND
- response = HttpResponse(book.get_media('epub'))
- return response
-
-
class EBooksHandler(AnonymousBooksHandler):
fields = ('author', 'href', 'title', 'cover') + tuple(Book.ebook_formats) + ('slug',)
return rc.NOT_FOUND
-class UserDataHandler(BaseHandler):
- model = BookUserData
- fields = ('state', 'username', 'premium')
- allowed_methods = ('GET', 'POST')
-
- def read(self, request, slug=None):
- if not request.user.is_authenticated():
- return rc.FORBIDDEN
- if slug is None:
- return {'username': request.user.username, 'premium': user_is_subscribed(request.user)}
- try:
- book = Book.objects.get(slug=slug)
- except Book.DoesNotExist:
- return rc.NOT_FOUND
- try:
- data = BookUserData.objects.get(book=book, user=request.user)
- except BookUserData.DoesNotExist:
- return {'state': 'not_started'}
- return data
-
- def create(self, request, slug, state):
- try:
- book = Book.objects.get(slug=slug)
- except Book.DoesNotExist:
- return rc.NOT_FOUND
- if not request.user.is_authenticated():
- return rc.FORBIDDEN
- if state not in ('reading', 'complete'):
- return rc.NOT_FOUND
- data, created = BookUserData.objects.get_or_create(book=book, user=request.user)
- data.state = state
- data.save()
- return data
-
-
class UserShelfHandler(BookDetailHandler):
fields = book_list_fields + ['liked']
complete = models.BooleanField(default=False)
last_changed = models.DateTimeField(auto_now=True)
- def get_state(self):
+ @property
+ def state(self):
return 'complete' if self.complete else 'reading'
- def set_state(self, state):
- self.complete = state == 'complete'
-
- state = property(get_state, set_state)
+ @classmethod
+ def update(cls, book, user, state):
+ instance, created = cls.objects.get_or_create(book=book, user=user)
+ instance.complete = state == 'complete'
+ instance.save()
+ return instance
--- /dev/null
+from django.contrib.auth.models import User
+from rest_framework import serializers
+from .fields import UserPremiumField
+from .models import BookUserData
+
+
+class UserSerializer(serializers.ModelSerializer):
+ premium = UserPremiumField()
+
+ class Meta:
+ model = User
+ fields = ['username', 'premium']
+
+
+class BookUserDataSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = BookUserData
+ fields = ['state']
{"username": "test", "premium": False})
self.assertEqual(
self.signed('/api/epub/grandchild/').status_code,
- 401) # Not 403 because Piston.
+ 403)
- with patch('api.handlers.user_is_subscribed', return_value=True):
+ with patch('api.fields.user_is_subscribed', return_value=True):
self.assertEqual(
self.signed_json('/api/username/'),
{"username": "test", "premium": True})
+ with patch('paypal.permissions.user_is_subscribed', return_value=True):
with patch('django.core.files.storage.Storage.open',
return_value=StringIO("<epub>")):
self.assertEqual(
from api import handlers
from api.helpers import CsrfExemptResource
from api.piston_patch import oauth_user_auth
+from . import views
auth = OAuthAuthentication(realm="Wolne Lektury")
ebook_list_resource = Resource(handler=handlers.EBooksHandler)
# book_list_resource = Resource(handler=handlers.BooksHandler)
filter_book_resource = auth_resource(handler=handlers.FilterBooksHandler)
-epub_resource = auth_resource(handler=handlers.EpubHandler)
preview_resource = Resource(handler=handlers.BookPreviewHandler)
-reading_resource = auth_resource(handler=handlers.UserDataHandler)
shelf_resource = auth_resource(handler=handlers.UserShelfHandler)
like_resource = auth_resource(handler=handlers.UserLikeHandler)
url(r'book/(?P<book_id>\d*?)/info\.html$', catalogue.views.book_info),
url(r'tag/(?P<tag_id>\d*?)/info\.html$', catalogue.views.tag_info),
- # epub preview
- url(r'^epub/(?P<slug>[a-z0-9-]+)/$', epub_resource, name='api_epub'),
-
# reading data
- url(r'^reading/(?P<slug>[a-z0-9-]+)/$', reading_resource, name='api_reading'),
- url(r'^reading/(?P<slug>[a-z0-9-]+)/(?P<state>[a-z]+)/$', reading_resource, name='api_reading'),
+ url(r'^reading/(?P<slug>[a-z0-9-]+)/$', views.BookUserDataView.as_view(), name='api_reading'),
+ url(r'^reading/(?P<slug>[a-z0-9-]+)/(?P<state>[a-z]+)/$', views.BookUserDataView.as_view(), name='api_reading'),
url(r'^shelf/(?P<state>[a-z]+)/$', shelf_resource, name='api_shelf'),
- url(r'^username/$', reading_resource, name='api_username'),
+ url(r'^username/$', views.UserView.as_view(), name='api_username'),
url(r'^like/(?P<slug>[a-z0-9-]+)/$', like_resource, name='api_like'),
--- /dev/null
+from django.http import Http404
+from rest_framework.permissions import IsAuthenticated
+from rest_framework.response import Response
+from rest_framework.views import APIView
+from rest_framework.generics import RetrieveAPIView, get_object_or_404
+from catalogue.models import Book
+from .models import BookUserData
+from . import serializers
+
+
+class UserView(RetrieveAPIView):
+ permission_classes = [IsAuthenticated]
+ serializer_class = serializers.UserSerializer
+
+ def get_object(self):
+ return self.request.user
+
+
+class BookUserDataView(RetrieveAPIView):
+ permission_classes = [IsAuthenticated]
+ serializer_class = serializers.BookUserDataSerializer
+ lookup_field = 'book__slug'
+ lookup_url_kwarg = 'slug'
+
+ def get_queryset(self):
+ return BookUserData.objects.filter(user=self.request.user)
+
+ def get(self, *args, **kwargs):
+ try:
+ return super(BookUserDataView, self).get(*args, **kwargs)
+ except Http404:
+ return Response({"state": "not_started"})
+
+ def post(self, request, slug, state):
+ if state not in ('reading', 'complete'):
+ raise Http404
+
+ book = get_object_or_404(Book, slug=slug)
+ instance = BookUserData.update(book, request.user, state)
+ serializer = self.get_serializer(instance)
+ return Response(serializer.data)
views.CollectionDetail.as_view(), name="collection-detail"),
url(r'^books/(?P<slug>[^/]+)/$', views.BookDetail.as_view(), name='catalogue_api_book'),
+
+ url(r'^epub/(?P<slug>[a-z0-9-]+)/$', views.EpubView.as_view(), name='catalogue_api_epub'),
]
+from django.http import HttpResponse
from rest_framework.generics import ListAPIView, RetrieveAPIView
+from paypal.permissions import IsSubscribed
from . import serializers
from catalogue.models import Book, Collection
queryset = Book.objects.all()
lookup_field = 'slug'
serializer_class = serializers.BookDetailSerializer
+
+
+class EpubView(RetrieveAPIView):
+ queryset = Book.objects.all()
+ lookup_field = 'slug'
+ permission_classes = [IsSubscribed]
+
+ def get(self, *args, **kwargs):
+ return HttpResponse(self.get_object().get_media('epub'))
--- /dev/null
+from rest_framework.permissions import BasePermission
+from .rest import user_is_subscribed
+
+
+class IsSubscribed(BasePermission):
+ def has_permission(self, request, view):
+ return request.user.is_authenticated and user_is_subscribed(request.user)
'api.renderers.LegacyXMLRenderer',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
+ 'api.drf_auth.PistonOAuthAuthentication',
)
}