|
16 | 16 | *
|
17 | 17 | */
|
18 | 18 |
|
| 19 | +use std::collections::HashSet; |
| 20 | + |
19 | 21 | use error::HotTierValidationError;
|
| 22 | +use once_cell::sync::Lazy; |
20 | 23 |
|
21 | 24 | use self::error::{StreamNameValidationError, UsernameValidationError};
|
22 | 25 | use crate::hottier::MIN_STREAM_HOT_TIER_SIZE_BYTES;
|
@@ -67,21 +70,65 @@ pub fn stream_name(
|
67 | 70 | Ok(())
|
68 | 71 | }
|
69 | 72 |
|
70 |
| -// validate if username is valid |
71 |
| -// username should be between 3 and 64 characters long |
72 |
| -// username should contain only alphanumeric characters or underscores |
73 |
| -// username should be lowercase |
74 |
| -pub fn user_name(username: &str) -> Result<(), UsernameValidationError> { |
75 |
| - // Check if the username meets the required criteria |
76 |
| - if username.len() < 3 || username.len() > 64 { |
| 73 | +static RESERVED_NAMES: Lazy<HashSet<&'static str>> = Lazy::new(|| { |
| 74 | + [ |
| 75 | + "admin", |
| 76 | + "user", |
| 77 | + "role", |
| 78 | + "null", |
| 79 | + "undefined", |
| 80 | + "none", |
| 81 | + "empty", |
| 82 | + "password", |
| 83 | + "username", |
| 84 | + ] |
| 85 | + .into_iter() |
| 86 | + .collect() |
| 87 | +}); |
| 88 | + |
| 89 | +pub fn user_role_name(name: &str) -> Result<(), UsernameValidationError> { |
| 90 | + // Normalize username to lowercase for validation |
| 91 | + let name = name.to_lowercase(); |
| 92 | + |
| 93 | + // Check length constraints |
| 94 | + if name.len() < 3 || name.len() > 64 { |
77 | 95 | return Err(UsernameValidationError::InvalidLength);
|
78 | 96 | }
|
79 |
| - // Username should contain only alphanumeric characters or underscores |
80 |
| - if !username |
81 |
| - .chars() |
82 |
| - .all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '_') |
| 97 | + |
| 98 | + // Check if name is reserved |
| 99 | + if RESERVED_NAMES.contains(name.as_str()) { |
| 100 | + return Err(UsernameValidationError::ReservedName); |
| 101 | + } |
| 102 | + |
| 103 | + let chars: Vec<char> = name.chars().collect(); |
| 104 | + |
| 105 | + // Check last character (must be alphanumeric) |
| 106 | + if let Some(last_char) = chars.last() |
| 107 | + && !last_char.is_ascii_alphanumeric() |
83 | 108 | {
|
84 |
| - return Err(UsernameValidationError::SpecialChar); |
| 109 | + return Err(UsernameValidationError::InvalidEndChar); |
| 110 | + } |
| 111 | + |
| 112 | + // Check all characters and consecutive special chars |
| 113 | + let mut prev_was_special = false; |
| 114 | + for &ch in &chars { |
| 115 | + match ch { |
| 116 | + // Allow alphanumeric |
| 117 | + c if c.is_ascii_alphanumeric() => { |
| 118 | + prev_was_special = false; |
| 119 | + } |
| 120 | + // Allow specific special characters |
| 121 | + '_' | '-' | '.' => { |
| 122 | + if prev_was_special { |
| 123 | + return Err(UsernameValidationError::ConsecutiveSpecialChars); |
| 124 | + } |
| 125 | + prev_was_special = true; |
| 126 | + } |
| 127 | + // Reject any other character |
| 128 | + _ => { |
| 129 | + return Err(UsernameValidationError::InvalidCharacter); |
| 130 | + } |
| 131 | + } |
85 | 132 | }
|
86 | 133 |
|
87 | 134 | Ok(())
|
@@ -138,9 +185,21 @@ pub mod error {
|
138 | 185 | #[error("Username should be between 3 and 64 chars long")]
|
139 | 186 | InvalidLength,
|
140 | 187 | #[error(
|
141 |
| - "Username contains invalid characters. Please use lowercase, alphanumeric or underscore" |
| 188 | + "Username contains invalid characters. Only alphanumeric characters and special characters (underscore, hyphen and dot) are allowed" |
142 | 189 | )]
|
143 | 190 | SpecialChar,
|
| 191 | + #[error("Username should start with an alphanumeric character")] |
| 192 | + InvalidStartChar, |
| 193 | + #[error("Username should end with an alphanumeric character")] |
| 194 | + InvalidEndChar, |
| 195 | + #[error( |
| 196 | + "Username contains invalid characters. Only alphanumeric characters and special characters (underscore, hyphen and dot) are allowed" |
| 197 | + )] |
| 198 | + InvalidCharacter, |
| 199 | + #[error("Username contains consecutive special characters")] |
| 200 | + ConsecutiveSpecialChars, |
| 201 | + #[error("Username is reserved")] |
| 202 | + ReservedName, |
144 | 203 | }
|
145 | 204 |
|
146 | 205 | #[derive(Debug, thiserror::Error)]
|
|
0 commit comments