diff --git a/src/group/mod.rs b/src/group/mod.rs index 8be57fe..2fec6c4 100644 --- a/src/group/mod.rs +++ b/src/group/mod.rs @@ -8,6 +8,12 @@ use std::convert::TryFrom; use std::fmt::{self, Debug, Display}; use std::{cmp::Eq, rc::Rc}; +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum Membership { + Primary, + Member, +} + #[derive(Debug, PartialEq, Eq)] pub struct Groupname { groupname: String, diff --git a/src/user/mod.rs b/src/user/mod.rs index 0b3285c..2d490f1 100644 --- a/src/user/mod.rs +++ b/src/user/mod.rs @@ -9,12 +9,6 @@ use log::{debug, error, info, trace, warn}; use std::convert::TryFrom; use std::fmt::{self, Display}; -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub enum GroupMembership { - Primary, - Member, -} - /// A record(line) in the user database `/etc/passwd` found in most linux systems. #[derive(Debug, PartialEq, Eq, Clone)] pub struct User { @@ -27,7 +21,7 @@ pub struct User { gecos: crate::Gecos, /* Real name. */ home_dir: crate::HomeDir, /* Home directory. */ shell_path: crate::ShellPath, /* Shell program. */ - groups: Vec<(GroupMembership, crate::Group)>, + groups: Vec<(crate::group::Membership, crate::Group)>, } impl User { @@ -73,6 +67,20 @@ impl User { self.shell_path = crate::ShellPath { shell: path }; self } + + pub fn add_group( + &mut self, + group_type: crate::group::Membership, + group: crate::Group, + ) -> &mut Self { + self.groups.push((group_type, group)); + self + } + + #[must_use] + pub const fn get_groups(&self) -> &Vec<(crate::group::Membership, crate::Group)> { + &self.groups + } } impl NewFromString for User { diff --git a/src/user/shadow_fields.rs b/src/user/shadow_fields.rs index 1df9459..f075425 100644 --- a/src/user/shadow_fields.rs +++ b/src/user/shadow_fields.rs @@ -90,7 +90,7 @@ impl NewFromString for Shadow { /// ``` /// use umanux::NewFromString; /// let shad = umanux::Shadow::new_from_string( - /// "test:!!$6$/RotIe4VZzzAun4W$7YUONvru1rDnllN5TvrnOMsWUD5wSDUPAD6t6/Xwsr/0QOuWF3HcfAhypRkGa8G1B9qqWV5kZSnCb8GKMN9N61:18260:0:99999:7:::".to_string(), + /// "test:$6$u0Hh.9WKRF1Aeu4g$XqoDyL6Re/4ZLNQCGAXlNacxCxbdigexEqzFzkOVPV5Z1H23hlenjW8ZLgq6GQtFURYwenIFpo1c.r4aW9l5S/:18260:0:99999:7:::".to_string(), /// 0, /// ).unwrap(); /// assert_eq!(shad.get_username(), "test"); @@ -152,7 +152,7 @@ fn duration_for_days(days_source: &str) -> Option { #[test] fn test_parse_and_back_identity() { - let line = "test:!!$6$/RotIe4VZzzAun4W$7YUONvru1rDnllN5TvrnOMsWUD5wSDUPAD6t6/Xwsr/0QOuWF3HcfAhypRkGa8G1B9qqWV5kZSnCb8GKMN9N61:18260:0:99999:7:::"; + let line = "test:$6$u0Hh.9WKRF1Aeu4g$XqoDyL6Re/4ZLNQCGAXlNacxCxbdigexEqzFzkOVPV5Z1H23hlenjW8ZLgq6GQtFURYwenIFpo1c.r4aW9l5S/:18260:0:99999:7:::"; let line2 = Shadow::new_from_string(line.to_owned(), 0).unwrap(); assert_eq!(format!("{}", line2), line); } diff --git a/src/userlib/mod.rs b/src/userlib/mod.rs index 4764654..6349faa 100644 --- a/src/userlib/mod.rs +++ b/src/userlib/mod.rs @@ -15,10 +15,12 @@ use std::collections::HashMap; use std::fs::File; use std::io::{BufReader, Read}; +pub type UserList = HashMap; + pub struct UserDBLocal { source_files: files::Files, source_hashes: hashes::Hashes, // to detect changes - pub users: HashMap, + pub users: UserList, pub groups: Vec, } @@ -64,6 +66,7 @@ impl UserDBLocal { let passwds: Vec = string_to(&my_shadow_lines); let groups: Vec = string_to(&my_group_lines); shadow_to_users(&mut users, passwds); + groups_to_users(&mut users, &groups); Ok(Self { source_files: files, users, @@ -346,11 +349,41 @@ fn file_to_string(file: &File) -> Result { } } +fn groups_to_users<'a>(users: &'a mut UserList, groups: &'a [crate::Group]) -> &'a mut UserList { + for group in groups { + match group.get_member_names() { + Some(usernames) => { + for username in usernames { + if let Some(user) = users.get_mut(username) { + user.add_group(crate::group::Membership::Member, group.clone()); + } + } + } + None => continue, + } + } + for user in users.values_mut() { + let gid = user.get_gid(); + let grouplist: Vec<&crate::Group> = groups + .iter() + .filter(|g| g.get_gid().unwrap() == gid) + .collect(); + if grouplist.len() == 1 { + let group = *grouplist.first().unwrap(); + user.add_group(crate::group::Membership::Primary, group.clone()); + } else { + error!( + "Somehow the group with gid {} was found {} times", + gid, + grouplist.len() + ); + } + } + users +} + /// Merge the Shadow passwords into the users -fn shadow_to_users( - users: &mut HashMap, - shadow: Vec, -) -> &mut HashMap { +fn shadow_to_users(users: &mut UserList, shadow: Vec) -> &mut UserList { for pass in shadow { let user = users .get_mut(pass.get_username()) @@ -360,8 +393,8 @@ fn shadow_to_users( users } -/// Convert a `Vec` to a `HashMap` where the username is used as key -fn user_vec_to_hashmap(users: Vec) -> HashMap { +/// Convert a `Vec` to a `UserList` (`HashMap`) where the username is used as key +fn user_vec_to_hashmap(users: Vec) -> UserList { users .into_iter() .map(|x| { @@ -413,11 +446,17 @@ where #[test] fn test_creator_user_db_local() { - 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"); + 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$u0Hh.9WKRF1Aeu4g$XqoDyL6Re/4ZLNQCGAXlNacxCxbdigexEqzFzkOVPV5Z1H23hlenjW8ZLgq6GQtFURYwenIFpo1c.r4aW9l5S/:18260:0:99999:7:::", "teste:x:1002:test,test"); assert_eq!( data.users.get("test").unwrap().get_username().unwrap(), "test" - ) + ); + for user in data.users.values() { + if let Some((member, group)) = user.get_groups().first() { + assert_eq!(*member, crate::group::Membership::Member); + assert_eq!(group.get_groupname(), Some("teste")); + } + } } #[test] @@ -465,7 +504,7 @@ fn test_user_db_read_implementation() { #[test] fn test_user_db_write_implementation() { use crate::api::DeleteUserArgs; - let mut 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"); + let mut data = UserDBLocal::import_from_strings("test:x:1001:1001:full Name,004,000342,001-2312,myemail@test.com:/home/test:/bin/test", "test:$6$u0Hh.9WKRF1Aeu4g$XqoDyL6Re/4ZLNQCGAXlNacxCxbdigexEqzFzkOVPV5Z1H23hlenjW8ZLgq6GQtFURYwenIFpo1c.r4aW9l5S/:18260:0:99999:7:::", "teste:x:1002:test,test"); let user = "test"; assert_eq!(data.get_all_users().len(), 1);