Additional permissions checks on playlist views
This commit is contained in:
parent
4f7fa09a78
commit
9a909798e7
|
@ -1,9 +1,11 @@
|
||||||
from rest_framework import generics, mixins, viewsets
|
from rest_framework import generics, mixins, viewsets
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
from rest_framework.permissions import IsAuthenticatedOrReadOnly
|
||||||
|
|
||||||
from funkwhale_api.music.models import Track
|
from funkwhale_api.music.models import Track
|
||||||
from funkwhale_api.common.permissions import ConditionalAuthentication
|
from funkwhale_api.common import permissions
|
||||||
|
from funkwhale_api.common import fields
|
||||||
|
|
||||||
from . import models
|
from . import models
|
||||||
from . import serializers
|
from . import serializers
|
||||||
|
@ -12,24 +14,22 @@ from . import serializers
|
||||||
class PlaylistViewSet(
|
class PlaylistViewSet(
|
||||||
mixins.RetrieveModelMixin,
|
mixins.RetrieveModelMixin,
|
||||||
mixins.CreateModelMixin,
|
mixins.CreateModelMixin,
|
||||||
|
mixins.UpdateModelMixin,
|
||||||
mixins.DestroyModelMixin,
|
mixins.DestroyModelMixin,
|
||||||
mixins.ListModelMixin,
|
mixins.ListModelMixin,
|
||||||
viewsets.GenericViewSet):
|
viewsets.GenericViewSet):
|
||||||
|
|
||||||
serializer_class = serializers.PlaylistSerializer
|
serializer_class = serializers.PlaylistSerializer
|
||||||
queryset = (models.Playlist.objects.all())
|
queryset = (models.Playlist.objects.all())
|
||||||
permission_classes = [ConditionalAuthentication]
|
permission_classes = [
|
||||||
|
permissions.ConditionalAuthentication,
|
||||||
def create(self, request, *args, **kwargs):
|
permissions.OwnerPermission,
|
||||||
serializer = self.get_serializer(data=request.data)
|
IsAuthenticatedOrReadOnly,
|
||||||
serializer.is_valid(raise_exception=True)
|
]
|
||||||
instance = self.perform_create(serializer)
|
|
||||||
serializer = self.get_serializer(instance=instance)
|
|
||||||
headers = self.get_success_headers(serializer.data)
|
|
||||||
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
|
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return self.queryset.filter(user=self.request.user)
|
return self.queryset.filter(
|
||||||
|
fields.privacy_level_query(self.request.user))
|
||||||
|
|
||||||
def perform_create(self, serializer):
|
def perform_create(self, serializer):
|
||||||
return serializer.save(
|
return serializer.save(
|
||||||
|
@ -41,23 +41,39 @@ class PlaylistViewSet(
|
||||||
|
|
||||||
|
|
||||||
class PlaylistTrackViewSet(
|
class PlaylistTrackViewSet(
|
||||||
|
mixins.RetrieveModelMixin,
|
||||||
mixins.CreateModelMixin,
|
mixins.CreateModelMixin,
|
||||||
|
mixins.UpdateModelMixin,
|
||||||
mixins.DestroyModelMixin,
|
mixins.DestroyModelMixin,
|
||||||
mixins.ListModelMixin,
|
mixins.ListModelMixin,
|
||||||
viewsets.GenericViewSet):
|
viewsets.GenericViewSet):
|
||||||
|
|
||||||
serializer_class = serializers.PlaylistTrackSerializer
|
serializer_class = serializers.PlaylistTrackSerializer
|
||||||
queryset = (models.PlaylistTrack.objects.all())
|
queryset = (models.PlaylistTrack.objects.all())
|
||||||
permission_classes = [ConditionalAuthentication]
|
permission_classes = [
|
||||||
|
permissions.ConditionalAuthentication,
|
||||||
|
permissions.OwnerPermission,
|
||||||
|
IsAuthenticatedOrReadOnly,
|
||||||
|
]
|
||||||
|
|
||||||
def create(self, request, *args, **kwargs):
|
def create(self, request, *args, **kwargs):
|
||||||
serializer = serializers.PlaylistTrackCreateSerializer(
|
serializer = serializers.PlaylistTrackCreateSerializer(
|
||||||
data=request.data)
|
data=request.data)
|
||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
|
if serializer.validated_data['playlist'].user != request.user:
|
||||||
|
return Response(
|
||||||
|
{'playlist': [
|
||||||
|
'This playlist does not exists or you do not have the'
|
||||||
|
'permission to edit it']
|
||||||
|
},
|
||||||
|
status=400)
|
||||||
instance = self.perform_create(serializer)
|
instance = self.perform_create(serializer)
|
||||||
serializer = self.get_serializer(instance=instance)
|
serializer = self.get_serializer(instance=instance)
|
||||||
headers = self.get_success_headers(serializer.data)
|
headers = self.get_success_headers(serializer.data)
|
||||||
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
|
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return self.queryset.filter(playlist__user=self.request.user)
|
return self.queryset.filter(
|
||||||
|
fields.privacy_level_query(
|
||||||
|
self.request.user,
|
||||||
|
lookup_field='playlist__privacy_level'))
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
import json
|
import json
|
||||||
|
import pytest
|
||||||
|
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
@ -48,3 +50,67 @@ def test_can_add_playlist_track_via_api(factories, logged_in_api_client):
|
||||||
response = logged_in_api_client.post(url, data)
|
response = logged_in_api_client.post(url, data)
|
||||||
plts = logged_in_api_client.user.playlists.latest('id').playlist_tracks.all()
|
plts = logged_in_api_client.user.playlists.latest('id').playlist_tracks.all()
|
||||||
assert plts.first().track == tracks[0]
|
assert plts.first().track == tracks[0]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('name,method', [
|
||||||
|
('api:v1:playlist-tracks-list', 'post'),
|
||||||
|
('api:v1:playlists-list', 'post'),
|
||||||
|
])
|
||||||
|
def test_url_requires_login(name, method, factories, api_client):
|
||||||
|
url = reverse(name)
|
||||||
|
|
||||||
|
response = getattr(api_client, method)(url, {})
|
||||||
|
|
||||||
|
assert response.status_code == 401
|
||||||
|
|
||||||
|
|
||||||
|
def test_only_can_add_track_on_own_playlist_via_api(
|
||||||
|
factories, logged_in_api_client):
|
||||||
|
track = factories['music.Track']()
|
||||||
|
playlist = factories['playlists.Playlist']()
|
||||||
|
url = reverse('api:v1:playlist-tracks-list')
|
||||||
|
data = {
|
||||||
|
'playlist': playlist.pk,
|
||||||
|
'track': track.pk
|
||||||
|
}
|
||||||
|
|
||||||
|
response = logged_in_api_client.post(url, data)
|
||||||
|
assert response.status_code == 400
|
||||||
|
assert playlist.playlist_tracks.count() == 0
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('level', ['instance', 'me', 'followers'])
|
||||||
|
def test_playlist_privacy_respected_in_list_anon(level, factories, api_client):
|
||||||
|
factories['playlists.Playlist'](privacy_level=level)
|
||||||
|
url = reverse('api:v1:playlists-list')
|
||||||
|
response = api_client.get(url)
|
||||||
|
|
||||||
|
assert response.data['count'] == 0
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('method', ['PUT', 'PATCH', 'DELETE'])
|
||||||
|
def test_only_owner_can_edit_playlist(method, factories, api_client):
|
||||||
|
playlist = factories['playlists.Playlist']()
|
||||||
|
url = reverse('api:v1:playlists-detail', kwargs={'pk': playlist.pk})
|
||||||
|
response = api_client.get(url)
|
||||||
|
|
||||||
|
assert response.status_code == 404
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('method', ['PUT', 'PATCH', 'DELETE'])
|
||||||
|
def test_only_owner_can_edit_playlist_track(method, factories, api_client):
|
||||||
|
plt = factories['playlists.PlaylistTrack']()
|
||||||
|
url = reverse('api:v1:playlist-tracks-detail', kwargs={'pk': plt.pk})
|
||||||
|
response = api_client.get(url)
|
||||||
|
|
||||||
|
assert response.status_code == 404
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('level', ['instance', 'me', 'followers'])
|
||||||
|
def test_playlist_track_privacy_respected_in_list_anon(
|
||||||
|
level, factories, api_client):
|
||||||
|
factories['playlists.PlaylistTrack'](playlist__privacy_level=level)
|
||||||
|
url = reverse('api:v1:playlist-tracks-list')
|
||||||
|
response = api_client.get(url)
|
||||||
|
|
||||||
|
assert response.data['count'] == 0
|
||||||
|
|
Loading…
Reference in New Issue