Compare commits

..

5 Commits

8 changed files with 111 additions and 42 deletions

View File

@ -17,7 +17,7 @@ pub trait UserDBValidation {
pub trait UserDBWrite { pub trait UserDBWrite {
fn delete_user(&mut self, user: &str) -> Result<crate::User, crate::UserLibError>; fn delete_user(&mut self, user: &str) -> Result<crate::User, crate::UserLibError>;
fn new_user( fn new_user(
&mut self, &mut self, /*
username: String, username: String,
enc_password: String, enc_password: String,
uid: u32, uid: u32,
@ -28,7 +28,7 @@ pub trait UserDBWrite {
phone_home: String, phone_home: String,
other: Option<Vec<String>>, other: Option<Vec<String>>,
home_dir: String, home_dir: String,
shell_path: String, shell_path: String,*/
) -> Result<&crate::User, crate::UserLibError>; ) -> Result<&crate::User, crate::UserLibError>;
fn delete_group(&mut self, group: &crate::Group) -> Result<(), crate::UserLibError>; fn delete_group(&mut self, group: &crate::Group) -> Result<(), crate::UserLibError>;
fn new_group(&mut self) -> Result<&crate::Group, crate::UserLibError>; fn new_group(&mut self) -> Result<&crate::Group, crate::UserLibError>;

22
src/bin/create_user.rs Normal file
View File

@ -0,0 +1,22 @@
extern crate adduser;
fn main() {
simplelog::CombinedLogger::init(vec![simplelog::TermLogger::new(
simplelog::LevelFilter::Warn,
simplelog::Config::default(),
simplelog::TerminalMode::Mixed,
)])
.unwrap();
use adduser::api::UserDBWrite;
let mut db = adduser::UserDBLocal::load_files(adduser::Files::default());
let user = adduser::User::default()
.username("fest".into())
.shell_path("/bin/mash".into())
.clone();
println!("{}", user);
//db.new_user().expect("failed to create the user");
}

View File

@ -51,6 +51,8 @@ pub(crate) fn is_groupname_valid(name: &str) -> bool {
/// A record(line) in the user database `/etc/shadow` found in most linux systems. /// A record(line) in the user database `/etc/shadow` found in most linux systems.
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub struct Group { pub struct Group {
pos: u32,
source: String,
groupname: Groupname, /* Username. */ groupname: Groupname, /* Username. */
pub(crate) password: crate::Password, /* Usually not used (disabled with x) */ pub(crate) password: crate::Password, /* Usually not used (disabled with x) */
gid: crate::Gid, /* Group ID. */ gid: crate::Gid, /* Group ID. */
@ -118,6 +120,8 @@ impl NewFromString for Group {
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 {
pos: position,
source: line.clone(),
groupname: Groupname::try_from(elements.get(0).unwrap().to_string())?, groupname: Groupname::try_from(elements.get(0).unwrap().to_string())?,
password: crate::Password::Disabled, password: crate::Password::Disabled,
gid: crate::Gid::try_from(elements.get(2).unwrap().to_string())?, gid: crate::Gid::try_from(elements.get(2).unwrap().to_string())?,

View File

@ -8,7 +8,7 @@ use std::fmt::{self, Display};
/// The first 4 values are more or less standardised to be full name, room, phone at work and phone at home. After that there can be some extra fields often containing the emailadress and even additional information. /// The first 4 values are more or less standardised to be full name, room, phone at work and phone at home. After that there can be some extra fields often containing the emailadress and even additional information.
/// ///
/// 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`]. /// 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)] #[derive(Debug, PartialEq, Eq, Clone)]
pub enum Gecos { pub enum Gecos {
Detail { Detail {
full_name: String, full_name: String,

View File

@ -1,4 +1,5 @@
pub mod gecos_fields; pub mod gecos_fields;
pub mod passwd_fields; pub mod passwd_fields;
pub mod shadow_fields; pub mod shadow_fields;
@ -7,7 +8,7 @@ use std::convert::TryFrom;
use std::fmt::{self, Display}; use std::fmt::{self, Display};
/// A record(line) in the user database `/etc/passwd` found in most linux systems. /// A record(line) in the user database `/etc/passwd` found in most linux systems.
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq, Clone)]
pub struct User { pub struct User {
source: String, source: String,
pos: u32, pos: u32,
@ -20,6 +21,35 @@ pub struct User {
shell_path: crate::ShellPath, /* Shell program. */ shell_path: crate::ShellPath, /* Shell program. */
} }
impl User {
pub fn username(&mut self, name: String) -> &mut Self {
self.username = crate::Username {
username: name.into(),
};
self
}
pub fn disable_password(&mut self) -> &mut Self {
self.password = crate::Password::Disabled;
self
}
pub fn uid(&mut self, uid: u32) -> &mut Self {
self.uid = crate::Uid { uid };
self
}
pub fn gid(&mut self, gid: u32) -> &mut Self {
self.gid = crate::Gid { gid };
self
}
pub fn home_dir(&mut self, path: String) -> &mut Self {
self.home_dir = crate::HomeDir { dir: path };
self
}
pub fn shell_path(&mut self, path: String) -> &mut Self {
self.shell_path = crate::ShellPath { shell: path };
self
}
}
impl NewFromString for User { impl NewFromString for User {
/// Parse a line formatted like one in `/etc/passwd` and construct a matching [`User`] instance /// Parse a line formatted like one in `/etc/passwd` and construct a matching [`User`] instance
/// ///
@ -132,21 +162,19 @@ impl Default for User {
source: "".to_owned(), source: "".to_owned(),
pos: u32::MAX, pos: u32::MAX,
username: crate::Username { username: crate::Username {
username: "defaultuser".to_owned(), username: "defaultusername".to_owned(),
}, },
password: crate::Password::Encrypted(crate::EncryptedPassword { password: crate::Password::Disabled,
password: "notencrypted".to_owned(),
}),
uid: crate::Uid { uid: 1001 }, uid: crate::Uid { uid: 1001 },
gid: crate::Gid { gid: 1001 }, gid: crate::Gid { gid: 1001 },
gecos: crate::Gecos::Simple { gecos: crate::Gecos::Simple {
comment: "gecos default comment".to_string(), comment: "".to_string(),
}, },
home_dir: crate::HomeDir { home_dir: crate::HomeDir {
dir: "/home/default".to_owned(), dir: "/".to_owned(),
}, },
shell_path: crate::ShellPath { shell_path: crate::ShellPath {
shell: "/bin/bash".to_owned(), shell: "/bin/nologin".to_owned(),
}, },
} }
} }
@ -171,10 +199,12 @@ impl Display for User {
#[test] #[test]
fn test_default_user() { fn test_default_user() {
// Check if a user can be created. // Check if a user can be created.
let pwd = User::default(); let mut pwd = User::default();
assert_eq!(pwd.username.username, "defaultuser"); assert_eq!(pwd.username.username, "defaultuser");
assert_eq!(pwd.home_dir.dir, "/home/default"); assert_eq!(pwd.home_dir.dir, "/home/default");
assert_eq!(pwd.uid.uid, 1001); assert_eq!(pwd.uid.uid, 1001);
let npw = pwd.username("test".to_owned()).clone();
assert_eq!(npw.username.username, "test");
} }
#[test] #[test]

View File

@ -20,7 +20,7 @@ use std::fmt::{self, Display};
/// When done the validity will automatically be checked in the `trait TryFrom`. /// When done the validity will automatically be checked in the `trait TryFrom`.
/// ///
/// In the future some extra fields might be added. /// In the future some extra fields might be added.
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq, Clone)]
pub struct Username { pub struct Username {
/// The username value /// The username value
pub(crate) username: String, pub(crate) username: String,
@ -57,7 +57,7 @@ pub(crate) fn is_username_valid(name: &str) -> bool {
USERVALIDATION.is_match(name) USERVALIDATION.is_match(name)
} }
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq, Clone)]
pub enum Password { pub enum Password {
Encrypted(crate::EncryptedPassword), Encrypted(crate::EncryptedPassword),
Shadow(crate::Shadow), Shadow(crate::Shadow),
@ -74,7 +74,7 @@ impl Display for Password {
} }
} }
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq, Clone)]
pub struct EncryptedPassword { pub struct EncryptedPassword {
pub(in crate::user) password: String, pub(in crate::user) password: String,
} }
@ -97,7 +97,7 @@ impl TryFrom<String> for EncryptedPassword {
} }
} }
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq, Clone)]
pub struct Uid { pub struct Uid {
pub(in crate::user) uid: u32, pub(in crate::user) uid: u32,
} }
@ -125,7 +125,7 @@ impl Uid {
} }
} }
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq, Clone)]
pub struct Gid { pub struct Gid {
pub(in crate::user) gid: u32, pub(in crate::user) gid: u32,
} }
@ -158,7 +158,7 @@ impl Gid {
} }
/// The home directory of a user /// The home directory of a user
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq, Clone)]
pub struct HomeDir { pub struct HomeDir {
pub(in crate::user) dir: String, pub(in crate::user) dir: String,
} }
@ -177,7 +177,7 @@ impl TryFrom<String> for HomeDir {
} }
/// The path to the Shell binary /// The path to the Shell binary
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq, Clone)]
pub struct ShellPath { pub struct ShellPath {
pub(in crate::user) shell: String, pub(in crate::user) shell: String,
} }

View File

@ -16,8 +16,10 @@ use std::convert::TryFrom;
use std::fmt::{self, Debug, Display}; use std::fmt::{self, Debug, Display};
/// A record(line) in the user database `/etc/shadow` found in most linux systems. /// A record(line) in the user database `/etc/shadow` found in most linux systems.
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq, Clone)]
pub struct Shadow { pub struct Shadow {
pos: u32,
source: String,
username: crate::Username, /* Username. */ username: crate::Username, /* Username. */
pub(crate) password: crate::EncryptedPassword, /* Hashed passphrase */ pub(crate) password: crate::EncryptedPassword, /* Hashed passphrase */
last_change: Option<chrono::NaiveDateTime>, /* User ID. */ last_change: Option<chrono::NaiveDateTime>, /* User ID. */
@ -98,6 +100,8 @@ impl NewFromString for Shadow {
if elements.len() == 9 { if elements.len() == 9 {
let extra = elements.get(8).unwrap(); let extra = elements.get(8).unwrap();
Ok(Self { Ok(Self {
pos: position,
source: line.clone(),
username: crate::Username::try_from(elements.get(0).unwrap().to_string())?, username: crate::Username::try_from(elements.get(0).unwrap().to_string())?,
password: crate::EncryptedPassword::try_from(elements.get(1).unwrap().to_string())?, password: crate::EncryptedPassword::try_from(elements.get(1).unwrap().to_string())?,
last_change: date_since_epoch(elements.get(2).unwrap()), last_change: date_since_epoch(elements.get(2).unwrap()),

View File

@ -91,7 +91,7 @@ impl UserDBWrite for UserDBLocal {
} }
fn new_user( fn new_user(
&mut self, &mut self, /*
username: String, username: String,
enc_password: String, enc_password: String,
uid: u32, uid: u32,
@ -102,7 +102,7 @@ impl UserDBWrite for UserDBLocal {
phone_home: String, phone_home: String,
other: Option<Vec<String>>, other: Option<Vec<String>>,
home_dir: String, home_dir: String,
shell_path: String, shell_path: String,*/
) -> Result<&crate::User, crate::UserLibError> { ) -> Result<&crate::User, crate::UserLibError> {
/*if self.users.contains_key(&username) { /*if self.users.contains_key(&username) {
Err(format!( Err(format!(
@ -216,6 +216,15 @@ impl UserDBValidation for UserDBLocal {
} }
} }
fn get_nth_line(path: Option<&PathBuf>, n: u32) -> String {
let lines = file_to_string(path);
let line = lines.lines().nth(n as usize);
match line {
Some(line) => line.to_owned(),
None => "".to_owned(),
}
}
/// Parse a file to a string /// Parse a file to a string
fn file_to_string(path: Option<&PathBuf>) -> String { fn file_to_string(path: Option<&PathBuf>) -> String {
let file = File::open(path.expect("Path cannot be None".into())) let file = File::open(path.expect("Path cannot be None".into()))