rustauth_plugins/username/
options.rs1use 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}