From 80a41fc1a2a290f2a7451b37af31bbdc27a2dc53 Mon Sep 17 00:00:00 2001 From: Dietrich Date: Fri, 27 Nov 2020 17:27:39 +0100 Subject: [PATCH] updating group membership on user deletion until now groups had members without useraccount after the account has been deleted. fixes: #5 --- README.md | 14 ++--- src/group/mod.rs | 5 ++ src/userlib/mod.rs | 109 ++++++++++++++++++++++++++++---------- tests/delete_user_test.rs | 13 ++++- 4 files changed, 104 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 7f7ad73..96587d3 100644 --- a/README.md +++ b/README.md @@ -6,17 +6,18 @@ When done this library intends to provide all the functionality needed to manage users on a linux system. What is working so far: - * Parsing: - * `/etc/passwd` - * `/etc/shadow` (root permission needed) - * `/etc/group` + * [x] Parsing: + * [x] `/etc/passwd` + * [x] `/etc/shadow` (root permission needed) + * [x] `/etc/group` + * Modifying: * delete a user * [x] passwd * [x] shadow * [X] group - * [x] own group - * [ ] member + * [x] own primary group + * [x] membership in other groups * [x] home dir * [x] delete * [x] keep @@ -32,7 +33,6 @@ What is working so far: - [x] shadow - [ ] group - [ ] own group - - [ ] member - [ ] home dir - [ ] create from skeleton - [ ] Skip diff --git a/src/group/mod.rs b/src/group/mod.rs index fc0a1a1..551ece4 100644 --- a/src/group/mod.rs +++ b/src/group/mod.rs @@ -80,6 +80,11 @@ impl Inner { }, }) } + + pub(super) fn remove_member(&mut self, kind: MembershipKind, username: &str) { + self.members + .retain(|u| !(u.username.username == username && u.kind == kind)) + } } use crate::api::GroupRead; diff --git a/src/userlib/mod.rs b/src/userlib/mod.rs index a53a7fe..516803e 100644 --- a/src/userlib/mod.rs +++ b/src/userlib/mod.rs @@ -7,6 +7,7 @@ use crate::{ api::{ CreateUserArgs, DeleteHome, DeleteUserArgs, GroupRead, UserDBRead, UserDBWrite, UserRead, }, + group::MembershipKind, UserLibError, }; #[allow(unused_imports)] @@ -132,6 +133,25 @@ impl UserDBLocal { } } + fn write_groups(&self, locked_g: &mut files::LockedFileGuard) -> Result<(), UserLibError> { + let content = self + .groups + .iter() + .map(|g| (g.borrow().to_string())) + .collect::>() + .join("\n"); + let replace_result = locked_g.replace_contents(content); + match replace_result { + Ok(_) => Ok(()), + Err(e) => Err(format!( + "Error during write to the database. \ + Please doublecheck as the groupdatabase could be corrupted: {}", + e, + ) + .into()), + } + } + fn delete_home(user: &crate::User) -> std::io::Result<()> { if let Some(dir) = user.get_home_dir() { std::fs::remove_dir_all(dir) @@ -145,13 +165,9 @@ impl UserDBLocal { } } - fn get_group_pos_by_id(&self, id: u32) -> Option<(&crate::Group, usize)> { - for (i, group) in self.groups.iter().enumerate() { - if group.borrow().get_gid()? == id { - return Some((group, i)); - } - } - None + fn delete_group_by_id(&mut self, gid: u32) { + self.groups + .retain(|g| g.borrow().get_gid().expect("groups have to have a gid") != gid); } } @@ -194,28 +210,65 @@ impl UserDBWrite for UserDBLocal { if args.delete_home == DeleteHome::Delete { Self::delete_home(user)?; } - let group = self.get_group_pos_by_id(user.get_gid()); - if let Some((group, id)) = group { - if group - .borrow() - .get_member_names() - .expect("groups have to have members") - .len() - == 1 - { - Self::delete_from_group(group, &group_file_content, &mut locked_g)?; - let _gres = self.groups.remove(id); - } else { - warn!( - "The primary group {} was not empty and is thus not removed.", - group.borrow().get_groupname().unwrap() - ); + println!("The users groups: {:#?}", user.get_groups()); + // Iterate over the GIDs to avoid borrowing issues + let users_groups: Vec<(MembershipKind, u32)> = user + .get_groups() + .iter() + .map(|(k, g)| (*k, g.borrow().get_gid().unwrap())) + .collect(); + for (kind, group) in users_groups { + println!("Woring on group: {:?} - {}", kind, group); + match kind { + crate::group::MembershipKind::Primary => { + if self + .get_group_by_id(group) + .expect("The group does not exist") + .borrow() + .get_member_names() + .expect("this group allways has a member") + .len() + == 1 + { + println!( + "Deleting group as the user to be deleted is the only member {:?}", self + .get_group_by_id(group) + .expect("The group does not exist") + .borrow() + .get_member_names() + ); + Self::delete_from_group( + self.get_group_by_id(group) + .expect("The group does not exist"), + &group_file_content, + &mut locked_g, + )?; + let _gres = self.delete_group_by_id(group); + } else { + println!("Do not delete the group as the user to be deleted is not the only member"); + // remove the from the group instead of deleting the group if he was not the only user in its primary group. + if let Some(group) = self.get_group_by_id(group) { + group + .borrow_mut() + .remove_member(MembershipKind::Primary, args.username) + }; + self.write_groups(&mut locked_g)?; + warn!( + "The primary group (GID: {}) was not empty and is thus not removed. Only the membership has been removed", + group + ); + } + } + crate::group::MembershipKind::Member => { + println!("delete the membership in the group"); + if let Some(group) = self.get_group_by_id(group) { + group + .borrow_mut() + .remove_member(MembershipKind::Member, args.username) + }; + self.write_groups(&mut locked_g)?; + } } - } else { - warn!( - "The users primary group could not be found {}", - user.get_gid() - ) } // Remove the user from the memory database(HashMap) let res = self.users.remove(args.username); diff --git a/tests/delete_user_test.rs b/tests/delete_user_test.rs index 31621ec..0684a07 100644 --- a/tests/delete_user_test.rs +++ b/tests/delete_user_test.rs @@ -18,7 +18,7 @@ fn test_delete_user_function() { let mf = umanux::Files { passwd: Some(p.path.clone()), shadow: Some(s.path), - group: Some(g.path), + group: Some(g.path.clone()), }; let mut db = umanux::UserDBLocal::load_files(mf).unwrap(); @@ -34,11 +34,20 @@ fn test_delete_user_function() { assert_eq!(user_res.unwrap().get_username().unwrap(), "teste"); let pflines = pf.lines(); let pflines2 = pf2.lines(); - for (l1, l2) in pflines.zip(pflines2) { + for (l1, l2) in pflines.zip(pflines2.clone()) { if l1 != l2 { assert!(l1.starts_with("teste")); assert!(l2.starts_with("bergfried")); break; } } + for line in pflines2 { + assert!(!line.starts_with("teste")) + } + let gf2 = fs::read_to_string(&g.path).unwrap(); + let gflines2 = gf2.lines(); + for line in gflines2 { + println!("{}", &line); + assert!(!line.ends_with("teste")) + } }