Skip to main content

bitwarden_policies/policies/
password_generator.rs

1use bitwarden_organizations::OrganizationUserType;
2use serde::{Deserialize, Serialize};
3
4use crate::{PolicyDefinition, PolicyType, policy_type::PolicyDataType};
5
6/// Password Generator policy.
7pub struct PasswordGeneratorPolicy;
8
9impl PolicyDefinition for PasswordGeneratorPolicy {
10    type Data = PasswordGeneratorPolicyData;
11
12    fn policy_type(&self) -> PolicyType {
13        PolicyType::PasswordGenerator
14    }
15
16    fn to_erased(&self, data: Self::Data) -> PolicyDataType {
17        PolicyDataType::PasswordGenerator(data)
18    }
19
20    fn exempt_roles(&self) -> &[OrganizationUserType] {
21        &[]
22    }
23}
24
25/// The generator type the policy forces members to use, overriding their own
26/// preference.
27#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)]
28#[serde(rename_all = "camelCase")]
29#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
30#[bitwarden_ffi::wasm_record]
31pub enum PasswordGeneratorType {
32    /// Force the password generator.
33    Password,
34    /// Force the passphrase generator.
35    Passphrase,
36}
37
38/// Configuration data for the Password Generator policy. Each field, when set,
39/// enforces a minimum or a required option on the member's generator.
40#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
41#[serde(rename_all = "camelCase")]
42#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
43#[bitwarden_ffi::wasm_record]
44pub struct PasswordGeneratorPolicyData {
45    /// Forces the generator type; `None` leaves the choice to the member.
46    #[serde(skip_serializing_if = "Option::is_none")]
47    pub override_password_type: Option<PasswordGeneratorType>,
48
49    /// Minimum password length.
50    #[serde(skip_serializing_if = "Option::is_none")]
51    pub min_length: Option<i32>,
52
53    /// Require uppercase letters.
54    #[serde(skip_serializing_if = "Option::is_none")]
55    pub use_upper: Option<bool>,
56
57    /// Require lowercase letters.
58    #[serde(skip_serializing_if = "Option::is_none")]
59    pub use_lower: Option<bool>,
60
61    /// Require numbers.
62    #[serde(skip_serializing_if = "Option::is_none")]
63    pub use_numbers: Option<bool>,
64
65    /// Require special characters.
66    #[serde(skip_serializing_if = "Option::is_none")]
67    pub use_special: Option<bool>,
68
69    /// Minimum number of numeric digits.
70    #[serde(skip_serializing_if = "Option::is_none")]
71    pub min_numbers: Option<i32>,
72
73    /// Minimum number of special characters.
74    #[serde(skip_serializing_if = "Option::is_none")]
75    pub min_special: Option<i32>,
76
77    /// Minimum number of words (passphrase).
78    #[serde(skip_serializing_if = "Option::is_none")]
79    pub min_number_words: Option<i32>,
80
81    /// Require the passphrase to capitalize each word.
82    #[serde(skip_serializing_if = "Option::is_none")]
83    pub capitalize: Option<bool>,
84
85    /// Require the passphrase to include a number.
86    #[serde(skip_serializing_if = "Option::is_none")]
87    pub include_number: Option<bool>,
88}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93
94    #[test]
95    fn round_trips_full() {
96        let data = PasswordGeneratorPolicyData {
97            override_password_type: Some(PasswordGeneratorType::Passphrase),
98            min_length: Some(14),
99            use_upper: Some(true),
100            use_lower: Some(true),
101            use_numbers: Some(true),
102            use_special: Some(false),
103            min_numbers: Some(1),
104            min_special: Some(0),
105            min_number_words: Some(4),
106            capitalize: Some(true),
107            include_number: Some(true),
108        };
109        let json = serde_json::to_string(&data).unwrap();
110        assert!(json.contains(r#""overridePasswordType":"passphrase""#));
111        assert!(json.contains("minNumberWords"));
112        assert_eq!(
113            serde_json::from_str::<PasswordGeneratorPolicyData>(&json).unwrap(),
114            data
115        );
116    }
117
118    #[test]
119    fn override_password_type_serializes_as_camel_case_string() {
120        let json = serde_json::to_string(&PasswordGeneratorPolicyData {
121            override_password_type: Some(PasswordGeneratorType::Password),
122            ..Default::default()
123        })
124        .unwrap();
125        assert_eq!(json, r#"{"overridePasswordType":"password"}"#);
126    }
127
128    #[test]
129    fn empty_serializes_to_empty_object() {
130        assert_eq!(
131            serde_json::to_string(&PasswordGeneratorPolicyData::default()).unwrap(),
132            "{}"
133        );
134    }
135}