first draft of working user deletion
TODO: * remove user group * remove shadow entry
This commit is contained in:
parent
b8c1e3989a
commit
bf6908346e
@ -1,14 +1,31 @@
|
|||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
extern crate adduser;
|
extern crate adduser;
|
||||||
|
|
||||||
extern crate env_logger;
|
extern crate env_logger;
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use log::{debug, error, info, trace, warn};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
|
|
||||||
use adduser::api::UserDBWrite;
|
use adduser::api::UserDBWrite;
|
||||||
|
use adduser::api::UserRead;
|
||||||
|
|
||||||
let mut 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 user = db.delete_user("teste").expect("failed to delete the user");
|
let mut db = adduser::UserDBLocal::load_files(mf);
|
||||||
println!("The user {} has been deleted!", user);
|
|
||||||
|
let user_res: Result<adduser::User, adduser::UserLibError> = db.delete_user("teste");
|
||||||
|
match user_res {
|
||||||
|
Ok(u) => info!(
|
||||||
|
"The user <{}> has been deleted! ",
|
||||||
|
u.get_username().unwrap()
|
||||||
|
),
|
||||||
|
Err(e) => error!("Failed to delete the user: {}", e),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,8 @@ pub mod passwd_fields;
|
|||||||
pub mod shadow_fields;
|
pub mod shadow_fields;
|
||||||
|
|
||||||
use crate::userlib::NewFromString;
|
use crate::userlib::NewFromString;
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use log::{debug, error, info, trace, warn};
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::fmt::{self, Display};
|
use std::fmt::{self, Display};
|
||||||
|
|
||||||
@ -22,6 +24,31 @@ pub struct User {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl User {
|
impl User {
|
||||||
|
/*fn get_nth_line(content: &str, n: u32) -> (String, u64) {
|
||||||
|
use std::io::BufRead;
|
||||||
|
let mut cursor = std::io::Cursor::new(content);
|
||||||
|
|
||||||
|
let mut offset: u64 = 0;
|
||||||
|
let mut tmp = String::new();
|
||||||
|
for _ in 0..n + 1 {
|
||||||
|
tmp = String::new();
|
||||||
|
offset += cursor.read_line(&mut tmp).expect("failed to read line") as u64;
|
||||||
|
//trace!("reading user{}: {}", i, &tmp);
|
||||||
|
}
|
||||||
|
(tmp.trim().to_owned(), offset)
|
||||||
|
}
|
||||||
|
pub(crate) fn is_unchanged_in(&self, content: &str) -> (u64, usize, bool) {
|
||||||
|
let (line, offset) = Self::get_nth_line(content, self.pos);
|
||||||
|
trace!("Olduser:\n\t{}\nNewuser\n\t{}", &self.source, &line);
|
||||||
|
(offset, line.len(), line == self.source)
|
||||||
|
}*/
|
||||||
|
pub fn remove_in(&self, content: &str) -> String {
|
||||||
|
content
|
||||||
|
.split(&self.source)
|
||||||
|
.map(|x| x.trim())
|
||||||
|
.collect::<Vec<&str>>()
|
||||||
|
.join("\n")
|
||||||
|
}
|
||||||
pub fn username(&mut self, name: String) -> &mut Self {
|
pub fn username(&mut self, name: String) -> &mut Self {
|
||||||
self.username = crate::Username {
|
self.username = crate::Username {
|
||||||
username: name.into(),
|
username: name.into(),
|
||||||
|
100
src/userlib.rs
100
src/userlib.rs
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
use crate::api::GroupRead;
|
use crate::api::GroupRead;
|
||||||
use crate::api::UserRead;
|
use crate::api::UserRead;
|
||||||
use log::{debug, error, info, warn};
|
use log::{debug, error, info, trace, warn};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{BufReader, Read};
|
use std::io::{BufReader, Read};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
@ -18,14 +18,15 @@ use std::{collections::HashMap, io::Write};
|
|||||||
|
|
||||||
pub struct UserDBLocal {
|
pub struct UserDBLocal {
|
||||||
source_files: Files,
|
source_files: Files,
|
||||||
|
source_hashes: Hashes, // to detect changes
|
||||||
pub users: HashMap<String, crate::User>,
|
pub users: HashMap<String, crate::User>,
|
||||||
pub groups: Vec<crate::Group>,
|
pub groups: Vec<crate::Group>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Files {
|
pub struct Files {
|
||||||
passwd: Option<PathBuf>,
|
pub passwd: Option<PathBuf>,
|
||||||
shadow: Option<PathBuf>,
|
pub shadow: Option<PathBuf>,
|
||||||
group: Option<PathBuf>,
|
pub group: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Files {
|
impl Default for Files {
|
||||||
@ -62,6 +63,7 @@ impl Files {
|
|||||||
|
|
||||||
pub struct LockedFileGuard {
|
pub struct LockedFileGuard {
|
||||||
lockfile: PathBuf,
|
lockfile: PathBuf,
|
||||||
|
path: PathBuf,
|
||||||
pub(crate) file: File,
|
pub(crate) file: File,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,11 +71,26 @@ impl LockedFileGuard {
|
|||||||
pub fn new(path: &PathBuf) -> Result<Self, crate::userlib_error::UserLibError> {
|
pub fn new(path: &PathBuf) -> Result<Self, crate::userlib_error::UserLibError> {
|
||||||
let locked = Self::try_to_lock_file(path);
|
let locked = Self::try_to_lock_file(path);
|
||||||
match locked {
|
match locked {
|
||||||
Ok((lockfile, file)) => Ok(Self { lockfile, file }),
|
Ok((lockfile, file)) => Ok(Self {
|
||||||
|
lockfile,
|
||||||
|
path: path.to_owned(),
|
||||||
|
file,
|
||||||
|
}),
|
||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn replace_contents(
|
||||||
|
&mut self,
|
||||||
|
new_content: String,
|
||||||
|
) -> Result<(), crate::userlib_error::UserLibError> {
|
||||||
|
self.file = File::create(&self.path).expect("Failed to truncate file.");
|
||||||
|
self.file
|
||||||
|
.write_all(&new_content.into_bytes())
|
||||||
|
.expect("Failed to write all users.");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// This function tries to lock a file in the way other passwd locking mechanisms work.
|
/// This function tries to lock a file in the way other passwd locking mechanisms work.
|
||||||
///
|
///
|
||||||
/// * get the pid
|
/// * get the pid
|
||||||
@ -246,6 +263,7 @@ impl UserDBLocal {
|
|||||||
},
|
},
|
||||||
users,
|
users,
|
||||||
groups,
|
groups,
|
||||||
|
source_hashes: Hashes::new(&passwd_content, &shadow_content, &group_content),
|
||||||
};
|
};
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
@ -272,17 +290,40 @@ impl UserDBLocal {
|
|||||||
source_files: files,
|
source_files: files,
|
||||||
users,
|
users,
|
||||||
groups: string_to(&my_group_lines),
|
groups: string_to(&my_group_lines),
|
||||||
|
source_hashes: Hashes::new(&my_passwd_lines, &my_shadow_lines, &my_group_lines),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use crate::api::UserDBWrite;
|
use crate::api::UserDBWrite;
|
||||||
impl UserDBWrite for UserDBLocal {
|
impl UserDBWrite for UserDBLocal {
|
||||||
fn delete_user(&mut self, user: &str) -> Result<crate::User, crate::UserLibError> {
|
fn delete_user(&mut self, username: &str) -> Result<crate::User, crate::UserLibError> {
|
||||||
let res = self.users.remove(user);
|
let opened = self.source_files.lock_all_get();
|
||||||
match res {
|
let (mut locked_p, locked_s, locked_g) = opened.expect("failed to lock files!");
|
||||||
Some(user) => Ok(user),
|
// read the files to strings
|
||||||
None => Err(format!("Failed to delete the user {}", user).into()),
|
let p = file_to_string(&locked_p.file);
|
||||||
|
let _s = file_to_string(&locked_s.file);
|
||||||
|
let _g = file_to_string(&locked_g.file);
|
||||||
|
{
|
||||||
|
let user_opt = self.users.get(username);
|
||||||
|
let user = match user_opt {
|
||||||
|
Some(user) => user,
|
||||||
|
None => {
|
||||||
|
return Err(crate::UserLibError::NotFound);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if self.source_hashes.passwd.has_changed(&p) {
|
||||||
|
error!("The source file has changed. Deleting the user could corrupt the userdatabase. Aborting!");
|
||||||
|
} else {
|
||||||
|
// create the new content of passwd
|
||||||
|
let modified = user.remove_in(&p);
|
||||||
|
// write the new content to the file.
|
||||||
|
locked_p
|
||||||
|
.replace_contents(modified)
|
||||||
|
.expect("Error during write to the database. Please doublecheck as the userdatabase could be corrupted");
|
||||||
|
return Ok(user.clone());
|
||||||
|
}
|
||||||
|
Err(format!("The user has been changed {}", username).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -412,12 +453,39 @@ impl UserDBValidation for UserDBLocal {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_nth_line(path: &File, n: u32) -> String {
|
pub struct SourceHash {
|
||||||
let lines = file_to_string(path);
|
hashvalue: String,
|
||||||
let line = lines.lines().nth(n as usize);
|
}
|
||||||
match line {
|
|
||||||
Some(line) => line.to_owned(),
|
impl SourceHash {
|
||||||
None => "".to_owned(),
|
pub fn new(src: &str) -> Self {
|
||||||
|
Self {
|
||||||
|
hashvalue: src.to_owned(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn has_changed(&self, new: &str) -> bool {
|
||||||
|
trace!(
|
||||||
|
"Old and new lengths: {}, {}",
|
||||||
|
self.hashvalue.len(),
|
||||||
|
new.len()
|
||||||
|
);
|
||||||
|
!self.hashvalue.eq(new)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Hashes {
|
||||||
|
pub passwd: SourceHash,
|
||||||
|
pub shadow: SourceHash,
|
||||||
|
pub group: SourceHash,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hashes {
|
||||||
|
pub fn new(passwd: &str, shadow: &str, group: &str) -> Self {
|
||||||
|
Self {
|
||||||
|
passwd: SourceHash::new(passwd),
|
||||||
|
shadow: SourceHash::new(shadow),
|
||||||
|
group: SourceHash::new(group),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user