From 8fa154bdd895ac0d1d7833675037b0c34e433256 Mon Sep 17 00:00:00 2001 From: Dietrich Date: Tue, 6 Oct 2020 13:05:26 +0200 Subject: [PATCH] restructuring beginn --- src/api.rs | 13 +++++++ src/bin/{main.rs => read_all.rs} | 4 +-- src/{group.rs => group/mod.rs} | 31 ++++++++-------- src/lib.rs | 9 ++--- src/user/mod.rs | 14 ++++++++ src/{passwd.rs => user/passwd_fields.rs} | 7 ++-- src/{shadow.rs => user/shadow_fields.rs} | 23 ++++++------ src/userlib.rs | 45 +++++++++++++++++++++--- 8 files changed, 108 insertions(+), 38 deletions(-) rename src/bin/{main.rs => read_all.rs} (94%) rename src/{group.rs => group/mod.rs} (80%) create mode 100644 src/user/mod.rs rename src/{passwd.rs => user/passwd_fields.rs} (99%) rename src/{shadow.rs => user/shadow_fields.rs} (83%) diff --git a/src/api.rs b/src/api.rs index 3ba0b68..318b9db 100644 --- a/src/api.rs +++ b/src/api.rs @@ -20,3 +20,16 @@ trait UserDBWrite { fn set_group(&self) -> Option; fn new_group(&self) -> Option; } + +trait UserRead { + fn get_username(&self) -> Option; + fn get_uid(&self) -> Option; + fn get_gid(&self) -> Option; + // … +} + +trait UserWrite {} + +trait GroupRead {} + +trait GroupWrite {} diff --git a/src/bin/main.rs b/src/bin/read_all.rs similarity index 94% rename from src/bin/main.rs rename to src/bin/read_all.rs index 59e9a66..fbe0a17 100644 --- a/src/bin/main.rs +++ b/src/bin/read_all.rs @@ -1,7 +1,7 @@ extern crate adduser; -use adduser::passwd::Passwd; -use adduser::shadow::Shadow; +use adduser::Passwd; +use adduser::Shadow; use std::fs::File; use std::io::{prelude::*, BufReader}; diff --git a/src/group.rs b/src/group/mod.rs similarity index 80% rename from src/group.rs rename to src/group/mod.rs index e3e5ffb..351b7ec 100644 --- a/src/group.rs +++ b/src/group/mod.rs @@ -10,7 +10,6 @@ use log::warn; use regex::Regex; -use crate::passwd; use crate::userlib_error::UserLibError; use std::cmp::Eq; use std::convert::TryFrom; @@ -41,7 +40,7 @@ impl<'a> TryFrom<&'a str> for Groupname<'a> { Ok(Self { groupname: source }) } else { Err(UserLibError::Message(format!( - "Invalid username {}", + "Invalid groupname -{}-", source ))) } @@ -51,10 +50,10 @@ impl<'a> TryFrom<&'a str> for Groupname<'a> { /// A record(line) in the user database `/etc/shadow` found in most linux systems. #[derive(Debug, PartialEq, Eq)] pub struct Group<'a> { - groupname: Groupname<'a>, /* Username. */ - pub(crate) password: passwd::Password<'a>, /* Usually not used (disabled with x) */ - gid: passwd::Gid, /* Group ID. */ - members: Vec>, /* Real name. */ + groupname: Groupname<'a>, /* Username. */ + pub(crate) password: crate::Password<'a>, /* Usually not used (disabled with x) */ + gid: crate::Gid, /* Group ID. */ + members: Vec>, /* Real name. */ } impl<'a> Group<'a> { @@ -63,7 +62,7 @@ impl<'a> Group<'a> { self.groupname.groupname } #[must_use] - pub const fn get_members(&self) -> &Vec> { + pub const fn get_members(&self) -> &Vec> { &self.members } } @@ -104,8 +103,8 @@ impl<'a> Group<'a> { if elements.len() == 4 { Ok(Group { groupname: Groupname::try_from(*elements.get(0).unwrap())?, - password: passwd::Password::Disabled, - gid: passwd::Gid::try_from(*elements.get(2).unwrap())?, + password: crate::Password::Disabled, + gid: crate::Gid::try_from(*elements.get(2).unwrap())?, members: parse_members_list(*elements.get(3).unwrap()), }) } else { @@ -118,17 +117,16 @@ impl<'a> Group<'a> { } } -fn parse_members_list<'a>(source: &'a str) -> Vec> { +fn parse_members_list<'a>(source: &'a str) -> Vec> { let mut res = vec![]; - for mem in source.split(',') { - res.push(passwd::Username::try_from(mem).expect("failed to parse username")); + for mem in source.split(',').filter(|x| !x.is_empty()) { + res.push(crate::Username::try_from(mem).expect("failed to parse username")); } res } #[test] fn test_parse_and_back_identity() { - println!("Test"); let line = "teste:x:1002:test,teste"; let line2 = Group::new_from_string(line).unwrap(); assert_eq!(format!("{}", line2), line); @@ -136,8 +134,13 @@ fn test_parse_and_back_identity() { #[test] fn test_groupname() { - println!("Test"); let line = "teste:x:1002:test,teste"; let line2 = Group::new_from_string(line).unwrap(); assert_eq!(line2.get_groupname(), "teste"); } +#[test] +fn test_root_group() { + let line = "root:x:0:"; + let line2 = Group::new_from_string(line).unwrap(); + assert_eq!(line2.get_groupname(), "root"); +} diff --git a/src/lib.rs b/src/lib.rs index b8cd37d..c8e947d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,10 +5,11 @@ extern crate log; pub mod api; pub mod group; -pub mod passwd; -pub mod shadow; +pub mod user; pub mod userlib; pub mod userlib_error; pub use group::Group; -pub use passwd::{Gecos, Gid, HomeDir, Passwd, Password, ShellPath, Uid, Username}; -pub use shadow::Shadow; +pub use user::passwd_fields::{ + EncryptedPassword, Gecos, Gid, HomeDir, Passwd, Password, ShellPath, Uid, Username, +}; +pub use user::shadow_fields::Shadow; diff --git a/src/user/mod.rs b/src/user/mod.rs new file mode 100644 index 0000000..53046cb --- /dev/null +++ b/src/user/mod.rs @@ -0,0 +1,14 @@ +pub mod passwd_fields; +pub mod shadow_fields; +/// A record(line) in the user database `/etc/passwd` found in most linux systems. +#[derive(Debug, PartialEq, Eq)] +pub struct User<'a> { + source: &'a str, + username: crate::Username<'a>, /* Username. */ + password: crate::Password<'a>, /* Hashed passphrase, if shadow database not in use (see shadow.h). */ + uid: crate::Uid, /* User ID. */ + gid: crate::Gid, /* Group ID. */ + gecos: crate::Gecos<'a>, /* Real name. */ + home_dir: crate::HomeDir<'a>, /* Home directory. */ + shell_path: crate::ShellPath<'a>, /* Shell program. */ +} diff --git a/src/passwd.rs b/src/user/passwd_fields.rs similarity index 99% rename from src/passwd.rs rename to src/user/passwd_fields.rs index 716810d..80b251a 100644 --- a/src/passwd.rs +++ b/src/user/passwd_fields.rs @@ -56,7 +56,7 @@ impl<'a> TryFrom<&'a str> for Username<'a> { #[derive(Debug, PartialEq, Eq)] pub enum Password<'a> { Encrypted(EncryptedPassword<'a>), - Shadow(crate::shadow::Shadow<'a>), + Shadow(crate::Shadow<'a>), Disabled, } @@ -333,6 +333,7 @@ impl<'a> TryFrom<&'a str> for ShellPath<'a> { /// A record(line) in the user database `/etc/passwd` found in most linux systems. #[derive(Debug, PartialEq, Eq)] pub struct Passwd<'a> { + source: &'a str, username: Username<'a>, /* Username. */ password: Password<'a>, /* Hashed passphrase, if shadow database not in use (see shadow.h). */ uid: Uid, /* User ID. */ @@ -359,6 +360,7 @@ impl<'a> Passwd<'a> { let elements: Vec<&str> = line.split(':').collect(); if elements.len() == 7 { Ok(Passwd { + source: line, username: Username::try_from(*elements.get(0).unwrap())?, password: Password::Encrypted(EncryptedPassword::try_from( *elements.get(1).unwrap(), @@ -381,7 +383,7 @@ impl<'a> Passwd<'a> { pub const fn get_password(&self) -> &'a str { match self.password { Password::Encrypted(EncryptedPassword { password }) => password, - Password::Shadow(crate::shadow::Shadow { ref password, .. }) => password.password, + Password::Shadow(crate::Shadow { ref password, .. }) => password.password, Password::Disabled => "x", } } @@ -410,6 +412,7 @@ impl<'a> Passwd<'a> { impl Default for Passwd<'_> { fn default() -> Self { Passwd { + source: "", username: Username { username: "defaultuser", }, diff --git a/src/shadow.rs b/src/user/shadow_fields.rs similarity index 83% rename from src/shadow.rs rename to src/user/shadow_fields.rs index 202dc67..aa7c0e4 100644 --- a/src/shadow.rs +++ b/src/user/shadow_fields.rs @@ -9,7 +9,6 @@ use log::warn; -use crate::passwd; use crate::userlib_error::UserLibError; use std::cmp::Eq; use std::convert::TryFrom; @@ -18,15 +17,15 @@ use std::fmt::{self, Debug, Display}; /// A record(line) in the user database `/etc/shadow` found in most linux systems. #[derive(Debug, PartialEq, Eq)] pub struct Shadow<'a> { - username: passwd::Username<'a>, /* Username. */ - pub(crate) password: passwd::EncryptedPassword<'a>, /* Hashed passphrase */ - last_change: Option, /* User ID. */ - earliest_change: Option, /* Group ID. */ - latest_change: Option, /* Real name. */ - warn_period: Option, /* Home directory. */ - deactivated: Option, /* Shell program. */ - deactivated_since: Option, /* Shell program. */ - extensions: Option, /* Shell program. */ + username: crate::Username<'a>, /* Username. */ + pub(crate) password: crate::EncryptedPassword<'a>, /* Hashed passphrase */ + last_change: Option, /* User ID. */ + earliest_change: Option, /* Group ID. */ + latest_change: Option, /* Real name. */ + warn_period: Option, /* Home directory. */ + deactivated: Option, /* Shell program. */ + deactivated_since: Option, /* Shell program. */ + extensions: Option, /* Shell program. */ } impl<'a> Shadow<'a> { @@ -97,8 +96,8 @@ impl<'a> Shadow<'a> { if elements.len() == 9 { let extra = elements.get(8).unwrap(); Ok(Shadow { - username: passwd::Username::try_from(*elements.get(0).unwrap())?, - password: passwd::EncryptedPassword::try_from(*elements.get(1).unwrap())?, + username: crate::Username::try_from(*elements.get(0).unwrap())?, + password: crate::EncryptedPassword::try_from(*elements.get(1).unwrap())?, last_change: date_since_epoch(elements.get(2).unwrap()), earliest_change: date_since_epoch(elements.get(3).unwrap()), latest_change: date_since_epoch(elements.get(4).unwrap()), diff --git a/src/userlib.rs b/src/userlib.rs index a861749..d12dbd6 100644 --- a/src/userlib.rs +++ b/src/userlib.rs @@ -14,7 +14,8 @@ use crate::userlib_error::UserLibError; use std::cmp::Eq; use std::convert::TryFrom; use std::fmt::{self, Display}; -use std::io::{BufRead, Read}; +use std::fs::File; +use std::io::{BufRead, BufReader, Read}; pub struct UserDBLocal<'a> { pub(crate) passwd_entries: Vec>, @@ -32,15 +33,34 @@ impl<'a> UserDBLocal<'a> { let res = UserDBLocal { passwd_entries: passwd_content .lines() - .map(|line| crate::Passwd::new_from_string(line).expect("failed to read lines")) + .filter_map(|line| { + if line.len() > 5 { + println!("{}", line); + Some(crate::Passwd::new_from_string(line).expect("failed to read lines")) + } else { + None + } + }) .collect(), group_entries: group_content .lines() - .map(|line| crate::Group::new_from_string(line).expect("Parsing failed")) + .filter_map(|line| { + if line.len() > 5 { + Some(crate::Group::new_from_string(line).expect("Parsing failed")) + } else { + None + } + }) .collect(), shadow_entries: shadow_content .lines() - .map(|line| crate::Shadow::new_from_string(line).expect("Parsing failed")) + .filter_map(|line| { + if line.len() > 5 { + Some(crate::Shadow::new_from_string(line).expect("Parsing failed")) + } else { + None + } + }) .collect(), }; res @@ -55,3 +75,20 @@ fn test_creator_user_db_local() { "testuser" ) } + +#[test] +fn test_parsing_local_database() { + let passwd_file = File::open("/etc/passwd").unwrap(); + let mut passwd_reader = BufReader::new(passwd_file); + let mut my_passwd_lines = "".to_string(); + passwd_reader.read_to_string(&mut my_passwd_lines).unwrap(); + let group_file = File::open("/etc/group").unwrap(); + let mut group_reader = BufReader::new(group_file); + let mut my_group_lines = "".to_string(); + group_reader.read_to_string(&mut my_group_lines).unwrap(); + let data = UserDBLocal::import_from_strings(&my_passwd_lines, "", &my_group_lines); + assert_eq!( + format!("{}", data.group_entries.get(0).unwrap().get_groupname()), + "teste" + ); +}