From 70fcd823da305f7981d0f987e0159e5ebbe78ca9 Mon Sep 17 00:00:00 2001 From: Dietrich Date: Thu, 22 Oct 2020 12:40:30 +0200 Subject: [PATCH] adjust and implement more of the api --- src/api.rs | 56 +++++++++++++++++++------------------- src/group/mod.rs | 38 ++++++++++++++++---------- src/user/gecos_fields.rs | 6 ++--- src/user/mod.rs | 57 ++++++++++++++++++++++++++------------- src/user/passwd_fields.rs | 2 +- src/userlib.rs | 46 +++++++++++++++++++++++-------- 6 files changed, 130 insertions(+), 75 deletions(-) diff --git a/src/api.rs b/src/api.rs index ef8ea44..4442d94 100644 --- a/src/api.rs +++ b/src/api.rs @@ -22,40 +22,40 @@ pub trait UserDBWrite { } pub trait UserRead { - fn get_username(&self) -> Option; - fn get_uid(&self) -> Option; - fn get_gid(&self) -> Option; - fn get_password(&self) -> Option; - fn get_gecos(&self) -> Option; - fn get_home_dir(&self) -> Option; - fn get_shell_path(&self) -> Option; - fn get_full_name(&self) -> Option; - fn get_room(&self) -> Option; - fn get_phone_work(&self) -> Option; - fn get_phone_home(&self) -> Option; - fn get_other(&self) -> Option>; + fn get_username(&self) -> Option<&str>; + fn get_uid(&self) -> u32; + fn get_gid(&self) -> u32; + fn get_password(&self) -> Option<&str>; + fn get_gecos(&self) -> Option<&crate::Gecos>; + fn get_home_dir(&self) -> Option<&str>; + fn get_shell_path(&self) -> Option<&str>; + fn get_full_name(&self) -> Option<&str>; + fn get_room(&self) -> Option<&str>; + fn get_phone_work(&self) -> Option<&str>; + fn get_phone_home(&self) -> Option<&str>; + fn get_other(&self) -> Option<&Vec>; } pub trait UserWrite { - fn set_username(&self) -> Option; - fn set_uid(&self) -> Option; - fn set_gid(&self) -> Option; - fn set_password(&self) -> Option; - fn set_gecos(&self) -> Option; - fn set_home_dir(&self) -> Option; - fn set_shell_path(&self) -> Option; - fn set_full_name(&self) -> Option; - fn set_room(&self) -> Option; - fn set_phone_work(&self) -> Option; - fn set_phone_home(&self) -> Option; - fn set_other(&self) -> Option>; + fn set_username(&self, username: String); + fn set_uid(&self, uid: u32); + fn set_gid(&self, gid: u32); + fn set_password(&self, password: String); + fn set_gecos(&self, gecos: crate::Gecos); + fn set_home_dir(&self, home_dir: String); + fn set_shell_path(&self, shell_path: String); + fn set_full_name(&self, full_name: String); + fn set_room(&self, room: String); + fn set_phone_work(&self, phone_work: String); + fn set_phone_home(&self, phone_home: String); + fn set_other(&self, other: Option>); } pub trait GroupRead { - fn get_groupname(&self) -> Option; - fn get_encrypted_password(&self) -> Option; - fn get_gid(&self) -> Option; - fn get_members(&self) -> Option; + fn get_groupname(&self) -> Option<&str>; + fn get_encrypted_password(&self) -> Option<&str>; + fn get_gid(&self) -> Option; + fn get_member_names(&self) -> Option>; } pub trait GroupWrite { diff --git a/src/group/mod.rs b/src/group/mod.rs index de487e9..b8a9df6 100644 --- a/src/group/mod.rs +++ b/src/group/mod.rs @@ -9,7 +9,6 @@ use crate::userlib::NewFromString; use log::warn; -use regex::Regex; use crate::userlib_error::UserLibError; use std::cmp::Eq; @@ -58,18 +57,27 @@ pub struct Group { members: Vec, /* Real name. */ } -impl Group { +use crate::api::GroupRead; +impl GroupRead for Group { #[must_use] - pub fn get_groupname(&self) -> &str { - &self.groupname.groupname + fn get_groupname(&self) -> Option<&str> { + Some(&self.groupname.groupname) } #[must_use] - pub const fn get_members(&self) -> &Vec { - &self.members + fn get_member_names(&self) -> Option> { + let mut r: Vec<&str> = Vec::new(); + for u in self.members.iter() { + r.push(&u.username); + } + Some(r) } - pub fn get_gid(&self) -> u32 { - self.gid.get_gid() + fn get_gid(&self) -> Option { + Some(self.gid.get_gid()) + } + + fn get_encrypted_password(&self) -> Option<&str> { + todo!() } } @@ -91,14 +99,16 @@ impl Display for Group { } impl NewFromString for Group { - /// Parse a line formatted like one in `/etc/shadow` and construct a matching `Shadow` instance + /// Parse a line formatted like one in `/etc/group` and construct a matching [`Group`] instance /// /// # Example /// ``` - /// /*let shad = adduser::shadow::Shadow::new_from_string( - /// "test:!!$6$/RotIe4VZzzAun4W$7YUONvru1rDnllN5TvrnOMsWUD5wSDUPAD6t6/Xwsr/0QOuWF3HcfAhypRkGa8G1B9qqWV5kZSnCb8GKMN9N61:18260:0:99999:7:::" + /// use crate::adduser::api::GroupRead; + /// use adduser::NewFromString; + /// let grp = adduser::Group::new_from_string( + /// "teste:x:1002:test,teste".to_owned() /// ).unwrap(); - /// assert_eq!(shad.get_username(), "test");*/ + /// assert_eq!(grp.get_groupname().unwrap(), "teste"); /// ``` /// /// # Errors @@ -148,11 +158,11 @@ fn test_parse_and_back_identity() { fn test_groupname() { let line = "teste:x:1002:test,teste"; let line2 = Group::new_from_string(line.to_owned()).unwrap(); - assert_eq!(line2.get_groupname(), "teste"); + assert_eq!(line2.get_groupname().unwrap(), "teste"); } #[test] fn test_root_group() { let line = "root:x:0:"; let line2 = Group::new_from_string(line.to_owned()).unwrap(); - assert_eq!(line2.get_groupname(), "root"); + assert_eq!(line2.get_groupname().unwrap(), "root"); } diff --git a/src/user/gecos_fields.rs b/src/user/gecos_fields.rs index af2ee16..cb52765 100644 --- a/src/user/gecos_fields.rs +++ b/src/user/gecos_fields.rs @@ -22,9 +22,9 @@ pub enum Gecos { }, } -impl<'a> Gecos { +impl Gecos { #[must_use] - pub fn get_comment(&'a self) -> Option<&'a str> { + pub fn get_comment(&self) -> Option<&str> { match &self { Gecos::Simple { comment, .. } => Some(&comment), Gecos::Detail { .. } => None, @@ -70,7 +70,7 @@ impl<'a> Gecos { } } #[must_use] - pub fn get_phone_home(&'_ self) -> Option<&'_ str> { + pub fn get_phone_home(&self) -> Option<&str> { match &self { Gecos::Simple { .. } => None, Gecos::Detail { phone_home, .. } => { diff --git a/src/user/mod.rs b/src/user/mod.rs index 5e1fb31..b5a468b 100644 --- a/src/user/mod.rs +++ b/src/user/mod.rs @@ -20,18 +20,19 @@ pub struct User { } impl NewFromString for User { - /// Parse a line formatted like one in `/etc/passwd` and construct a matching [`adduser::User`] instance + /// Parse a line formatted like one in `/etc/passwd` and construct a matching [`User`] instance /// /// # Example /// ``` + /// use crate::adduser::api::UserRead; /// use adduser::NewFromString; /// let pwd = adduser::User::new_from_string( /// "testuser:testpassword:1001:1001:full Name,,,,:/home/test:/bin/test".to_string()).unwrap(); - /// assert_eq!(pwd.get_username(), "testuser"); + /// assert_eq!(pwd.get_username().unwrap(), "testuser"); /// ``` /// /// # Errors - /// When parsing fails this function returns a `UserLibError::Message` containing some information as to why the function failed. + /// When parsing fails this function returns a [`UserLibError::Message`](crate::userlib_error::UserLibError::Message) containing some information as to why the function failed. fn new_from_string(line: String) -> Result where Self: Sized, @@ -56,38 +57,58 @@ impl NewFromString for User { } } -impl User { +impl crate::api::UserRead for User { #[must_use] - pub fn get_username(&self) -> &str { - &self.username.username + fn get_username(&self) -> Option<&str> { + Some(&self.username.username) } #[must_use] - pub fn get_password(&self) -> &str { + fn get_password(&self) -> Option<&str> { match &self.password { - crate::Password::Encrypted(crate::EncryptedPassword { password }) => &password, - crate::Password::Shadow(crate::Shadow { ref password, .. }) => &password.password, - crate::Password::Disabled => &"x", + crate::Password::Encrypted(crate::EncryptedPassword { password }) => Some(&password), + crate::Password::Shadow(crate::Shadow { ref password, .. }) => Some(&password.password), + crate::Password::Disabled => None, } } #[must_use] - pub const fn get_uid(&self) -> u32 { + fn get_uid(&self) -> u32 { self.uid.uid } #[must_use] - pub const fn get_gid(&self) -> u32 { + fn get_gid(&self) -> u32 { self.gid.gid } #[must_use] - pub const fn get_comment(&self) -> &crate::Gecos { - &self.gecos + fn get_gecos(&self) -> Option<&crate::Gecos> { + Some(&self.gecos) } #[must_use] - pub fn get_home_dir(&self) -> &str { - &self.home_dir.dir + fn get_home_dir(&self) -> Option<&str> { + Some(&self.home_dir.dir) } #[must_use] - pub fn get_shell_path(&self) -> &str { - &self.shell_path.shell + fn get_shell_path(&self) -> Option<&str> { + Some(&self.shell_path.shell) + } + + fn get_full_name(&self) -> Option<&str> { + self.gecos.get_full_name() + } + + fn get_room(&self) -> Option<&str> { + self.gecos.get_room() + } + + fn get_phone_work(&self) -> Option<&str> { + self.gecos.get_phone_work() + } + + fn get_phone_home(&self) -> Option<&str> { + self.gecos.get_phone_home() + } + + fn get_other(&self) -> Option<&Vec> { + self.gecos.get_other() } } diff --git a/src/user/passwd_fields.rs b/src/user/passwd_fields.rs index 9029250..e975546 100644 --- a/src/user/passwd_fields.rs +++ b/src/user/passwd_fields.rs @@ -23,7 +23,7 @@ use std::fmt::{self, Display}; #[derive(Debug, PartialEq, Eq)] pub struct Username { /// The username value - pub(in crate::user) username: String, + pub(crate) username: String, } impl Display for Username { diff --git a/src/userlib.rs b/src/userlib.rs index 79c0b92..c179573 100644 --- a/src/userlib.rs +++ b/src/userlib.rs @@ -7,6 +7,8 @@ )] #![allow(clippy::non_ascii_literal)] +use crate::api::GroupRead; +use crate::api::UserRead; use log::warn; use std::collections::HashMap; use std::fs::File; @@ -103,7 +105,7 @@ impl UserDBRead for UserDBLocal { fn get_group_by_name(&self, name: &str) -> Option<&crate::Group> { for group in self.groups.iter() { - if group.get_groupname() == name { + if group.get_groupname().unwrap() == name { return Some(group); } } @@ -112,7 +114,7 @@ impl UserDBRead for UserDBLocal { fn get_group_by_id(&self, id: u32) -> Option<&crate::Group> { for group in self.groups.iter() { - if group.get_gid() == id { + if group.get_gid().unwrap() == id { return Some(group); } } @@ -136,12 +138,15 @@ 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() != gid) + self.groups.iter().all(|x| x.get_gid().unwrap() != gid) } fn is_groupname_valid_and_free(&self, name: &str) -> bool { let valid = crate::group::is_groupname_valid(name); - let free = self.groups.iter().all(|x| x.get_groupname() != name); + let free = self + .groups + .iter() + .all(|x| x.get_groupname().unwrap() != name); valid && free } } @@ -174,14 +179,21 @@ fn shadow_to_users( fn user_vec_to_hashmap(users: Vec) -> HashMap { users .into_iter() - .map(|x| (x.get_username().to_owned(), x)) + .map(|x| { + ( + x.get_username() + .expect("An empty username is not supported") + .to_owned(), + x, + ) + }) .collect() } /// Try to parse a String into some Object /// /// # Errors -/// if the parsing failed a [`UserLibError::Message`] is returned containing a more detailed error message. +/// if the parsing failed a [`UserLibError::Message`](crate::userlib_error::UserLibError::Message) is returned containing a more detailed error message. pub trait NewFromString { fn new_from_string(line: String) -> Result where @@ -209,7 +221,10 @@ 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"); - assert_eq!(data.users.get("test").unwrap().get_username(), "test") + assert_eq!( + data.users.get("test").unwrap().get_username().unwrap(), + "test" + ) } #[test] @@ -218,7 +233,7 @@ fn test_parsing_local_database() { let my_passwd_lines = file_to_string(Some(&PathBuf::from("/etc/passwd"))); let my_group_lines = file_to_string(Some(&PathBuf::from("/etc/group"))); let data = UserDBLocal::import_from_strings(&my_passwd_lines, "", &my_group_lines); - assert_eq!(data.groups.get(0).unwrap().get_groupname(), "root"); + assert_eq!(data.groups.get(0).unwrap().get_groupname().unwrap(), "root"); } #[test] @@ -230,11 +245,20 @@ fn test_user_db_read_implementation() { assert!(data.get_all_users().len() > 10); assert!(data.get_user_by_name("root").is_some()); assert_eq!(data.get_user_by_name("root").unwrap().get_uid(), 0); - assert_eq!(data.get_user_by_id(0).unwrap().get_username(), "root"); + assert_eq!( + data.get_user_by_id(0).unwrap().get_username().unwrap(), + "root" + ); 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(), 0); - assert_eq!(data.get_group_by_id(0).unwrap().get_groupname(), "root"); + assert_eq!( + data.get_group_by_name("root").unwrap().get_gid().unwrap(), + 0 + ); + assert_eq!( + data.get_group_by_id(0).unwrap().get_groupname().unwrap(), + "root" + ); assert!(data.get_user_by_name("norealnameforsure").is_none()); assert!(data.get_group_by_name("norealgroupforsure").is_none()); }