From de17ff12be76e156d17d0fc2aa4a5c6bea1825c3 Mon Sep 17 00:00:00 2001 From: Dietrich Date: Tue, 10 Nov 2020 13:45:21 +0100 Subject: [PATCH] first version of create_user --- src/api/createuser_args.rs | 40 ++++++++++ .../{newuser_args.rs => deleteuser_args.rs} | 10 +-- src/api/mod.rs | 26 ++----- src/bin/create_user.rs | 27 +++++-- src/bin/delete_user.rs | 2 +- src/lib.rs | 1 + src/userlib/files.rs | 33 +++++++- src/userlib/mod.rs | 76 ++++++++----------- tests/create_user_test.rs | 42 ++++++++++ tests/delete_user_test.rs | 2 +- 10 files changed, 174 insertions(+), 85 deletions(-) create mode 100644 src/api/createuser_args.rs rename src/api/{newuser_args.rs => deleteuser_args.rs} (78%) create mode 100644 tests/create_user_test.rs diff --git a/src/api/createuser_args.rs b/src/api/createuser_args.rs new file mode 100644 index 0000000..07fd37e --- /dev/null +++ b/src/api/createuser_args.rs @@ -0,0 +1,40 @@ +#![allow(clippy::default_trait_access)] +use std::path::PathBuf; + +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum CreateHome { + Create, + Skip, + HomeFromDir { path: PathBuf }, +} +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum CreatePrimaryGroup { + Create, + Skip, + CreateIfEmptyOrAdd, +} +#[derive(Debug, Builder, Eq, PartialEq)] +#[builder(public)] +#[builder(default)] +pub struct CreateUserArgs<'a> { + pub username: &'a str, + pub delete_home: CreateHome, + pub delete_primary_group: CreatePrimaryGroup, +} + +impl<'a> CreateUserArgs<'a> { + #[must_use] + pub fn builder() -> CreateUserArgsBuilder<'a> { + CreateUserArgsBuilder::default() + } +} + +impl Default for CreateUserArgs<'_> { + fn default() -> Self { + Self { + username: "defaultuser", + delete_home: CreateHome::Create, + delete_primary_group: CreatePrimaryGroup::CreateIfEmptyOrAdd, + } + } +} diff --git a/src/api/newuser_args.rs b/src/api/deleteuser_args.rs similarity index 78% rename from src/api/newuser_args.rs rename to src/api/deleteuser_args.rs index c62f887..336a231 100644 --- a/src/api/newuser_args.rs +++ b/src/api/deleteuser_args.rs @@ -16,20 +16,20 @@ pub enum DeletePrimaryGroup { #[derive(Debug, Builder, Eq, PartialEq)] #[builder(public)] #[builder(default)] -pub struct NewUserArgs<'a> { +pub struct DeleteUserArgs<'a> { pub username: &'a str, pub delete_home: DeleteHome, pub delete_primary_group: DeletePrimaryGroup, } -impl<'a> NewUserArgs<'a> { +impl<'a> DeleteUserArgs<'a> { #[must_use] - pub fn builder() -> NewUserArgsBuilder<'a> { - NewUserArgsBuilder::default() + pub fn builder() -> DeleteUserArgsBuilder<'a> { + DeleteUserArgsBuilder::default() } } -impl Default for NewUserArgs<'_> { +impl Default for DeleteUserArgs<'_> { fn default() -> Self { Self { username: "defaultuser", diff --git a/src/api/mod.rs b/src/api/mod.rs index fa560a1..23b6bef 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -1,6 +1,8 @@ -pub mod newuser_args; +pub mod createuser_args; +pub mod deleteuser_args; -pub use newuser_args::{DeleteHome, DeletePrimaryGroup, NewUserArgs}; +pub use createuser_args::{CreateHome, CreatePrimaryGroup, CreateUserArgs}; +pub use deleteuser_args::{DeleteHome, DeletePrimaryGroup, DeleteUserArgs}; pub trait UserDBRead { fn get_all_users(&self) -> Vec<&crate::User>; fn get_user_by_name(&self, name: &str) -> Option<&crate::User>; @@ -18,24 +20,8 @@ pub trait UserDBValidation { } pub trait UserDBWrite { - fn delete_user( - &mut self, - params: newuser_args::NewUserArgs, - ) -> Result; - fn new_user( - &mut self, /* - username: String, - enc_password: String, - uid: u32, - gid: u32, - full_name: String, - room: String, - phone_work: String, - phone_home: String, - other: Option>, - home_dir: String, - shell_path: String,*/ - ) -> Result<&crate::User, crate::UserLibError>; + fn delete_user(&mut self, params: DeleteUserArgs) -> Result; + fn new_user(&mut self, params: CreateUserArgs) -> Result<&crate::User, crate::UserLibError>; fn delete_group(&mut self, group: &crate::Group) -> Result<(), crate::UserLibError>; fn new_group(&mut self) -> Result<&crate::Group, crate::UserLibError>; } diff --git a/src/bin/create_user.rs b/src/bin/create_user.rs index 2f2bcd7..a834771 100644 --- a/src/bin/create_user.rs +++ b/src/bin/create_user.rs @@ -1,15 +1,26 @@ +use std::path::PathBuf; + extern crate adduser; +use adduser::api::UserDBWrite; fn main() { - simplelog::CombinedLogger::init(vec![simplelog::TermLogger::new( - simplelog::LevelFilter::Warn, - simplelog::Config::default(), - simplelog::TerminalMode::Mixed, - )]) - .unwrap(); - //use adduser::api::UserDBWrite; + env_logger::init(); - let _db = adduser::UserDBLocal::load_files(adduser::Files::default()); + let mf = adduser::Files { + passwd: Some(PathBuf::from("./passwd")), + shadow: Some(PathBuf::from("./shadow")), + group: Some(PathBuf::from("./group")), + }; + + let mut db = adduser::UserDBLocal::load_files(mf).unwrap(); + + let _user_res: Result<&adduser::User, adduser::UserLibError> = db.new_user( + adduser::api::CreateUserArgs::builder() + .username("teste") + // .delete_home(adduser::api::DeleteHome::Delete) + .build() + .unwrap(), + ); let user = adduser::User::default() .username("fest".into()) diff --git a/src/bin/delete_user.rs b/src/bin/delete_user.rs index 726b4cd..96d70c3 100644 --- a/src/bin/delete_user.rs +++ b/src/bin/delete_user.rs @@ -21,7 +21,7 @@ fn main() { let mut db = adduser::UserDBLocal::load_files(mf).unwrap(); let user_res: Result = db.delete_user( - adduser::api::NewUserArgs::builder() + adduser::api::DeleteUserArgs::builder() .username("teste") // .delete_home(adduser::api::DeleteHome::Delete) .build() diff --git a/src/lib.rs b/src/lib.rs index bc10321..94ee729 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ +#![feature(option_expect_none)] #![warn( clippy::all, //clippy::restriction, diff --git a/src/userlib/files.rs b/src/userlib/files.rs index b81fac3..10d8442 100644 --- a/src/userlib/files.rs +++ b/src/userlib/files.rs @@ -1,12 +1,13 @@ -use std::path::PathBuf; +use std::{io::Seek, io::SeekFrom, path::PathBuf}; #[allow(unused_imports)] use log::{debug, error, info, trace, warn}; -use std::fs::File; +use std::fs::{File, OpenOptions}; use std::io::Read; use std::io::Write; use std::ops::Deref; +#[derive(Debug)] pub struct Files { pub passwd: Option, pub shadow: Option, @@ -62,14 +63,18 @@ impl Files { } } +#[derive(Debug)] pub struct LockedFileGuard { lockfile: PathBuf, path: PathBuf, pub(crate) file: File, } + +#[derive(Debug)] struct TempLockFile { tlf: PathBuf, } + impl Drop for TempLockFile { fn drop(&mut self) { info!("removing temporary lockfile {}", self.tlf.to_str().unwrap()); @@ -82,7 +87,6 @@ impl Deref for TempLockFile { &self.tlf } } - impl LockedFileGuard { pub fn new(path: &PathBuf) -> Result { let locked = Self::try_to_lock_file(path); @@ -109,6 +113,27 @@ impl LockedFileGuard { Ok(()) } + pub fn append(&mut self, appendee: String) -> Result<(), crate::UserLibError> { + // Seek to the last character. + self.file.seek(SeekFrom::End(-1)).map_or_else( + |e| Err(format!("Failed to append to file {}", e)), + |_| Ok(()), + )?; + // Read the last character + let mut b = [0 as u8; 1]; + self.file.read_exact(&mut b)?; + // Verify it is '\n' else append '\n' so in any case the file ends with with a newline now + if &b != b"\n" { + //self.file.write_all(&b)?; + self.file.write_all(b"\n")?; + } + // write the new line. + self.file.write_all(&appendee.into_bytes()).map_or_else( + |e| Err(("Failed to append to file".to_owned(), e).into()), + Ok, + ) + } + /// This function tries to lock a file in the way other passwd locking mechanisms work. /// /// * get the pid @@ -177,7 +202,7 @@ impl LockedFileGuard { debug!("successfully locked"); // open the file - let resfile = File::open(&path); + let resfile = OpenOptions::new().read(true).write(true).open(&path); return match resfile { Ok(file) => Ok((lockfilepath, file)), Err(e) => { diff --git a/src/userlib/mod.rs b/src/userlib/mod.rs index f6085c5..b15655a 100644 --- a/src/userlib/mod.rs +++ b/src/userlib/mod.rs @@ -3,8 +3,12 @@ pub mod files; pub mod hashes; -use crate::api::UserRead; -use crate::{api::GroupRead, UserLibError}; +use crate::{ + api::{ + CreateUserArgs, DeleteHome, DeleteUserArgs, GroupRead, UserDBRead, UserDBWrite, UserRead, + }, + UserLibError, +}; #[allow(unused_imports)] use log::{debug, error, info, trace, warn}; use std::collections::HashMap; @@ -146,9 +150,8 @@ impl UserDBLocal { } } -use crate::api::{DeleteHome, NewUserArgs, UserDBRead, UserDBWrite}; impl UserDBWrite for UserDBLocal { - fn delete_user(&mut self, args: NewUserArgs) -> Result { + fn delete_user(&mut self, args: DeleteUserArgs) -> Result { // try to get the user from the database let user_opt = self.get_user_by_name(args.username); let user = match user_opt { @@ -218,47 +221,28 @@ impl UserDBWrite for UserDBLocal { } } - fn new_user( - &mut self, /* - username: String, - enc_password: String, - uid: u32, - gid: u32, - full_name: String, - room: String, - phone_work: String, - phone_home: String, - other: Option>, - home_dir: String, - shell_path: String,*/ - ) -> Result<&crate::User, crate::UserLibError> { - /*if self.users.contains_key(&username) { - Err(format!( - "The username {} already exists! Aborting!", - username - ) - .into()) + fn new_user(&mut self, args: CreateUserArgs) -> Result<&crate::User, crate::UserLibError> { + if self.users.contains_key(args.username) { + Err(format!("The username {} already exists! Aborting!", args.username).into()) } else { - let pwd = if self.source_files.shadow.is_none(){ - crate::Password::Encrypted(crate::EncryptedPassword{}); + let mut new_user = crate::User::default(); + new_user.username(args.username.to_owned()); + if self.users.contains_key(args.username) { + Err("Failed to create the user. A user with the same Name already exists".into()) + } else { + let opened = self.source_files.lock_all_get(); + let (mut locked_p, mut _locked_s, mut _locked_g) = + opened.expect("failed to lock files!"); + dbg!(&locked_p); + locked_p.append(format!("{}", new_user))?; + self.users + .insert(args.username.to_owned(), new_user) + .expect_none("Is always none"); + self.users + .get(args.username) + .map_or_else(|| Err("User was not successfully added!".into()), Ok) } - else{ - crate::Password::Shadow(crate::Shadow{}) - } - self.users.insert( - username, - crate::User { - username: crate::Username { username }, - password:, - uid: crate::Uid{uid}, - gid:crate::Gid{gid}, - gecos: crate::Gecos{}, - home_dir:crate::HomeDir{dir: home_dir}, - shell_path: crate::ShellPath{shell: shell_path}, - }, - ) - }*/ - todo!() + } } fn delete_group(&mut self, _group: &crate::Group) -> Result<(), crate::UserLibError> { @@ -472,16 +456,16 @@ fn test_user_db_read_implementation() { #[test] fn test_user_db_write_implementation() { - use crate::api::NewUserArgs; + use crate::api::DeleteUserArgs; let mut 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"); let user = "test"; assert_eq!(data.get_all_users().len(), 1); assert!(data - .delete_user(NewUserArgs::builder().username(user).build().unwrap()) + .delete_user(DeleteUserArgs::builder().username(user).build().unwrap()) .is_ok()); assert!(data - .delete_user(NewUserArgs::builder().username(user).build().unwrap()) + .delete_user(DeleteUserArgs::builder().username(user).build().unwrap()) .is_err()); assert_eq!(data.get_all_users().len(), 0); } diff --git a/tests/create_user_test.rs b/tests/create_user_test.rs new file mode 100644 index 0000000..b7701a3 --- /dev/null +++ b/tests/create_user_test.rs @@ -0,0 +1,42 @@ +extern crate adduser; +mod testfiles; + +#[test] +fn test_create_user_function() { + use testfiles::Fixture; + + use adduser::api::UserDBWrite; + use adduser::api::UserRead; + use std::fs; + + let p = Fixture::copy("passwd"); + let s = Fixture::copy("shadow"); + let g = Fixture::copy("group"); + + let pf = fs::read_to_string(&p.path).unwrap(); + + let mf = adduser::Files { + passwd: Some(p.path.clone()), + shadow: Some(s.path), + group: Some(g.path), + }; + + let mut db = adduser::UserDBLocal::load_files(mf).unwrap(); + + let user_res: Result<&adduser::User, adduser::UserLibError> = db.new_user( + adduser::api::CreateUserArgs::builder() + .username("test2") + // .delete_home(adduser::api::DeleteHome::Delete) + .build() + .unwrap(), + ); + let pf2 = fs::read_to_string(&p.path).unwrap(); + assert_eq!(user_res.unwrap().get_username().unwrap(), "test2"); + let pflines = pf.lines(); + let pflines2 = pf2.lines(); + for (l1, l2) in pflines.zip(pflines2) { + dbg!(l1, l2); + assert!(l1 == l2); + } + assert!(pf2.lines().last().unwrap().starts_with("test2")); +} diff --git a/tests/delete_user_test.rs b/tests/delete_user_test.rs index afbbbb1..8bd77c2 100644 --- a/tests/delete_user_test.rs +++ b/tests/delete_user_test.rs @@ -24,7 +24,7 @@ fn test_delete_user_function() { let mut db = adduser::UserDBLocal::load_files(mf).unwrap(); let user_res: Result = db.delete_user( - adduser::api::NewUserArgs::builder() + adduser::api::DeleteUserArgs::builder() .username("teste") // .delete_home(adduser::api::DeleteHome::Delete) .build()