From ddfcacbb621e6ba90484be4462c550f305c4b7e3 Mon Sep 17 00:00:00 2001 From: Mubelotix Date: Mon, 4 Aug 2025 16:53:41 +0200 Subject: [PATCH] Add nice error message for users trying to set uuid or isEditable --- crates/meilisearch-types/src/error.rs | 4 +- crates/meilisearch/src/routes/webhooks.rs | 29 ++++++-- crates/meilisearch/tests/tasks/webhook.rs | 86 ++++++++++++++++++++++- 3 files changed, 111 insertions(+), 8 deletions(-) diff --git a/crates/meilisearch-types/src/error.rs b/crates/meilisearch-types/src/error.rs index e14a06909..e25ce61a4 100644 --- a/crates/meilisearch-types/src/error.rs +++ b/crates/meilisearch-types/src/error.rs @@ -425,7 +425,9 @@ InvalidWebhooksUrl , InvalidRequest , BAD_REQU InvalidWebhooksHeaders , InvalidRequest , BAD_REQUEST ; ReservedWebhook , InvalidRequest , BAD_REQUEST ; InvalidWebhookUuid , InvalidRequest , BAD_REQUEST ; -WebhookNotFound , InvalidRequest , NOT_FOUND +WebhookNotFound , InvalidRequest , NOT_FOUND ; +ImmutableWebhookUuid , InvalidRequest , BAD_REQUEST ; +ImmutableWebhookIsEditable , InvalidRequest , BAD_REQUEST } impl ErrorCode for JoinError { diff --git a/crates/meilisearch/src/routes/webhooks.rs b/crates/meilisearch/src/routes/webhooks.rs index fc03f43d0..610e28271 100644 --- a/crates/meilisearch/src/routes/webhooks.rs +++ b/crates/meilisearch/src/routes/webhooks.rs @@ -7,12 +7,15 @@ use actix_http::header::{ }; use actix_web::web::{self, Data, Path}; use actix_web::{HttpRequest, HttpResponse}; +use core::convert::Infallible; use deserr::actix_web::AwebJson; -use deserr::Deserr; +use deserr::{DeserializeError, Deserr, ValuePointerRef}; use index_scheduler::IndexScheduler; -use meilisearch_types::deserr::DeserrJsonError; -use meilisearch_types::error::deserr_codes::{InvalidWebhooksHeaders, InvalidWebhooksUrl}; -use meilisearch_types::error::{ErrorCode, ResponseError}; +use meilisearch_types::deserr::{immutable_field_error, DeserrJsonError}; +use meilisearch_types::error::deserr_codes::{ + BadRequest, InvalidWebhooksHeaders, InvalidWebhooksUrl, +}; +use meilisearch_types::error::{Code, ErrorCode, ResponseError}; use meilisearch_types::keys::actions; use meilisearch_types::milli::update::Setting; use meilisearch_types::webhooks::Webhook; @@ -54,7 +57,7 @@ pub fn configure(cfg: &mut web::ServiceConfig) { } #[derive(Debug, Deserr, ToSchema)] -#[deserr(error = DeserrJsonError, rename_all = camelCase, deny_unknown_fields)] +#[deserr(error = DeserrJsonError, rename_all = camelCase, deny_unknown_fields = deny_immutable_fields_webhook)] #[serde(rename_all = "camelCase")] #[schema(rename_all = "camelCase")] pub(super) struct WebhookSettings { @@ -68,6 +71,22 @@ pub(super) struct WebhookSettings { headers: Setting>>, } +fn deny_immutable_fields_webhook( + field: &str, + accepted: &[&str], + location: ValuePointerRef, +) -> DeserrJsonError { + match field { + "uuid" => immutable_field_error(field, accepted, Code::ImmutableWebhookUuid), + "isEditable" => immutable_field_error(field, accepted, Code::ImmutableWebhookIsEditable), + _ => deserr::take_cf_content(DeserrJsonError::::error::( + None, + deserr::ErrorKind::UnknownKey { key: field, accepted }, + location, + )), + } +} + #[derive(Debug, Serialize, ToSchema)] #[serde(rename_all = "camelCase")] #[schema(rename_all = "camelCase")] diff --git a/crates/meilisearch/tests/tasks/webhook.rs b/crates/meilisearch/tests/tasks/webhook.rs index 155312b9d..03f732f0d 100644 --- a/crates/meilisearch/tests/tasks/webhook.rs +++ b/crates/meilisearch/tests/tasks/webhook.rs @@ -273,7 +273,7 @@ async fn reserved_names() { snapshot!(code, @"400 Bad Request"); snapshot!(value, @r#" { - "message": "Cannot edit webhook `[uuid]`. Webhooks prefixed with an underscore are reserved and may not be modified using the API.", + "message": "Cannot edit webhook `[uuid]`. The webhook defined from the command line cannot be modified using the API.", "code": "reserved_webhook", "type": "invalid_request", "link": "https://docs.meilisearch.com/errors#reserved_webhook" @@ -284,7 +284,7 @@ async fn reserved_names() { snapshot!(code, @"400 Bad Request"); snapshot!(value, @r#" { - "message": "Cannot edit webhook `[uuid]`. Webhooks prefixed with an underscore are reserved and may not be modified using the API.", + "message": "Cannot edit webhook `[uuid]`. The webhook defined from the command line cannot be modified using the API.", "code": "reserved_webhook", "type": "invalid_request", "link": "https://docs.meilisearch.com/errors#reserved_webhook" @@ -562,3 +562,85 @@ async fn invalid_uuid() { } "#); } + +#[actix_web::test] +async fn forbidden_fields() { + let server = Server::new().await; + + // Test creating webhook with uuid field + let custom_uuid = Uuid::new_v4(); + let (value, code) = server + .create_webhook(json!({ + "url": "https://example.com/hook", + "uuid": custom_uuid.to_string(), + "headers": { "authorization": "TOKEN" } + })) + .await; + snapshot!(code, @"400 Bad Request"); + snapshot!(value, @r#" + { + "message": "Immutable field `uuid`: expected one of `url`, `headers`", + "code": "immutable_webhook_uuid", + "type": "invalid_request", + "link": "https://docs.meilisearch.com/errors#immutable_webhook_uuid" + } + "#); + + // Test creating webhook with isEditable field + let (value, code) = server + .create_webhook(json!({ + "url": "https://example.com/hook2", + "isEditable": false, + "headers": { "authorization": "TOKEN" } + })) + .await; + snapshot!(code, @"400 Bad Request"); + snapshot!(value, @r#" + { + "message": "Immutable field `isEditable`: expected one of `url`, `headers`", + "code": "immutable_webhook_is_editable", + "type": "invalid_request", + "link": "https://docs.meilisearch.com/errors#immutable_webhook_is_editable" + } + "#); + + // Test patching webhook with uuid field + let (value, code) = server + .patch_webhook( + "uuid-whatever", + json!({ + "uuid": Uuid::new_v4(), + "headers": { "new-header": "value" } + }), + ) + .await; + snapshot!(code, @"400 Bad Request"); + snapshot!(value, @r#" + { + "message": "Immutable field `uuid`: expected one of `url`, `headers`", + "code": "immutable_webhook_uuid", + "type": "invalid_request", + "link": "https://docs.meilisearch.com/errors#immutable_webhook_uuid" + } + "#); + + // Test patching webhook with isEditable field + let (value, code) = server + .patch_webhook( + "uuid-whatever", + json!({ + "isEditable": false, + "headers": { "another-header": "value" } + }), + ) + .await; + snapshot!(code, @"400 Bad Request"); + snapshot!(json_string!(value, { ".uuid" => "[uuid]" }), @r#" + { + "message": "Immutable field `isEditable`: expected one of `url`, `headers`", + "code": "immutable_webhook_is_editable", + "type": "invalid_request", + "link": "https://docs.meilisearch.com/errors#immutable_webhook_is_editable" + } + "#); +}