diff --git a/src/documents/migrations/0009_workflowaction_passwords_alter_workflowaction_type.py b/src/documents/migrations/0009_workflowaction_passwords_alter_workflowaction_type.py index 9488cffb5..ae3fef79f 100644 --- a/src/documents/migrations/0009_workflowaction_passwords_alter_workflowaction_type.py +++ b/src/documents/migrations/0009_workflowaction_passwords_alter_workflowaction_type.py @@ -13,7 +13,7 @@ class Migration(migrations.Migration): migrations.AddField( model_name="workflowaction", name="passwords", - field=models.TextField( + field=models.JSONField( blank=True, help_text="Passwords to try when removing PDF protection. Separate with commas or new lines.", null=True, diff --git a/src/documents/models.py b/src/documents/models.py index 823bc5186..046a4b179 100644 --- a/src/documents/models.py +++ b/src/documents/models.py @@ -1638,7 +1638,7 @@ class WorkflowAction(models.Model): verbose_name=_("webhook"), ) - passwords = models.TextField( + passwords = models.JSONField( _("passwords"), null=True, blank=True, diff --git a/src/documents/serialisers.py b/src/documents/serialisers.py index 509afe55b..a72c13ef8 100644 --- a/src/documents/serialisers.py +++ b/src/documents/serialisers.py @@ -2675,11 +2675,14 @@ class WorkflowActionSerializer(serializers.ModelSerializer): and attrs["type"] == WorkflowAction.WorkflowActionType.PASSWORD_REMOVAL ): passwords = attrs.get("passwords") - if passwords is None or not isinstance(passwords, str): - raise serializers.ValidationError( - "Passwords are required for password removal actions", - ) - if not passwords.strip(): + # ensure passwords is a non-empty list of non-empty strings + if ( + passwords is None + or not isinstance(passwords, list) + or len(passwords) == 0 + or any(not isinstance(pw, str) for pw in passwords) + or any(len(pw.strip()) == 0 for pw in passwords) + ): raise serializers.ValidationError( "Passwords are required for password removal actions", ) diff --git a/src/documents/tests/test_api_workflows.py b/src/documents/tests/test_api_workflows.py index c4c0821c0..4e8e6a916 100644 --- a/src/documents/tests/test_api_workflows.py +++ b/src/documents/tests/test_api_workflows.py @@ -848,13 +848,16 @@ class TestApiWorkflows(DirectoriesMixin, APITestCase): THEN: - The passwords field is correctly stored and retrieved """ - passwords = "password1,password2\npassword3" + passwords = ["password1", "password2", "password3"] response = self.client.post( "/api/workflow_actions/", - { - "type": WorkflowAction.WorkflowActionType.PASSWORD_REMOVAL, - "passwords": passwords, - }, + json.dumps( + { + "type": WorkflowAction.WorkflowActionType.PASSWORD_REMOVAL, + "passwords": passwords, + }, + ), + content_type="application/json", ) self.assertEqual(response.status_code, status.HTTP_201_CREATED) self.assertEqual(response.data["passwords"], passwords) @@ -882,10 +885,43 @@ class TestApiWorkflows(DirectoriesMixin, APITestCase): ) response = self.client.post( "/api/workflow_actions/", - { - "type": WorkflowAction.WorkflowActionType.PASSWORD_REMOVAL, - "passwords": "", - }, + json.dumps( + { + "type": WorkflowAction.WorkflowActionType.PASSWORD_REMOVAL, + "passwords": "", + }, + ), + content_type="application/json", + ) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertIn( + "Passwords are required", + str(response.data["non_field_errors"][0]), + ) + response = self.client.post( + "/api/workflow_actions/", + json.dumps( + { + "type": WorkflowAction.WorkflowActionType.PASSWORD_REMOVAL, + "passwords": [], + }, + ), + content_type="application/json", + ) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertIn( + "Passwords are required", + str(response.data["non_field_errors"][0]), + ) + response = self.client.post( + "/api/workflow_actions/", + json.dumps( + { + "type": WorkflowAction.WorkflowActionType.PASSWORD_REMOVAL, + "passwords": ["", "password2"], + }, + ), + content_type="application/json", ) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertIn(