diff --git a/src/api/identity.rs b/src/api/identity.rs index 13e7ab43..12becf6b 100644 --- a/src/api/identity.rs +++ b/src/api/identity.rs @@ -9,9 +9,11 @@ use serde_json::Value; use crate::{ api::{ - core::accounts::{PreloginData, RegisterData, _prelogin, _register}, - core::log_user_event, - core::two_factor::{duo, email, email::EmailTokenData, yubikey}, + core::{ + accounts::{PreloginData, RegisterData, _prelogin, _register}, + log_user_event, + two_factor::{authenticator, duo, email, enforce_2fa_policy, webauthn, yubikey}, + }, ApiResult, EmptyResult, JsonResult, JsonUpcase, }, auth::{generate_organization_api_key_login_claims, ClientHeaders, ClientIp}, @@ -247,7 +249,7 @@ async fn _password_login( let (mut device, new_device) = get_device(&data, conn, &user).await; - let twofactor_token = twofactor_auth(&user.uuid, &data, &mut device, ip, conn).await?; + let twofactor_token = twofactor_auth(&user, &data, &mut device, ip, conn).await?; if CONFIG.mail_enabled() && new_device { if let Err(e) = mail::send_new_device_logged_in(&user.email, &ip.ip.to_string(), &now, &device.name).await { @@ -468,32 +470,32 @@ async fn get_device(data: &ConnectData, conn: &mut DbConn, user: &User) -> (Devi } async fn twofactor_auth( - user_uuid: &str, + user: &User, data: &ConnectData, device: &mut Device, ip: &ClientIp, conn: &mut DbConn, ) -> ApiResult> { - let twofactors = TwoFactor::find_by_user(user_uuid, conn).await; + let twofactors = TwoFactor::find_by_user(&user.uuid, conn).await; // No twofactor token if twofactor is disabled if twofactors.is_empty() { + enforce_2fa_policy(user, &user.uuid, device.atype, &ip.ip, conn).await?; return Ok(None); } - TwoFactorIncomplete::mark_incomplete(user_uuid, &device.uuid, &device.name, ip, conn).await?; + TwoFactorIncomplete::mark_incomplete(&user.uuid, &device.uuid, &device.name, ip, conn).await?; let twofactor_ids: Vec<_> = twofactors.iter().map(|tf| tf.atype).collect(); let selected_id = data.two_factor_provider.unwrap_or(twofactor_ids[0]); // If we aren't given a two factor provider, assume the first one let twofactor_code = match data.two_factor_token { Some(ref code) => code, - None => err_json!(_json_err_twofactor(&twofactor_ids, user_uuid, conn).await?, "2FA token not provided"), + None => err_json!(_json_err_twofactor(&twofactor_ids, &user.uuid, conn).await?, "2FA token not provided"), }; let selected_twofactor = twofactors.into_iter().find(|tf| tf.atype == selected_id && tf.enabled); - use crate::api::core::two_factor as _tf; use crate::crypto::ct_eq; let selected_data = _selected_data(selected_twofactor); @@ -501,17 +503,15 @@ async fn twofactor_auth( match TwoFactorType::from_i32(selected_id) { Some(TwoFactorType::Authenticator) => { - _tf::authenticator::validate_totp_code_str(user_uuid, twofactor_code, &selected_data?, ip, conn).await? - } - Some(TwoFactorType::Webauthn) => { - _tf::webauthn::validate_webauthn_login(user_uuid, twofactor_code, conn).await? + authenticator::validate_totp_code_str(&user.uuid, twofactor_code, &selected_data?, ip, conn).await? } - Some(TwoFactorType::YubiKey) => _tf::yubikey::validate_yubikey_login(twofactor_code, &selected_data?).await?, + Some(TwoFactorType::Webauthn) => webauthn::validate_webauthn_login(&user.uuid, twofactor_code, conn).await?, + Some(TwoFactorType::YubiKey) => yubikey::validate_yubikey_login(twofactor_code, &selected_data?).await?, Some(TwoFactorType::Duo) => { - _tf::duo::validate_duo_login(data.username.as_ref().unwrap().trim(), twofactor_code, conn).await? + duo::validate_duo_login(data.username.as_ref().unwrap().trim(), twofactor_code, conn).await? } Some(TwoFactorType::Email) => { - _tf::email::validate_email_code_str(user_uuid, twofactor_code, &selected_data?, conn).await? + email::validate_email_code_str(&user.uuid, twofactor_code, &selected_data?, conn).await? } Some(TwoFactorType::Remember) => { @@ -521,7 +521,7 @@ async fn twofactor_auth( } _ => { err_json!( - _json_err_twofactor(&twofactor_ids, user_uuid, conn).await?, + _json_err_twofactor(&twofactor_ids, &user.uuid, conn).await?, "2FA Remember token not provided" ) } @@ -535,7 +535,7 @@ async fn twofactor_auth( ), } - TwoFactorIncomplete::mark_complete(user_uuid, &device.uuid, conn).await?; + TwoFactorIncomplete::mark_complete(&user.uuid, &device.uuid, conn).await?; if !CONFIG.disable_2fa_remember() && remember == 1 { Ok(Some(device.refresh_twofactor_remember())) @@ -550,8 +550,6 @@ fn _selected_data(tf: Option) -> ApiResult { } async fn _json_err_twofactor(providers: &[i32], user_uuid: &str, conn: &mut DbConn) -> ApiResult { - use crate::api::core::two_factor; - let mut result = json!({ "error" : "invalid_grant", "error_description" : "Two factor required.", @@ -566,7 +564,7 @@ async fn _json_err_twofactor(providers: &[i32], user_uuid: &str, conn: &mut DbCo Some(TwoFactorType::Authenticator) => { /* Nothing to do for TOTP */ } Some(TwoFactorType::Webauthn) if CONFIG.domain_set() => { - let request = two_factor::webauthn::generate_webauthn_login(user_uuid, conn).await?; + let request = webauthn::generate_webauthn_login(user_uuid, conn).await?; result["TwoFactorProviders2"][provider.to_string()] = request.0; } @@ -598,8 +596,6 @@ async fn _json_err_twofactor(providers: &[i32], user_uuid: &str, conn: &mut DbCo } Some(tf_type @ TwoFactorType::Email) => { - use crate::api::core::two_factor as _tf; - let twofactor = match TwoFactor::find_by_user_and_type(user_uuid, tf_type as i32, conn).await { Some(tf) => tf, None => err!("No twofactor email registered"), @@ -607,10 +603,10 @@ async fn _json_err_twofactor(providers: &[i32], user_uuid: &str, conn: &mut DbCo // Send email immediately if email is the only 2FA option if providers.len() == 1 { - _tf::email::send_token(user_uuid, conn).await? + email::send_token(user_uuid, conn).await? } - let email_data = EmailTokenData::from_json(&twofactor.data)?; + let email_data = email::EmailTokenData::from_json(&twofactor.data)?; result["TwoFactorProviders2"][provider.to_string()] = json!({ "Email": email::obscure_email(&email_data.email), })