bitwarden_managed_settings_types/
profile.rs1use std::collections::HashMap;
2
3use bitwarden_error::bitwarden_error;
4use serde::{Deserialize, Serialize};
5use thiserror::Error;
6
7#[bitwarden_error(flat)]
9#[derive(Debug, Error)]
10pub enum ManagedSettingsError {
11 #[error("Failed to decode managed settings value: {0}")]
13 Decode(String),
14}
15
16#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
25#[serde(rename_all = "camelCase")]
26pub struct ManagementProfile {
27 pub version: u32,
29 pub updated_at: i64,
35 pub settings: HashMap<String, String>,
37}
38
39impl ManagementProfile {
40 pub fn empty() -> Self {
42 Self {
43 version: 1,
44 updated_at: 0,
45 settings: HashMap::new(),
46 }
47 }
48
49 pub fn is_managed(&self, key: &str) -> bool {
51 self.settings.contains_key(key)
52 }
53
54 pub fn get(&self, key: &str) -> Option<String> {
56 self.settings.get(key).cloned()
57 }
58
59 pub fn get_as<T: serde::de::DeserializeOwned>(
61 &self,
62 key: &str,
63 ) -> Result<Option<T>, ManagedSettingsError> {
64 match self.settings.get(key) {
65 None => Ok(None),
66 Some(raw) => serde_json::from_str(raw)
67 .map(Some)
68 .map_err(|e| ManagedSettingsError::Decode(e.to_string())),
69 }
70 }
71}
72
73#[cfg(test)]
74mod tests {
75 use super::*;
76
77 fn profile_with(entries: &[(&str, &str)]) -> ManagementProfile {
78 ManagementProfile {
79 version: 1,
80 updated_at: 1_750_000_000,
81 settings: entries
82 .iter()
83 .map(|(k, v)| (k.to_string(), v.to_string()))
84 .collect(),
85 }
86 }
87
88 #[test]
89 fn is_managed_reflects_key_presence() {
90 let profile = profile_with(&[("environment.base", "\"https://vault.example.com\"")]);
91
92 assert!(profile.is_managed("environment.base"));
93 assert!(!profile.is_managed("environment.api"));
94 }
95
96 #[test]
97 fn get_returns_the_raw_json_encoded_value() {
98 let profile = profile_with(&[("environment.base", "\"https://vault.example.com\"")]);
99
100 assert_eq!(
101 profile.get("environment.base"),
102 Some("\"https://vault.example.com\"".to_string())
103 );
104 }
105
106 #[test]
107 fn get_returns_none_for_an_absent_key() {
108 let profile = profile_with(&[("environment.base", "\"https://vault.example.com\"")]);
109
110 assert_eq!(profile.get("environment.api"), None);
111 }
112
113 #[test]
114 fn get_as_decodes_a_present_value() {
115 let profile = profile_with(&[("generator.password.length", "14")]);
116
117 assert_eq!(
118 profile.get_as::<u32>("generator.password.length").unwrap(),
119 Some(14)
120 );
121 }
122
123 #[test]
124 fn get_as_returns_none_for_an_absent_key() {
125 let profile = profile_with(&[("generator.password.length", "14")]);
126
127 assert_eq!(
128 profile
129 .get_as::<u32>("generator.password.uppercase")
130 .unwrap(),
131 None
132 );
133 }
134
135 #[test]
136 fn get_as_errors_when_the_value_does_not_match_the_requested_type() {
137 let profile = profile_with(&[("generator.password.length", "\"fourteen\"")]);
138
139 let error = profile
140 .get_as::<u32>("generator.password.length")
141 .expect_err("decoding a string as u32 should fail");
142
143 assert!(matches!(error, ManagedSettingsError::Decode(_)));
144 }
145
146 #[test]
147 fn empty_profile_manages_nothing() {
148 let profile = ManagementProfile::empty();
149
150 assert!(!profile.is_managed("environment.base"));
151 assert_eq!(profile.get("environment.base"), None);
152 assert_eq!(profile.get_as::<u32>("environment.base").unwrap(), None);
153 }
154}