From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by lists.proxmox.com (Postfix) with ESMTPS id C120EF2B5 for ; Thu, 20 Jul 2023 16:34:11 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 96DAE14C51 for ; Thu, 20 Jul 2023 16:33:28 +0200 (CEST) Received: from proxmox-new.maurer-it.com (proxmox-new.maurer-it.com [94.136.29.106]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by firstgate.proxmox.com (Proxmox) with ESMTPS for ; Thu, 20 Jul 2023 16:33:26 +0200 (CEST) Received: from proxmox-new.maurer-it.com (localhost.localdomain [127.0.0.1]) by proxmox-new.maurer-it.com (Proxmox) with ESMTP id B8B0A41EAA for ; Thu, 20 Jul 2023 16:33:08 +0200 (CEST) From: Lukas Wagner To: pve-devel@lists.proxmox.com Date: Thu, 20 Jul 2023 16:31:49 +0200 Message-Id: <20230720143236.652292-23-l.wagner@proxmox.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20230720143236.652292-1-l.wagner@proxmox.com> References: <20230720143236.652292-1-l.wagner@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL -0.072 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record T_SCC_BODY_TEXT_LINE -0.01 - Subject: [pve-devel] [PATCH v4 proxmox 22/69] notify: on deletion, check if a filter/endp. is still used by anything X-BeenThere: pve-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox VE development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 20 Jul 2023 14:34:11 -0000 Signed-off-by: Lukas Wagner --- Notes: Changes since v3: - get_referrers: minor stylistic touchups proxmox-notify/src/api/filter.rs | 1 + proxmox-notify/src/api/gotify.rs | 1 + proxmox-notify/src/api/mod.rs | 111 ++++++++++++++++++++++++++--- proxmox-notify/src/api/sendmail.rs | 1 + 4 files changed, 104 insertions(+), 10 deletions(-) diff --git a/proxmox-notify/src/api/filter.rs b/proxmox-notify/src/api/filter.rs index 3fcff6b9..824f802d 100644 --- a/proxmox-notify/src/api/filter.rs +++ b/proxmox-notify/src/api/filter.rs @@ -115,6 +115,7 @@ pub fn update_filter( pub fn delete_filter(config: &mut Config, name: &str) -> Result<(), ApiError> { // Check if the filter exists let _ = get_filter(config, name)?; + super::ensure_unused(config, name)?; config.config.sections.remove(name); diff --git a/proxmox-notify/src/api/gotify.rs b/proxmox-notify/src/api/gotify.rs index d6f33064..5c4db4be 100644 --- a/proxmox-notify/src/api/gotify.rs +++ b/proxmox-notify/src/api/gotify.rs @@ -145,6 +145,7 @@ pub fn update_endpoint( pub fn delete_gotify_endpoint(config: &mut Config, name: &str) -> Result<(), ApiError> { // Check if the endpoint exists let _ = get_endpoint(config, name)?; + super::ensure_unused(config, name)?; remove_private_config_entry(config, name)?; config.config.sections.remove(name); diff --git a/proxmox-notify/src/api/mod.rs b/proxmox-notify/src/api/mod.rs index 1d9aaca7..e064b607 100644 --- a/proxmox-notify/src/api/mod.rs +++ b/proxmox-notify/src/api/mod.rs @@ -102,6 +102,57 @@ fn endpoint_exists(config: &Config, name: &str) -> bool { exists } +fn get_referrers(config: &Config, entity: &str) -> Result, ApiError> { + let mut referrers = HashSet::new(); + + for group in group::get_groups(config)? { + if group.endpoint.iter().any(|endpoint| endpoint == entity) { + referrers.insert(group.name.clone()); + } + + if let Some(filter) = group.filter { + if filter == entity { + referrers.insert(group.name); + } + } + } + + #[cfg(feature = "sendmail")] + for endpoint in sendmail::get_endpoints(config)? { + if let Some(filter) = endpoint.filter { + if filter == entity { + referrers.insert(endpoint.name); + } + } + } + + #[cfg(feature = "gotify")] + for endpoint in gotify::get_endpoints(config)? { + if let Some(filter) = endpoint.filter { + if filter == entity { + referrers.insert(endpoint.name); + } + } + } + + Ok(referrers) +} + +fn ensure_unused(config: &Config, entity: &str) -> Result<(), ApiError> { + let referrers = get_referrers(config, entity)?; + + if !referrers.is_empty() { + let used_by = referrers.into_iter().collect::>().join(", "); + + return Err(ApiError::bad_request( + format!("cannot delete '{entity}', referenced by: {used_by}"), + None, + )); + } + + Ok(()) +} + fn get_referenced_entities(config: &Config, entity: &str) -> HashSet { let mut to_expand = HashSet::new(); let mut expanded = HashSet::new(); @@ -161,8 +212,7 @@ mod tests { use crate::filter::FilterConfig; use crate::group::GroupConfig; - #[test] - fn test_get_referenced_entities() { + fn prepare_config() -> Result { let mut config = super::test_helpers::empty_config(); filter::add_filter( @@ -171,8 +221,7 @@ mod tests { name: "filter".to_string(), ..Default::default() }, - ) - .unwrap(); + )?; sendmail::add_endpoint( &mut config, @@ -182,8 +231,7 @@ mod tests { filter: Some("filter".to_string()), ..Default::default() }, - ) - .unwrap(); + )?; gotify::add_endpoint( &mut config, @@ -197,8 +245,7 @@ mod tests { name: "gotify".to_string(), token: "foo".to_string(), }, - ) - .unwrap(); + )?; group::add_group( &mut config, @@ -208,8 +255,14 @@ mod tests { filter: Some("filter".to_string()), ..Default::default() }, - ) - .unwrap(); + )?; + + Ok(config) + } + + #[test] + fn test_get_referenced_entities() { + let config = prepare_config().unwrap(); assert_eq!( get_referenced_entities(&config, "filter"), @@ -233,4 +286,42 @@ mod tests { ]) ); } + + #[test] + fn test_get_referrers_for_entity() -> Result<(), ApiError> { + let config = prepare_config().unwrap(); + + assert_eq!( + get_referrers(&config, "filter")?, + HashSet::from([ + "gotify".to_string(), + "sendmail".to_string(), + "group".to_string() + ]) + ); + + assert_eq!( + get_referrers(&config, "sendmail")?, + HashSet::from(["group".to_string()]) + ); + + assert_eq!( + get_referrers(&config, "gotify")?, + HashSet::from(["group".to_string()]) + ); + + assert!(get_referrers(&config, "group")?.is_empty(),); + + Ok(()) + } + + #[test] + fn test_ensure_unused() { + let config = prepare_config().unwrap(); + + assert!(ensure_unused(&config, "filter").is_err()); + assert!(ensure_unused(&config, "gotify").is_err()); + assert!(ensure_unused(&config, "sendmail").is_err()); + assert!(ensure_unused(&config, "group").is_ok()); + } } diff --git a/proxmox-notify/src/api/sendmail.rs b/proxmox-notify/src/api/sendmail.rs index 070ed6e7..bf225f29 100644 --- a/proxmox-notify/src/api/sendmail.rs +++ b/proxmox-notify/src/api/sendmail.rs @@ -147,6 +147,7 @@ pub fn update_endpoint( pub fn delete_endpoint(config: &mut Config, name: &str) -> Result<(), ApiError> { // Check if the endpoint exists let _ = get_endpoint(config, name)?; + super::ensure_unused(config, name)?; config.config.sections.remove(name); -- 2.39.2