From 2a8d98229f8106895e246e25dcb51b4fc3cb86d4 Mon Sep 17 00:00:00 2001 From: Dominik George <dominik.george@teckids.org> Date: Tue, 11 May 2021 18:18:38 +0200 Subject: [PATCH] [NSS] Implement by_uid and by_name for passwd --- etc/nss_pam_oidc.example.toml | 4 +- src/nss.rs | 90 ++++++++++++++++++++++++----------- src/oauth.rs | 10 ++-- 3 files changed, 72 insertions(+), 32 deletions(-) diff --git a/etc/nss_pam_oidc.example.toml b/etc/nss_pam_oidc.example.toml index 663c74f..a1d6291 100644 --- a/etc/nss_pam_oidc.example.toml +++ b/etc/nss_pam_oidc.example.toml @@ -10,7 +10,9 @@ client_secret = "" client_id = "z8Oz0tG56QRo9QEPUZTs5Eda410FMiJtYxlInxKE" client_secret = "" -urls.passwd = "https://ticdesk-dev.teckids.org/app/nis/api/passwd/" +urls.passwd.list = "https://ticdesk-dev.teckids.org/app/nis/api/passwd/" +urls.passwd.by_uid = "https://ticdesk-dev.teckids.org/app/nis/api/passwd/{}/" +urls.passwd.by_name = "https://ticdesk-dev.teckids.org/app/nis/api/passwd/{}/" # The following configuration maps the attributes as returned by AlekSIS, as # example onto a system that also has local accounts (thus mapping IDs and diff --git a/src/nss.rs b/src/nss.rs index c4be7f1..9d76fce 100644 --- a/src/nss.rs +++ b/src/nss.rs @@ -94,7 +94,7 @@ impl PasswdHooks for OidcPasswd { } }; - let data: Vec<PasswdHelper> = match get_data_jq(&conf, "nss", "passwd", &token, true) { + let data: Vec<PasswdHelper> = match get_data_jq(&conf, "nss", "passwd.list", "".to_string(), &token, true) { Ok(d) => d, Err(_) => { error!("Could not load JSON data for passwd"); @@ -105,35 +105,71 @@ impl PasswdHooks for OidcPasswd { } fn get_entry_by_uid(uid: libc::uid_t) -> Response<Passwd> { - if uid == 1005 { - return Response::Success(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::NotFound + let conf = nss_hook_prepare(); + let mut cache = get_cache(); + + let user = get_current_user(); + let ctc; + let token = match cache.load_user_token(&user) { + Some(t) => t, + None => { + // FIXME Implement caching of system token + debug!("Could not find a user token for {} to request NSS data; trying client credentials", user); + match get_access_token_client(&conf, "nss", "", "") { + Ok(ct) => { + ctc = ct.clone(); + &ctc + }, + Err(_) => { + error!("Failed to get access token with client credentials"); + return Response::Unavail; + } + } + } + }; + + let data: Passwd = match get_data_jq(&conf, "nss", "passwd.by_uid", uid.to_string(), &token, false).map(|PasswdHelper(p)| p) { + Ok(d) => d, + Err(_) => { + error!("Could not load JSON data for passwd"); + return Response::NotFound; + } + }; + Response::Success(data) } fn get_entry_by_name(name: String) -> Response<Passwd> { - if name == "test" { - return Response::Success(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::NotFound + let conf = nss_hook_prepare(); + let mut cache = get_cache(); + + let user = get_current_user(); + let ctc; + let token = match cache.load_user_token(&user) { + Some(t) => t, + None => { + // FIXME Implement caching of system token + debug!("Could not find a user token for {} to request NSS data; trying client credentials", user); + match get_access_token_client(&conf, "nss", "", "") { + Ok(ct) => { + ctc = ct.clone(); + &ctc + }, + Err(_) => { + error!("Failed to get access token with client credentials"); + return Response::Unavail; + } + } + } + }; + + let data: Passwd = match get_data_jq(&conf, "nss", "passwd.by_name", name, &token, false).map(|PasswdHelper(p)| p) { + Ok(d) => d, + Err(_) => { + error!("Could not load JSON data for passwd"); + return Response::NotFound; + } + }; + Response::Success(data) } } diff --git a/src/oauth.rs b/src/oauth.rs index 14d1882..c6f913c 100644 --- a/src/oauth.rs +++ b/src/oauth.rs @@ -136,9 +136,11 @@ pub fn get_access_token_password<E: Copy>(conf: &Config, prefix: &str, username: } } -fn get_data(conf: &Config, prefix: &str, endpoint: &str, token: &BasicTokenResponse) -> Result<String, Box<dyn error::Error>> { +fn get_data(conf: &Config, prefix: &str, endpoint: &str, param: String, token: &BasicTokenResponse) -> Result<String, Box<dyn error::Error>> { let access_token = token.access_token().secret(); - let endpoint_url: String = get_or_error(&conf, &full_key(vec![prefix, "urls", endpoint]), "")?; + + let mut endpoint_url: String = get_or_error(&conf, &full_key(vec![prefix, "urls", endpoint]), "")?; + endpoint_url = endpoint_url.replace("{}", ¶m); debug!("Loading text data from {}", endpoint_url); let client = reqwest::blocking::Client::new(); @@ -149,7 +151,7 @@ fn get_data(conf: &Config, prefix: &str, endpoint: &str, token: &BasicTokenRespo .text()?) } -pub fn get_data_jq<T: for<'de> Deserialize<'de>>(conf: &Config, prefix: &str, endpoint: &str, token: &BasicTokenResponse, multi: bool) -> Result<T, Box<dyn error::Error>> { +pub fn get_data_jq<T: for<'de> Deserialize<'de>>(conf: &Config, prefix: &str, endpoint: &str, param: String, token: &BasicTokenResponse, multi: bool) -> Result<T, Box<dyn error::Error>> { let res: Option<String> = get_optional(&conf, &full_key(vec![prefix, "maps", endpoint])); let jq_code = match res { Some(s) => match multi { @@ -160,7 +162,7 @@ pub fn get_data_jq<T: for<'de> Deserialize<'de>>(conf: &Config, prefix: &str, en }; let mut jq_prog = jq_rs::compile(&jq_code)?; - let data_raw = get_data(&conf, prefix, endpoint, token)?; + let data_raw = get_data(&conf, prefix, endpoint, param, token)?; let data_trans = jq_prog.run(&data_raw)?; Ok(serde_json::from_str(&data_trans)?) -- GitLab