From 553e0837da7d466b1307d859d7a9907dbba94e88 Mon Sep 17 00:00:00 2001
From: Eliot Berriot <contact@hat.eliotberriot.com>
Date: Wed, 15 Jun 2016 21:06:18 +0200
Subject: [PATCH] Fixed #3: can now require authentication via environment
variable
---
config/settings/common.py | 3 ++-
funkwhale_api/common/__init__.py | 0
funkwhale_api/common/permissions.py | 11 +++++++++
.../favorites/tests/test_favorites.py | 21 +++++++++++++++++
funkwhale_api/favorites/views.py | 3 +++
funkwhale_api/history/views.py | 5 ++--
funkwhale_api/music/tests/test_api.py | 23 +++++++++++++++++++
funkwhale_api/music/views.py | 10 ++++----
funkwhale_api/musicbrainz/views.py | 9 ++++----
funkwhale_api/providers/youtube/views.py | 4 ++--
funkwhale_api/radios/views.py | 5 ++--
test.yml | 1 +
12 files changed, 79 insertions(+), 16 deletions(-)
create mode 100644 funkwhale_api/common/__init__.py
create mode 100644 funkwhale_api/common/permissions.py
diff --git a/config/settings/common.py b/config/settings/common.py
index 1af80e9..a176a4d 100644
--- a/config/settings/common.py
+++ b/config/settings/common.py
@@ -271,11 +271,12 @@ CORS_ORIGIN_ALLOW_ALL = True
# 'funkwhale.localhost',
# )
CORS_ALLOW_CREDENTIALS = True
+API_AUTHENTICATION_REQUIRED = env.bool("API_AUTHENTICATION_REQUIRED", True)
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
- 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
+ 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 25,
'DEFAULT_AUTHENTICATION_CLASSES': (
diff --git a/funkwhale_api/common/__init__.py b/funkwhale_api/common/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/funkwhale_api/common/permissions.py b/funkwhale_api/common/permissions.py
new file mode 100644
index 0000000..3f13b20
--- /dev/null
+++ b/funkwhale_api/common/permissions.py
@@ -0,0 +1,11 @@
+from django.conf import settings
+
+from rest_framework.permissions import BasePermission
+
+
+class ConditionalAuthentication(BasePermission):
+
+ def has_permission(self, request, view):
+ if settings.API_AUTHENTICATION_REQUIRED:
+ return request.user and request.user.is_authenticated()
+ return True
diff --git a/funkwhale_api/favorites/tests/test_favorites.py b/funkwhale_api/favorites/tests/test_favorites.py
index 9918c67..1182222 100644
--- a/funkwhale_api/favorites/tests/test_favorites.py
+++ b/funkwhale_api/favorites/tests/test_favorites.py
@@ -66,3 +66,24 @@ class TestFavorites(TestCase):
self.assertEqual(response.status_code, 204)
self.assertEqual(TrackFavorite.objects.count(), 0)
+
+ from funkwhale_api.users.models import User
+
+ def test_can_restrict_api_views_to_authenticated_users(self):
+ urls = [
+ ('api:favorites:tracks-list', 'get'),
+ ]
+
+ for route_name, method in urls:
+ url = self.reverse(route_name)
+ with self.settings(API_AUTHENTICATION_REQUIRED=True):
+ response = getattr(self.client, method)(url)
+ self.assertEqual(response.status_code, 401)
+
+ self.client.login(username=self.user.username, password='test')
+
+ for route_name, method in urls:
+ url = self.reverse(route_name)
+ with self.settings(API_AUTHENTICATION_REQUIRED=False):
+ response = getattr(self.client, method)(url)
+ self.assertEqual(response.status_code, 200)
diff --git a/funkwhale_api/favorites/views.py b/funkwhale_api/favorites/views.py
index edcb00f..dc8098b 100644
--- a/funkwhale_api/favorites/views.py
+++ b/funkwhale_api/favorites/views.py
@@ -3,6 +3,7 @@ from rest_framework import status
from rest_framework.response import Response
from funkwhale_api.music.models import Track
+from funkwhale_api.common.permissions import ConditionalAuthentication
from . import models
from . import serializers
@@ -14,7 +15,9 @@ class TrackFavoriteViewSet(mixins.CreateModelMixin,
serializer_class = serializers.UserTrackFavoriteSerializer
queryset = models.TrackFavorite.objects.all()
+ permission_classes = [ConditionalAuthentication]
+
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
diff --git a/funkwhale_api/history/views.py b/funkwhale_api/history/views.py
index 317ca78..d65a70f 100644
--- a/funkwhale_api/history/views.py
+++ b/funkwhale_api/history/views.py
@@ -4,6 +4,7 @@ from rest_framework.response import Response
from rest_framework.decorators import detail_route
from funkwhale_api.music.serializers import TrackSerializerNested
+from funkwhale_api.common.permissions import ConditionalAuthentication
from . import models
from . import serializers
@@ -14,11 +15,11 @@ class ListeningViewSet(mixins.CreateModelMixin,
serializer_class = serializers.ListeningSerializer
queryset = models.Listening.objects.all()
- permission_classes = []
+ permission_classes = [ConditionalAuthentication]
def create(self, request, *args, **kwargs):
return super().create(request, *args, **kwargs)
-
+
def get_queryset(self):
queryset = super().get_queryset()
if self.request.user.is_authenticated():
diff --git a/funkwhale_api/music/tests/test_api.py b/funkwhale_api/music/tests/test_api.py
index d4a1c14..36c1fa6 100644
--- a/funkwhale_api/music/tests/test_api.py
+++ b/funkwhale_api/music/tests/test_api.py
@@ -139,3 +139,26 @@ class TestAPI(TMPDirTestCaseMixin, TestCase):
response = self.client.get(url + '?query={0}'.format(query))
self.assertJSONEqual(expected, json.loads(response.content.decode('utf-8')))
+
+ def test_can_restrict_api_views_to_authenticated_users(self):
+ urls = [
+ ('api:tags-list', 'get'),
+ ('api:tracks-list', 'get'),
+ ('api:artists-list', 'get'),
+ ('api:albums-list', 'get'),
+ ]
+
+ for route_name, method in urls:
+ url = self.reverse(route_name)
+ with self.settings(API_AUTHENTICATION_REQUIRED=True):
+ response = getattr(self.client, method)(url)
+ self.assertEqual(response.status_code, 401)
+
+ user = User.objects.create_user(username='test', email='test@test.com', password='test')
+ self.client.login(username=user.username, password='test')
+
+ for route_name, method in urls:
+ url = self.reverse(route_name)
+ with self.settings(API_AUTHENTICATION_REQUIRED=False):
+ response = getattr(self.client, method)(url)
+ self.assertEqual(response.status_code, 200)
diff --git a/funkwhale_api/music/views.py b/funkwhale_api/music/views.py
index 971fbd7..6622b1e 100644
--- a/funkwhale_api/music/views.py
+++ b/funkwhale_api/music/views.py
@@ -11,6 +11,7 @@ from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from funkwhale_api.musicbrainz import api
+from funkwhale_api.common.permissions import ConditionalAuthentication
from taggit.models import Tag
from . import models
@@ -40,14 +41,14 @@ class TagViewSetMixin(object):
class ArtistViewSet(SearchMixin, viewsets.ReadOnlyModelViewSet):
queryset = models.Artist.objects.all().order_by('-creation_date').prefetch_related('albums__tracks__files', 'albums__tracks__tags')
serializer_class = serializers.ArtistSerializerNested
- permission_classes = []
+ permission_classes = [ConditionalAuthentication]
search_fields = ['name']
ordering_fields = ('creation_date',)
class AlbumViewSet(SearchMixin, viewsets.ReadOnlyModelViewSet):
queryset = models.Album.objects.all().order_by('-creation_date').prefetch_related('tracks__tags')
serializer_class = serializers.AlbumSerializerNested
- permission_classes = []
+ permission_classes = [ConditionalAuthentication]
search_fields = ['title']
ordering_fields = ('creation_date',)
@@ -64,15 +65,14 @@ class TrackViewSet(TagViewSetMixin, SearchMixin, viewsets.ReadOnlyModelViewSet):
"""
queryset = models.Track.objects.all().select_related('album__artist', 'artist').prefetch_related('files')
serializer_class = serializers.TrackSerializerNested
- permission_classes = []
- authentication_classes = []
+ permission_classes = [ConditionalAuthentication]
search_fields = ['title', 'artist__name']
ordering_fields = ('creation_date',)
class TagViewSet(viewsets.ReadOnlyModelViewSet):
queryset = Tag.objects.all()
serializer_class = serializers.TagSerializer
- permission_classes = []
+ permission_classes = [ConditionalAuthentication]
class Search(views.APIView):
diff --git a/funkwhale_api/musicbrainz/views.py b/funkwhale_api/musicbrainz/views.py
index d6d8d38..21b2d88 100644
--- a/funkwhale_api/musicbrainz/views.py
+++ b/funkwhale_api/musicbrainz/views.py
@@ -3,19 +3,20 @@ from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.decorators import list_route
+from funkwhale_api.common.permissions import ConditionalAuthentication
+
+
from .client import api
class ReleaseDetail(APIView):
- authentication_classes = []
- permission_classes = []
+ permission_classes = [ConditionalAuthentication]
def get(self, request, *args, **kwargs):
result = api.releases.get(id=kwargs['uuid'], includes=['artists', 'recordings'])
return Response(result)
class SearchViewSet(viewsets.ViewSet):
- authentication_classes = []
- permission_classes = []
+ permission_classes = [ConditionalAuthentication]
@list_route(methods=['get'])
def recordings(self, request, *args, **kwargs):
diff --git a/funkwhale_api/providers/youtube/views.py b/funkwhale_api/providers/youtube/views.py
index 8a41551..9e909d7 100644
--- a/funkwhale_api/providers/youtube/views.py
+++ b/funkwhale_api/providers/youtube/views.py
@@ -1,11 +1,11 @@
from rest_framework.views import APIView
from rest_framework.response import Response
+from funkwhale_api.common.permissions import ConditionalAuthentication
from .client import client
class APISearch(APIView):
- authentication_classes = []
- permission_classes = []
+ permission_classes = [ConditionalAuthentication]
def get(self, request, *args, **kwargs):
results = client.search(request.GET['query'])
diff --git a/funkwhale_api/radios/views.py b/funkwhale_api/radios/views.py
index 37874d0..1ae788f 100644
--- a/funkwhale_api/radios/views.py
+++ b/funkwhale_api/radios/views.py
@@ -4,6 +4,7 @@ from rest_framework.response import Response
from rest_framework.decorators import detail_route
from funkwhale_api.music.serializers import TrackSerializerNested
+from funkwhale_api.common.permissions import ConditionalAuthentication
from . import models
from . import serializers
@@ -14,7 +15,7 @@ class RadioSessionViewSet(mixins.CreateModelMixin,
serializer_class = serializers.RadioSessionSerializer
queryset = models.RadioSession.objects.all()
- permission_classes = []
+ permission_classes = [ConditionalAuthentication]
def get_queryset(self):
queryset = super().get_queryset()
@@ -36,7 +37,7 @@ class RadioSessionTrackViewSet(mixins.CreateModelMixin,
viewsets.GenericViewSet):
serializer_class = serializers.RadioSessionTrackSerializer
queryset = models.RadioSessionTrack.objects.all()
- permission_classes = []
+ permission_classes = [ConditionalAuthentication]
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
diff --git a/test.yml b/test.yml
index dc50a9b..c28a813 100644
--- a/test.yml
+++ b/test.yml
@@ -6,3 +6,4 @@ test:
- .:/app
environment:
- DJANGO_SETTINGS_MODULE=config.settings.test
+ - API_AUTHENTICATION_REQUIRED=False
--
GitLab