Skip to main content

alibi_core/utils/
username.rs

1//! Shared username normalization and validation helpers.
2
3mod 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
24/// # Errors
25///
26/// Returns an error if the username fails the configured length or character validation.
27pub 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}