test2
This commit is contained in:
parent
310575a8e9
commit
bceb479c78
22
src/api.rs
Normal file
22
src/api.rs
Normal 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
143
src/group.rs
Normal 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
57
src/userlib.rs
Normal 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"
|
||||
)
|
||||
}
|
Loading…
Reference in New Issue
Block a user