From 48e744274b455ecbb86d335c34c9a6dcbc2a1695 Mon Sep 17 00:00:00 2001
From: Dominik George <dominik.george@teckids.org>
Date: Fri, 7 May 2021 00:17:27 +0200
Subject: [PATCH] Move get_optional to config, make it type-agnostic, and allow
 scopes as array

---
 Cargo.toml    |  1 +
 src/config.rs |  9 +++++++++
 src/pam.rs    | 46 +++++++++++++++++++---------------------------
 3 files changed, 29 insertions(+), 27 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml
index 288fb6c..f407f3a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -21,6 +21,7 @@ lazy_static = "^1.3.0"
 oauth2 = "^4.0.0"
 reqwest = "^0.11.3"
 config = "^0.11.0"
+serde = "^1.0.125"
 log = "^0.4.11"
 syslog = "^5.0.0"
 
diff --git a/src/config.rs b/src/config.rs
index 7d30459..4828be0 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -13,6 +13,8 @@
  * limitations under the License.
  */
 
+use serde::de::Deserialize;
+
 extern crate config;
 
 const DEFAULT_CONFIG_FILE: &str = "/etc/nss_pam_oidc";
@@ -54,3 +56,10 @@ pub fn argv_to_config(argv: &Vec<String>) -> config::Config {
     }
     return conf;
 }
+
+pub fn get_optional<'de, T: Deserialize<'de>>(conf: &config::Config, key: &str) -> Option<T> {
+    match conf.get(key) {
+        Ok(v) => Some(v),
+        Err(_) => None,
+    }
+}
diff --git a/src/pam.rs b/src/pam.rs
index 2200ee3..2761e25 100644
--- a/src/pam.rs
+++ b/src/pam.rs
@@ -15,10 +15,13 @@
 
 use crate::config::{
     argv_to_config,
-    get_config
+    get_config,
+    get_optional
 };
 use config::Config;
 
+use serde::de::Deserialize;
+
 use crate::logging::setup_log;
 
 use oauth2::{
@@ -40,32 +43,19 @@ use oauth2::reqwest::http_client;
 
 use pamsm::{PamServiceModule, Pam, PamFlag, PamError, PamLibExt};
 
-fn get_or_pam_error(config: &Config, key: &str) -> Result<String, PamError> {
-    match config.get_str(key) {
-        Ok(v) => {
-            debug!("Configuration key found: {} = {}", key, v);
+fn get_or_pam_error<'de, T: Deserialize<'de>>(config: &Config, key: &str) -> Result<T, PamError> {
+    match get_optional(config, key) {
+        Some(v) => {
+            debug!("Configuration key found: {}", key);
             return Ok(v);
         },
-        Err(_) => {
+        None => {
             error!("Configuration key not found: {}", key);
             return Err(PamError::SERVICE_ERR);
         },
     }
 }
 
-fn get_optional(config: &Config, key: &str) -> Option<String> {
-    match config.get_str(key) {
-        Ok(v) => {
-            debug!("Configuration key found: {} = {}", key, v);
-            return Some(v);
-        },
-        Err(_) => {
-            debug!("Configuration key not found (optional): {}", key);
-            return None;
-        },
-    }
-}
-
 fn do_legacy_auth(username: String, password: String, config: Config) -> Result<BasicTokenResponse, PamError> {
     let client_id = ClientId::new(get_or_pam_error(&config, "pam.client_id")?);
     let client_secret = match get_optional(&config, "pam.client_secret") {
@@ -89,16 +79,18 @@ fn do_legacy_auth(username: String, password: String, config: Config) -> Result<
         },
         None => None,
     };
-    let scope = get_or_pam_error(&config, "pam.scope")?;
+    let scopes: Vec<&str> = get_or_pam_error(&config, "pam.scopes")?;
+
+    let res_username = ResourceOwnerUsername::new(username);
+    let res_password = ResourceOwnerPassword::new(password);
 
     let client = BasicClient::new(client_id, client_secret, auth_url, token_url);
-    let result = client
-        .exchange_password(
-            &ResourceOwnerUsername::new(username),
-            &ResourceOwnerPassword::new(password)
-        )
-        .add_scope(Scope::new(scope.to_string()))
-        .request(http_client);
+    let mut request = client.exchange_password(&res_username, &res_password);
+    for scope in scopes {
+        request = request.add_scope(Scope::new(scope.to_string()));
+    }
+    let result = request.request(http_client);
+
     match result {
             Ok(t) => Ok(t),
             Err(e) => match e {
-- 
GitLab