bitwarden_policies/policies/
master_password.rs1use bitwarden_api_api::models::MasterPasswordPolicyResponseModel;
2use bitwarden_organizations::OrganizationUserType;
3use serde::{Deserialize, Serialize};
4
5use crate::{Policy, PolicyType, policy_type::PolicyDataType};
6
7pub struct MasterPasswordPolicy;
9
10impl Policy for MasterPasswordPolicy {
11 type Data = MasterPasswordPolicyData;
12
13 fn policy_type(&self) -> PolicyType {
14 PolicyType::MasterPassword
15 }
16
17 fn to_erased(&self, data: Self::Data) -> PolicyDataType {
18 PolicyDataType::MasterPassword(data)
19 }
20
21 fn exempt_roles(&self) -> &[OrganizationUserType] {
22 &[]
23 }
24}
25
26#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
34#[serde(rename_all = "camelCase")]
35#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
36#[cfg_attr(
37 feature = "wasm",
38 derive(tsify::Tsify),
39 tsify(into_wasm_abi, from_wasm_abi)
40)]
41pub struct MasterPasswordPolicyData {
42 #[serde(skip_serializing_if = "Option::is_none")]
45 pub min_complexity: Option<i32>,
46
47 #[serde(skip_serializing_if = "Option::is_none")]
49 pub min_length: Option<i32>,
50
51 #[serde(skip_serializing_if = "Option::is_none")]
53 pub require_lower: Option<bool>,
54
55 #[serde(skip_serializing_if = "Option::is_none")]
57 pub require_upper: Option<bool>,
58
59 #[serde(skip_serializing_if = "Option::is_none")]
61 pub require_numbers: Option<bool>,
62
63 #[serde(skip_serializing_if = "Option::is_none")]
65 pub require_special: Option<bool>,
66
67 #[serde(skip_serializing_if = "Option::is_none")]
71 pub enforce_on_login: Option<bool>,
72}
73
74impl From<MasterPasswordPolicyResponseModel> for MasterPasswordPolicyData {
75 fn from(api: MasterPasswordPolicyResponseModel) -> Self {
76 Self {
77 min_complexity: api.min_complexity,
78 min_length: api.min_length,
79 require_lower: api.require_lower,
80 require_upper: api.require_upper,
81 require_numbers: api.require_numbers,
82 require_special: api.require_special,
83 enforce_on_login: api.enforce_on_login,
84 }
85 }
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91
92 #[test]
93 fn round_trips() {
94 let data = MasterPasswordPolicyData {
95 min_complexity: Some(3),
96 min_length: Some(12),
97 require_lower: Some(true),
98 require_upper: Some(true),
99 require_numbers: Some(false),
100 require_special: Some(false),
101 enforce_on_login: Some(true),
102 };
103 let json = serde_json::to_string(&data).unwrap();
104 assert!(json.contains(r#""minComplexity":3"#));
105 assert!(json.contains(r#""enforceOnLogin":true"#));
106 assert_eq!(
107 serde_json::from_str::<MasterPasswordPolicyData>(&json).unwrap(),
108 data
109 );
110 }
111
112 #[test]
113 fn empty_serializes_to_empty_object() {
114 assert_eq!(
115 serde_json::to_string(&MasterPasswordPolicyData::default()).unwrap(),
116 "{}"
117 );
118 }
119
120 #[test]
121 fn from_api_model_full() {
122 let api = MasterPasswordPolicyResponseModel {
123 object: Some("masterPasswordPolicy".to_string()),
124 min_complexity: Some(4),
125 min_length: Some(12),
126 require_lower: Some(true),
127 require_upper: Some(true),
128 require_numbers: Some(true),
129 require_special: Some(true),
130 enforce_on_login: Some(true),
131 };
132
133 let domain: MasterPasswordPolicyData = api.into();
134
135 assert_eq!(domain.min_complexity, Some(4));
136 assert_eq!(domain.min_length, Some(12));
137 assert_eq!(domain.require_lower, Some(true));
138 assert_eq!(domain.require_upper, Some(true));
139 assert_eq!(domain.require_numbers, Some(true));
140 assert_eq!(domain.require_special, Some(true));
141 assert_eq!(domain.enforce_on_login, Some(true));
142 }
143
144 #[test]
145 fn from_api_model_minimal() {
146 let api = MasterPasswordPolicyResponseModel {
147 object: Some("masterPasswordPolicy".to_string()),
148 min_complexity: None,
149 min_length: Some(8),
150 require_lower: None,
151 require_upper: None,
152 require_numbers: None,
153 require_special: None,
154 enforce_on_login: Some(false),
155 };
156
157 let domain: MasterPasswordPolicyData = api.into();
158
159 assert_eq!(domain.min_complexity, None);
160 assert_eq!(domain.min_length, Some(8));
161 assert_eq!(domain.require_lower, None);
162 assert_eq!(domain.require_upper, None);
163 assert_eq!(domain.require_numbers, None);
164 assert_eq!(domain.require_special, None);
165 assert_eq!(domain.enforce_on_login, Some(false));
166 }
167
168 #[test]
169 fn from_api_model_empty() {
170 let api = MasterPasswordPolicyResponseModel {
171 object: Some("masterPasswordPolicy".to_string()),
172 min_complexity: None,
173 min_length: None,
174 require_lower: None,
175 require_upper: None,
176 require_numbers: None,
177 require_special: None,
178 enforce_on_login: None,
179 };
180
181 let domain: MasterPasswordPolicyData = api.into();
182
183 assert_eq!(domain.min_complexity, None);
184 assert_eq!(domain.min_length, None);
185 assert_eq!(domain.require_lower, None);
186 assert_eq!(domain.require_upper, None);
187 assert_eq!(domain.require_numbers, None);
188 assert_eq!(domain.require_special, None);
189 assert_eq!(domain.enforce_on_login, None);
190 }
191}