Enum to discriminate primary and reg. memberships
This commit is contained in:
parent
93e958108b
commit
e9fbbd91ce
@ -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
|
||||
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
|
Loading…
Reference in New Issue
Block a user