bitwarden_pam/access_rules/
conditions.rs1use serde::{Deserialize, Serialize};
2
3#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
5#[bitwarden_ffi::wasm_record]
6#[serde(tag = "kind", rename_all = "snake_case")]
7pub enum AccessCondition {
8 HumanApproval,
10 IpAllowlist {
12 cidrs: Vec<String>,
14 },
15 #[serde(untagged)]
19 Unknown(
20 #[cfg_attr(feature = "wasm", tsify(type = "Record<string, unknown>"))] serde_json::Value,
21 ),
22}
23
24#[cfg(test)]
25mod tests {
26 use serde_json::json;
27
28 use super::*;
29
30 #[test]
31 fn human_approval_roundtrips() {
32 let condition = AccessCondition::HumanApproval;
33 let json = serde_json::to_value(&condition).unwrap();
34 assert_eq!(json, json!({ "kind": "human_approval" }));
35
36 let parsed: AccessCondition = serde_json::from_value(json).unwrap();
37 assert_eq!(parsed, condition);
38 }
39
40 #[test]
41 fn ip_allowlist_roundtrips() {
42 let condition = AccessCondition::IpAllowlist {
43 cidrs: vec!["10.0.0.0/8".to_string(), "2001:db8::/32".to_string()],
44 };
45 let json = serde_json::to_value(&condition).unwrap();
46 assert_eq!(
47 json,
48 json!({ "kind": "ip_allowlist", "cidrs": ["10.0.0.0/8", "2001:db8::/32"] })
49 );
50
51 let parsed: AccessCondition = serde_json::from_value(json).unwrap();
52 assert_eq!(parsed, condition);
53 }
54
55 #[test]
56 fn unknown_kind_is_preserved_verbatim() {
57 let raw = json!({
58 "kind": "time_of_day",
59 "tz": "UTC",
60 "windows": [{ "start": "09:00", "end": "17:00" }],
61 });
62
63 let parsed: AccessCondition = serde_json::from_value(raw.clone()).unwrap();
64 assert_eq!(parsed, AccessCondition::Unknown(raw.clone()));
65
66 let reserialized = serde_json::to_value(&parsed).unwrap();
69 assert_eq!(reserialized, raw);
70 }
71
72 #[test]
75 fn malformed_known_kind_degrades_to_unknown() {
76 let raw = json!({ "kind": "ip_allowlist" });
77
78 let parsed: AccessCondition = serde_json::from_value(raw.clone()).unwrap();
79 assert_eq!(parsed, AccessCondition::Unknown(raw));
80 }
81
82 #[test]
85 fn extra_fields_on_known_kind_are_dropped() {
86 let raw = json!({ "kind": "human_approval", "future_field": "not preserved" });
87 let parsed: AccessCondition = serde_json::from_value(raw).unwrap();
88 assert_eq!(parsed, AccessCondition::HumanApproval);
89 assert_eq!(
90 serde_json::to_value(&parsed).unwrap(),
91 json!({ "kind": "human_approval" })
92 );
93
94 let raw = json!({ "kind": "ip_allowlist", "cidrs": ["10.0.0.0/8"], "extra": "dropped" });
95 let parsed: AccessCondition = serde_json::from_value(raw).unwrap();
96 assert_eq!(
97 serde_json::to_value(&parsed).unwrap(),
98 json!({ "kind": "ip_allowlist", "cidrs": ["10.0.0.0/8"] })
99 );
100 }
101}