mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-24 21:26:28 +00:00
fix(notify): match filters against decoded event keys (#2921)
Co-authored-by: houseme <housemecn@gmail.com>
This commit is contained in:
Generated
+1
@@ -9836,6 +9836,7 @@ dependencies = [
|
|||||||
"chrono",
|
"chrono",
|
||||||
"form_urlencoded",
|
"form_urlencoded",
|
||||||
"hashbrown 0.17.1",
|
"hashbrown 0.17.1",
|
||||||
|
"percent-encoding",
|
||||||
"quick-xml 0.39.4",
|
"quick-xml 0.39.4",
|
||||||
"rayon",
|
"rayon",
|
||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ async-trait = { workspace = true }
|
|||||||
chrono = { workspace = true, features = ["serde"] }
|
chrono = { workspace = true, features = ["serde"] }
|
||||||
form_urlencoded = { workspace = true }
|
form_urlencoded = { workspace = true }
|
||||||
hashbrown = { workspace = true }
|
hashbrown = { workspace = true }
|
||||||
|
percent-encoding = { workspace = true }
|
||||||
rayon = { workspace = true }
|
rayon = { workspace = true }
|
||||||
rustc-hash = { workspace = true }
|
rustc-hash = { workspace = true }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
|
|||||||
@@ -12,8 +12,14 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use crate::{error::NotificationError, event::Event, integration::NotificationMetrics, rules::RulesMap};
|
use crate::{
|
||||||
|
error::NotificationError,
|
||||||
|
event::Event,
|
||||||
|
integration::NotificationMetrics,
|
||||||
|
rules::{RulesMap, TargetIdSet},
|
||||||
|
};
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
|
use percent_encoding::percent_decode_str;
|
||||||
use rustfs_config::notify::{DEFAULT_NOTIFY_SEND_CONCURRENCY, ENV_NOTIFY_SEND_CONCURRENCY};
|
use rustfs_config::notify::{DEFAULT_NOTIFY_SEND_CONCURRENCY, ENV_NOTIFY_SEND_CONCURRENCY};
|
||||||
use rustfs_s3_common::EventName;
|
use rustfs_s3_common::EventName;
|
||||||
use rustfs_targets::Target;
|
use rustfs_targets::Target;
|
||||||
@@ -24,6 +30,23 @@ use std::sync::Arc;
|
|||||||
use tokio::sync::{RwLock, Semaphore};
|
use tokio::sync::{RwLock, Semaphore};
|
||||||
use tracing::{debug, error, info, instrument, warn};
|
use tracing::{debug, error, info, instrument, warn};
|
||||||
|
|
||||||
|
fn decoded_object_key_for_matching(object_key: &str) -> Option<String> {
|
||||||
|
if !object_key.contains('%') {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let decoded = percent_decode_str(object_key).decode_utf8().ok()?;
|
||||||
|
(decoded != object_key).then(|| decoded.into_owned())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn match_event_targets(rules: &RulesMap, event_name: EventName, object_key: &str) -> TargetIdSet {
|
||||||
|
let mut target_ids = rules.match_rules(event_name, object_key);
|
||||||
|
if let Some(decoded_key) = decoded_object_key_for_matching(object_key) {
|
||||||
|
target_ids.extend(rules.match_rules(event_name, &decoded_key));
|
||||||
|
}
|
||||||
|
target_ids
|
||||||
|
}
|
||||||
|
|
||||||
/// Manages event notification to targets based on rules
|
/// Manages event notification to targets based on rules
|
||||||
pub struct EventNotifier {
|
pub struct EventNotifier {
|
||||||
metrics: Arc<NotificationMetrics>,
|
metrics: Arc<NotificationMetrics>,
|
||||||
@@ -188,7 +211,7 @@ impl EventNotifier {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let target_ids = rules.match_rules(event_name, object_key);
|
let target_ids = match_event_targets(&rules, event_name, object_key);
|
||||||
if target_ids.is_empty() {
|
if target_ids.is_empty() {
|
||||||
debug!("No matching targets for event in bucket: {}", bucket_name);
|
debug!("No matching targets for event in bucket: {}", bucket_name);
|
||||||
self.metrics.increment_skipped();
|
self.metrics.increment_skipped();
|
||||||
@@ -422,6 +445,30 @@ mod tests {
|
|||||||
atomic::{AtomicUsize, Ordering},
|
atomic::{AtomicUsize, Ordering},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn encoded_event_key_matches_raw_prefix_suffix_filter() {
|
||||||
|
let target_id = TargetID::new("primary".to_string(), "webhook".to_string());
|
||||||
|
let mut rules_map = RulesMap::new();
|
||||||
|
rules_map.add_rule_config(&[EventName::ObjectCreatedPut], "uploads/*.csv".to_string(), target_id.clone());
|
||||||
|
|
||||||
|
let targets = match_event_targets(&rules_map, EventName::ObjectCreatedPut, "uploads%2Freport.csv");
|
||||||
|
|
||||||
|
assert!(targets.contains(&target_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn encoded_event_key_does_not_bypass_suffix_filter() {
|
||||||
|
let target_id = TargetID::new("primary".to_string(), "webhook".to_string());
|
||||||
|
let mut rules_map = RulesMap::new();
|
||||||
|
rules_map.add_rule_config(&[EventName::ObjectCreatedPut], "uploads/*.csv".to_string(), target_id);
|
||||||
|
|
||||||
|
let root_targets = match_event_targets(&rules_map, EventName::ObjectCreatedPut, "report.csv");
|
||||||
|
let suffix_targets = match_event_targets(&rules_map, EventName::ObjectCreatedPut, "uploads%2Freport.txt");
|
||||||
|
|
||||||
|
assert!(root_targets.is_empty());
|
||||||
|
assert!(suffix_targets.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct TestTarget {
|
struct TestTarget {
|
||||||
id: TargetID,
|
id: TargetID,
|
||||||
|
|||||||
Reference in New Issue
Block a user