mirror of
https://github.com/kanidm/kanidm.git
synced 2025-02-24 04:57:00 +01:00
116 lines
2.9 KiB
Rust
116 lines
2.9 KiB
Rust
use serde::{
|
|
de::{self, Visitor},
|
|
Deserialize, Deserializer, Serialize,
|
|
};
|
|
|
|
use std::fmt;
|
|
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
pub struct EtcUser {
|
|
pub name: String,
|
|
pub password: String,
|
|
pub uid: u32,
|
|
pub gid: u32,
|
|
pub gecos: String,
|
|
pub homedir: String,
|
|
pub shell: String,
|
|
}
|
|
|
|
pub fn parse_etc_passwd(bytes: &[u8]) -> Result<Vec<EtcUser>, UnixIntegrationError> {
|
|
use csv::ReaderBuilder;
|
|
let mut rdr = ReaderBuilder::new()
|
|
.has_headers(false)
|
|
.delimiter(b':')
|
|
.from_reader(bytes);
|
|
rdr.deserialize()
|
|
.map(|result| result.map_err(|_e| UnixIntegrationError))
|
|
.collect::<Result<Vec<EtcUser>, UnixIntegrationError>>()
|
|
}
|
|
|
|
fn members<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
|
|
where
|
|
D: Deserializer<'de>,
|
|
{
|
|
struct InnerCsv;
|
|
|
|
impl<'de> Visitor<'de> for InnerCsv {
|
|
type Value = Vec<String>;
|
|
|
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
|
formatter.write_str("string")
|
|
}
|
|
|
|
fn visit_str<E>(self, value: &str) -> Result<Vec<String>, E>
|
|
where
|
|
E: de::Error,
|
|
{
|
|
Ok(value.split(',').map(|s| s.to_string()).collect())
|
|
}
|
|
}
|
|
|
|
deserializer.deserialize_str(InnerCsv)
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
pub struct EtcGroup {
|
|
pub name: String,
|
|
pub password: String,
|
|
pub gid: u32,
|
|
#[serde(deserialize_with = "members")]
|
|
pub members: Vec<String>,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct UnixIntegrationError;
|
|
|
|
pub fn parse_etc_group(bytes: &[u8]) -> Result<Vec<EtcGroup>, UnixIntegrationError> {
|
|
use csv::ReaderBuilder;
|
|
let mut rdr = ReaderBuilder::new()
|
|
.has_headers(false)
|
|
.delimiter(b':')
|
|
.from_reader(bytes);
|
|
rdr.deserialize()
|
|
.map(|result| result.map_err(|_e| UnixIntegrationError))
|
|
.collect::<Result<Vec<EtcGroup>, UnixIntegrationError>>()
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
const EXAMPLE_PASSWD: &str = r#"root:x:0:0:root:/root:/bin/bash
|
|
systemd-timesync:x:498:498:systemd Time Synchronization:/:/usr/sbin/nologin
|
|
messagebus:x:484:484:User for D-Bus:/run/dbus:/usr/sbin/nologin
|
|
tftp:x:483:483:TFTP Account:/srv/tftpboot:/usr/sbin/nologin
|
|
nobody:x:65534:65534:nobody:/var/lib/nobody:/bin/bash
|
|
"#;
|
|
|
|
const EXAMPLE_GROUP: &str = r#"root:x:0:
|
|
shadow:x:15:
|
|
trusted:x:42:
|
|
users:x:100:
|
|
systemd-journal:x:499:
|
|
systemd-timesync:x:498:
|
|
kmem:x:497:
|
|
lock:x:496:
|
|
tty:x:5:
|
|
wheel:x:481:admin,testuser
|
|
"#;
|
|
|
|
#[test]
|
|
fn test_parse_passwd() {
|
|
for record in
|
|
parse_etc_passwd(EXAMPLE_PASSWD.as_bytes()).expect("Failed to parse passwd data")
|
|
{
|
|
println!("{:?}", record);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_group() {
|
|
for record in parse_etc_group(EXAMPLE_GROUP.as_bytes()).expect("Failed to parse group") {
|
|
println!("{:?}", record);
|
|
}
|
|
}
|
|
}
|