diff --git a/DEFAULT_CONFIG.json5 b/DEFAULT_CONFIG.json5 index 44cce3d..5b6f767 100644 --- a/DEFAULT_CONFIG.json5 +++ b/DEFAULT_CONFIG.json5 @@ -52,10 +52,14 @@ // shm_enabled: false, //// - //// allow: Sets of 1 or more regular expression per ROS interface kind matching the interface names that must be routed via zenoh. - //// By default, all interfaces are allowed. - //// If both 'allow' and 'deny' are set an interface will be allowed if it matches only the expression in 'allow' set. - //// + //// allow / deny: Specify the lists of ROS 2 interfaces that are allowed or denied to be routed over Zenoh. + //// Each element of the lists is a regular expression that must match the full interface name. + //// You cannot set both 'allow' and 'deny' in the same configuration. + //// If neither 'allow' nor 'deny' are set, all interfaces are allowed. + //// Use 'allow' to allow only the specified interfaces. If an interface type is set to an empty list + //// or is not specified at all, it means that NO such interface is allowed. + //// Use 'deny' to allow all except the specified interfaces. If an interface type is set to an empty list + //// or is not specified at all, it means that ALL such interface are allowed. // allow: { // publishers: [".*/laser_scan", "/tf", ".*/pose"], // subscribers: [".*/cmd_vel"], @@ -64,12 +68,6 @@ // action_servers: [".*/rotate_absolute"], // action_clients: [], // }, - - //// - //// deny: Sets of 1 or more regular expression per ROS interface kind matching the interface names that must NOT be routed via zenoh. - //// By default, no interface are denied. - //// If both 'allow' and 'deny' are set an interface will be allowed if it matches only the expression in 'allow' set. - //// // deny: { // publishers: ["/rosout", "/parameter_events"], // subscribers: ["/rosout"], @@ -80,7 +78,7 @@ // }, //// - //// pub_max_frequencies: Specifies a list of maximum frequency of publications routing over zenoh for a set of Publishers. + //// pub_max_frequencies: Specify a list of maximum frequency of publications routing over zenoh for a set of Publishers. //// The strings must have the format "=": //// - "regex" is a regular expression matching a Publisher interface name //// - "float" is the maximum frequency in Hertz; diff --git a/zenoh-plugin-ros2dds/src/config.rs b/zenoh-plugin-ros2dds/src/config.rs index 6745d6c..b8ce98c 100644 --- a/zenoh-plugin-ros2dds/src/config.rs +++ b/zenoh-plugin-ros2dds/src/config.rs @@ -336,6 +336,10 @@ impl<'de> Visitor<'de> for RegexVisitor { while let Some(s) = seq.next_element::()? { vec.push(format!("^{s}$")); } + if vec.is_empty() { + return Ok(None); + }; + let s: String = vec.join("|"); Regex::new(&s) .map(Some) @@ -377,3 +381,154 @@ where } seq.end() } + +mod tests { + + #[test] + fn test_allowance() { + use super::*; + + let allow: Allowance = serde_json::from_str( + r#"{ + "allow": { + "publishers": ["/tf", ".*/pose"], + "subscribers": [], + "service_servers": [".*"], + "action_servers": [".*/rotate_absolute"], + "action_clients": [ "" ] + } + }"#, + ) + .unwrap(); + println!("allow: {}", serde_json::to_string(&allow).unwrap()); + + assert!(matches!( + allow, + Allowance::Allow(ROS2InterfacesRegex { + publishers: Some(_), + subscribers: None, + service_servers: Some(_), + service_clients: None, + action_servers: Some(_), + action_clients: Some(_), + }) + )); + + assert!(allow.is_publisher_allowed("/tf")); + assert!(allow.is_publisher_allowed("/x/y/pose")); + assert!(!allow.is_publisher_allowed("/abc/rotate_absolute")); + assert!(!allow.is_publisher_allowed("/cmd_vel")); + assert!(!allow.is_service_cli_allowed("/some_pseudo_random_name")); + + assert!(!allow.is_subscriber_allowed("/tf")); + assert!(!allow.is_subscriber_allowed("/x/y/pose")); + assert!(!allow.is_publisher_allowed("/abc/rotate_absolute")); + assert!(!allow.is_subscriber_allowed("/cmd_vel")); + assert!(!allow.is_service_cli_allowed("/some_pseudo_random_name")); + + assert!(allow.is_service_srv_allowed("/tf")); + assert!(allow.is_service_srv_allowed("/x/y/pose")); + assert!(allow.is_service_srv_allowed("/abc/rotate_absolute")); + assert!(allow.is_service_srv_allowed("/cmd_vel")); + assert!(allow.is_service_srv_allowed("/some_pseudo_random_name")); + + assert!(!allow.is_service_cli_allowed("/tf")); + assert!(!allow.is_service_cli_allowed("/x/y/pose")); + assert!(!allow.is_service_cli_allowed("/abc/rotate_absolute")); + assert!(!allow.is_service_cli_allowed("/cmd_vel")); + assert!(!allow.is_service_cli_allowed("/some_pseudo_random_name")); + + assert!(!allow.is_action_srv_allowed("/tf")); + assert!(!allow.is_action_srv_allowed("/x/y/pose")); + assert!(allow.is_action_srv_allowed("/abc/rotate_absolute")); + assert!(!allow.is_action_srv_allowed("/cmd_vel")); + assert!(!allow.is_action_srv_allowed("/some_pseudo_random_name")); + + assert!(!allow.is_action_cli_allowed("/tf")); + assert!(!allow.is_action_cli_allowed("/x/y/pose")); + assert!(!allow.is_action_cli_allowed("/abc/rotate_absolute")); + assert!(!allow.is_action_cli_allowed("/cmd_vel")); + assert!(!allow.is_action_cli_allowed("/some_pseudo_random_name")); + + let deny: Allowance = serde_json::from_str( + r#"{ + "deny": { + "publishers": ["/tf", ".*/pose"], + "subscribers": [], + "service_servers": [".*"], + "action_servers": [".*/rotate_absolute"], + "action_clients": [ "" ] + } + }"#, + ) + .unwrap(); + println!("deny: {}", serde_json::to_string(&allow).unwrap()); + + assert!(matches!( + deny, + Allowance::Deny(ROS2InterfacesRegex { + publishers: Some(_), + subscribers: None, + service_servers: Some(_), + service_clients: None, + action_servers: Some(_), + action_clients: Some(_), + }) + )); + + assert!(!deny.is_publisher_allowed("/tf")); + assert!(!deny.is_publisher_allowed("/x/y/pose")); + assert!(deny.is_publisher_allowed("/abc/rotate_absolute")); + assert!(deny.is_publisher_allowed("/cmd_vel")); + assert!(deny.is_service_cli_allowed("/some_pseudo_random_name")); + + assert!(deny.is_subscriber_allowed("/tf")); + assert!(deny.is_subscriber_allowed("/x/y/pose")); + assert!(deny.is_publisher_allowed("/abc/rotate_absolute")); + assert!(deny.is_subscriber_allowed("/cmd_vel")); + assert!(deny.is_service_cli_allowed("/some_pseudo_random_name")); + + assert!(!deny.is_service_srv_allowed("/tf")); + assert!(!deny.is_service_srv_allowed("/x/y/pose")); + assert!(!deny.is_service_srv_allowed("/abc/rotate_absolute")); + assert!(!deny.is_service_srv_allowed("/cmd_vel")); + assert!(!deny.is_service_srv_allowed("/some_pseudo_random_name")); + + assert!(deny.is_service_cli_allowed("/tf")); + assert!(deny.is_service_cli_allowed("/x/y/pose")); + assert!(deny.is_service_cli_allowed("/abc/rotate_absolute")); + assert!(deny.is_service_cli_allowed("/cmd_vel")); + assert!(deny.is_service_cli_allowed("/some_pseudo_random_name")); + + assert!(deny.is_action_srv_allowed("/tf")); + assert!(deny.is_action_srv_allowed("/x/y/pose")); + assert!(!deny.is_action_srv_allowed("/abc/rotate_absolute")); + assert!(deny.is_action_srv_allowed("/cmd_vel")); + assert!(deny.is_action_srv_allowed("/some_pseudo_random_name")); + + assert!(deny.is_action_cli_allowed("/tf")); + assert!(deny.is_action_cli_allowed("/x/y/pose")); + assert!(deny.is_action_cli_allowed("/abc/rotate_absolute")); + assert!(deny.is_action_cli_allowed("/cmd_vel")); + assert!(deny.is_action_cli_allowed("/some_pseudo_random_name")); + + let invalid = serde_json::from_str::( + r#"{ + "allow": { + "publishers": ["/tf", ".*/pose"], + "subscribers": [], + "service_servers": [".*"], + "action_servers": [".*/rotate_absolute"], + "action_clients": [ "" ] + }, + "deny": { + "subscribers": ["/tf", ".*/pose"], + "service_clients": [".*"], + "action_servers": [""], + "action_clients": [ ".*/rotate_absolute" ] + }, + }"#, + ); + assert!(invalid.is_err()); + } +}