diff --git a/src/user/mod.rs b/src/user/mod.rs index 7b0c8b8..5e1fb31 100644 --- a/src/user/mod.rs +++ b/src/user/mod.rs @@ -10,13 +10,13 @@ use std::fmt::{self, Display}; #[derive(Debug, PartialEq, Eq)] pub struct User { source: String, - username: crate::Username, /* Username. */ - password: crate::Password, /* Hashed passphrase, if shadow database not in use (see shadow.h). */ - uid: crate::Uid, /* User ID. */ - gid: crate::Gid, /* Group ID. */ - gecos: crate::Gecos, /* Real name. */ - home_dir: crate::HomeDir, /* Home directory. */ - shell_path: crate::ShellPath, /* Shell program. */ + username: crate::Username, /* Username. */ + pub(crate) password: crate::Password, /* Hashed passphrase, if shadow database not in use (see shadow.h). */ + uid: crate::Uid, /* User ID. */ + gid: crate::Gid, /* Group ID. */ + gecos: crate::Gecos, /* Real name. */ + home_dir: crate::HomeDir, /* Home directory. */ + shell_path: crate::ShellPath, /* Shell program. */ } impl NewFromString for User { diff --git a/src/userlib.rs b/src/userlib.rs index 82c3d24..61ca443 100644 --- a/src/userlib.rs +++ b/src/userlib.rs @@ -8,14 +8,14 @@ #![allow(clippy::non_ascii_literal)] use log::warn; +use std::collections::HashMap; use std::fs::File; use std::io::{BufReader, Read}; use std::path::PathBuf; pub struct UserDBLocal { source_files: Files, - pub passwd_entries: Vec, - pub shadow_entries: Vec, + pub users: HashMap, pub group_entries: Vec, } @@ -42,21 +42,30 @@ impl UserDBLocal { shadow_content: &str, group_content: &str, ) -> Self { - let res = Self { + let shadow_entries: Vec = shadow_content + .lines() + .filter_map(|line| { + if line.len() > 5 { + Some(crate::Shadow::new_from_string(line.to_owned()).expect("Parsing failed")) + } else { + None + } + }) + .collect(); + let mut res = Self { source_files: Files { passwd: None, group: None, shadow: None, }, - passwd_entries: passwd_content + users: passwd_content .lines() .filter_map(|line| { if line.len() > 5 { println!("{}", line); - Some( - crate::User::new_from_string(line.to_owned()) - .expect("failed to read lines"), - ) + let user = crate::User::new_from_string(line.to_owned()) + .expect("failed to read lines"); + Some((user.get_username().to_owned(), user)) } else { None } @@ -74,60 +83,65 @@ impl UserDBLocal { } }) .collect(), - shadow_entries: shadow_content - .lines() - .filter_map(|line| { - if line.len() > 5 { - Some( - crate::Shadow::new_from_string(line.to_owned()) - .expect("Parsing failed"), - ) - } else { - None - } - }) - .collect(), }; + for shadow in shadow_entries { + let user = res.users.get_mut(shadow.get_username()).expect(&format!( + "the user {} does not exist", + shadow.get_username() + )); + user.password = crate::Password::Shadow(shadow); + } res } #[must_use] pub fn load_files(files: Files) -> Self { - let passwd_file = File::open( - files - .group - .clone() - .expect("passwd file path cannot be None"), - ) - .unwrap(); - let mut passwd_reader = BufReader::new(passwd_file); - let mut my_passwd_lines = String::new(); - passwd_reader.read_to_string(&mut my_passwd_lines).unwrap(); - let group_file = - File::open(files.group.clone().expect("group file path cannot be None")).unwrap(); - let mut group_reader = BufReader::new(group_file); - let mut my_group_lines = String::new(); - group_reader.read_to_string(&mut my_group_lines).unwrap(); - let shadow_file = File::open( - files - .shadow - .clone() - .expect("shadow file path cannot be None"), - ) - .expect("Failed to read the shadow file. Most of the time root permissions are needed"); - let mut shadow_reader = BufReader::new(shadow_file); - let mut my_shadow_lines = String::new(); - shadow_reader.read_to_string(&mut my_shadow_lines).unwrap(); + let my_passwd_lines = file_to_string(files.passwd.as_ref()); + let my_group_lines = file_to_string(files.group.as_ref()); + let my_shadow_lines = file_to_string(files.shadow.as_ref()); + let mut users = user_vec_to_hashmap(string_to(&my_passwd_lines)); + let passwds: Vec = string_to(&my_shadow_lines); + shadow_to_users(&mut users, passwds); Self { source_files: files, - passwd_entries: string_to(&my_passwd_lines), + users, group_entries: string_to(&my_group_lines), - shadow_entries: string_to(&my_shadow_lines), } } } +fn file_to_string(path: Option<&PathBuf>) -> String { + let file = File::open(path.expect("Path cannot be None".into())) + .expect("Failed to read the file. Most of the time root permissions are needed".into()); + let mut reader = BufReader::new(file); + let mut lines = String::new(); + reader.read_to_string(&mut lines).unwrap(); + lines +} + +/// Merge the Shadow passwords into the users. +fn shadow_to_users( + users: &mut HashMap, + shadow: Vec, +) -> &mut HashMap { + for pass in shadow { + let user = users + .get_mut(pass.get_username()) + .expect(&format!("the user {} does not exist", pass.get_username())); + user.password = crate::Password::Shadow(pass); + } + users +} + +/// Convert a `Vec` to a `HashMap` where the username is used as key. +fn user_vec_to_hashmap(users: Vec) -> HashMap { + users + .into_iter() + .map(|x| (x.get_username().to_owned(), x)) + .collect() +} + /// Try to parse a String into some Object /// /// # Errors @@ -157,11 +171,8 @@ where #[test] fn test_creator_user_db_local() { - let data = UserDBLocal::import_from_strings("testuser:x:1001:1001:full Name,004,000342,001-2312,myemail@test.com:/home/test:/bin/test", "test:!!$6$/RotIe4VZzzAun4W$7YUONvru1rDnllN5TvrnOMsWUD5wSDUPAD6t6/Xwsr/0QOuWF3HcfAhypRkGa8G1B9qqWV5kZSnCb8GKMN9N61:18260:0:99999:7:::", "teste:x:1002:test,teste"); - assert_eq!( - data.passwd_entries.get(0).unwrap().get_username(), - "testuser" - ) + let data = UserDBLocal::import_from_strings("test:x:1001:1001:full Name,004,000342,001-2312,myemail@test.com:/home/test:/bin/test", "test:!!$6$/RotIe4VZzzAun4W$7YUONvru1rDnllN5TvrnOMsWUD5wSDUPAD6t6/Xwsr/0QOuWF3HcfAhypRkGa8G1B9qqWV5kZSnCb8GKMN9N61:18260:0:99999:7:::", "teste:x:1002:test,test"); + assert_eq!(data.users.get("test").unwrap().get_username(), "test") } #[test]