alibi_core/utils/
username.rs1mod policy;
4pub use policy::{
5 UsernameConfig, UsernameNormalization, UsernameNormalizer, UsernameValidationOrder,
6 UsernameValidator,
7};
8
9pub const USERNAME_MIN_LENGTH: usize = 3;
10pub const USERNAME_MAX_LENGTH: usize = 30;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum UsernameValidationError {
14 TooShort,
15 TooLong,
16 Invalid,
17}
18
19#[must_use]
20pub fn normalize_username(username: &str) -> String {
21 username.to_lowercase()
22}
23
24pub fn validate_username(username: &str) -> Result<(), UsernameValidationError> {
28 let length = username.encode_utf16().count();
29 if length < USERNAME_MIN_LENGTH {
30 Err(UsernameValidationError::TooShort)
31 } else if length > USERNAME_MAX_LENGTH {
32 Err(UsernameValidationError::TooLong)
33 } else if valid_default_characters(username) {
34 Ok(())
35 } else {
36 Err(UsernameValidationError::Invalid)
37 }
38}
39
40#[must_use]
41pub fn normalize_username_fields(
42 username: Option<String>,
43 display_username: Option<String>,
44) -> (Option<String>, Option<String>) {
45 let display_username = display_username.or_else(|| username.clone());
46 let username = username
47 .or_else(|| display_username.clone())
48 .map(|username| normalize_username(&username));
49 (username, display_username)
50}
51
52fn valid_default_characters(username: &str) -> bool {
53 username
54 .chars()
55 .all(|ch| ch.is_ascii_alphanumeric() || ch == '_' || ch == '.')
56}
57impl UsernameValidationError {
58 #[must_use]
59 pub const fn auth_error(self, status: u16) -> crate::AuthError {
60 let (code, message) = match self {
61 Self::TooShort => ("USERNAME_TOO_SHORT", "Username is too short"),
62 Self::TooLong => ("USERNAME_TOO_LONG", "Username is too long"),
63 Self::Invalid => ("INVALID_USERNAME", "Username is invalid"),
64 };
65 crate::AuthError::Upstream {
66 status,
67 code,
68 message,
69 }
70 }
71}