From 6f59cf17ba401060a8c5690d17c4a6e73ccec65e Mon Sep 17 00:00:00 2001 From: dietrich Date: Wed, 7 Oct 2020 17:05:49 +0200 Subject: [PATCH] Compiles agin. --- src/bin/read_all.rs | 10 ++-- src/group/mod.rs | 58 ++++++++++++----------- src/lib.rs | 1 + src/user/gecos_fields.rs | 83 +++++++++++++++++---------------- src/user/mod.rs | 97 ++++++++++++++++++++------------------- src/user/passwd_fields.rs | 92 +++++++++++++++++++------------------ src/user/shadow_fields.rs | 47 +++++++++---------- src/userlib.rs | 68 +++++++++++++++++---------- 8 files changed, 248 insertions(+), 208 deletions(-) diff --git a/src/bin/read_all.rs b/src/bin/read_all.rs index 7cc05ca..51fc998 100644 --- a/src/bin/read_all.rs +++ b/src/bin/read_all.rs @@ -1,5 +1,6 @@ extern crate adduser; +use adduser::NewFromString; use adduser::Shadow; use adduser::User; use std::fs::File; @@ -17,11 +18,14 @@ fn main() { for line in reader.lines() { let line = line.unwrap(); - println!("{}", User::new_from_string(&line).unwrap()); + println!("{}", User::new_from_string(line).unwrap()); } - let line = "test:!!$6$/RotIe4VZzzAun4W$7YUONvru1rDnllN5TvrnOMsWUD5wSDUPAD6t6/Xwsr/0QOuWF3HcfAhypRkGa8G1B9qqWV5kZSnCb8GKMN9N61:18260:0:99999:7:::"; - assert_eq!(format!("{}", Shadow::new_from_string(line).unwrap()), line); + let line = "test:!!$6$/RotIe4VZzzAun4W$7YUONvru1rDnllN5TvrnOMsWUD5wSDUPAD6t6/Xwsr/0QOuWF3HcfAhypRkGa8G1B9qqWV5kZSnCb8GKMN9N61:18260:0:99999:7:::".to_string(); + assert_eq!( + format!("{}", Shadow::new_from_string(line.clone()).unwrap()), + line + ); // let pwd = User::default(); // let pwd2 = diff --git a/src/group/mod.rs b/src/group/mod.rs index 963c0bf..7aaae21 100644 --- a/src/group/mod.rs +++ b/src/group/mod.rs @@ -17,24 +17,24 @@ use std::convert::TryFrom; use std::fmt::{self, Debug, Display}; #[derive(Debug, PartialEq, Eq)] -pub struct Groupname<'a> { - groupname: &'a str, +pub struct Groupname { + groupname: String, } -impl Display for Groupname<'_> { +impl Display for Groupname { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.groupname,) } } -impl<'a> TryFrom<&'a str> for Groupname<'a> { +impl TryFrom for Groupname { type Error = UserLibError; - fn try_from(source: &'a str) -> std::result::Result { + fn try_from(source: String) -> std::result::Result { lazy_static! { static ref USERVALIDATION: Regex = Regex::new("^[a-z_]([a-z0-9_\\-]{0,31}|[a-z0-9_\\-]{0,30}\\$)$").unwrap(); } - if USERVALIDATION.is_match(source) { + if USERVALIDATION.is_match(&source) { Ok(Self { groupname: source }) } else if source == "Debian-exim" { warn!("username {} is not a valid username. This might cause problems. (It is default in Debian and Ubuntu)", source); @@ -50,25 +50,25 @@ 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: crate::Password<'a>, /* Usually not used (disabled with x) */ - gid: crate::Gid, /* Group ID. */ - members: Vec>, /* Real name. */ +pub struct Group { + groupname: Groupname, /* Username. */ + pub(crate) password: crate::Password, /* Usually not used (disabled with x) */ + gid: crate::Gid, /* Group ID. */ + members: Vec, /* Real name. */ } -impl<'a> Group<'a> { +impl Group { #[must_use] - pub const fn get_groupname(&self) -> &'a str { - self.groupname.groupname + pub fn get_groupname(&self) -> &str { + &self.groupname.groupname } #[must_use] - pub const fn get_members(&self) -> &Vec> { + pub const fn get_members(&self) -> &Vec { &self.members } } -impl<'a> Display for Group<'a> { +impl Display for Group { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { write!( f, @@ -85,7 +85,7 @@ impl<'a> Display for Group<'a> { } } -impl<'a> NewFromString<'a> for Group<'a> { +impl NewFromString for Group { /// Parse a line formatted like one in `/etc/shadow` and construct a matching `Shadow` instance /// /// # Example @@ -98,15 +98,15 @@ impl<'a> NewFromString<'a> for Group<'a> { /// /// # Errors /// When parsing fails this function returns a `UserLibError::Message` containing some information as to why the function failed. - fn new_from_string(line: &'a str) -> Result { + fn new_from_string(line: String) -> Result { println!("{}", &line); - let elements: Vec<&str> = line.split(':').collect(); + let elements: Vec = line.split(':').map(ToString::to_string).collect(); if elements.len() == 4 { Ok(Group { - groupname: Groupname::try_from(*elements.get(0).unwrap())?, + groupname: Groupname::try_from(elements.get(0).unwrap().to_string())?, password: crate::Password::Disabled, - gid: crate::Gid::try_from(*elements.get(2).unwrap())?, - members: parse_members_list(*elements.get(3).unwrap()), + gid: crate::Gid::try_from(elements.get(2).unwrap().to_string())?, + members: parse_members_list(elements.get(3).unwrap().to_string()), }) } else { Err(UserLibError::Message(format!( @@ -118,9 +118,13 @@ impl<'a> NewFromString<'a> for Group<'a> { } } -fn parse_members_list<'a>(source: &'a str) -> Vec> { +fn parse_members_list(source: String) -> Vec { let mut res = vec![]; - for mem in source.split(',').filter(|x| !x.is_empty()) { + for mem in source + .split(',') + .filter(|x| !x.is_empty()) + .map(ToString::to_string) + { res.push(crate::Username::try_from(mem).expect("failed to parse username")); } res @@ -129,19 +133,19 @@ fn parse_members_list<'a>(source: &'a str) -> Vec> { #[test] fn test_parse_and_back_identity() { let line = "teste:x:1002:test,teste"; - let line2 = Group::new_from_string(line).unwrap(); + let line2 = Group::new_from_string(line.to_owned()).unwrap(); assert_eq!(format!("{}", line2), line); } #[test] fn test_groupname() { let line = "teste:x:1002:test,teste"; - let line2 = Group::new_from_string(line).unwrap(); + let line2 = Group::new_from_string(line.to_owned()).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(); + let line2 = Group::new_from_string(line.to_owned()).unwrap(); assert_eq!(line2.get_groupname(), "root"); } diff --git a/src/lib.rs b/src/lib.rs index d19baf2..a451ea8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,4 +15,5 @@ pub use user::passwd_fields::{ }; pub use user::shadow_fields::Shadow; pub use user::User; +pub use userlib::NewFromString; pub use userlib_error::UserLibError; diff --git a/src/user/gecos_fields.rs b/src/user/gecos_fields.rs index d6fc0fa..af2ee16 100644 --- a/src/user/gecos_fields.rs +++ b/src/user/gecos_fields.rs @@ -9,81 +9,81 @@ use std::fmt::{self, Display}; /// /// This enum represents the first 4 values by name and adds the other values to a list of strings [`Gecos::Detail`]. If only one field is found and no `,` at all this value is used as a human readable comment [`Gecos::Simple`]. #[derive(Debug, PartialEq, Eq)] -pub enum Gecos<'a> { +pub enum Gecos { Detail { - full_name: &'a str, - room: &'a str, - phone_work: &'a str, - phone_home: &'a str, - other: Option>, + full_name: String, + room: String, + phone_work: String, + phone_home: String, + other: Option>, }, Simple { - comment: &'a str, + comment: String, }, } -impl<'a> Gecos<'a> { +impl<'a> Gecos { #[must_use] - pub const fn get_comment(&'a self) -> Option<&'a str> { - match *self { - Gecos::Simple { comment, .. } => Some(comment), + pub fn get_comment(&'a self) -> Option<&'a str> { + match &self { + Gecos::Simple { comment, .. } => Some(&comment), Gecos::Detail { .. } => None, } } #[must_use] - pub const fn get_full_name(&'a self) -> Option<&'a str> { - match *self { + pub fn get_full_name(&self) -> Option<&str> { + match &self { Gecos::Simple { .. } => None, Gecos::Detail { full_name, .. } => { if full_name.is_empty() { None } else { - Some(full_name) + Some(&full_name) } } } } #[must_use] - pub const fn get_room(&'a self) -> Option<&'a str> { - match *self { + pub fn get_room(&self) -> Option<&str> { + match &self { Gecos::Simple { .. } => None, Gecos::Detail { room, .. } => { if room.is_empty() { None } else { - Some(room) + Some(&room) } } } } #[must_use] - pub const fn get_phone_work(&'a self) -> Option<&'a str> { - match *self { + pub fn get_phone_work(&self) -> Option<&str> { + match &self { Gecos::Simple { .. } => None, Gecos::Detail { phone_work, .. } => { if phone_work.is_empty() { None } else { - Some(phone_work) + Some(&phone_work) } } } } #[must_use] - pub const fn get_phone_home(&'a self) -> Option<&'a str> { - match *self { + pub fn get_phone_home(&'_ self) -> Option<&'_ str> { + match &self { Gecos::Simple { .. } => None, Gecos::Detail { phone_home, .. } => { if phone_home.is_empty() { None } else { - Some(phone_home) + Some(&phone_home) } } } } #[must_use] - pub const fn get_other(&'a self) -> Option<&Vec<&'a str>> { + pub const fn get_other(&self) -> Option<&Vec> { match self { Gecos::Simple { .. } => None, Gecos::Detail { other, .. } => match other { @@ -94,7 +94,7 @@ impl<'a> Gecos<'a> { } } -impl Display for Gecos<'_> { +impl Display for Gecos { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match &self { Gecos::Simple { comment } => write!(f, "{}", comment), @@ -120,16 +120,16 @@ impl Display for Gecos<'_> { } } -impl<'a> TryFrom<&'a str> for Gecos<'a> { +impl TryFrom for Gecos { type Error = UserLibError; - fn try_from(source: &'a str) -> std::result::Result { - let vals: Vec<&str> = source.split(',').collect(); + fn try_from(source: String) -> std::result::Result { + let vals: Vec = source.split(',').map(ToString::to_string).collect(); if vals.len() > 3 { Ok(Gecos::Detail { - full_name: vals[0], - room: vals[1], - phone_work: vals[2], - phone_home: vals[3], + full_name: vals[0].clone(), + room: vals[1].clone(), + phone_work: vals[2].clone(), + phone_home: vals[3].clone(), other: if vals.len() == 4 { None } else { @@ -138,7 +138,7 @@ impl<'a> TryFrom<&'a str> for Gecos<'a> { }) } else if vals.len() == 1 { Ok(Gecos::Simple { - comment: vals.get(0).unwrap(), + comment: vals.get(0).unwrap().into(), }) } else { panic!(format!("Could not parse this string: {}", source)) @@ -149,9 +149,9 @@ impl<'a> TryFrom<&'a str> for Gecos<'a> { #[test] fn test_parse_gecos() { // test if the Gecos field can be parsed and the resulting struct is populated correctly. - let gcdetail = "Full Name,504,11345342,ä1-2312,myemail@test.com"; - let gcsimple = "A böring comment →"; - let gc_no_other: &str = "systemd Network Management,,,"; + let gcdetail = "Full Name,504,11345342,ä1-2312,myemail@test.com".to_string(); + let gcsimple = "A böring comment →".to_string(); + let gc_no_other = "systemd Network Management,,,".to_string(); let res_detail = crate::Gecos::try_from(gcdetail).unwrap(); let res_simple = crate::Gecos::try_from(gcsimple).unwrap(); let res_no_other = crate::Gecos::try_from(gc_no_other).unwrap(); @@ -196,9 +196,9 @@ fn test_parse_gecos() { #[test] fn test_gecos_getters() { // test if the Gecos field can be parsed and the resulting struct is populated correctly. - let gcdetail = "Full Name,504,11345342,ä1-2312,myemail@test.com"; - let gcsimple = "A böring comment →"; - let gc_no_other: &str = "systemd Network Management,,,"; + let gcdetail = "Full Name,504,11345342,ä1-2312,myemail@test.com".to_string(); + let gcsimple = "A böring comment →".to_string(); + let gc_no_other = "systemd Network Management,,,".to_string(); let res_detail = crate::Gecos::try_from(gcdetail).unwrap(); let res_simple = crate::Gecos::try_from(gcsimple).unwrap(); let res_no_other = crate::Gecos::try_from(gc_no_other).unwrap(); @@ -210,7 +210,10 @@ fn test_gecos_getters() { assert_eq!(res_detail.get_room(), Some("504")); assert_eq!(res_detail.get_phone_work(), Some("11345342")); assert_eq!(res_detail.get_phone_home(), Some("ä1-2312")); - assert_eq!(res_detail.get_other(), Some(&vec!["myemail@test.com"])); + assert_eq!( + res_detail.get_other(), + Some(&vec!["myemail@test.com".to_string()]) + ); assert_eq!( res_no_other.get_full_name(), diff --git a/src/user/mod.rs b/src/user/mod.rs index cde6916..7b0c8b8 100644 --- a/src/user/mod.rs +++ b/src/user/mod.rs @@ -8,47 +8,47 @@ use std::fmt::{self, Display}; /// 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. */ +pub struct User { + source: String, + username: crate::Username, /* Username. */ + password: crate::Password, /* Hashed passphrase, if shadow database not in use (see shadow.h). */ + uid: crate::Uid, /* User ID. */ + gid: crate::Gid, /* Group ID. */ + gecos: crate::Gecos, /* Real name. */ + home_dir: crate::HomeDir, /* Home directory. */ + shell_path: crate::ShellPath, /* Shell program. */ } -impl<'a> NewFromString<'a> for User<'a> { +impl NewFromString for User { /// Parse a line formatted like one in `/etc/passwd` and construct a matching [`adduser::User`] instance /// /// # Example /// ``` + /// use adduser::NewFromString; /// let pwd = adduser::User::new_from_string( - /// "testuser:testpassword:1001:1001:full Name,,,,:/home/test:/bin/test" - /// ).unwrap(); + /// "testuser:testpassword:1001:1001:full Name,,,,:/home/test:/bin/test".to_string()).unwrap(); /// assert_eq!(pwd.get_username(), "testuser"); /// ``` /// /// # Errors /// When parsing fails this function returns a `UserLibError::Message` containing some information as to why the function failed. - fn new_from_string(line: &'a str) -> Result + fn new_from_string(line: String) -> Result where Self: Sized, { - let elements: Vec<&str> = line.split(':').collect(); + let elements: Vec = line.split(':').map(ToString::to_string).collect(); if elements.len() == 7 { Ok(Self { source: line, - username: crate::Username::try_from(*elements.get(0).unwrap())?, + username: crate::Username::try_from(elements.get(0).unwrap().to_string())?, password: crate::Password::Encrypted(crate::EncryptedPassword::try_from( - *elements.get(1).unwrap(), + elements.get(1).unwrap().to_string(), )?), - uid: crate::Uid::try_from(*elements.get(2).unwrap())?, - gid: crate::Gid::try_from(*elements.get(3).unwrap())?, - gecos: crate::Gecos::try_from(*elements.get(4).unwrap())?, - home_dir: crate::HomeDir::try_from(*elements.get(5).unwrap())?, - shell_path: crate::ShellPath::try_from(*elements.get(6).unwrap())?, + uid: crate::Uid::try_from(elements.get(2).unwrap().to_string())?, + gid: crate::Gid::try_from(elements.get(3).unwrap().to_string())?, + gecos: crate::Gecos::try_from(elements.get(4).unwrap().to_string())?, + home_dir: crate::HomeDir::try_from(elements.get(5).unwrap().to_string())?, + shell_path: crate::ShellPath::try_from(elements.get(6).unwrap().to_string())?, }) } else { Err("Failed to parse: not enough elements".into()) @@ -56,17 +56,17 @@ impl<'a> NewFromString<'a> for User<'a> { } } -impl<'a> User<'a> { +impl User { #[must_use] - pub const fn get_username(&self) -> &'a str { - self.username.username + pub fn get_username(&self) -> &str { + &self.username.username } #[must_use] - pub const fn get_password(&self) -> &'a str { - match self.password { - crate::Password::Encrypted(crate::EncryptedPassword { password }) => password, - crate::Password::Shadow(crate::Shadow { ref password, .. }) => password.password, - crate::Password::Disabled => "x", + pub fn get_password(&self) -> &str { + match &self.password { + crate::Password::Encrypted(crate::EncryptedPassword { password }) => &password, + crate::Password::Shadow(crate::Shadow { ref password, .. }) => &password.password, + crate::Password::Disabled => &"x", } } #[must_use] @@ -82,39 +82,41 @@ impl<'a> User<'a> { &self.gecos } #[must_use] - pub const fn get_home_dir(&self) -> &'a str { - self.home_dir.dir + pub fn get_home_dir(&self) -> &str { + &self.home_dir.dir } #[must_use] - pub const fn get_shell_path(&self) -> &'a str { - self.shell_path.shell + pub fn get_shell_path(&self) -> &str { + &self.shell_path.shell } } -impl Default for User<'_> { +impl Default for User { fn default() -> Self { Self { - source: "", + source: "".to_owned(), username: crate::Username { - username: "defaultuser", + username: "defaultuser".to_owned(), }, password: crate::Password::Encrypted(crate::EncryptedPassword { - password: "notencrypted", + password: "notencrypted".to_owned(), }), uid: crate::Uid { uid: 1001 }, gid: crate::Gid { gid: 1001 }, gecos: crate::Gecos::Simple { - comment: "gecos default comment", + comment: "gecos default comment".to_string(), }, home_dir: crate::HomeDir { - dir: "/home/default", + dir: "/home/default".to_owned(), + }, + shell_path: crate::ShellPath { + shell: "/bin/bash".to_owned(), }, - shell_path: crate::ShellPath { shell: "/bin/bash" }, } } } -impl Display for User<'_> { +impl Display for User { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, @@ -142,16 +144,17 @@ fn test_default_user() { #[test] fn test_new_from_string() { // Test if a single line can be parsed and if the resulting struct is populated correctly. - let fail = User::new_from_string("").err().unwrap(); + let fail = User::new_from_string("".into()).err().unwrap(); assert_eq!( fail, crate::UserLibError::Message("Failed to parse: not enough elements".into()) ); - let pwd = - User::new_from_string("testuser:testpassword:1001:1001:testcomment:/home/test:/bin/test") - .unwrap(); + let pwd = User::new_from_string( + "testuser:testpassword:1001:1001:testcomment:/home/test:/bin/test".into(), + ) + .unwrap(); let pwd2 = - User::new_from_string("testuser:testpassword:1001:1001:full Name,004,000342,001-2312,myemail@test.com:/home/test:/bin/test") + User::new_from_string("testuser:testpassword:1001:1001:full Name,004,000342,001-2312,myemail@test.com:/home/test:/bin/test".into()) .unwrap(); assert_eq!(pwd.username.username, "testuser"); assert_eq!(pwd.home_dir.dir, "/home/test"); @@ -189,7 +192,7 @@ fn test_parse_passwd() { for line in reader.lines() { let lineorig: String = line.unwrap(); let linecopy = lineorig.clone(); - let pass_struc = User::new_from_string(&linecopy).unwrap(); + let pass_struc = User::new_from_string(linecopy).unwrap(); assert_eq!( // ignoring the numbers of `,` since the implementation does not (yet) reproduce a missing comment field. lineorig, diff --git a/src/user/passwd_fields.rs b/src/user/passwd_fields.rs index 69f2ee4..811b8cf 100644 --- a/src/user/passwd_fields.rs +++ b/src/user/passwd_fields.rs @@ -10,7 +10,7 @@ use log::warn; use regex::Regex; -use crate::userlib_error::UserLibError; +use crate::UserLibError; use std::cmp::Eq; use std::convert::TryFrom; use std::fmt::{self, Display}; @@ -21,25 +21,25 @@ use std::fmt::{self, Display}; /// /// In the future some extra fields might be added. #[derive(Debug, PartialEq, Eq)] -pub struct Username<'a> { +pub struct Username { /// The username value - pub(in crate::user) username: &'a str, + pub(in crate::user) username: String, } -impl Display for Username<'_> { +impl Display for Username { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.username,) } } -impl<'a> TryFrom<&'a str> for Username<'a> { +impl TryFrom for Username { type Error = UserLibError; - fn try_from(source: &'a str) -> std::result::Result { + fn try_from(source: String) -> std::result::Result { lazy_static! { static ref USERVALIDATION: Regex = Regex::new("^[a-z_]([a-z0-9_\\-]{0,31}|[a-z0-9_\\-]{0,30}\\$)$").unwrap(); } - if USERVALIDATION.is_match(source) { + if USERVALIDATION.is_match(&source) { Ok(Self { username: source }) } else if source == "Debian-exim" { warn!("username {} is not a valid username. This might cause problems. (It is default in Debian and Ubuntu)", source); @@ -54,13 +54,13 @@ impl<'a> TryFrom<&'a str> for Username<'a> { } #[derive(Debug, PartialEq, Eq)] -pub enum Password<'a> { - Encrypted(crate::EncryptedPassword<'a>), - Shadow(crate::Shadow<'a>), +pub enum Password { + Encrypted(crate::EncryptedPassword), + Shadow(crate::Shadow), Disabled, } -impl Display for Password<'_> { +impl Display for Password { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Password::Encrypted(EncryptedPassword { password }) => write!(f, "{}", password,), @@ -71,19 +71,19 @@ impl Display for Password<'_> { } #[derive(Debug, PartialEq, Eq)] -pub struct EncryptedPassword<'a> { - pub(in crate::user) password: &'a str, +pub struct EncryptedPassword { + pub(in crate::user) password: String, } -impl Display for EncryptedPassword<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +impl Display for EncryptedPassword { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.password,) } } -impl<'a> TryFrom<&'a str> for EncryptedPassword<'a> { +impl TryFrom for EncryptedPassword { type Error = UserLibError; - fn try_from(source: &'a str) -> std::result::Result { + fn try_from(source: String) -> std::result::Result { if source == "x" { warn!("password from shadow not loaded!") } else { @@ -104,9 +104,9 @@ impl Display for Uid { } } -impl TryFrom<&str> for Uid { +impl TryFrom for Uid { type Error = UserLibError; - fn try_from(source: &str) -> std::result::Result { + fn try_from(source: String) -> std::result::Result { Ok(Self { uid: source.parse::().unwrap(), }) @@ -132,9 +132,9 @@ impl Display for Gid { } } -impl TryFrom<&str> for Gid { +impl TryFrom for Gid { type Error = UserLibError; - fn try_from(source: &str) -> std::result::Result { + fn try_from(source: String) -> std::result::Result { Ok(Self { gid: source.parse::().unwrap(), }) @@ -151,38 +151,38 @@ impl Gid { /// The home directory of a user #[derive(Debug, PartialEq, Eq)] -pub struct HomeDir<'a> { - pub(in crate::user) dir: &'a str, +pub struct HomeDir { + pub(in crate::user) dir: String, } -impl Display for HomeDir<'_> { +impl Display for HomeDir { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.dir,) } } -impl<'a> TryFrom<&'a str> for HomeDir<'a> { +impl TryFrom for HomeDir { type Error = UserLibError; - fn try_from(source: &'a str) -> std::result::Result { + fn try_from(source: String) -> std::result::Result { Ok(Self { dir: source }) } } /// The path to the Shell binary #[derive(Debug, PartialEq, Eq)] -pub struct ShellPath<'a> { - pub(in crate::user) shell: &'a str, +pub struct ShellPath { + pub(in crate::user) shell: String, } -impl Display for ShellPath<'_> { +impl Display for ShellPath { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.shell,) } } -impl<'a> TryFrom<&'a str> for ShellPath<'a> { +impl TryFrom for ShellPath { type Error = UserLibError; - fn try_from(source: &'a str) -> std::result::Result { + fn try_from(source: String) -> std::result::Result { Ok(ShellPath { shell: source }) } } @@ -192,47 +192,51 @@ impl<'a> TryFrom<&'a str> for ShellPath<'a> { #[test] fn test_username_validation() { // Failing tests - let umlauts = Username::try_from("täst"); // umlauts + let umlauts: Result = Username::try_from("täst".to_owned()); // umlauts assert_eq!( Err(UserLibError::Message("Invalid username täst".into())), umlauts ); - let number_first = Username::try_from("11elf"); // numbers first + let number_first = Username::try_from("11elf".to_owned()); // numbers first assert_eq!( Err(UserLibError::Message("Invalid username 11elf".into())), number_first ); - let slashes = Username::try_from("test/name"); // slashes in the name + let slashes = Username::try_from("test/name".to_owned()); // slashes in the name assert_eq!( Err(UserLibError::Message("Invalid username test/name".into())), slashes ); - let long = Username::try_from("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); // maximum size 32 letters + let long = Username::try_from("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".to_owned()); // maximum size 32 letters assert_eq!( Err(UserLibError::Message( - "Invalid username aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".into() + "Invalid username aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".to_owned() )), long ); // Working tests - let ubuntu_exception = Username::try_from("Debian-exim"); // for some reason ubuntu and debian have a capital user. + let ubuntu_exception = Username::try_from("Debian-exim".to_owned()); // for some reason ubuntu and debian have a capital user. assert_eq!(ubuntu_exception.unwrap().username, "Debian-exim"); - let single = Username::try_from("t"); // single characters are ok + let single = Username::try_from("t".to_owned()); // single characters are ok assert_eq!(single.unwrap().username, "t"); - let normal = Username::try_from("superman"); // regular username + let normal = Username::try_from("superman".to_owned()); // regular username assert_eq!(normal.unwrap().username, "superman"); - let normal = Username::try_from("anna3pete"); // regular username containing a number + let normal = Username::try_from("anna3pete".to_owned()); // regular username containing a number assert_eq!(normal.unwrap().username, "anna3pete"); - let normal = Username::try_from("enya$"); // regular username ending in a $ + let normal = Username::try_from("enya$".to_owned()); // regular username ending in a $ assert_eq!(normal.unwrap().username, "enya$"); } #[test] fn test_guid_system_user() { // Check uids of system users. - let values = vec![("999", true), ("0", true), ("1000", false)]; + let values = vec![ + ("999".to_owned(), true), + ("0".to_owned(), true), + ("1000".to_owned(), false), + ]; for val in values { - assert_eq!(Uid::try_from(val.0).unwrap().is_system_uid(), val.1); - assert_eq!(Gid::try_from(val.0).unwrap().is_system_gid(), val.1); + assert_eq!(Uid::try_from(val.0.clone()).unwrap().is_system_uid(), val.1); + assert_eq!(Gid::try_from(val.0.clone()).unwrap().is_system_gid(), val.1); } } diff --git a/src/user/shadow_fields.rs b/src/user/shadow_fields.rs index ec53357..3e0494f 100644 --- a/src/user/shadow_fields.rs +++ b/src/user/shadow_fields.rs @@ -17,30 +17,30 @@ 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: 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. */ +pub struct Shadow { + username: crate::Username, /* Username. */ + pub(crate) password: crate::EncryptedPassword, /* 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> { +impl Shadow { #[must_use] - pub const fn get_username(&self) -> &'a str { - self.username.username + pub fn get_username(&self) -> &str { + &self.username.username } #[must_use] - pub const fn get_password(&self) -> &'a str { - self.password.password + pub fn get_password(&self) -> &str { + &self.password.password } } -impl<'a> Display for Shadow<'a> { +impl Display for Shadow { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { write!( f, @@ -78,27 +78,28 @@ fn show_option_duration(input: Option) -> String { } } -impl<'a> NewFromString<'a> for Shadow<'a> { +impl NewFromString for Shadow { /// Parse a line formatted like one in `/etc/shadow` and construct a matching `Shadow` instance /// /// # Example /// ``` + /// use adduser::NewFromString; /// let shad = adduser::Shadow::new_from_string( - /// "test:!!$6$/RotIe4VZzzAun4W$7YUONvru1rDnllN5TvrnOMsWUD5wSDUPAD6t6/Xwsr/0QOuWF3HcfAhypRkGa8G1B9qqWV5kZSnCb8GKMN9N61:18260:0:99999:7:::" + /// "test:!!$6$/RotIe4VZzzAun4W$7YUONvru1rDnllN5TvrnOMsWUD5wSDUPAD6t6/Xwsr/0QOuWF3HcfAhypRkGa8G1B9qqWV5kZSnCb8GKMN9N61:18260:0:99999:7:::".to_string() /// ).unwrap(); /// assert_eq!(shad.get_username(), "test"); /// ``` /// /// # Errors /// When parsing fails this function returns a `UserLibError::Message` containing some information as to why the function failed. - fn new_from_string(line: &'a str) -> Result { + fn new_from_string(line: String) -> Result { println!("{}", &line); - let elements: Vec<&str> = line.split(':').collect(); + let elements: Vec = line.split(':').map(ToString::to_string).collect(); if elements.len() == 9 { let extra = elements.get(8).unwrap(); Ok(Shadow { - username: crate::Username::try_from(*elements.get(0).unwrap())?, - password: crate::EncryptedPassword::try_from(*elements.get(1).unwrap())?, + username: crate::Username::try_from(elements.get(0).unwrap().to_string())?, + password: crate::EncryptedPassword::try_from(elements.get(1).unwrap().to_string())?, 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()), @@ -145,7 +146,7 @@ fn duration_for_days(days_source: &str) -> Option { fn test_parse_and_back_identity() { println!("Test"); let line = "test:!!$6$/RotIe4VZzzAun4W$7YUONvru1rDnllN5TvrnOMsWUD5wSDUPAD6t6/Xwsr/0QOuWF3HcfAhypRkGa8G1B9qqWV5kZSnCb8GKMN9N61:18260:0:99999:7:::"; - let line2 = Shadow::new_from_string(line).unwrap(); + let line2 = Shadow::new_from_string(line.to_owned()).unwrap(); println!("{:#?}", line2); assert_eq!(format!("{}", line2), line); } diff --git a/src/userlib.rs b/src/userlib.rs index a15b06d..93be679 100644 --- a/src/userlib.rs +++ b/src/userlib.rs @@ -12,11 +12,11 @@ use std::fs::File; use std::io::{BufReader, Read}; use std::path::PathBuf; -pub struct UserDBLocal<'a> { +pub struct UserDBLocal { source_files: Files, - pub passwd_entries: Vec>, - pub shadow_entries: Vec>, - pub group_entries: Vec>, + pub passwd_entries: Vec, + pub shadow_entries: Vec, + pub group_entries: Vec, } pub struct Files { @@ -35,12 +35,12 @@ impl Default for Files { } } -impl<'a> UserDBLocal<'a> { +impl UserDBLocal { #[must_use] pub fn import_from_strings( - passwd_content: &'a str, - shadow_content: &'a str, - group_content: &'a str, + passwd_content: String, + shadow_content: String, + group_content: String, ) -> Self { let res = UserDBLocal { source_files: Files { @@ -53,7 +53,10 @@ impl<'a> UserDBLocal<'a> { .filter_map(|line| { if line.len() > 5 { println!("{}", line); - Some(crate::User::new_from_string(line).expect("failed to read lines")) + Some( + crate::User::new_from_string(line.to_owned()) + .expect("failed to read lines"), + ) } else { None } @@ -63,7 +66,9 @@ impl<'a> UserDBLocal<'a> { .lines() .filter_map(|line| { if line.len() > 5 { - Some(crate::Group::new_from_string(line).expect("Parsing failed")) + Some( + crate::Group::new_from_string(line.to_owned()).expect("Parsing failed"), + ) } else { None } @@ -73,7 +78,10 @@ impl<'a> UserDBLocal<'a> { .lines() .filter_map(|line| { if line.len() > 5 { - Some(crate::Shadow::new_from_string(line).expect("Parsing failed")) + Some( + crate::Shadow::new_from_string(line.to_owned()) + .expect("Parsing failed"), + ) } else { None } @@ -83,19 +91,31 @@ impl<'a> UserDBLocal<'a> { res } + #[must_use] pub fn load_files(files: Files) -> Self { - let passwd_file = - File::open(files.group.expect("passwd file path cannot be None")).unwrap(); + let passwd_file = File::open( + files + .group + .clone() + .expect("passwd file path cannot be None"), + ) + .unwrap(); let mut passwd_reader = BufReader::new(passwd_file); let mut my_passwd_lines = String::new(); passwd_reader.read_to_string(&mut my_passwd_lines).unwrap(); - let group_file = File::open(files.group.expect("group file path cannot be None")).unwrap(); + let group_file = + File::open(files.group.clone().expect("group file path cannot be None")).unwrap(); let mut group_reader = BufReader::new(group_file); let mut my_group_lines = String::new(); group_reader.read_to_string(&mut my_group_lines).unwrap(); - let shadow_file = File::open(files.shadow.expect("shadow file path cannot be None")) - .expect("Failed to read the shadow file. Most of the time root permissions are needed"); - let mut shadow_reader = BufReader::new(group_file); + let shadow_file = File::open( + files + .shadow + .clone() + .expect("shadow file path cannot be None"), + ) + .expect("Failed to read the shadow file. Most of the time root permissions are needed"); + let mut shadow_reader = BufReader::new(shadow_file); let mut my_shadow_lines = String::new(); shadow_reader.read_to_string(&mut my_shadow_lines).unwrap(); @@ -108,22 +128,22 @@ impl<'a> UserDBLocal<'a> { } } -pub trait NewFromString<'a> { - fn new_from_string(line: &'a str) -> Result +pub trait NewFromString { + fn new_from_string(line: String) -> Result where Self: Sized; } -fn string_to<'a, T>(source: &'a str) -> Vec +fn string_to(source: &str) -> Vec where - T: NewFromString<'a>, + T: NewFromString, { source .lines() .filter_map(|line| { if line.len() > 5 { println!("{}", line); - Some(T::new_from_string(line).expect("failed to read lines")) + Some(T::new_from_string(line.to_owned()).expect("failed to read lines")) } else { None } @@ -133,7 +153,7 @@ where #[test] fn test_creator_user_db_local() { - let data = UserDBLocal::import_from_strings("testuser: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,teste"); + let data = UserDBLocal::import_from_strings("testuser:x:1001:1001:full Name,004,000342,001-2312,myemail@test.com:/home/test:/bin/test".to_string(), "test:!!$6$/RotIe4VZzzAun4W$7YUONvru1rDnllN5TvrnOMsWUD5wSDUPAD6t6/Xwsr/0QOuWF3HcfAhypRkGa8G1B9qqWV5kZSnCb8GKMN9N61:18260:0:99999:7:::".to_string(), "teste:x:1002:test,teste".to_string()); assert_eq!( data.passwd_entries.get(0).unwrap().get_username(), "testuser" @@ -152,6 +172,6 @@ fn test_parsing_local_database() { 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); + let data = UserDBLocal::import_from_strings(my_passwd_lines, "".to_string(), my_group_lines); assert_eq!(data.group_entries.get(0).unwrap().get_groupname(), "root"); }