See #223: dangerous actions can now prevent executing an action on all objects
This commit is contained in:
parent
7df9112d55
commit
7b84a988fd
|
@ -12,6 +12,9 @@ class ActionSerializer(serializers.Serializer):
|
||||||
filters = serializers.DictField(required=False)
|
filters = serializers.DictField(required=False)
|
||||||
actions = None
|
actions = None
|
||||||
filterset_class = 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):
|
def __init__(self, *args, **kwargs):
|
||||||
self.queryset = kwargs.pop('queryset')
|
self.queryset = kwargs.pop('queryset')
|
||||||
|
@ -49,6 +52,10 @@ class ActionSerializer(serializers.Serializer):
|
||||||
'list of identifiers or the string "all".'.format(value))
|
'list of identifiers or the string "all".'.format(value))
|
||||||
|
|
||||||
def validate(self, data):
|
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:
|
if self.filterset_class and 'filters' in data:
|
||||||
qs_filterset = self.filterset_class(
|
qs_filterset = self.filterset_class(
|
||||||
data['filters'], queryset=data['objects'])
|
data['filters'], queryset=data['objects'])
|
||||||
|
|
|
@ -18,6 +18,17 @@ class TestSerializer(serializers.ActionSerializer):
|
||||||
return {'hello': 'world'}
|
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():
|
def test_action_serializer_validates_action():
|
||||||
data = {'objects': 'all', 'action': 'nope'}
|
data = {'objects': 'all', 'action': 'nope'}
|
||||||
serializer = TestSerializer(data, queryset=models.User.objects.none())
|
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 serializer.is_valid() is False
|
||||||
assert 'non_field_errors' in serializer.errors
|
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
|
||||||
|
|
Loading…
Reference in New Issue