temp #2

Merged
dietrich merged 6 commits from temp into master 2020-10-07 17:11:11 +02:00
3 changed files with 222 additions and 0 deletions
Showing only changes of commit bceb479c78 - Show all commits

22
src/api.rs Normal file
View File

@ -0,0 +1,22 @@
trait UserDBRead {
fn get_all_users(&self) -> Vec<crate::Passwd>;
fn get_user_by_name(&self, name: &str) -> Option<crate::Passwd>;
fn get_user_by_id(&self, uid: u64) -> Option<crate::Passwd>;
fn get_all_groups(&self) -> Vec<crate::Group>;
fn get_group_by_name(&self) -> Option<crate::Group>;
fn get_group_by_id(&self) -> Option<crate::Group>;
}
trait UserDBValidation {
fn is_uid_valid_and_free(&self) -> bool;
fn is_username_valid_and_free(&self) -> bool;
fn is_gid_valid_and_free(&self) -> bool;
fn is_groupname_valid_and_free(&self) -> bool;
}
trait UserDBWrite {
fn set_user(&self) -> Option<crate::Passwd>;
fn new_user(&self) -> Option<crate::Passwd>;
fn set_group(&self) -> Option<crate::Group>;
fn new_group(&self) -> Option<crate::Group>;
}

143
src/group.rs Normal file
View File

@ -0,0 +1,143 @@
#![warn(
clippy::all,
/* clippy::restriction,*/
clippy::pedantic,
clippy::nursery,
clippy::cargo
)]
#![allow(clippy::non_ascii_literal)]
use log::warn;
use regex::Regex;
use crate::passwd;
use crate::userlib_error::UserLibError;
use std::cmp::Eq;
use std::convert::TryFrom;
use std::fmt::{self, Debug, Display};
#[derive(Debug, PartialEq, Eq)]
pub struct Groupname<'a> {
groupname: &'a str,
}
impl Display for Groupname<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.groupname,)
}
}
impl<'a> TryFrom<&'a str> for Groupname<'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 { groupname: source })
} else if source == "Debian-exim" {
warn!("username {} is not a valid username. This might cause problems. (It is default in Debian and Ubuntu)", source);
Ok(Self { groupname: source })
} else {
Err(UserLibError::Message(format!(
"Invalid username {}",
source
)))
}
}
}
/// A record(line) in the user database `/etc/shadow` found in most linux systems.
#[derive(Debug, PartialEq, Eq)]
pub struct Group<'a> {
groupname: Groupname<'a>, /* Username. */
pub(crate) password: passwd::Password<'a>, /* Usually not used (disabled with x) */
gid: passwd::Gid, /* Group ID. */
members: Vec<passwd::Username<'a>>, /* Real name. */
}
impl<'a> Group<'a> {
#[must_use]
pub const fn get_groupname(&self) -> &'a str {
self.groupname.groupname
}
#[must_use]
pub const fn get_members(&self) -> &Vec<passwd::Username<'a>> {
&self.members
}
}
impl<'a> Display for Group<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(
f,
"{}:{}:{}:{}",
self.groupname,
self.password,
self.gid,
self.members
.iter()
.map(|mem| format!("{}", mem))
.collect::<Vec<String>>()
.join(",")
)
}
}
impl<'a> Group<'a> {
/// Parse a line formatted like one in `/etc/shadow` and construct a matching `Shadow` instance
///
/// # Example
/// ```
/// /*let shad = adduser::shadow::Shadow::new_from_string(
/// "test:!!$6$/RotIe4VZzzAun4W$7YUONvru1rDnllN5TvrnOMsWUD5wSDUPAD6t6/Xwsr/0QOuWF3HcfAhypRkGa8G1B9qqWV5kZSnCb8GKMN9N61:18260:0:99999:7:::"
/// ).unwrap();
/// assert_eq!(shad.get_username(), "test");*/
/// ```
///
/// # Errors
/// When parsing fails this function returns a `UserLibError::Message` containing some information as to why the function failed.
pub fn new_from_string(line: &'a str) -> Result<Self, UserLibError> {
println!("{}", &line);
let elements: Vec<&str> = line.split(':').collect();
if elements.len() == 4 {
Ok(Group {
groupname: Groupname::try_from(*elements.get(0).unwrap())?,
password: passwd::Password::Disabled,
gid: passwd::Gid::try_from(*elements.get(2).unwrap())?,
members: parse_members_list(*elements.get(3).unwrap()),
})
} else {
Err(UserLibError::Message(format!(
"Failed to parse: not enough elements ({}): {:?}",
elements.len(),
elements
)))
}
}
}
fn parse_members_list<'a>(source: &'a str) -> Vec<passwd::Username<'a>> {
let mut res = vec![];
for mem in source.split(',') {
res.push(passwd::Username::try_from(mem).expect("failed to parse username"));
}
res
}
#[test]
fn test_parse_and_back_identity() {
println!("Test");
let line = "teste:x:1002:test,teste";
let line2 = Group::new_from_string(line).unwrap();
assert_eq!(format!("{}", line2), line);
}
#[test]
fn test_groupname() {
println!("Test");
let line = "teste:x:1002:test,teste";
let line2 = Group::new_from_string(line).unwrap();
assert_eq!(line2.get_groupname(), "teste");
}

57
src/userlib.rs Normal file
View File

@ -0,0 +1,57 @@
#![warn(
clippy::all,
/* clippy::restriction,*/
clippy::pedantic,
clippy::nursery,
clippy::cargo
)]
#![allow(clippy::non_ascii_literal)]
use log::warn;
use regex::Regex;
use crate::userlib_error::UserLibError;
use std::cmp::Eq;
use std::convert::TryFrom;
use std::fmt::{self, Display};
use std::io::{BufRead, Read};
pub struct UserDBLocal<'a> {
pub(crate) passwd_entries: Vec<crate::Passwd<'a>>,
pub(crate) shadow_entries: Vec<crate::Shadow<'a>>,
pub(crate) group_entries: Vec<crate::Group<'a>>,
}
impl<'a> UserDBLocal<'a> {
#[must_use]
pub fn import_from_strings(
passwd_content: &'a str,
shadow_content: &'a str,
group_content: &'a str,
) -> Self {
let res = UserDBLocal {
passwd_entries: passwd_content
.lines()
.map(|line| crate::Passwd::new_from_string(line).expect("failed to read lines"))
.collect(),
group_entries: group_content
.lines()
.map(|line| crate::Group::new_from_string(line).expect("Parsing failed"))
.collect(),
shadow_entries: shadow_content
.lines()
.map(|line| crate::Shadow::new_from_string(line).expect("Parsing failed"))
.collect(),
};
res
}
}
#[test]
fn test_creator_user_db_local() {
let data = UserDBLocal::import_from_strings("testuser: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,teste");
assert_eq!(
data.passwd_entries.get(0).unwrap().get_username(),
"testuser"
)
}