Skip to content
Snippets Groups Projects
Verified Commit 9a909798 authored by Eliot Berriot's avatar Eliot Berriot
Browse files

Additional permissions checks on playlist views

parent 4f7fa09a
No related branches found
No related tags found
No related merge requests found
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 ...@@ -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( ...@@ -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'))
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): ...@@ -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
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment