diff --git a/src/api/core/two_factor/duo_oidc.rs b/src/api/core/two_factor/duo_oidc.rs index f504e055..ae153ce0 100644 --- a/src/api/core/two_factor/duo_oidc.rs +++ b/src/api/core/two_factor/duo_oidc.rs @@ -6,21 +6,18 @@ use ring::digest::{digest, Digest, SHA512_256}; use serde::Serialize; use std::collections::HashMap; -use url::Url; use crate::{ api::{core::two_factor::duo::get_duo_keys_email, EmptyResult}, crypto, - db::{models::{ - EventType, - TwoFactorDuoContext, - }, - DbConn, - DbPool, + db::{ + models::{EventType, TwoFactorDuoContext}, + DbConn, DbPool, }, error::Error, util::get_reqwest_client, CONFIG, }; +use url::Url; // State length must be at least 16 characters and at most 1024 characters. const STATE_LENGTH: usize = 64; @@ -128,10 +125,9 @@ struct DuoClient { } impl DuoClient { - // Construct a new DuoClient fn new(client_id: String, client_secret: String, api_host: String, redirect_uri: String) -> DuoClient { - return DuoClient { + DuoClient { client_id, client_secret, api_host, @@ -254,7 +250,7 @@ impl DuoClient { } let final_auth_url = auth_url.to_string(); - return Ok(final_auth_url); + Ok(final_auth_url) } // Exchange the authorization code obtained from an access token provided by the user @@ -345,12 +341,12 @@ struct DuoAuthContext { async fn extract_context(state: &str, conn: &mut DbConn) -> Option { let ctx: TwoFactorDuoContext = match TwoFactorDuoContext::find_by_state(state, conn).await { Some(c) => c, - None => return None + None => return None, }; if ctx.exp < Utc::now().timestamp() { ctx.delete(conn).await.ok(); - return None + return None; } // Copy the context data, so that we can delete the context from @@ -363,7 +359,7 @@ async fn extract_context(state: &str, conn: &mut DbConn) -> Option Result { let mut query_params = callback.query_pairs_mut(); query_params.append_pair("client", client_name); } - return Ok(callback.to_string()); + Ok(callback.to_string()) } // Pre-redirect first stage of the Duo WebSDKv4 authentication flow. // Returns the "AuthUrl" that should be returned to clients for MFA. -pub async fn get_duo_auth_url(email: &str, - client_id: &String, - device_identifier: &String, - conn: &mut DbConn) -> Result { +pub async fn get_duo_auth_url( + email: &str, + client_id: &String, + device_identifier: &String, + conn: &mut DbConn, +) -> Result { let (ik, sk, _, host) = get_duo_keys_email(email, conn).await?; let callback_url = match make_callback_url(client_id.as_str()) { @@ -434,7 +432,7 @@ pub async fn get_duo_auth_url(email: &str, match TwoFactorDuoContext::save(state.as_str(), email, nonce.as_str(), CTX_VALIDITY_SECS, conn).await { Ok(()) => client.make_authz_req_url(email, state, hash), - Err(e) => err!(format!("Error saving Duo authentication context: {e:?}")) + Err(e) => err!(format!("Error saving Duo authentication context: {e:?}")), } } @@ -520,4 +518,4 @@ pub async fn validate_duo_login( ) } } -} \ No newline at end of file +} diff --git a/src/api/identity.rs b/src/api/identity.rs index ac02cb1d..1a10f5b2 100644 --- a/src/api/identity.rs +++ b/src/api/identity.rs @@ -502,7 +502,9 @@ async fn twofactor_auth( let twofactor_code = match data.two_factor_token { Some(ref code) => code, - None => err_json!(_json_err_twofactor(&twofactor_ids, &user.uuid, &data, conn).await?, "2FA token not provided"), + None => { + err_json!(_json_err_twofactor(&twofactor_ids, &user.uuid, &data, conn).await?, "2FA token not provided") + } }; let selected_twofactor = twofactors.into_iter().find(|tf| tf.atype == selected_id && tf.enabled); @@ -526,11 +528,14 @@ async fn twofactor_auth( } false => { // OIDC based flow - duo_oidc::validate_duo_login(data.username.as_ref().unwrap().trim(), - twofactor_code, - data.client_id.as_ref().unwrap(), - data.device_identifier.as_ref().unwrap(), - conn).await? + duo_oidc::validate_duo_login( + data.username.as_ref().unwrap().trim(), + twofactor_code, + data.client_id.as_ref().unwrap(), + data.device_identifier.as_ref().unwrap(), + conn, + ) + .await? } } } @@ -573,7 +578,12 @@ fn _selected_data(tf: Option) -> ApiResult { tf.map(|t| t.data).map_res("Two factor doesn't exist") } -async fn _json_err_twofactor(providers: &[i32], user_uuid: &str, data: &ConnectData, conn: &mut DbConn) -> ApiResult { +async fn _json_err_twofactor( + providers: &[i32], + user_uuid: &str, + data: &ConnectData, + conn: &mut DbConn, +) -> ApiResult { let mut result = json!({ "error" : "invalid_grant", "error_description" : "Two factor required.", @@ -609,10 +619,13 @@ async fn _json_err_twofactor(providers: &[i32], user_uuid: &str, data: &ConnectD } false => { // OIDC based flow - let auth_url = duo_oidc::get_duo_auth_url(&email, - data.client_id.as_ref().unwrap(), - data.device_identifier.as_ref().unwrap(), - conn).await?; + let auth_url = duo_oidc::get_duo_auth_url( + &email, + data.client_id.as_ref().unwrap(), + data.device_identifier.as_ref().unwrap(), + conn, + ) + .await?; result["TwoFactorProviders2"][provider.to_string()] = json!({ "AuthUrl": auth_url, diff --git a/src/db/models/two_factor_duo_context.rs b/src/db/models/two_factor_duo_context.rs index 669fd961..776c3c0a 100644 --- a/src/db/models/two_factor_duo_context.rs +++ b/src/db/models/two_factor_duo_context.rs @@ -27,17 +27,11 @@ impl TwoFactorDuoContext { } } - pub async fn save( - state: &str, - user_email: &str, - nonce: &str, - ttl: i64, - conn: &mut DbConn, - ) -> EmptyResult { + pub async fn save(state: &str, user_email: &str, nonce: &str, ttl: i64, conn: &mut DbConn) -> EmptyResult { // A saved context should never be changed, only created or deleted. let exists = Self::find_by_state(state, conn).await; if exists.is_some() { - return Ok(()) + return Ok(()); }; let exp = Utc::now().timestamp() + ttl; @@ -89,4 +83,4 @@ impl TwoFactorDuoContext { } } } -} \ No newline at end of file +} diff --git a/src/main.rs b/src/main.rs index 8b6a93d6..5b25b344 100644 --- a/src/main.rs +++ b/src/main.rs @@ -51,6 +51,7 @@ mod mail; mod ratelimit; mod util; +use crate::api::core::two_factor::duo_oidc::purge_duo_contexts; use crate::api::purge_auth_requests; use crate::api::{WS_ANONYMOUS_SUBSCRIPTIONS, WS_USERS}; pub use config::CONFIG; @@ -58,7 +59,6 @@ pub use error::{Error, MapResult}; use rocket::data::{Limits, ToByteUnit}; use std::sync::Arc; pub use util::is_running_in_container; -use crate::api::core::two_factor::duo_oidc::purge_duo_contexts; #[rocket::main] async fn main() -> Result<(), Error> { @@ -586,9 +586,7 @@ fn schedule_jobs(pool: db::DbPool) { } // Clean unused, expired Duo authentication contexts. - if !CONFIG.duo_context_purge_schedule().is_empty() - && CONFIG._enable_duo() - && !CONFIG.duo_use_iframe() { + if !CONFIG.duo_context_purge_schedule().is_empty() && CONFIG._enable_duo() && !CONFIG.duo_use_iframe() { sched.add(Job::new(CONFIG.duo_context_purge_schedule().parse().unwrap(), || { runtime.spawn(purge_duo_contexts(pool.clone())); }));