diff --git a/src/nss.rs b/src/nss.rs index 57183d9232248feba464b1149abf4e4b05a042fb..9982492050cfc6db86b569bff3daae90326e7143 100644 --- a/src/nss.rs +++ b/src/nss.rs @@ -26,6 +26,8 @@ use crate::logging::setup_log; use crate::oauth::get_data; use std::collections::HashMap; use serde_json::value::Value; +use std::fmt; +use std::convert::TryInto; use libc::{getpwuid, geteuid}; use std::ffi::CStr; @@ -55,6 +57,51 @@ fn get_current_user() -> String { euser.to_str().ok().unwrap().to_string() } +// FIXME Provide more specific error types and messages +#[derive(Debug, Clone)] +struct TransformMappingError { + msg: String, + field: String +} +impl fmt::Display for TransformMappingError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Invalid mapping configuration: field={} - {}", self.field, self.msg) + } +} + +fn transform_ent(conf: &Config, row: &mut HashMap<String, Value>, map_name: &str) -> Result<(), TransformMappingError> { + let mapping: HashMap<String, HashMap<String, Value>> = get_optional(&conf, &("nss.maps.".to_string() + map_name)).unwrap_or_default(); + + for (field, rule) in &mapping { + let type_: String = rule.get("type").ok_or(TransformMappingError { field: field.to_string(), msg: "No type".to_string() })?.as_str().unwrap().to_string(); + let value: &Value = rule.get("value").ok_or(TransformMappingError { field: field.to_string(), msg: "No value".to_string() })?; + + if type_ == "static" { + row.insert(field.to_string(), value.clone()); + } else if type_ == "rename" { + let old_value: Value = row.remove(&value.as_str().unwrap().to_string()).ok_or(TransformMappingError { field: field.to_string(), msg: "No value to rename".to_string() })?; + row.insert(field.to_string(), old_value); + } else { + return Err(TransformMappingError { field: field.to_string(), msg: (&("Unknown type ".to_string() + &type_)).to_string() }); + }; + } + + Ok(()) +} + +fn map_to_passwd(conf: &Config, row: &mut HashMap<String, Value>) -> Result<Passwd, TransformMappingError> { + transform_ent(&conf, row, "passwd")?; + Ok(Passwd { + name: row.get("name").ok_or(TransformMappingError { field: "name".to_string(), msg: "No value in JSON data".to_string() })?.as_str().unwrap().to_string(), + passwd: row.get("passwd").ok_or(TransformMappingError { field: "passwd".to_string(), msg: "No value in JSON data".to_string() })?.as_str().unwrap().to_string(), + uid: row.get("uid").ok_or(TransformMappingError { field: "uid".to_string(), msg: "No value in JSON data".to_string() })?.as_u64().ok_or(TransformMappingError { field: "uid".to_string(), msg: "Invalid integer".to_string() })?.try_into().or(Err(TransformMappingError { field: "uid".to_string(), msg: "Overflow converting to u32".to_string() }))?, + gid: row.get("gid").ok_or(TransformMappingError { field: "gid".to_string(), msg: "No value in JSON data".to_string() })?.as_u64().ok_or(TransformMappingError { field: "gid".to_string(), msg: "Invalid integer".to_string() })?.try_into().or(Err(TransformMappingError { field: "gid".to_string(), msg: "Overflow converting to u32".to_string() }))?, + gecos: row.get("gecos").ok_or(TransformMappingError { field: "gecos".to_string(), msg: "No value in JSON data".to_string() })?.as_str().unwrap().to_string(), + dir: row.get("dir").ok_or(TransformMappingError { field: "dir".to_string(), msg: "No value in JSON data".to_string() })?.as_str().unwrap().to_string(), + shell: row.get("shell").ok_or(TransformMappingError { field: "shell".to_string(), msg: "No value in JSON data".to_string() })?.as_str().unwrap().to_string(), + }) +} + struct OidcPasswd; impl PasswdHooks for OidcPasswd { @@ -72,29 +119,20 @@ impl PasswdHooks for OidcPasswd { } }; - let data: Vec<HashMap<String, Value>> = match get_data(conf, "nss", "passwd", token, "") { + let mut data: Vec<HashMap<String, Value>> = match get_data(&conf, "nss", "passwd", token, "") { Ok(d) => d, Err(_) => return Response::Unavail }; - for ent in &data { - for (k, v) in ent { - debug!("{} {}", k, v); - } + + let mut passwd_vec: Vec<Passwd> = Vec::new(); + for row in &mut data { + match map_to_passwd(&conf, row) { + Ok(p) => passwd_vec.push(p), + Err(e) => error!("Error converting JSON to passwd entry: {}", e) + }; } - Response::Success( - vec![ - Passwd { - name: "test".to_string(), - passwd: "x".to_string(), - uid: 1005, - gid: 1005, - gecos: "Test Account".to_string(), - dir: "/home/test".to_string(), - shell: "/bin/bash".to_string(), - } - ] - ) + Response::Success(passwd_vec) } fn get_entry_by_uid(uid: libc::uid_t) -> Response<Passwd> { diff --git a/src/oauth.rs b/src/oauth.rs index 58c8490571c6e3848cb8e928e28855f3bd724ddf..71e8c04f18f21f4638da564935b950a200e82bda 100644 --- a/src/oauth.rs +++ b/src/oauth.rs @@ -133,7 +133,7 @@ pub fn get_access_token_password<E: Copy>(conf: Config, prefix: &str, username: } } -pub fn get_data<T: for<'de> Deserialize<'de>, E: Copy>(conf: Config, prefix: &str, endpoint: &str, token: &BasicTokenResponse, error_value: E) -> Result<T, E> { +pub fn get_data<T: for<'de> Deserialize<'de>, E: Copy>(conf: &Config, prefix: &str, endpoint: &str, token: &BasicTokenResponse, error_value: E) -> Result<T, E> { let access_token = token.access_token().secret(); let endpoint_url: String = get_or_error(&conf, &full_key(prefix, &(endpoint.to_string() + "_url")), error_value)?;