From cad63f97616ac4f003a65543860c49e75b32a08c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Garc=C3=ADa?=
 <dani-garcia@users.noreply.github.com>
Date: Thu, 11 Apr 2019 16:08:26 +0200
Subject: [PATCH] Auto generate akey

---
 src/api/core/two_factor.rs | 17 +++++++++-----
 src/config.rs              | 46 +++++++++++++++++++++++++++-----------
 2 files changed, 44 insertions(+), 19 deletions(-)

diff --git a/src/api/core/two_factor.rs b/src/api/core/two_factor.rs
index 94cbc1e7..1849f035 100644
--- a/src/api/core/two_factor.rs
+++ b/src/api/core/two_factor.rs
@@ -821,12 +821,19 @@ const APP_PREFIX: &str = "APP";
 
 use chrono::Utc;
 
+// let (ik, sk, ak) = get_duo_keys();
+fn get_duo_keys() -> (String, String, String) {
+    (
+        CONFIG.duo_ikey().unwrap(),
+        CONFIG.duo_skey().unwrap(),
+        CONFIG.get_duo_akey(),
+    )
+}
+
 pub fn generate_duo_signature(email: &str) -> String {
     let now = Utc::now().timestamp();
 
-    let ik = CONFIG.duo_ikey().unwrap();
-    let sk = CONFIG.duo_skey().unwrap();
-    let ak = CONFIG.duo_akey().unwrap();
+    let (ik, sk, ak) = get_duo_keys();
 
     let duo_sign = sign_duo_values(&sk, email, &ik, DUO_PREFIX, now + DUO_EXPIRE);
     let app_sign = sign_duo_values(&ak, email, &ik, APP_PREFIX, now + APP_EXPIRE);
@@ -852,9 +859,7 @@ pub fn validate_duo_login(email: &str, response: &str) -> EmptyResult {
 
     let now = Utc::now().timestamp();
 
-    let ik = CONFIG.duo_ikey().unwrap();
-    let sk = CONFIG.duo_skey().unwrap();
-    let ak = CONFIG.duo_akey().unwrap();
+    let (ik, sk, ak) = get_duo_keys();
 
     let auth_user = parse_duo_values(&sk, auth_sig, &ik, AUTH_PREFIX, now)?;
     let app_user = parse_duo_values(&ak, app_sig, &ik, APP_PREFIX, now)?;
diff --git a/src/config.rs b/src/config.rs
index 309b65ae..d40eecae 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -9,7 +9,7 @@ lazy_static! {
         println!("Error loading config:\n\t{:?}\n", e);
         exit(12)
     });
-    pub static ref CONFIG_FILE: String =  {
+    pub static ref CONFIG_FILE: String = {
         let data_folder = get_env("DATA_FOLDER").unwrap_or_else(|| String::from("data"));
         get_env("CONFIG_FILE").unwrap_or_else(|| format!("{}/config.json", data_folder))
     };
@@ -64,7 +64,7 @@ macro_rules! make_config {
 
             /// Merges the values of both builders into a new builder.
             /// If both have the same element, `other` wins.
-            fn merge(&self, other: &Self) -> Self {
+            fn merge(&self, other: &Self, show_overrides: bool) -> Self {
                 let mut overrides = Vec::new();
                 let mut builder = self.clone();
                 $($(
@@ -77,7 +77,7 @@ macro_rules! make_config {
                     }
                 )+)+
 
-                if !overrides.is_empty() {
+                if show_overrides && !overrides.is_empty() {
                     // We can't use warn! here because logging isn't setup yet.
                     println!("[WARNING] The following environment variables are being overriden by the config file,");
                     println!("[WARNING] please use the admin panel to make changes to them:");
@@ -315,8 +315,8 @@ make_config! {
         duo_skey:               Pass,   true,   option;
         /// Host
         duo_host:               String, true,   option;
-        /// Application Key
-        duo_akey:               Pass,   true,   option;
+        /// Application Key (generated automatically)
+        _duo_akey:              Pass,   false,  option;
     },
 
     /// SMTP Email Settings
@@ -349,10 +349,10 @@ fn validate_config(cfg: &ConfigItems) -> Result<(), Error> {
         }
     }
 
-    if (cfg.duo_host.is_some() || cfg.duo_ikey.is_some() || cfg.duo_skey.is_some() || cfg.duo_akey.is_some())
-        && !(cfg.duo_host.is_some() && cfg.duo_ikey.is_some() && cfg.duo_skey.is_some() && cfg.duo_akey.is_some())
+    if (cfg.duo_host.is_some() || cfg.duo_ikey.is_some() || cfg.duo_skey.is_some())
+        && !(cfg.duo_host.is_some() && cfg.duo_ikey.is_some() && cfg.duo_skey.is_some())
     {
-        err!("All Duo options need to be set for Duo support")
+        err!("All Duo options need to be set for global Duo support")
     }
 
     if cfg.yubico_client_id.is_some() != cfg.yubico_secret_key.is_some() {
@@ -377,7 +377,7 @@ impl Config {
         let _usr = ConfigBuilder::from_file(&CONFIG_FILE).unwrap_or_default();
 
         // Create merged config, config file overwrites env
-        let builder = _env.merge(&_usr);
+        let builder = _env.merge(&_usr, true);
 
         // Fill any missing with defaults
         let config = builder.build();
@@ -406,7 +406,7 @@ impl Config {
         // Prepare the combined config
         let config = {
             let env = &self.inner.read().unwrap()._env;
-            env.merge(&builder).build()
+            env.merge(&builder, false).build()
         };
         validate_config(&config)?;
 
@@ -425,6 +425,14 @@ impl Config {
         Ok(())
     }
 
+    pub fn update_config_partial(&self, other: ConfigBuilder) -> Result<(), Error> {
+        let builder = {
+            let usr = &self.inner.read().unwrap()._usr;
+            usr.merge(&other, false)
+        };
+        self.update_config(builder)
+    }
+
     pub fn delete_user_config(&self) -> Result<(), Error> {
         crate::util::delete_file(&CONFIG_FILE)?;
 
@@ -460,9 +468,21 @@ impl Config {
         let inner = &self.inner.read().unwrap().config;
         inner._enable_smtp && inner.smtp_host.is_some()
     }
-    pub fn yubico_enabled(&self) -> bool {
-        let inner = &self.inner.read().unwrap().config;
-        inner._enable_yubico && inner.yubico_client_id.is_some() && inner.yubico_secret_key.is_some()
+
+    pub fn get_duo_akey(&self) -> String {
+        if let Some(akey) = self._duo_akey() {
+            akey
+        } else {
+            let akey = crate::crypto::get_random_64();
+            let akey_s = data_encoding::BASE64.encode(&akey);
+
+            // Save the new value
+            let mut builder = ConfigBuilder::default();
+            builder._duo_akey = Some(akey_s.clone());
+            self.update_config_partial(builder).ok();
+
+            akey_s
+        }
     }
 
     pub fn render_template<T: serde::ser::Serialize>(