diff --git a/api/funkwhale_api/users/serializers.py b/api/funkwhale_api/users/serializers.py index e8adf9eda..b21aa6935 100644 --- a/api/funkwhale_api/users/serializers.py +++ b/api/funkwhale_api/users/serializers.py @@ -29,7 +29,16 @@ class UserBasicSerializer(serializers.ModelSerializer): fields = ['id', 'username', 'name', 'date_joined'] -class UserSerializer(serializers.ModelSerializer): +class UserWriteSerializer(serializers.ModelSerializer): + class Meta: + model = models.User + fields = [ + 'name', + 'privacy_level' + ] + + +class UserReadSerializer(serializers.ModelSerializer): permissions = serializers.SerializerMethodField() @@ -44,6 +53,7 @@ class UserSerializer(serializers.ModelSerializer): 'is_superuser', 'permissions', 'date_joined', + 'privacy_level' ] def get_permissions(self, o): diff --git a/api/funkwhale_api/users/views.py b/api/funkwhale_api/users/views.py index b7c1df28f..7c58363a3 100644 --- a/api/funkwhale_api/users/views.py +++ b/api/funkwhale_api/users/views.py @@ -1,4 +1,5 @@ from rest_framework.response import Response +from rest_framework import mixins from rest_framework import viewsets from rest_framework.decorators import list_route @@ -23,12 +24,25 @@ class RegisterView(BaseRegisterView): return get_adapter().is_open_for_signup(request) -class UserViewSet(viewsets.GenericViewSet): +class UserViewSet( + mixins.UpdateModelMixin, + viewsets.GenericViewSet): queryset = models.User.objects.all() - serializer_class = serializers.UserSerializer + serializer_class = serializers.UserWriteSerializer + lookup_field = 'username' @list_route(methods=['get']) def me(self, request, *args, **kwargs): """Return information about the current user""" - serializer = self.serializer_class(request.user) + serializer = serializers.UserReadSerializer(request.user) return Response(serializer.data) + + def update(self, request, *args, **kwargs): + if not self.request.user.username == kwargs.get('username'): + return Response(status=403) + return super().update(request, *args, **kwargs) + + def partial_update(self, request, *args, **kwargs): + if not self.request.user.username == kwargs.get('username'): + return Response(status=403) + return super().partial_update(request, *args, **kwargs) diff --git a/api/tests/requests/test_views.py b/api/tests/requests/test_views.py index 6c34f9ad1..3956fb405 100644 --- a/api/tests/requests/test_views.py +++ b/api/tests/requests/test_views.py @@ -7,7 +7,8 @@ def test_request_viewset_requires_auth(db, api_client): assert response.status_code == 401 -def test_user_can_create_request(logged_in_api_client): +@pytest.mark.parametrize('method', ['put', 'patch']) +def test_user_can_create_request(method, logged_in_api_client): url = reverse('api:v1:requests:import-requests-list') user = logged_in_api_client.user data = { diff --git a/api/tests/users/test_views.py b/api/tests/users/test_views.py index 569acbd15..02b903bf4 100644 --- a/api/tests/users/test_views.py +++ b/api/tests/users/test_views.py @@ -1,4 +1,5 @@ import json +import pytest from django.test import RequestFactory from django.urls import reverse @@ -116,3 +117,37 @@ def test_changing_password_updates_secret_key(logged_in_client): assert user.secret_key != secret_key assert user.password != password + + +def test_user_can_patch_his_own_settings(logged_in_api_client): + user = logged_in_api_client.user + payload = { + 'privacy_level': 'me', + } + url = reverse( + 'api:v1:users:users-detail', + kwargs={'username': user.username}) + + response = logged_in_api_client.patch(url, payload) + + assert response.status_code == 200 + user.refresh_from_db() + + assert user.privacy_level == 'me' + + +@pytest.mark.parametrize('method', ['put', 'patch']) +def test_user_cannot_patch_another_user( + method, logged_in_api_client, factories): + user = factories['users.User']() + payload = { + 'privacy_level': 'me', + } + url = reverse( + 'api:v1:users:users-detail', + kwargs={'username': user.username}) + + handler = getattr(logged_in_api_client, method) + response = handler(url, payload) + + assert response.status_code == 403