bitwarden_policies/policies/
master_password.rs1use bitwarden_api_api::models::MasterPasswordPolicyResponseModel;
2use bitwarden_organizations::OrganizationUserType;
3use serde::{Deserialize, Serialize};
4
5use crate::{PolicyDefinition, PolicyType, policy_type::PolicyDataType};
6
7pub struct MasterPasswordPolicy;
9
10impl PolicyDefinition 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#[bitwarden_ffi::wasm_record]
37pub struct MasterPasswordPolicyData {
38 #[serde(skip_serializing_if = "Option::is_none")]
41 pub min_complexity: Option<i32>,
42
43 #[serde(skip_serializing_if = "Option::is_none")]
45 pub min_length: Option<i32>,
46
47 #[serde(skip_serializing_if = "Option::is_none")]
49 pub require_lower: Option<bool>,
50
51 #[serde(skip_serializing_if = "Option::is_none")]
53 pub require_upper: Option<bool>,
54
55 #[serde(skip_serializing_if = "Option::is_none")]
57 pub require_numbers: Option<bool>,
58
59 #[serde(skip_serializing_if = "Option::is_none")]
61 pub require_special: Option<bool>,
62
63 #[serde(skip_serializing_if = "Option::is_none")]
67 pub enforce_on_login: Option<bool>,
68}
69
70impl From<MasterPasswordPolicyResponseModel> for MasterPasswordPolicyData {
71 fn from(api: MasterPasswordPolicyResponseModel) -> Self {
72 Self {
73 min_complexity: api.min_complexity,
74 min_length: api.min_length,
75 require_lower: api.require_lower,
76 require_upper: api.require_upper,
77 require_numbers: api.require_numbers,
78 require_special: api.require_special,
79 enforce_on_login: api.enforce_on_login,
80 }
81 }
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87
88 #[test]
89 fn round_trips() {
90 let data = MasterPasswordPolicyData {
91 min_complexity: Some(3),
92 min_length: Some(12),
93 require_lower: Some(true),
94 require_upper: Some(true),
95 require_numbers: Some(false),
96 require_special: Some(false),
97 enforce_on_login: Some(true),
98 };
99 let json = serde_json::to_string(&data).unwrap();
100 assert!(json.contains(r#""minComplexity":3"#));
101 assert!(json.contains(r#""enforceOnLogin":true"#));
102 assert_eq!(
103 serde_json::from_str::<MasterPasswordPolicyData>(&json).unwrap(),
104 data
105 );
106 }
107
108 #[test]
109 fn empty_serializes_to_empty_object() {
110 assert_eq!(
111 serde_json::to_string(&MasterPasswordPolicyData::default()).unwrap(),
112 "{}"
113 );
114 }
115
116 #[test]
117 fn from_api_model_full() {
118 let api = MasterPasswordPolicyResponseModel {
119 object: Some("masterPasswordPolicy".to_string()),
120 min_complexity: Some(4),
121 min_length: Some(12),
122 require_lower: Some(true),
123 require_upper: Some(true),
124 require_numbers: Some(true),
125 require_special: Some(true),
126 enforce_on_login: Some(true),
127 };
128
129 let domain: MasterPasswordPolicyData = api.into();
130
131 assert_eq!(domain.min_complexity, Some(4));
132 assert_eq!(domain.min_length, Some(12));
133 assert_eq!(domain.require_lower, Some(true));
134 assert_eq!(domain.require_upper, Some(true));
135 assert_eq!(domain.require_numbers, Some(true));
136 assert_eq!(domain.require_special, Some(true));
137 assert_eq!(domain.enforce_on_login, Some(true));
138 }
139
140 #[test]
141 fn from_api_model_minimal() {
142 let api = MasterPasswordPolicyResponseModel {
143 object: Some("masterPasswordPolicy".to_string()),
144 min_complexity: None,
145 min_length: Some(8),
146 require_lower: None,
147 require_upper: None,
148 require_numbers: None,
149 require_special: None,
150 enforce_on_login: Some(false),
151 };
152
153 let domain: MasterPasswordPolicyData = api.into();
154
155 assert_eq!(domain.min_complexity, None);
156 assert_eq!(domain.min_length, Some(8));
157 assert_eq!(domain.require_lower, None);
158 assert_eq!(domain.require_upper, None);
159 assert_eq!(domain.require_numbers, None);
160 assert_eq!(domain.require_special, None);
161 assert_eq!(domain.enforce_on_login, Some(false));
162 }
163
164 #[test]
165 fn from_api_model_empty() {
166 let api = MasterPasswordPolicyResponseModel {
167 object: Some("masterPasswordPolicy".to_string()),
168 min_complexity: None,
169 min_length: None,
170 require_lower: None,
171 require_upper: None,
172 require_numbers: None,
173 require_special: None,
174 enforce_on_login: None,
175 };
176
177 let domain: MasterPasswordPolicyData = api.into();
178
179 assert_eq!(domain.min_complexity, None);
180 assert_eq!(domain.min_length, None);
181 assert_eq!(domain.require_lower, None);
182 assert_eq!(domain.require_upper, None);
183 assert_eq!(domain.require_numbers, None);
184 assert_eq!(domain.require_special, None);
185 assert_eq!(domain.enforce_on_login, None);
186 }
187}