restructure

This commit is contained in:
Dietrich 2020-10-01 09:38:13 +02:00
parent 1ab0adbc06
commit 063fb41708
5 changed files with 305 additions and 159 deletions

129
Cargo.lock generated
View File

@ -5,7 +5,9 @@ name = "adduser"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"lazy_static", "lazy_static",
"log",
"regex", "regex",
"simplelog",
] ]
[[package]] [[package]]
@ -17,18 +19,77 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "autocfg"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]]
name = "chrono"
version = "0.4.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
dependencies = [
"libc",
"num-integer",
"num-traits",
"time",
"winapi",
]
[[package]] [[package]]
name = "lazy_static" name = "lazy_static"
version = "1.4.0" version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa7087f49d294270db4e1928fc110c976cd4b9e5a16348e0a1df09afa99e6c98"
[[package]]
name = "log"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b"
dependencies = [
"cfg-if",
]
[[package]] [[package]]
name = "memchr" name = "memchr"
version = "2.3.3" version = "2.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
[[package]]
name = "num-integer"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611"
dependencies = [
"autocfg",
]
[[package]] [[package]]
name = "regex" name = "regex"
version = "1.3.9" version = "1.3.9"
@ -47,6 +108,26 @@ version = "0.6.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8" checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8"
[[package]]
name = "simplelog"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b2736f58087298a448859961d3f4a0850b832e72619d75adc69da7993c2cd3c"
dependencies = [
"chrono",
"log",
"termcolor",
]
[[package]]
name = "termcolor"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f"
dependencies = [
"winapi-util",
]
[[package]] [[package]]
name = "thread_local" name = "thread_local"
version = "1.0.1" version = "1.0.1"
@ -55,3 +136,51 @@ checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
dependencies = [ dependencies = [
"lazy_static", "lazy_static",
] ]
[[package]]
name = "time"
version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
dependencies = [
"libc",
"wasi",
"winapi",
]
[[package]]
name = "wasi"
version = "0.10.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

View File

@ -9,3 +9,5 @@ edition = "2018"
[dependencies] [dependencies]
regex = "1" regex = "1"
lazy_static = "1.4" lazy_static = "1.4"
log = "0.4"
simplelog = "0.8"

View File

@ -5,11 +5,19 @@ use std::fs::File;
use std::io::{prelude::*, BufReader}; use std::io::{prelude::*, BufReader};
fn main() { fn main() {
simplelog::CombinedLogger::init(vec![simplelog::TermLogger::new(
simplelog::LevelFilter::Warn,
simplelog::Config::default(),
simplelog::TerminalMode::Mixed,
)])
.unwrap();
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 line in reader.lines() {
println!("{}", Passwd::new_from_string(&line.unwrap()).unwrap()); let line = line.unwrap();
println!("{}", line);
println!("{}", Passwd::new_from_string(&line).unwrap());
} }
// let pwd = Passwd::default(); // let pwd = Passwd::default();

View File

@ -1,5 +1,6 @@
#[macro_use] #[macro_use]
extern crate lazy_static; extern crate lazy_static;
extern crate log;
pub mod passwd; pub mod passwd;
pub mod userlib_error; pub mod userlib_error;

View File

@ -7,6 +7,7 @@
)] )]
#![allow(clippy::non_ascii_literal)] #![allow(clippy::non_ascii_literal)]
use log::warn;
use regex::Regex; use regex::Regex;
use crate::userlib_error::UserLibError; use crate::userlib_error::UserLibError;
@ -25,11 +26,50 @@ pub struct Username<'a> {
username: &'a str, username: &'a str,
} }
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> {
type Error = UserLibError;
fn try_from(source: &'a str) -> std::result::Result<Self, Self::Error> {
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) {
Ok(Self { username: source })
} else {
Err(UserLibError::Message("Invalid username".into()))
}
}
}
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub struct Password<'a> { pub struct Password<'a> {
password: &'a str, password: &'a str,
} }
impl Display for Password<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.password,)
}
}
impl<'a> TryFrom<&'a str> for Password<'a> {
type Error = UserLibError;
fn try_from(source: &'a str) -> std::result::Result<Self, Self::Error> {
if source == "x" {
warn!("password from shadow not loaded!")
} else {
warn!("Password field has an unexpected value")
};
Ok(Self { password: source })
}
}
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub struct Uid { pub struct Uid {
uid: u32, uid: u32,
@ -39,6 +79,7 @@ pub struct Uid {
pub struct Gid { pub struct Gid {
gid: u32, gid: u32,
} }
/// The gecos field of a user. /// The gecos field of a user.
/// ///
/// In the `/etc/passwd` file this field is a `,` sepparated list of items. /// In the `/etc/passwd` file this field is a `,` sepparated list of items.
@ -59,6 +100,129 @@ pub enum Gecos<'a> {
}, },
} }
impl<'a> Gecos<'a> {
#[must_use]
pub const 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 {
Gecos::Simple { .. } => None,
Gecos::Detail { full_name, .. } => {
if full_name.is_empty() {
None
} else {
Some(full_name)
}
}
}
}
#[must_use]
pub const fn get_room(&'a self) -> Option<&'a str> {
match *self {
Gecos::Simple { .. } => None,
Gecos::Detail { room, .. } => {
if room.is_empty() {
None
} else {
Some(room)
}
}
}
}
#[must_use]
pub const fn get_phone_work(&'a self) -> Option<&'a str> {
match *self {
Gecos::Simple { .. } => None,
Gecos::Detail { phone_work, .. } => {
if phone_work.is_empty() {
None
} else {
Some(phone_work)
}
}
}
}
#[must_use]
pub const fn get_phone_home(&'a self) -> Option<&'a str> {
match *self {
Gecos::Simple { .. } => None,
Gecos::Detail { phone_home, .. } => {
if phone_home.is_empty() {
None
} else {
Some(phone_home)
}
}
}
}
#[must_use]
pub const fn get_other(&'a self) -> Option<&Vec<&'a str>> {
match self {
Gecos::Simple { .. } => None,
Gecos::Detail { other, .. } => match other {
None => None,
Some(comments) => Some(comments),
},
}
}
}
impl Display for Gecos<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self {
Gecos::Simple { comment } => write!(f, "{}", comment),
Gecos::Detail {
full_name,
room,
phone_work,
phone_home,
other,
} => write!(
f,
"{},{},{},{}{}",
full_name,
room,
phone_work,
phone_home,
match other {
None => "".to_string(),
Some(cont) => format!(",{}", cont.join(",")),
}
),
}
}
}
impl<'a> TryFrom<&'a str> for Gecos<'a> {
type Error = UserLibError;
fn try_from(source: &'a str) -> std::result::Result<Self, Self::Error> {
let vals: Vec<&str> = source.split(',').collect();
if vals.len() > 3 {
Ok(Gecos::Detail {
full_name: vals[0],
room: vals[1],
phone_work: vals[2],
phone_home: vals[3],
other: if vals.len() == 4 {
None
} else {
Some(vals[4..].to_vec())
},
})
} else if vals.len() == 1 {
Ok(Gecos::Simple {
comment: vals.get(0).unwrap(),
})
} else {
panic!(format!("Could not parse this string: {}", source))
}
}
}
/// The home directory of a user /// The home directory of a user
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub struct HomeDir<'a> { pub struct HomeDir<'a> {
@ -142,78 +306,6 @@ impl<'a> Passwd<'a> {
} }
} }
impl<'a> Gecos<'a> {
#[must_use]
pub const 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 {
Gecos::Simple { .. } => None,
Gecos::Detail { full_name, .. } => {
if full_name.is_empty() {
None
} else {
Some(full_name)
}
}
}
}
#[must_use]
pub const fn get_room(&'a self) -> Option<&'a str> {
match *self {
Gecos::Simple { .. } => None,
Gecos::Detail { room, .. } => {
if room.is_empty() {
None
} else {
Some(room)
}
}
}
}
#[must_use]
pub const fn get_phone_work(&'a self) -> Option<&'a str> {
match *self {
Gecos::Simple { .. } => None,
Gecos::Detail { phone_work, .. } => {
if phone_work.is_empty() {
None
} else {
Some(phone_work)
}
}
}
}
#[must_use]
pub const fn get_phone_home(&'a self) -> Option<&'a str> {
match *self {
Gecos::Simple { .. } => None,
Gecos::Detail { phone_home, .. } => {
if phone_home.is_empty() {
None
} else {
Some(phone_home)
}
}
}
}
#[must_use]
pub const fn get_other(&'a self) -> Option<&Vec<&'a str>> {
match self {
Gecos::Simple { .. } => None,
Gecos::Detail { other, .. } => match other {
None => None,
Some(comments) => Some(comments),
},
}
}
}
impl Default for Passwd<'_> { impl Default for Passwd<'_> {
fn default() -> Self { fn default() -> Self {
Passwd { Passwd {
@ -252,40 +344,6 @@ impl Display for Passwd<'_> {
} }
} }
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> {
type Error = UserLibError;
fn try_from(source: &'a str) -> std::result::Result<Self, Self::Error> {
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) {
Ok(Self { username: source })
} else {
Err(UserLibError::Message("Invalid username".into()))
}
}
}
impl Display for Password<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.password,)
}
}
impl<'a> TryFrom<&'a str> for Password<'a> {
type Error = UserLibError;
fn try_from(source: &'a str) -> std::result::Result<Self, Self::Error> {
Ok(Self { password: source })
}
}
impl Display for Uid { impl Display for Uid {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.uid,) write!(f, "{}", self.uid,)
@ -316,58 +374,6 @@ impl TryFrom<&str> for Gid {
} }
} }
impl Display for Gecos<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self {
Gecos::Simple { comment } => write!(f, "{}", comment),
Gecos::Detail {
full_name,
room,
phone_work,
phone_home,
other,
} => write!(
f,
"{},{},{},{}{}",
full_name,
room,
phone_work,
phone_home,
match other {
None => "".to_string(),
Some(cont) => format!(",{}", cont.join(",")),
}
),
}
}
}
impl<'a> TryFrom<&'a str> for Gecos<'a> {
type Error = UserLibError;
fn try_from(source: &'a str) -> std::result::Result<Self, Self::Error> {
let vals: Vec<&str> = source.split(',').collect();
if vals.len() > 3 {
Ok(Gecos::Detail {
full_name: vals[0],
room: vals[1],
phone_work: vals[2],
phone_home: vals[3],
other: if vals.len() == 4 {
None
} else {
Some(vals[4..].to_vec())
},
})
} else if vals.len() == 1 {
Ok(Gecos::Simple {
comment: vals.get(0).unwrap(),
})
} else {
panic!(format!("Could not parse this string: {}", source))
}
}
}
impl Display for HomeDir<'_> { impl Display for HomeDir<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.dir,) write!(f, "{}", self.dir,)