diff --git a/Cargo.lock b/Cargo.lock index d3b6965..6380ee8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,3 +3,55 @@ [[package]] name = "adduser" version = "0.1.0" +dependencies = [ + "lazy_static", + "regex", +] + +[[package]] +name = "aho-corasick" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "043164d8ba5c4c3035fec9bbee8647c0261d788f3474306f93bb65901cae0e86" +dependencies = [ + "memchr", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "memchr" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" + +[[package]] +name = "regex" +version = "1.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", + "thread_local", +] + +[[package]] +name = "regex-syntax" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8" + +[[package]] +name = "thread_local" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" +dependencies = [ + "lazy_static", +] diff --git a/Cargo.toml b/Cargo.toml index 4882398..4188fd3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,3 +7,5 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +regex = "1" +lazy_static = "1.4" diff --git a/src/lib.rs b/src/lib.rs index bf8894b..e942693 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,6 @@ +#[macro_use] +extern crate lazy_static; + pub mod passwd; pub mod userlib_error; pub use passwd::{Password, Username}; diff --git a/src/passwd.rs b/src/passwd.rs index 8f8f372..09742bd 100644 --- a/src/passwd.rs +++ b/src/passwd.rs @@ -7,6 +7,8 @@ )] #![allow(clippy::non_ascii_literal)] +use regex::Regex; + use crate::userlib_error::UserLibError; use std::cmp::Eq; use std::convert::TryFrom; @@ -259,7 +261,15 @@ impl Display for Username<'_> { impl<'a> TryFrom<&'a str> for Username<'a> { type Error = UserLibError; fn try_from(source: &'a str) -> std::result::Result { - Ok(Self { username: source }) + lazy_static! { + static ref USERVALIDATION: Regex = + Regex::new("^[a-z_]([a-z0-9_-]{0,31}|[a-z0-9_-]{0,30}\\$)$").unwrap(); + } + if USERVALIDATION.is_match(source) { + Ok(Self { username: source }) + } else { + Err(UserLibError::Message("Invalid username".into())) + } } } @@ -386,6 +396,37 @@ impl<'a> TryFrom<&'a str> for ShellPath<'a> { // Tests ---------------------------------------------------------------------- +#[test] +fn test_username_validation() { + // Failing tests + let umlauts = Username::try_from("täst"); // umlauts + assert_eq!( + Err(UserLibError::Message("Invalid username".into())), + umlauts + ); + let number_first = Username::try_from("11elf"); // numbers first + assert_eq!( + Err(UserLibError::Message("Invalid username".into())), + number_first + ); + let slashes = Username::try_from("test/name"); // slashes in the name + assert_eq!( + Err(UserLibError::Message("Invalid username".into())), + slashes + ); + let long = Username::try_from("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); // maximum size 32 letters + assert_eq!(Err(UserLibError::Message("Invalid username".into())), long); + // Working tests + let single = Username::try_from("t"); // single characters are ok + assert_eq!(single.unwrap().username, "t"); + let normal = Username::try_from("superman"); // regular username + assert_eq!(normal.unwrap().username, "superman"); + let normal = Username::try_from("anna3pete"); // regular username containing a number + assert_eq!(normal.unwrap().username, "anna3pete"); + let normal = Username::try_from("enya$"); // regular username ending in a $ + assert_eq!(normal.unwrap().username, "enya$"); +} + #[test] fn test_default_user() { // Check if a user can be created.