kanidm/kanidmd/src/lib/utils.rs

221 lines
6.3 KiB
Rust
Raw Normal View History

2021-06-25 04:39:05 +02:00
use std::collections::HashSet;
2021-07-08 02:09:15 +02:00
use std::io::ErrorKind;
use std::path::PathBuf;
use std::time::{Duration, SystemTime};
2021-07-08 02:09:15 +02:00
use filetime::FileTime;
use touch::file as touch_file;
2019-10-15 04:34:07 +02:00
use uuid::{Builder, Uuid};
use rand::distributions::Distribution;
use rand::{thread_rng, Rng};
2021-06-27 03:30:40 +02:00
use std::fs::Metadata;
#[cfg(target_os = "linux")]
use std::os::linux::fs::MetadataExt;
#[cfg(target_os = "macos")]
use std::os::macos::fs::MetadataExt;
#[cfg(target_os = "windows")]
use std::os::windows::fs::MetadataExt;
2021-06-27 03:30:40 +02:00
use users::{get_current_gid, get_current_uid};
#[derive(Debug)]
pub struct DistinctAlpha;
pub type Sid = [u8; 4];
pub fn uuid_to_gid_u32(u: &Uuid) -> u32 {
let b_ref = u.as_bytes();
let mut x: [u8; 4] = [0; 4];
x.clone_from_slice(&b_ref[12..16]);
u32::from_be_bytes(x)
}
fn uuid_from_u64_u32(a: u64, b: u32, sid: Sid) -> Uuid {
let mut v: Vec<u8> = Vec::with_capacity(16);
v.extend_from_slice(&a.to_be_bytes());
v.extend_from_slice(&b.to_be_bytes());
v.extend_from_slice(&sid);
2020-08-04 04:58:11 +02:00
#[allow(clippy::expect_used)]
Builder::from_slice(v.as_slice())
.expect("invalid slice for uuid builder")
.build()
}
pub fn uuid_from_duration(d: Duration, sid: Sid) -> Uuid {
uuid_from_u64_u32(d.as_secs(), d.subsec_nanos(), sid)
}
pub fn password_from_random() -> String {
let rand_string: String = thread_rng().sample_iter(&DistinctAlpha).take(48).collect();
rand_string
}
2021-06-25 04:39:05 +02:00
pub fn backup_code_from_random() -> HashSet<String> {
(0..8)
.into_iter()
.map(|_| readable_password_from_random())
.collect()
}
pub fn readable_password_from_random() -> String {
let mut trng = thread_rng();
format!(
"{}-{}-{}-{}",
(&mut trng)
.sample_iter(&DistinctAlpha)
.take(4)
.collect::<String>(),
(&mut trng)
.sample_iter(&DistinctAlpha)
.take(4)
.collect::<String>(),
(&mut trng)
.sample_iter(&DistinctAlpha)
.take(4)
.collect::<String>(),
(&mut trng)
.sample_iter(&DistinctAlpha)
.take(4)
.collect::<String>(),
)
}
pub fn duration_from_epoch_now() -> Duration {
2020-08-04 04:58:11 +02:00
#[allow(clippy::expect_used)]
SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
2020-08-04 04:58:11 +02:00
.expect("invalid duration from epoch now")
}
2021-07-08 02:09:15 +02:00
pub fn touch_file_or_quit(file_path: &str) {
/*
Attempt to touch the file file_path, will quit the application if it fails for any reason.
Will also create a new file if it doesn't already exist.
*/
if PathBuf::from(file_path).exists() {
let t = FileTime::from_system_time(SystemTime::now());
match filetime::set_file_times(file_path, t, t) {
Ok(_) => debug!(
"Successfully touched existing file {}, can continue",
file_path
),
Err(e) => {
match e.kind() {
ErrorKind::PermissionDenied => {
// we bail here because you won't be able to write them back...
error!("Permission denied writing to {}, quitting.", file_path)
}
_ => {
error!(
"Failed to write to {} due to error: {:?} ... quitting.",
file_path, e
)
}
}
std::process::exit(1);
}
}
} else {
match touch_file::write(file_path, "", false) {
Ok(_) => debug!("Successfully touched new file {}", file_path),
Err(e) => {
error!(
"Failed to write to {} due to error: {:?} ... quitting.",
file_path, e
);
std::process::exit(1);
}
};
}
}
/*
2019-10-10 11:42:33 +02:00
#[allow(dead_code)]
pub fn uuid_from_now(sid: Sid) -> Uuid {
2019-10-15 04:34:07 +02:00
let d = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap();
2019-10-10 11:42:33 +02:00
uuid_from_duration(d, sid)
}
*/
2019-10-10 11:42:33 +02:00
impl Distribution<char> for DistinctAlpha {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> char {
const RANGE: u32 = 55;
const GEN_ASCII_STR_CHARSET: &[u8] = b"ABCDEFGHJKLMNPQRSTUVWXYZ\
abcdefghjkpqrstuvwxyz\
0123456789";
// This probably needs to be checked for entropy/quality
loop {
let var = rng.next_u32() >> (32 - 6);
if var < RANGE {
return GEN_ASCII_STR_CHARSET[var as usize] as char;
}
}
}
}
2021-06-27 03:30:40 +02:00
#[cfg(target_family = "unix")]
pub fn file_permissions_readonly(meta: &Metadata) -> bool {
// Who are we running as?
let cuid = get_current_uid();
let cgid = get_current_gid();
// Who owns the file?
// Who is the group owner of the file?
let f_gid = meta.st_gid();
let f_uid = meta.st_uid();
let f_mode = meta.st_mode();
!(
// If we are the owner, we have write perms as we can alter the DAC rights
cuid == f_uid ||
// If we are the group owner, check the mode bits do not have write.
(cgid == f_gid && (f_mode & 0o0020) != 0) ||
// Finally, check that everyone bits don't have write.
((f_mode & 0o0002) != 0)
)
}
#[cfg(test)]
mod tests {
use crate::utils::{uuid_from_duration, uuid_to_gid_u32};
use std::time::Duration;
use uuid::Uuid;
#[test]
fn test_utils_uuid_from_duration() {
let u1 = uuid_from_duration(Duration::from_secs(1), [0xff; 4]);
assert_eq!(
"00000000-0000-0001-0000-0000ffffffff",
u1.to_hyphenated().to_string()
);
let u2 = uuid_from_duration(Duration::from_secs(1000), [0xff; 4]);
assert_eq!(
"00000000-0000-03e8-0000-0000ffffffff",
u2.to_hyphenated().to_string()
);
}
#[test]
fn test_utils_uuid_to_gid_u32() {
let u1 = Uuid::parse_str("00000000-0000-0001-0000-000000000000").unwrap();
let r1 = uuid_to_gid_u32(&u1);
assert!(r1 == 0);
let u2 = Uuid::parse_str("00000000-0000-0001-0000-0000ffffffff").unwrap();
let r2 = uuid_to_gid_u32(&u2);
assert!(r2 == 0xffffffff);
let u3 = Uuid::parse_str("00000000-0000-0001-0000-ffff12345678").unwrap();
let r3 = uuid_to_gid_u32(&u3);
assert!(r3 == 0x12345678);
}
}