updating group membership on user deletion

until now groups had members without useraccount after the account has been deleted.

fixes: #5
This commit is contained in:
Dietrich 2020-11-27 17:27:39 +01:00
parent e9fbbd91ce
commit d03ed65f60
Signed by: dietrich
GPG Key ID: 9F3C20C0F85DF67C
4 changed files with 104 additions and 37 deletions

View File

@ -6,17 +6,18 @@
When done this library intends to provide all the functionality needed to manage users on a linux system. When done this library intends to provide all the functionality needed to manage users on a linux system.
What is working so far: What is working so far:
* Parsing: * [x] Parsing:
* `/etc/passwd` * [x] `/etc/passwd`
* `/etc/shadow` (root permission needed) * [x] `/etc/shadow` (root permission needed)
* `/etc/group` * [x] `/etc/group`
* Modifying: * Modifying:
* delete a user * delete a user
* [x] passwd * [x] passwd
* [x] shadow * [x] shadow
* [X] group * [X] group
* [x] own group * [x] own primary group
* [ ] member * [x] membership in other groups
* [x] home dir * [x] home dir
* [x] delete * [x] delete
* [x] keep * [x] keep
@ -32,7 +33,6 @@ What is working so far:
- [x] shadow - [x] shadow
- [ ] group - [ ] group
- [ ] own group - [ ] own group
- [ ] member
- [ ] home dir - [ ] home dir
- [ ] create from skeleton - [ ] create from skeleton
- [ ] Skip - [ ] Skip

View File

@ -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; use crate::api::GroupRead;

View File

@ -7,6 +7,7 @@ use crate::{
api::{ api::{
CreateUserArgs, DeleteHome, DeleteUserArgs, GroupRead, UserDBRead, UserDBWrite, UserRead, CreateUserArgs, DeleteHome, DeleteUserArgs, GroupRead, UserDBRead, UserDBWrite, UserRead,
}, },
group::MembershipKind,
UserLibError, UserLibError,
}; };
#[allow(unused_imports)] #[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::<Vec<String>>()
.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<()> { fn delete_home(user: &crate::User) -> std::io::Result<()> {
if let Some(dir) = user.get_home_dir() { if let Some(dir) = user.get_home_dir() {
std::fs::remove_dir_all(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)> { fn delete_group_by_id(&mut self, gid: u32) {
for (i, group) in self.groups.iter().enumerate() { self.groups
if group.borrow().get_gid()? == id { .retain(|g| g.borrow().get_gid().expect("groups have to have a gid") != gid);
return Some((group, i));
}
}
None
} }
} }
@ -194,28 +210,65 @@ impl UserDBWrite for UserDBLocal {
if args.delete_home == DeleteHome::Delete { if args.delete_home == DeleteHome::Delete {
Self::delete_home(user)?; Self::delete_home(user)?;
} }
let group = self.get_group_pos_by_id(user.get_gid()); println!("The users groups: {:#?}", user.get_groups());
if let Some((group, id)) = group { // Iterate over the GIDs to avoid borrowing issues
if group 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() .borrow()
.get_member_names() .get_member_names()
.expect("groups have to have members") .expect("this group allways has a member")
.len() .len()
== 1 == 1
{ {
Self::delete_from_group(group, &group_file_content, &mut locked_g)?; println!(
let _gres = self.groups.remove(id); "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 { } 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!( warn!(
"The primary group {} was not empty and is thus not removed.", "The primary group (GID: {}) was not empty and is thus not removed. Only the membership has been removed",
group.borrow().get_groupname().unwrap() group
); );
} }
} else { }
warn!( crate::group::MembershipKind::Member => {
"The users primary group could not be found {}", println!("delete the membership in the group");
user.get_gid() 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)?;
}
}
} }
// Remove the user from the memory database(HashMap) // Remove the user from the memory database(HashMap)
let res = self.users.remove(args.username); let res = self.users.remove(args.username);

View File

@ -18,7 +18,7 @@ fn test_delete_user_function() {
let mf = umanux::Files { let mf = umanux::Files {
passwd: Some(p.path.clone()), passwd: Some(p.path.clone()),
shadow: Some(s.path), shadow: Some(s.path),
group: Some(g.path), group: Some(g.path.clone()),
}; };
let mut db = umanux::UserDBLocal::load_files(mf).unwrap(); 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"); assert_eq!(user_res.unwrap().get_username().unwrap(), "teste");
let pflines = pf.lines(); let pflines = pf.lines();
let pflines2 = pf2.lines(); let pflines2 = pf2.lines();
for (l1, l2) in pflines.zip(pflines2) { for (l1, l2) in pflines.zip(pflines2.clone()) {
if l1 != l2 { if l1 != l2 {
assert!(l1.starts_with("teste")); assert!(l1.starts_with("teste"));
assert!(l2.starts_with("bergfried")); assert!(l2.starts_with("bergfried"));
break; 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"))
}
} }