diff --git a/api/funkwhale_api/common/serializers.py b/api/funkwhale_api/common/serializers.py index 62d9c567e..a995cc360 100644 --- a/api/funkwhale_api/common/serializers.py +++ b/api/funkwhale_api/common/serializers.py @@ -12,6 +12,9 @@ class ActionSerializer(serializers.Serializer): filters = serializers.DictField(required=False) actions = None filterset_class = None + # those are actions identifier where we don't want to allow the "all" + # selector because it's to dangerous. Like object deletion. + dangerous_actions = [] def __init__(self, *args, **kwargs): self.queryset = kwargs.pop('queryset') @@ -49,6 +52,10 @@ class ActionSerializer(serializers.Serializer): 'list of identifiers or the string "all".'.format(value)) def validate(self, data): + dangerous = data['action'] in self.dangerous_actions + if dangerous and self.initial_data['objects'] == 'all': + raise serializers.ValidationError( + 'This action is to dangerous to be applied to all objects') if self.filterset_class and 'filters' in data: qs_filterset = self.filterset_class( data['filters'], queryset=data['objects']) diff --git a/api/tests/common/test_serializers.py b/api/tests/common/test_serializers.py index 563676556..f0f5fb7e6 100644 --- a/api/tests/common/test_serializers.py +++ b/api/tests/common/test_serializers.py @@ -18,6 +18,17 @@ class TestSerializer(serializers.ActionSerializer): return {'hello': 'world'} +class TestDangerousSerializer(serializers.ActionSerializer): + actions = ['test', 'test_dangerous'] + dangerous_actions = ['test_dangerous'] + + def handle_test(self, objects): + pass + + def handle_test_dangerous(self, objects): + pass + + def test_action_serializer_validates_action(): data = {'objects': 'all', 'action': 'nope'} serializer = TestSerializer(data, queryset=models.User.objects.none()) @@ -98,3 +109,28 @@ def test_action_serializers_validates_at_least_one_object(): assert serializer.is_valid() is False assert 'non_field_errors' in serializer.errors + + +def test_dangerous_actions_refuses_all(factories): + factories['users.User']() + data = { + 'objects': 'all', + 'action': 'test_dangerous', + } + serializer = TestDangerousSerializer( + data, queryset=models.User.objects.all()) + + assert serializer.is_valid() is False + assert 'non_field_errors' in serializer.errors + + +def test_dangerous_actions_refuses_not_listed(factories): + factories['users.User']() + data = { + 'objects': 'all', + 'action': 'test', + } + serializer = TestDangerousSerializer( + data, queryset=models.User.objects.all()) + + assert serializer.is_valid() is True