Skip to main content

rustauth_plugins/username/
options.rs

1use std::sync::Arc;
2
3pub type UsernameValidator = Arc<dyn Fn(&str) -> bool + Send + Sync>;
4pub type UsernameNormalizer = Arc<dyn Fn(&str) -> String + Send + Sync>;
5
6#[derive(Clone)]
7pub struct UsernameOptions {
8    pub min_username_length: usize,
9    pub max_username_length: usize,
10    pub username_validator: UsernameValidator,
11    pub display_username_validator: Option<UsernameValidator>,
12    pub username_normalization: Option<UsernameNormalizer>,
13    pub display_username_normalization: Option<UsernameNormalizer>,
14    pub validation_order: ValidationOrder,
15    pub schema: super::schema::UsernameSchemaOptions,
16}
17
18impl Default for UsernameOptions {
19    fn default() -> Self {
20        Self {
21            min_username_length: 3,
22            max_username_length: 30,
23            username_validator: Arc::new(default_username_validator),
24            display_username_validator: None,
25            username_normalization: Some(Arc::new(|username| username.to_lowercase())),
26            display_username_normalization: None,
27            validation_order: ValidationOrder::default(),
28            schema: super::schema::UsernameSchemaOptions::default(),
29        }
30    }
31}
32
33impl UsernameOptions {
34    #[must_use]
35    pub fn builder() -> UsernameOptionsBuilder {
36        UsernameOptionsBuilder::default()
37    }
38
39    pub fn normalize_username(&self, username: &str) -> String {
40        self.username_normalization
41            .as_ref()
42            .map(|normalizer| normalizer(username))
43            .unwrap_or_else(|| username.to_owned())
44    }
45
46    pub fn normalize_display_username(&self, display_username: &str) -> String {
47        self.display_username_normalization
48            .as_ref()
49            .map(|normalizer| normalizer(display_username))
50            .unwrap_or_else(|| display_username.to_owned())
51    }
52
53    pub fn username_for_validation(&self, username: &str) -> String {
54        if self.validation_order.username == ValidationPhase::PostNormalization {
55            self.normalize_username(username)
56        } else {
57            username.to_owned()
58        }
59    }
60
61    pub fn display_username_for_validation(&self, display_username: &str) -> String {
62        if self.validation_order.display_username == ValidationPhase::PostNormalization {
63            self.normalize_display_username(display_username)
64        } else {
65            display_username.to_owned()
66        }
67    }
68
69    pub fn validate_username(
70        &self,
71        username: &str,
72        _phase: ValidationPhase,
73    ) -> Result<(), UsernameValidationError> {
74        if username.len() < self.min_username_length {
75            return Err(UsernameValidationError::TooShort);
76        }
77        if username.len() > self.max_username_length {
78            return Err(UsernameValidationError::TooLong);
79        }
80        if !(self.username_validator)(username) {
81            return Err(UsernameValidationError::Invalid);
82        }
83        Ok(())
84    }
85
86    pub fn validate_display_username(
87        &self,
88        display_username: &str,
89    ) -> Result<(), UsernameValidationError> {
90        if let Some(validator) = &self.display_username_validator {
91            if !validator(display_username) {
92                return Err(UsernameValidationError::InvalidDisplay);
93            }
94        }
95        Ok(())
96    }
97}
98
99#[derive(Debug, Clone, Copy, PartialEq, Eq)]
100pub struct ValidationOrder {
101    pub username: ValidationPhase,
102    pub display_username: ValidationPhase,
103}
104
105impl Default for ValidationOrder {
106    fn default() -> Self {
107        Self {
108            username: ValidationPhase::PreNormalization,
109            display_username: ValidationPhase::PreNormalization,
110        }
111    }
112}
113
114#[derive(Debug, Clone, Copy, PartialEq, Eq)]
115pub enum ValidationPhase {
116    PreNormalization,
117    PostNormalization,
118    Endpoint,
119}
120
121#[derive(Debug, Clone, Copy, PartialEq, Eq)]
122pub enum UsernameValidationError {
123    TooShort,
124    TooLong,
125    Invalid,
126    InvalidDisplay,
127}
128
129fn default_username_validator(username: &str) -> bool {
130    username
131        .chars()
132        .all(|character| character.is_ascii_alphanumeric() || character == '_' || character == '.')
133}
134
135#[derive(Clone, Default)]
136pub struct UsernameOptionsBuilder {
137    min_username_length: Option<usize>,
138    max_username_length: Option<usize>,
139    username_validator: Option<UsernameValidator>,
140    display_username_validator: Option<Option<UsernameValidator>>,
141    username_normalization: Option<Option<UsernameNormalizer>>,
142    display_username_normalization: Option<Option<UsernameNormalizer>>,
143    validation_order: Option<ValidationOrder>,
144    schema: Option<super::schema::UsernameSchemaOptions>,
145}
146
147impl UsernameOptionsBuilder {
148    #[must_use]
149    pub fn min_username_length(mut self, length: usize) -> Self {
150        self.min_username_length = Some(length);
151        self
152    }
153
154    #[must_use]
155    pub fn max_username_length(mut self, length: usize) -> Self {
156        self.max_username_length = Some(length);
157        self
158    }
159
160    #[must_use]
161    pub fn username_validator(mut self, validator: UsernameValidator) -> Self {
162        self.username_validator = Some(validator);
163        self
164    }
165
166    #[must_use]
167    pub fn display_username_validator(mut self, validator: UsernameValidator) -> Self {
168        self.display_username_validator = Some(Some(validator));
169        self
170    }
171
172    #[must_use]
173    pub fn username_normalization(mut self, normalizer: UsernameNormalizer) -> Self {
174        self.username_normalization = Some(Some(normalizer));
175        self
176    }
177
178    #[must_use]
179    pub fn display_username_normalization(mut self, normalizer: UsernameNormalizer) -> Self {
180        self.display_username_normalization = Some(Some(normalizer));
181        self
182    }
183
184    #[must_use]
185    pub fn validation_order(mut self, validation_order: ValidationOrder) -> Self {
186        self.validation_order = Some(validation_order);
187        self
188    }
189
190    #[must_use]
191    pub fn schema(mut self, schema: super::schema::UsernameSchemaOptions) -> Self {
192        self.schema = Some(schema);
193        self
194    }
195
196    #[must_use]
197    pub fn build(self) -> UsernameOptions {
198        let defaults = UsernameOptions::default();
199        UsernameOptions {
200            min_username_length: self
201                .min_username_length
202                .unwrap_or(defaults.min_username_length),
203            max_username_length: self
204                .max_username_length
205                .unwrap_or(defaults.max_username_length),
206            username_validator: self
207                .username_validator
208                .unwrap_or(defaults.username_validator),
209            display_username_validator: self
210                .display_username_validator
211                .unwrap_or(defaults.display_username_validator),
212            username_normalization: self
213                .username_normalization
214                .unwrap_or(defaults.username_normalization),
215            display_username_normalization: self
216                .display_username_normalization
217                .unwrap_or(defaults.display_username_normalization),
218            validation_order: self.validation_order.unwrap_or(defaults.validation_order),
219            schema: self.schema.unwrap_or(defaults.schema),
220        }
221    }
222}