Mail rules: group and tenant ids in their JMAP form #112
@@ -60,6 +60,41 @@ fn one() -> u32 {
|
||||
1
|
||||
}
|
||||
|
||||
/// Group and tenant ids in the JMAP form clients use (`"b"`, `"c"`…), held
|
||||
/// as numbers for matching. Plain numbers are read too.
|
||||
mod jmap_ids {
|
||||
use serde::{Deserialize, Deserializer, Serializer, de::Error, ser::SerializeSeq};
|
||||
use std::str::FromStr;
|
||||
use types::id::Id;
|
||||
|
||||
pub fn serialize<S: Serializer>(ids: &[u32], serializer: S) -> Result<S::Ok, S::Error> {
|
||||
let mut seq = serializer.serialize_seq(Some(ids.len()))?;
|
||||
for id in ids {
|
||||
seq.serialize_element(&Id::from(*id).to_string())?;
|
||||
}
|
||||
seq.end()
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(untagged)]
|
||||
enum Either {
|
||||
Text(String),
|
||||
Number(u32),
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Vec<u32>, D::Error> {
|
||||
Vec::<Either>::deserialize(deserializer)?
|
||||
.into_iter()
|
||||
.map(|id| match id {
|
||||
Either::Number(n) => Ok(n),
|
||||
Either::Text(text) => Id::from_str(&text)
|
||||
.map(|id| id.document_id())
|
||||
.map_err(|_| D::Error::custom(format!("\"{text}\" isn't an id"))),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
/// A detector and the least it must find.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, SerdeSerialize, SerdeDeserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
@@ -83,9 +118,11 @@ pub enum Condition {
|
||||
domains: Vec<String>,
|
||||
},
|
||||
SenderGroup {
|
||||
#[serde(with = "jmap_ids")]
|
||||
groups: Vec<u32>,
|
||||
},
|
||||
SenderTenant {
|
||||
#[serde(with = "jmap_ids")]
|
||||
tenants: Vec<u32>,
|
||||
},
|
||||
/// Any recipient is one of these.
|
||||
@@ -96,6 +133,7 @@ pub enum Condition {
|
||||
domains: Vec<String>,
|
||||
},
|
||||
RecipientGroup {
|
||||
#[serde(with = "jmap_ids")]
|
||||
groups: Vec<u32>,
|
||||
},
|
||||
/// Any recipient isn't at a domain this server hosts.
|
||||
@@ -609,6 +647,21 @@ mod tests {
|
||||
assert_eq!(back["actions"][0]["notifySender"], true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn group_and_tenant_ids_are_jmap_ids() {
|
||||
let condition: Condition =
|
||||
serde_json::from_str(r#"{"type":"senderGroup","groups":["b", 7]}"#).unwrap();
|
||||
assert_eq!(condition, Condition::SenderGroup { groups: vec![1, 7] });
|
||||
assert_eq!(
|
||||
serde_json::to_value(&condition).unwrap()["groups"],
|
||||
serde_json::json!(["b", "h"])
|
||||
);
|
||||
assert!(
|
||||
serde_json::from_str::<Condition>(r#"{"type":"senderTenant","tenants":["!!"]}"#)
|
||||
.is_err()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dlp_rules_have_one_dlp_action_on_outgoing_mail() {
|
||||
let block = Action::Block {
|
||||
|
||||
@@ -114,6 +114,33 @@ pub async fn test(test: &mut TestServer) {
|
||||
"{rule}"
|
||||
);
|
||||
|
||||
// Group and tenant ids travel in their JMAP form
|
||||
let mut by_tenant = transport_rule();
|
||||
by_tenant["name"] = "By tenant".into();
|
||||
by_tenant["conditions"] = json!([{"type": "senderTenant", "tenants": ["b"]}]);
|
||||
let (_, response) = call(
|
||||
&admin,
|
||||
"inbuxa:MailRule/set",
|
||||
json!({"create": {"g": by_tenant}}),
|
||||
)
|
||||
.await;
|
||||
let tenant_rule = response["created"]["g"]["id"]
|
||||
.as_str()
|
||||
.unwrap_or_else(|| panic!("{response}"))
|
||||
.to_string();
|
||||
let (_, response) = call(&admin, "inbuxa:MailRule/get", json!({"ids": [tenant_rule]})).await;
|
||||
assert_eq!(
|
||||
response["list"][0]["conditions"][0]["tenants"],
|
||||
json!(["b"]),
|
||||
"{response}"
|
||||
);
|
||||
call(
|
||||
&admin,
|
||||
"inbuxa:MailRule/set",
|
||||
json!({"destroy": [tenant_rule]}),
|
||||
)
|
||||
.await;
|
||||
|
||||
// Checked when written
|
||||
let mut inbound = dlp_rule();
|
||||
inbound["direction"] = "incoming".into();
|
||||
|
||||
Reference in New Issue
Block a user