# Copyright © Fundacja Wolne Lektury. See NOTICE for more information.
#
from oauthlib.oauth1 import ResourceEndpoint
-from rest_framework.authentication import BaseAuthentication
+from rest_framework.authentication import BaseAuthentication, TokenAuthentication
from .request_validator import PistonRequestValidator
from .utils import oauthlib_request
+from .models import Token
class PistonOAuthAuthentication(BaseAuthentication):
)
if v:
return r.token.user, r.token
+
+
+class WLTokenAuthentication(TokenAuthentication):
+ model = Token
--- /dev/null
+# Generated by Django 4.0.8 on 2024-11-08 10:14
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('api', '0006_alter_bookuserdata_unique_together'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='token',
+ name='consumer',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='api.consumer'),
+ ),
+ ]
settings.AUTH_USER_MODEL, models.CASCADE,
null=True, blank=True, related_name='tokens'
)
- consumer = models.ForeignKey(Consumer, models.CASCADE)
+ consumer = models.ForeignKey(Consumer, models.CASCADE, null=True, blank=True)
def __str__(self):
return "%s Token %s for %s" % (self.get_token_type_display(), self.key, self.consumer)
class Meta:
model = BookUserData
fields = ['state']
+
+
+class LoginSerializer(serializers.Serializer):
+ username = serializers.CharField()
+ password = serializers.CharField(style={'input_type': 'password'})
path('oauth/request_token/', csrf_exempt(views.OAuth1RequestTokenView.as_view())),
path('oauth/authorize/', views.oauth_user_auth, name='oauth_user_auth'),
path('oauth/access_token/', csrf_exempt(views.OAuth1AccessTokenView.as_view())),
+ path('login/', csrf_exempt(views.LoginView.as_view())),
path('', TemplateView.as_view(template_name='api/main.html'), name='api'),
# 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 django.shortcuts import render
from django.views.generic.base import View
-from oauthlib.common import urlencode
+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 RetrieveAPIView, get_object_or_404
+from rest_framework.generics import GenericAPIView, RetrieveAPIView, get_object_or_404
from catalogue.models import Book
-from .models import BookUserData, KEY_SIZE, SECRET_SIZE
+from .models import BookUserData, KEY_SIZE, SECRET_SIZE, Token
from . import serializers
from .request_validator import PistonRequestValidator
from .utils import oauthlib_request, oauthlib_response, vary_on_auth
)
+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]
'api.renderers.LegacyXMLRenderer',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
+ 'api.drf_auth.WLTokenAuthentication',
'api.drf_auth.PistonOAuthAuthentication',
'rest_framework.authentication.SessionAuthentication',
)