diff --git a/src/documents/tests/test_api_app_config.py b/src/documents/tests/test_api_app_config.py index 0ca6f4412..faef96345 100644 --- a/src/documents/tests/test_api_app_config.py +++ b/src/documents/tests/test_api_app_config.py @@ -194,6 +194,56 @@ class TestApiAppConfig(DirectoriesMixin, APITestCase): self.assertEqual(config.language, None) self.assertEqual(config.barcode_tag_mapping, None) + def test_api_update_config_rejects_non_dict_user_args(self) -> None: + """ + GIVEN: + - API request to update app config with a JSON-encoded non-dict + value (e.g. a bare string) for the user_args JSONField + WHEN: + - API is called + THEN: + - Request is rejected with a 400, not silently accepted + - Config is not updated + """ + response = self.client.patch( + f"{self.ENDPOINT}1/", + json.dumps( + { + "user_args": json.dumps("not a dict"), + }, + ), + content_type="application/json", + ) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + config = ApplicationConfiguration.objects.first() + assert config is not None + self.assertEqual(config.user_args, None) + + def test_api_update_config_rejects_non_dict_barcode_tag_mapping(self) -> None: + """ + GIVEN: + - API request to update app config with a JSON-encoded non-dict + value (e.g. a bare list) for the barcode_tag_mapping JSONField + WHEN: + - API is called + THEN: + - Request is rejected with a 400, not silently accepted + - Config is not updated + """ + response = self.client.patch( + f"{self.ENDPOINT}1/", + json.dumps( + { + "barcode_tag_mapping": json.dumps([1, 2, 3]), + }, + ), + content_type="application/json", + ) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + config = ApplicationConfiguration.objects.first() + assert config is not None + self.assertEqual(config.barcode_tag_mapping, None) + def test_api_replace_app_logo(self) -> None: """ GIVEN: diff --git a/src/paperless/serialisers.py b/src/paperless/serialisers.py index f7cfc3d45..e6000f4a2 100644 --- a/src/paperless/serialisers.py +++ b/src/paperless/serialisers.py @@ -235,6 +235,20 @@ class ApplicationConfigurationSerializer( ) -> list[str]: return sorted(name for name in os.environ if name.startswith("PAPERLESS_")) + def validate_user_args(self, value): + if value is not None and not isinstance(value, dict): + raise serializers.ValidationError( + "user_args must be a JSON object.", + ) + return value + + def validate_barcode_tag_mapping(self, value): + if value is not None and not isinstance(value, dict): + raise serializers.ValidationError( + "barcode_tag_mapping must be a JSON object.", + ) + return value + def run_validation(self, data): # Empty strings treated as None to avoid unexpected behavior if "user_args" in data and data["user_args"] == "":