Enum to discriminate primary and reg. memberships

This commit is contained in:
Dietrich 2020-11-24 19:20:48 +01:00
parent 93e958108b
commit e9fbbd91ce
Signed by: dietrich
GPG Key ID: 9F3C20C0F85DF67C
5 changed files with 114 additions and 43 deletions

View File

@ -9,7 +9,7 @@ What is working so far:
* Parsing:
* `/etc/passwd`
* `/etc/shadow` (root permission needed)
* `/etc/group` (not yet really assigned to the users)
* `/etc/group`
* Modifying:
* delete a user
* [x] passwd

View File

@ -1,4 +1,5 @@
extern crate umanux;
use umanux::api::GroupRead;
use umanux::api::UserDBRead;
fn main() {
@ -13,5 +14,23 @@ fn main() {
for u in db.get_all_users() {
println!("{}", u);
println!(
"Groups: {:?}",
u.get_groups()
.iter()
.map(|group| {
(
format!("{:?}", group.0),
group.1.borrow().get_groupname().unwrap().to_owned(),
)
})
.collect::<Vec<(String, String)>>()
);
}
for group in db.get_all_groups() {
let gp = group.borrow();
println!("{}", gp);
println!("{:?}", gp.get_member_names())
}
}

View File

@ -4,16 +4,22 @@ use crate::userlib::NewFromString;
use log::warn;
use crate::UserLibError;
use std::convert::TryFrom;
use std::fmt::{self, Debug, Display};
use std::{cell::RefCell, convert::TryFrom};
use std::{cmp::Eq, rc::Rc};
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum Membership {
pub enum MembershipKind {
Primary,
Member,
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct Membership {
kind: MembershipKind,
username: crate::Username,
}
#[derive(Debug, PartialEq, Eq)]
pub struct Groupname {
groupname: String,
@ -44,7 +50,7 @@ pub(crate) fn is_groupname_valid(name: &str) -> bool {
crate::user::passwd_fields::is_username_valid(name)
}
pub type Group = Rc<Inner>;
pub type Group = Rc<RefCell<Inner>>;
/// A record(line) in the user database `/etc/shadow` found in most linux systems.
#[derive(Debug, PartialEq, Eq)]
pub struct Inner {
@ -53,7 +59,7 @@ pub struct Inner {
groupname: Groupname, /* Username. */
pub(crate) password: crate::Password, /* Usually not used (disabled with x) */
gid: crate::Gid, /* Group ID. */
members: Vec<crate::Username>, /* Real name. */
members: Vec<Membership>, /* Real name. */
}
impl Inner {
@ -65,6 +71,15 @@ impl Inner {
.collect::<Vec<&str>>()
.join("\n")
}
pub(super) fn append_user(&mut self, username: &str) {
self.members.push(Membership {
kind: MembershipKind::Primary,
username: crate::Username {
username: username.to_owned(),
},
})
}
}
use crate::api::GroupRead;
@ -77,7 +92,7 @@ impl GroupRead for Inner {
fn get_member_names(&self) -> Option<Vec<&str>> {
let mut r: Vec<&str> = Vec::new();
for u in &self.members {
r.push(&u.username);
r.push(&u.username.username);
}
Some(r)
}
@ -101,14 +116,18 @@ impl Display for Inner {
self.gid,
self.members
.iter()
.map(|mem| format!("{}", mem))
.filter_map(|mem| if mem.kind == MembershipKind::Member {
Some(format!("{}", mem.username))
} else {
None
})
.collect::<Vec<String>>()
.join(",")
)
}
}
impl NewFromString for Rc<Inner> {
impl NewFromString for Group {
/// Parse a line formatted like one in `/etc/group` and construct a matching [`Group`] instance
///
/// # Example
@ -119,7 +138,7 @@ impl NewFromString for Rc<Inner> {
/// "teste:x:1002:test,teste".to_owned(),
/// 0,
/// ).unwrap();
/// assert_eq!(grp.get_groupname().unwrap(), "teste");
/// assert_eq!(grp.borrow().get_groupname().unwrap(), "teste");
/// ```
///
/// # Errors
@ -127,14 +146,14 @@ impl NewFromString for Rc<Inner> {
fn new_from_string(line: String, position: u32) -> Result<Self, UserLibError> {
let elements: Vec<String> = line.split(':').map(ToString::to_string).collect();
if elements.len() == 4 {
Ok(Self::new(Inner {
Ok(Self::new(RefCell::new(Inner {
pos: position,
source: line,
groupname: Groupname::try_from(elements.get(0).unwrap().to_string())?,
password: crate::Password::Disabled,
gid: crate::Gid::try_from(elements.get(2).unwrap().to_string())?,
members: parse_members_list(elements.get(3).unwrap()),
}))
})))
} else {
Err(format!(
"Failed to parse: not enough elements ({}): {:?}",
@ -146,7 +165,7 @@ impl NewFromString for Rc<Inner> {
}
}
fn parse_members_list(source: &str) -> Vec<crate::Username> {
fn parse_members_list(source: &str) -> Vec<Membership> {
let mut res = vec![];
for mem in source.split(',').filter_map(|x| {
if x.is_empty() {
@ -155,7 +174,10 @@ fn parse_members_list(source: &str) -> Vec<crate::Username> {
Some(x.to_string())
}
}) {
res.push(crate::Username::try_from(mem).expect("failed to parse username"));
res.push(Membership {
kind: MembershipKind::Member,
username: crate::Username::try_from(mem).expect("failed to parse username"),
});
}
res
}
@ -164,18 +186,18 @@ fn parse_members_list(source: &str) -> Vec<crate::Username> {
fn test_parse_and_back_identity() {
let line = "teste:x:1002:test,teste";
let line2: Group = Group::new_from_string(line.to_owned(), 0).unwrap();
assert_eq!(format!("{}", line2), line);
assert_eq!(format!("{}", line2.borrow()), line);
}
#[test]
fn test_groupname() {
let line = "teste:x:1002:test,teste";
let line2 = Group::new_from_string(line.to_owned(), 0).unwrap();
assert_eq!(line2.get_groupname().unwrap(), "teste");
assert_eq!(line2.borrow().get_groupname().unwrap(), "teste");
}
#[test]
fn test_root_group() {
let line = "root:x:0:";
let line2 = Group::new_from_string(line.to_owned(), 0).unwrap();
assert_eq!(line2.get_groupname().unwrap(), "root");
assert_eq!(line2.borrow().get_groupname().unwrap(), "root");
}

View File

@ -21,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<(crate::group::Membership, crate::Group)>,
groups: Vec<(crate::group::MembershipKind, crate::Group)>,
}
impl User {
@ -70,7 +70,7 @@ impl User {
pub fn add_group(
&mut self,
group_type: crate::group::Membership,
group_type: crate::group::MembershipKind,
group: crate::Group,
) -> &mut Self {
self.groups.push((group_type, group));
@ -78,7 +78,7 @@ impl User {
}
#[must_use]
pub const fn get_groups(&self) -> &Vec<(crate::group::Membership, crate::Group)> {
pub const fn get_groups(&self) -> &Vec<(crate::group::MembershipKind, crate::Group)> {
&self.groups
}
}

View File

@ -34,9 +34,9 @@ impl UserDBLocal {
) -> Self {
let shadow_entries: Vec<crate::Shadow> = string_to(shadow_content);
let mut users = user_vec_to_hashmap(string_to(passwd_content));
let groups = string_to(group_content);
let mut groups = string_to(group_content);
shadow_to_users(&mut users, shadow_entries);
groups_to_users(&mut users, &groups);
groups_to_users(&mut users, &mut groups);
Self {
source_files: files::Files {
passwd: None,
@ -65,9 +65,9 @@ impl UserDBLocal {
let mut users = user_vec_to_hashmap(string_to(&my_passwd_lines));
let passwds: Vec<crate::Shadow> = string_to(&my_shadow_lines);
let groups: Vec<crate::Group> = string_to(&my_group_lines);
let mut groups: Vec<crate::Group> = string_to(&my_group_lines);
shadow_to_users(&mut users, passwds);
groups_to_users(&mut users, &groups);
groups_to_users(&mut users, &mut groups);
Ok(Self {
source_files: files,
users,
@ -119,7 +119,7 @@ impl UserDBLocal {
group_file_content: &str,
locked_g: &mut files::LockedFileGuard,
) -> Result<(), UserLibError> {
let modified_g = group.remove_in(group_file_content);
let modified_g = group.borrow().remove_in(group_file_content);
let replace_result = locked_g.replace_contents(modified_g);
match replace_result {
Ok(_) => Ok(()),
@ -147,7 +147,7 @@ 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.get_gid()? == id {
if group.borrow().get_gid()? == id {
return Some((group, i));
}
}
@ -197,6 +197,7 @@ impl UserDBWrite for UserDBLocal {
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()
@ -207,7 +208,7 @@ impl UserDBWrite for UserDBLocal {
} else {
warn!(
"The primary group {} was not empty and is thus not removed.",
group.get_groupname().unwrap()
group.borrow().get_groupname().unwrap()
);
}
} else {
@ -293,7 +294,7 @@ impl UserDBRead for UserDBLocal {
fn get_group_by_name(&self, name: &str) -> Option<&crate::Group> {
for group in &self.groups {
if group.get_groupname()? == name {
if group.borrow().get_groupname()? == name {
return Some(group);
}
}
@ -302,7 +303,7 @@ impl UserDBRead for UserDBLocal {
fn get_group_by_id(&self, id: u32) -> Option<&crate::Group> {
for group in &self.groups {
if group.get_gid()? == id {
if group.borrow().get_gid()? == id {
return Some(group);
}
}
@ -326,7 +327,9 @@ impl UserDBValidation for UserDBLocal {
fn is_gid_valid_and_free(&self, gid: u32) -> bool {
warn!("No valid check, only free check");
self.groups.iter().all(|x| x.get_gid().unwrap() != gid)
self.groups
.iter()
.all(|x| x.borrow().get_gid().unwrap() != gid)
}
fn is_groupname_valid_and_free(&self, name: &str) -> bool {
@ -334,7 +337,7 @@ impl UserDBValidation for UserDBLocal {
let free = self
.groups
.iter()
.all(|x| x.get_groupname().unwrap() != name);
.all(|x| x.borrow().get_groupname().unwrap() != name);
valid && free
}
}
@ -350,28 +353,39 @@ fn file_to_string(file: &File) -> Result<String, crate::UserLibError> {
}
}
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() {
fn groups_to_users<'a>(
users: &'a mut UserList,
groups: &'a mut [crate::Group],
) -> &'a mut UserList {
// Populate the regular groups
for group in groups.iter() {
match group.borrow().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());
user.add_group(crate::group::MembershipKind::Member, group.clone());
}
}
}
None => continue,
}
}
// Populate the primary membership
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)
.filter(|g| g.borrow().get_gid().unwrap() == gid)
.collect();
if grouplist.len() == 1 {
let group = *grouplist.first().unwrap();
user.add_group(crate::group::Membership::Primary, group.clone());
group.borrow_mut().append_user(
user.get_username()
.expect("Users without username are not supported"),
);
user.add_group(crate::group::MembershipKind::Primary, group.clone());
} else {
error!(
"Somehow the group with gid {} was found {} times",
@ -457,10 +471,10 @@ fn test_creator_user_db_local() {
let (member_group1, group1) = user.get_groups().first().unwrap();
let (member_group2, group2) = user.get_groups().get(1).unwrap();
assert_eq!(*member_group1, crate::group::Membership::Member);
assert_eq!(group1.get_groupname(), Some("another"));
assert_eq!(*member_group2, crate::group::Membership::Primary);
assert_eq!(group2.get_groupname(), Some("teste"));
assert_eq!(*member_group1, crate::group::MembershipKind::Member);
assert_eq!(group1.borrow().get_groupname(), Some("another"));
assert_eq!(*member_group2, crate::group::MembershipKind::Primary);
assert_eq!(group2.borrow().get_groupname(), Some("teste"));
}
}
@ -473,7 +487,15 @@ fn test_parsing_local_database() {
let my_passwd_lines = file_to_string(&pwdfile).unwrap();
let my_group_lines = file_to_string(&grpfile).unwrap();
let data = UserDBLocal::import_from_strings(&my_passwd_lines, "", &my_group_lines);
assert_eq!(data.groups.get(0).unwrap().get_groupname().unwrap(), "root");
assert_eq!(
data.groups
.get(0)
.unwrap()
.borrow()
.get_groupname()
.unwrap(),
"root"
);
}
#[test]
@ -495,11 +517,19 @@ fn test_user_db_read_implementation() {
assert!(data.get_all_groups().len() > 10);
assert!(data.get_group_by_name("root").is_some());
assert_eq!(
data.get_group_by_name("root").unwrap().get_gid().unwrap(),
data.get_group_by_name("root")
.unwrap()
.borrow()
.get_gid()
.unwrap(),
0
);
assert_eq!(
data.get_group_by_id(0).unwrap().get_groupname().unwrap(),
data.get_group_by_id(0)
.unwrap()
.borrow()
.get_groupname()
.unwrap(),
"root"
);
assert!(data.get_user_by_name("norealnameforsure").is_none());