bitwarden_policies/policies/
password_generator.rs1use bitwarden_organizations::OrganizationUserType;
2use serde::{Deserialize, Serialize};
3#[cfg(feature = "wasm")]
4use tsify::Tsify;
5
6use crate::{Policy, PolicyType, policy_type::PolicyDataType};
7
8pub struct PasswordGeneratorPolicy;
10
11impl Policy for PasswordGeneratorPolicy {
12 type Data = PasswordGeneratorPolicyData;
13
14 fn policy_type(&self) -> PolicyType {
15 PolicyType::PasswordGenerator
16 }
17
18 fn to_erased(&self, data: Self::Data) -> PolicyDataType {
19 PolicyDataType::PasswordGenerator(data)
20 }
21
22 fn exempt_roles(&self) -> &[OrganizationUserType] {
23 &[]
24 }
25}
26
27#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)]
30#[serde(rename_all = "camelCase")]
31#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
32#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
33pub enum PasswordGeneratorType {
34 Password,
36 Passphrase,
38}
39
40#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
43#[serde(rename_all = "camelCase")]
44#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
45#[cfg_attr(
46 feature = "wasm",
47 derive(tsify::Tsify),
48 tsify(into_wasm_abi, from_wasm_abi)
49)]
50pub struct PasswordGeneratorPolicyData {
51 #[serde(skip_serializing_if = "Option::is_none")]
53 pub override_password_type: Option<PasswordGeneratorType>,
54
55 #[serde(skip_serializing_if = "Option::is_none")]
57 pub min_length: Option<i32>,
58
59 #[serde(skip_serializing_if = "Option::is_none")]
61 pub use_upper: Option<bool>,
62
63 #[serde(skip_serializing_if = "Option::is_none")]
65 pub use_lower: Option<bool>,
66
67 #[serde(skip_serializing_if = "Option::is_none")]
69 pub use_numbers: Option<bool>,
70
71 #[serde(skip_serializing_if = "Option::is_none")]
73 pub use_special: Option<bool>,
74
75 #[serde(skip_serializing_if = "Option::is_none")]
77 pub min_numbers: Option<i32>,
78
79 #[serde(skip_serializing_if = "Option::is_none")]
81 pub min_special: Option<i32>,
82
83 #[serde(skip_serializing_if = "Option::is_none")]
85 pub min_number_words: Option<i32>,
86
87 #[serde(skip_serializing_if = "Option::is_none")]
89 pub capitalize: Option<bool>,
90
91 #[serde(skip_serializing_if = "Option::is_none")]
93 pub include_number: Option<bool>,
94}
95
96#[cfg(test)]
97mod tests {
98 use super::*;
99
100 #[test]
101 fn round_trips_full() {
102 let data = PasswordGeneratorPolicyData {
103 override_password_type: Some(PasswordGeneratorType::Passphrase),
104 min_length: Some(14),
105 use_upper: Some(true),
106 use_lower: Some(true),
107 use_numbers: Some(true),
108 use_special: Some(false),
109 min_numbers: Some(1),
110 min_special: Some(0),
111 min_number_words: Some(4),
112 capitalize: Some(true),
113 include_number: Some(true),
114 };
115 let json = serde_json::to_string(&data).unwrap();
116 assert!(json.contains(r#""overridePasswordType":"passphrase""#));
117 assert!(json.contains("minNumberWords"));
118 assert_eq!(
119 serde_json::from_str::<PasswordGeneratorPolicyData>(&json).unwrap(),
120 data
121 );
122 }
123
124 #[test]
125 fn override_password_type_serializes_as_camel_case_string() {
126 let json = serde_json::to_string(&PasswordGeneratorPolicyData {
127 override_password_type: Some(PasswordGeneratorType::Password),
128 ..Default::default()
129 })
130 .unwrap();
131 assert_eq!(json, r#"{"overridePasswordType":"password"}"#);
132 }
133
134 #[test]
135 fn empty_serializes_to_empty_object() {
136 assert_eq!(
137 serde_json::to_string(&PasswordGeneratorPolicyData::default()).unwrap(),
138 "{}"
139 );
140 }
141}