diff --git a/src/config.rs b/src/config.rs index ca2a71c..08c81c5 100644 --- a/src/config.rs +++ b/src/config.rs @@ -21,6 +21,12 @@ const STANDARD_SYSTEM_SERVICEDIRS: &[&str] = &[ "/lib/dbus-1/system-services", ]; +#[derive(Clone, Debug, PartialEq)] +pub enum Access { + Allow, + Deny, +} + /// implements [`dbus-daemon`'s Configuration File](https://dbus.freedesktop.org/doc/dbus-daemon.1.html#configuration_file) #[derive(Clone, Debug, Default, Deserialize, PartialEq)] #[serde(try_from = "Document")] @@ -76,7 +82,7 @@ pub struct BusConfig { impl TryFrom for BusConfig { type Error = Error; - fn try_from(value: Document) -> Result { + fn try_from(value: Document) -> std::result::Result { let mut bc = BusConfig::default(); for element in value.busconfig { @@ -298,6 +304,47 @@ pub struct Limits { pub reply_timeout: Duration, } +#[derive(Clone, Debug, PartialEq)] +pub enum Operation { + Connect, + Own, + Receive, + Send, +} +type OptionalOperation = Option; +impl TryFrom for OptionalOperation { + type Error = Error; + + fn try_from(value: RuleAttributes) -> std::result::Result { + let has_connect = false; + let has_own = value.own.is_some(); + let has_send = value.send_destination.is_some(); + let has_receive = + value.receive_sender.is_some() || (!has_send && value.eavesdrop.is_some()); + + let operations_count: i8 = vec![has_connect, has_own, has_receive, has_send] + .into_iter() + .map(i8::from) + .sum(); + + if operations_count > 1 { + return Err(Error::msg(format!("do not mix rule attributes for connect, own, receive, and/or send operations in the same rule: {value:?}"))); + } + + if has_connect { + Ok(Some(Operation::Connect)) + } else if has_own { + Ok(Some(Operation::Own)) + } else if has_receive { + Ok(Some(Operation::Receive)) + } else if has_send { + Ok(Some(Operation::Send)) + } else { + Err(Error::msg(format!("rule must specify supported attributes for connect, own, receive, or send operations: {value:?}"))) + } + } +} + #[derive(Clone, Debug, PartialEq)] pub enum Policy { DefaultContext(Vec), @@ -311,7 +358,7 @@ type OptionalPolicy = Option; impl TryFrom for OptionalPolicy { type Error = Error; - fn try_from(value: PolicyElement) -> Result { + fn try_from(value: PolicyElement) -> std::result::Result { match value { PolicyElement { at_console: Some(_), @@ -389,10 +436,16 @@ type OptionalRule = Option; impl TryFrom for OptionalRule { type Error = Error; - fn try_from(value: RuleElement) -> Result { + fn try_from(value: RuleElement) -> std::result::Result { match value { - RuleElement::Allow => Ok(Some(Rule::Allow)), - RuleElement::Deny => Ok(Some(Rule::Deny)), + RuleElement::Allow(attrs) => match OptionalOperation::try_from(attrs)? { + Some(some) => Ok(Some((Access::Allow, some))), + None => Ok(None), + }, + RuleElement::Deny(attrs) => match OptionalOperation::try_from(attrs)? { + Some(some) => Ok(Some((Access::Deny, some))), + None => Ok(None), + }, } } } @@ -408,17 +461,29 @@ fn rules_try_from_rule_elements(value: Vec) -> Result> { Ok(rules) } -#[derive(Clone, Debug, PartialEq)] -pub enum Rule { - Allow, - Deny, +pub type Rule = (Access, Operation); + +#[derive(Clone, Debug, Default, Deserialize, PartialEq)] +struct RuleAttributes { + #[serde(rename = "@eavesdrop")] + eavesdrop: Option, + #[serde(rename = "@group")] + group: Option, + #[serde(rename = "@own")] + own: Option, + #[serde(rename = "@receive_sender")] + receive_sender: Option, + #[serde(rename = "@send_destination")] + send_destination: Option, + #[serde(rename = "@user")] + user: Option, } #[derive(Clone, Debug, Deserialize, PartialEq)] #[serde(rename_all = "snake_case")] enum RuleElement { - Allow, - Deny, + Allow(RuleAttributes), + Deny(RuleAttributes), } #[derive(Clone, Debug, Deserialize, PartialEq)] @@ -620,16 +685,59 @@ mod tests { busconfig, BusConfig { policies: vec![ - Policy::DefaultContext(vec![Rule::Allow, Rule::Allow, Rule::Allow,]), - Policy::User(vec![Rule::Allow,], String::from("root")), - Policy::Group(vec![Rule::Allow, Rule::Allow,], String::from("network")), - Policy::MandatoryContext(vec![Rule::Deny]), + Policy::DefaultContext(vec![ + (Access::Allow, Operation::Send), + (Access::Allow, Operation::Receive), + (Access::Allow, Operation::Own), + ]), + Policy::User( + vec![(Access::Allow, Operation::Send),], + String::from("root") + ), + Policy::Group( + vec![ + (Access::Allow, Operation::Send), + (Access::Allow, Operation::Receive), + ], + String::from("network") + ), + Policy::MandatoryContext(vec![(Access::Deny, Operation::Send),]), ], ..Default::default() } ); } + #[should_panic] + #[test] + fn bus_config_parse_with_policies_with_send_and_receive_attributes_error() { + let input = r#" + + + + + + "#; + + BusConfig::parse(input).expect("should parse XML input"); + } + + #[should_panic] + #[test] + fn bus_config_parse_with_policies_without_attributes_error() { + let input = r#" + + + + + + "#; + + BusConfig::parse(input).expect("should parse XML input"); + } + #[test] fn bus_config_parse_with_servicedir_and_standard_session_servicedirs_ok() { let input = r#"