persistent ordering of the passwd file

This commit is contained in:
Dietrich 2020-10-23 15:21:30 +02:00
parent 98d4c72f7f
commit f33ba8e767
5 changed files with 39 additions and 24 deletions

View File

@ -106,15 +106,15 @@ impl NewFromString for Group {
/// use crate::adduser::api::GroupRead; /// use crate::adduser::api::GroupRead;
/// use adduser::NewFromString; /// use adduser::NewFromString;
/// let grp = adduser::Group::new_from_string( /// let grp = adduser::Group::new_from_string(
/// "teste:x:1002:test,teste".to_owned() /// "teste:x:1002:test,teste".to_owned(),
/// 0,
/// ).unwrap(); /// ).unwrap();
/// assert_eq!(grp.get_groupname().unwrap(), "teste"); /// assert_eq!(grp.get_groupname().unwrap(), "teste");
/// ``` /// ```
/// ///
/// # Errors /// # 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` containing some information as to why the function failed.
fn new_from_string(line: String) -> Result<Self, UserLibError> { fn new_from_string(line: String, position: u32) -> Result<Self, UserLibError> {
println!("{}", &line);
let elements: Vec<String> = line.split(':').map(ToString::to_string).collect(); let elements: Vec<String> = line.split(':').map(ToString::to_string).collect();
if elements.len() == 4 { if elements.len() == 4 {
Ok(Self { Ok(Self {
@ -150,19 +150,19 @@ fn parse_members_list(source: &str) -> Vec<crate::Username> {
#[test] #[test]
fn test_parse_and_back_identity() { fn test_parse_and_back_identity() {
let line = "teste:x:1002:test,teste"; let line = "teste:x:1002:test,teste";
let line2 = Group::new_from_string(line.to_owned()).unwrap(); let line2 = Group::new_from_string(line.to_owned(), 0).unwrap();
assert_eq!(format!("{}", line2), line); assert_eq!(format!("{}", line2), line);
} }
#[test] #[test]
fn test_groupname() { fn test_groupname() {
let line = "teste:x:1002:test,teste"; let line = "teste:x:1002:test,teste";
let line2 = Group::new_from_string(line.to_owned()).unwrap(); let line2 = Group::new_from_string(line.to_owned(), 0).unwrap();
assert_eq!(line2.get_groupname().unwrap(), "teste"); assert_eq!(line2.get_groupname().unwrap(), "teste");
} }
#[test] #[test]
fn test_root_group() { fn test_root_group() {
let line = "root:x:0:"; let line = "root:x:0:";
let line2 = Group::new_from_string(line.to_owned()).unwrap(); let line2 = Group::new_from_string(line.to_owned(), 0).unwrap();
assert_eq!(line2.get_groupname().unwrap(), "root"); assert_eq!(line2.get_groupname().unwrap(), "root");
} }

View File

@ -205,7 +205,6 @@ fn test_gecos_getters() {
assert_eq!(res_simple.get_comment(), Some("A böring comment →")); assert_eq!(res_simple.get_comment(), Some("A böring comment →"));
assert_eq!(res_detail.get_comment(), None); assert_eq!(res_detail.get_comment(), None);
println!("{:?}", res_detail);
assert_eq!(res_detail.get_full_name(), Some("Full Name")); assert_eq!(res_detail.get_full_name(), Some("Full Name"));
assert_eq!(res_detail.get_room(), Some("504")); assert_eq!(res_detail.get_room(), Some("504"));
assert_eq!(res_detail.get_phone_work(), Some("11345342")); assert_eq!(res_detail.get_phone_work(), Some("11345342"));

View File

@ -10,6 +10,7 @@ use std::fmt::{self, Display};
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub struct User { pub struct User {
source: String, source: String,
pos: u32,
username: crate::Username, /* Username. */ username: crate::Username, /* Username. */
pub(crate) password: crate::Password, /* Hashed passphrase, if shadow database not in use (see shadow.h). */ pub(crate) password: crate::Password, /* Hashed passphrase, if shadow database not in use (see shadow.h). */
uid: crate::Uid, /* User ID. */ uid: crate::Uid, /* User ID. */
@ -27,13 +28,13 @@ impl NewFromString for User {
/// use crate::adduser::api::UserRead; /// use crate::adduser::api::UserRead;
/// use adduser::NewFromString; /// use adduser::NewFromString;
/// let pwd = adduser::User::new_from_string( /// let pwd = adduser::User::new_from_string(
/// "testuser:testpassword:1001:1001:full Name,,,,:/home/test:/bin/test".to_string()).unwrap(); /// "testuser:testpassword:1001:1001:full Name,,,,:/home/test:/bin/test".to_string(), 0).unwrap();
/// assert_eq!(pwd.get_username().unwrap(), "testuser"); /// assert_eq!(pwd.get_username().unwrap(), "testuser");
/// ``` /// ```
/// ///
/// # Errors /// # Errors
/// When parsing fails this function returns a [`UserLibError::Message`](crate::userlib_error::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<Self, crate::UserLibError> fn new_from_string(line: String, position: u32) -> Result<Self, crate::UserLibError>
where where
Self: Sized, Self: Sized,
{ {
@ -41,6 +42,7 @@ impl NewFromString for User {
if elements.len() == 7 { if elements.len() == 7 {
Ok(Self { Ok(Self {
source: line, source: line,
pos: position,
username: crate::Username::try_from(elements.get(0).unwrap().to_string())?, username: crate::Username::try_from(elements.get(0).unwrap().to_string())?,
password: crate::Password::Encrypted(crate::EncryptedPassword::try_from( password: crate::Password::Encrypted(crate::EncryptedPassword::try_from(
elements.get(1).unwrap().to_string(), elements.get(1).unwrap().to_string(),
@ -112,10 +114,23 @@ impl crate::api::UserRead for User {
} }
} }
impl PartialOrd for User {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.pos.cmp(&other.pos))
}
}
impl Ord for User {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.pos.cmp(&other.pos)
}
}
impl Default for User { impl Default for User {
fn default() -> Self { fn default() -> Self {
Self { Self {
source: "".to_owned(), source: "".to_owned(),
pos: u32::MAX,
username: crate::Username { username: crate::Username {
username: "defaultuser".to_owned(), username: "defaultuser".to_owned(),
}, },
@ -165,17 +180,18 @@ fn test_default_user() {
#[test] #[test]
fn test_new_from_string() { fn test_new_from_string() {
// Test if a single line can be parsed and if the resulting struct is populated correctly. // Test if a single line can be parsed and if the resulting struct is populated correctly.
let fail = User::new_from_string("".into()).err().unwrap(); let fail = User::new_from_string("".into(), 0).err().unwrap();
assert_eq!( assert_eq!(
fail, fail,
crate::UserLibError::Message("Failed to parse: not enough elements".into()) crate::UserLibError::Message("Failed to parse: not enough elements".into())
); );
let pwd = User::new_from_string( let pwd = User::new_from_string(
"testuser:testpassword:1001:1001:testcomment:/home/test:/bin/test".into(), "testuser:testpassword:1001:1001:testcomment:/home/test:/bin/test".into(),
0,
) )
.unwrap(); .unwrap();
let pwd2 = let pwd2 =
User::new_from_string("testuser:testpassword:1001:1001:full Name,004,000342,001-2312,myemail@test.com:/home/test:/bin/test".into()) User::new_from_string("testuser:testpassword:1001:1001:full Name,004,000342,001-2312,myemail@test.com:/home/test:/bin/test".into(),0)
.unwrap(); .unwrap();
assert_eq!(pwd.username.username, "testuser"); assert_eq!(pwd.username.username, "testuser");
assert_eq!(pwd.home_dir.dir, "/home/test"); assert_eq!(pwd.home_dir.dir, "/home/test");
@ -210,10 +226,10 @@ fn test_parse_passwd() {
let file = File::open("/etc/passwd").unwrap(); let file = File::open("/etc/passwd").unwrap();
let reader = BufReader::new(file); let reader = BufReader::new(file);
for line in reader.lines() { for (n, line) in reader.lines().enumerate() {
let lineorig: String = line.unwrap(); let lineorig: String = line.unwrap();
let linecopy = lineorig.clone(); let linecopy = lineorig.clone();
let pass_struc = User::new_from_string(linecopy).unwrap(); let pass_struc = User::new_from_string(linecopy, n as u32).unwrap();
assert_eq!( assert_eq!(
// ignoring the numbers of `,` since the implementation does not (yet) reproduce a missing comment field. // ignoring the numbers of `,` since the implementation does not (yet) reproduce a missing comment field.
lineorig, lineorig,

View File

@ -85,15 +85,15 @@ impl NewFromString for Shadow {
/// ``` /// ```
/// use adduser::NewFromString; /// use adduser::NewFromString;
/// let shad = adduser::Shadow::new_from_string( /// let shad = adduser::Shadow::new_from_string(
/// "test:!!$6$/RotIe4VZzzAun4W$7YUONvru1rDnllN5TvrnOMsWUD5wSDUPAD6t6/Xwsr/0QOuWF3HcfAhypRkGa8G1B9qqWV5kZSnCb8GKMN9N61:18260:0:99999:7:::".to_string() /// "test:!!$6$/RotIe4VZzzAun4W$7YUONvru1rDnllN5TvrnOMsWUD5wSDUPAD6t6/Xwsr/0QOuWF3HcfAhypRkGa8G1B9qqWV5kZSnCb8GKMN9N61:18260:0:99999:7:::".to_string(),
/// 0,
/// ).unwrap(); /// ).unwrap();
/// assert_eq!(shad.get_username(), "test"); /// assert_eq!(shad.get_username(), "test");
/// ``` /// ```
/// ///
/// # Errors /// # 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` containing some information as to why the function failed.
fn new_from_string(line: String) -> Result<Self, UserLibError> { fn new_from_string(line: String, position: u32) -> Result<Self, UserLibError> {
println!("{}", &line);
let elements: Vec<String> = line.split(':').map(ToString::to_string).collect(); let elements: Vec<String> = line.split(':').map(ToString::to_string).collect();
if elements.len() == 9 { if elements.len() == 9 {
let extra = elements.get(8).unwrap(); let extra = elements.get(8).unwrap();
@ -144,9 +144,7 @@ fn duration_for_days(days_source: &str) -> Option<chrono::Duration> {
#[test] #[test]
fn test_parse_and_back_identity() { fn test_parse_and_back_identity() {
println!("Test");
let line = "test:!!$6$/RotIe4VZzzAun4W$7YUONvru1rDnllN5TvrnOMsWUD5wSDUPAD6t6/Xwsr/0QOuWF3HcfAhypRkGa8G1B9qqWV5kZSnCb8GKMN9N61:18260:0:99999:7:::"; let line = "test:!!$6$/RotIe4VZzzAun4W$7YUONvru1rDnllN5TvrnOMsWUD5wSDUPAD6t6/Xwsr/0QOuWF3HcfAhypRkGa8G1B9qqWV5kZSnCb8GKMN9N61:18260:0:99999:7:::";
let line2 = Shadow::new_from_string(line.to_owned()).unwrap(); let line2 = Shadow::new_from_string(line.to_owned(), 0).unwrap();
println!("{:#?}", line2);
assert_eq!(format!("{}", line2), line); assert_eq!(format!("{}", line2), line);
} }

View File

@ -145,7 +145,9 @@ impl UserDBWrite for UserDBLocal {
use crate::api::UserDBRead; use crate::api::UserDBRead;
impl UserDBRead for UserDBLocal { impl UserDBRead for UserDBLocal {
fn get_all_users(&self) -> Vec<&crate::User> { fn get_all_users(&self) -> Vec<&crate::User> {
self.users.iter().map(|(_, x)| x).collect() let mut res: Vec<&crate::User> = self.users.iter().map(|(_, x)| x).collect();
res.sort();
res
} }
fn get_user_by_name(&self, name: &str) -> Option<&crate::User> { fn get_user_by_name(&self, name: &str) -> Option<&crate::User> {
@ -258,7 +260,7 @@ fn user_vec_to_hashmap(users: Vec<crate::User>) -> HashMap<String, crate::User>
/// # Errors /// # Errors
/// if the parsing failed a [`UserLibError::Message`](crate::userlib_error::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 { pub trait NewFromString {
fn new_from_string(line: String) -> Result<Self, crate::UserLibError> fn new_from_string(line: String, position: u32) -> Result<Self, crate::UserLibError>
where where
Self: Sized; Self: Sized;
} }
@ -270,10 +272,10 @@ where
{ {
source source
.lines() .lines()
.filter_map(|line| { .enumerate()
.filter_map(|(n, line)| {
if line.len() > 5 { if line.len() > 5 {
//println!("{}", line); Some(T::new_from_string(line.to_owned(), n as u32).expect("failed to read lines"))
Some(T::new_from_string(line.to_owned()).expect("failed to read lines"))
} else { } else {
None None
} }