alibi_core/utils/username/
policy.rs1use super::UsernameValidationError;
4use crate::{AuthError, AuthResult, utils::json::JsValue};
5use async_trait::async_trait;
6use std::{fmt, future::Future, sync::Arc};
7
8#[async_trait]
10pub trait UsernameValidator: Send + Sync {
11 async fn validate(&self, value: &str) -> AuthResult<bool>;
12}
13#[async_trait]
14impl<F, Fut> UsernameValidator for F
15where
16 F: Fn(String) -> Fut + Send + Sync,
17 Fut: Future<Output = AuthResult<bool>> + Send,
18{
19 async fn validate(&self, value: &str) -> AuthResult<bool> {
20 self(value.to_owned()).await
21 }
22}
23
24pub trait UsernameNormalizer: Send + Sync {
26 fn normalize(&self, value: &str) -> AuthResult<String>;
27}
28impl<F> UsernameNormalizer for F
29where
30 F: Fn(&str) -> AuthResult<String> + Send + Sync,
31{
32 fn normalize(&self, value: &str) -> AuthResult<String> {
33 self(value)
34 }
35}
36
37#[derive(Clone, Default)]
38pub enum UsernameNormalization {
39 #[default]
40 Lowercase,
41 Preserve,
42 Custom(Arc<dyn UsernameNormalizer>),
43}
44impl fmt::Debug for UsernameNormalization {
45 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46 f.write_str(match self {
47 Self::Lowercase => "Lowercase",
48 Self::Preserve => "Preserve",
49 Self::Custom(_) => "Custom",
50 })
51 }
52}
53
54#[derive(Clone, Copy, Debug, PartialEq, Eq)]
57pub enum UsernameValidationOrder {
58 PreNormalization,
59 PostNormalization,
60}
61
62#[derive(Clone)]
63pub struct UsernameConfig {
64 pub min_length: usize,
66 pub max_length: usize,
67 pub normalization: UsernameNormalization,
68 pub validator: Option<Arc<dyn UsernameValidator>>,
69 pub include_display_username: bool,
70 pub display_normalizer: Option<Arc<dyn UsernameNormalizer>>,
71 pub display_validator: Option<Arc<dyn UsernameValidator>>,
72 pub validation_order: Option<UsernameValidationOrder>,
73 pub display_validation_order: Option<UsernameValidationOrder>,
74 pub immutable_username: bool,
75 pub input: bool,
77}
78impl Default for UsernameConfig {
79 fn default() -> Self {
80 Self {
81 min_length: 3,
82 max_length: 30,
83 normalization: UsernameNormalization::Lowercase,
84 validator: None,
85 include_display_username: true,
86 display_normalizer: None,
87 display_validator: None,
88 validation_order: None,
89 display_validation_order: None,
90 immutable_username: false,
91 input: true,
92 }
93 }
94}
95impl fmt::Debug for UsernameConfig {
96 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97 f.debug_struct("UsernameConfig")
98 .field("min_length", &self.min_length)
99 .field("max_length", &self.max_length)
100 .field("normalization", &self.normalization)
101 .field("validator", &self.validator.as_ref().map(|_| "callback"))
102 .field("include_display_username", &self.include_display_username)
103 .field(
104 "display_normalizer",
105 &self.display_normalizer.as_ref().map(|_| "callback"),
106 )
107 .field(
108 "display_validator",
109 &self.display_validator.as_ref().map(|_| "callback"),
110 )
111 .field("validation_order", &self.validation_order)
112 .field("display_validation_order", &self.display_validation_order)
113 .field("immutable_username", &self.immutable_username)
114 .field("input", &self.input)
115 .finish()
116 }
117}
118impl UsernameConfig {
119 pub fn normalize(&self, value: &str) -> AuthResult<String> {
120 match &self.normalization {
121 UsernameNormalization::Lowercase => Ok(value.to_lowercase()),
122 UsernameNormalization::Preserve => Ok(value.to_owned()),
123 UsernameNormalization::Custom(normalizer) => {
124 normalizer.normalize(value).map_err(callback_error)
125 }
126 }
127 }
128 pub fn normalize_display(&self, value: &str) -> AuthResult<String> {
129 self.display_normalizer.as_ref().map_or_else(
130 || Ok(value.to_owned()),
131 |normalizer| normalizer.normalize(value).map_err(callback_error),
132 )
133 }
134 pub async fn value_error(&self, value: &str) -> AuthResult<Option<UsernameValidationError>> {
135 let length = value.encode_utf16().count();
136 let minimum = if self.min_length == 0 {
137 3
138 } else {
139 self.min_length
140 };
141 let maximum = if self.max_length == 0 {
142 30
143 } else {
144 self.max_length
145 };
146 if length < minimum {
147 return Ok(Some(UsernameValidationError::TooShort));
148 }
149 if length > maximum {
150 return Ok(Some(UsernameValidationError::TooLong));
151 }
152 let valid = if let Some(validator) = &self.validator {
153 validator.validate(value).await.map_err(callback_error)?
154 } else {
155 super::valid_default_characters(value)
156 };
157 Ok((!valid).then_some(UsernameValidationError::Invalid))
158 }
159 pub async fn validate_value(&self, value: &str, status: u16) -> AuthResult<()> {
160 if let Some(error) = self.value_error(value).await? {
161 return Err(error.auth_error(status));
162 }
163 Ok(())
164 }
165 pub async fn validate_hook_value(&self, value: &str) -> AuthResult<()> {
166 if self.validation_order == Some(UsernameValidationOrder::PostNormalization) {
167 self.validate_value(&self.normalize(value)?, 400).await
168 } else {
169 self.validate_value(value, 400).await
170 }
171 }
172 pub async fn validate_display(&self, value: &str) -> AuthResult<()> {
173 if !self.include_display_username {
174 return Ok(());
175 }
176 if let Some(validator) = &self.display_validator {
177 let normalized;
178 let input = if self.display_validation_order
179 == Some(UsernameValidationOrder::PostNormalization)
180 {
181 normalized = self.normalize_display(value)?;
182 &normalized
183 } else {
184 value
185 };
186 if !validator.validate(input).await.map_err(callback_error)? {
187 return Err(AuthError::Upstream {
188 status: 400,
189 code: "INVALID_DISPLAY_USERNAME",
190 message: "Display username is invalid",
191 });
192 }
193 }
194 Ok(())
195 }
196 pub fn fields(&self) -> crate::field_policy::FieldConfigs {
198 use crate::field_policy::FieldConfig;
199 let mut fields = crate::field_policy::FieldConfigs::new();
200 let policy = self.clone();
201 let _ = fields.insert(
202 "username".into(),
203 FieldConfig::new(serde_json::json!({"type":"string"})).transform(move |value| {
204 match value {
205 Some(JsValue::String(value)) => {
206 Ok(Some(JsValue::String(policy.normalize(value)?)))
207 }
208 value => Ok(value.cloned()),
209 }
210 }),
211 );
212 if let Some(field) = fields.get_mut("username") {
213 field.input = self.input;
214 }
215 if self.include_display_username {
216 let policy = self.clone();
217 let _ = fields.insert(
218 "displayUsername".into(),
219 FieldConfig::new(serde_json::json!({"type":"string"})).transform(move |value| {
220 match value {
221 Some(JsValue::String(value)) => {
222 Ok(Some(JsValue::String(policy.normalize_display(value)?)))
223 }
224 value => Ok(value.cloned()),
225 }
226 }),
227 );
228 } else {
229 let mut hidden = FieldConfig::new(serde_json::json!({"type":"string"}));
230 hidden.input = false;
231 hidden.returned = false;
232 let _ = fields.insert("displayUsername".into(), hidden);
233 }
234 fields
235 }
236 pub fn normalize_fields(
238 &self,
239 username: &mut Option<String>,
240 display: &mut Option<String>,
241 values: &mut crate::field_policy::FieldValues,
242 creation: bool,
243 ) -> AuthResult<()> {
244 let original_username = username.clone();
245 if let Some(value) = username.as_mut() {
246 if !value.is_empty() {
247 *value = self.normalize(value)?;
248 }
249 let _ = values.insert("username".into(), JsValue::String(value.clone()));
250 }
251 if self.include_display_username {
252 if creation
253 && username.as_ref().is_some_and(|value| !value.is_empty())
254 && display.as_ref().is_none_or(String::is_empty)
255 {
256 display.clone_from(&original_username);
257 if let Some(value) = &display {
258 let _ = values.insert("displayUsername".into(), JsValue::String(value.clone()));
259 }
260 } else if let Some(value) = display.as_mut().filter(|value| !value.is_empty()) {
261 *value = self.normalize_display(value)?;
262 let _ = values.insert("displayUsername".into(), JsValue::String(value.clone()));
263 }
264 } else {
265 *display = None;
266 }
267 Ok(())
268 }
269}
270
271fn callback_error(error: AuthError) -> AuthError {
272 match error {
273 AuthError::Api { .. } | AuthError::Upstream { .. } | AuthError::CallbackFailure(_) => error,
274 error => AuthError::CallbackFailure(Box::new(error)),
275 }
276}