diff --git a/.gitignore b/.gitignore index f2379c74..ac8220e6 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ .envrc /node_modules .env +.DS_Store diff --git a/Cargo.lock b/Cargo.lock index 5502f9f2..a7eae952 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -161,9 +161,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "axum" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "021e862c184ae977658b36c4500f7feac3221ca5da43e3f25bd04ab6c79a29b5" +checksum = "98e529aee37b5c8206bb4bf4c44797127566d72f76952c970bd3d1e85de8f4e2" dependencies = [ "axum-core", "base64", @@ -181,8 +181,7 @@ dependencies = [ "mime", "percent-encoding", "pin-project-lite", - "rustversion", - "serde", + "serde_core", "serde_json", "serde_path_to_error", "serde_urlencoded", @@ -209,9 +208,9 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.5.2" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68464cd0412f486726fb3373129ef5d2993f90c34bc2bc1c1e9943b2f4fc7ca6" +checksum = "0ac7a6beb1182c7e30253ee75c3e918080bfb83f5a3023bcdf7209d85fd147e6" dependencies = [ "bytes", "futures-core", @@ -220,7 +219,6 @@ dependencies = [ "http-body-util", "mime", "pin-project-lite", - "rustversion", "sync_wrapper", "tower-layer", "tower-service", @@ -229,9 +227,9 @@ dependencies = [ [[package]] name = "axum-extra" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45bf463831f5131b7d3c756525b305d40f1185b688565648a92e1392ca35713d" +checksum = "d86d701cd16f401888ebe9c3214dc838c7ef27a405d5726196765a913603b5dd" dependencies = [ "axum", "axum-core", @@ -245,10 +243,10 @@ dependencies = [ "mime", "pin-project-lite", "rustversion", - "serde", - "tower", + "serde_core", "tower-layer", "tower-service", + "tracing", ] [[package]] @@ -311,9 +309,9 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "cc" -version = "1.2.38" +version = "1.2.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80f41ae168f955c12fb8960b057d70d0ca153fb83182b57d86380443527be7e9" +checksum = "e1354349954c6fc9cb0deab020f27f783cf0b604e8bb754dc4658ecf0d29c35f" dependencies = [ "find-msvc-tools", "jobserver", @@ -556,7 +554,7 @@ dependencies = [ "rust-embed", "serde", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.17", "time", "tokio", "tokio-stream", @@ -583,7 +581,7 @@ dependencies = [ "os_info", "semver", "serde", - "thiserror 2.0.16", + "thiserror 2.0.17", "tonic", "tower", "tracing", @@ -1858,9 +1856,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.40" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" dependencies = [ "proc-macro2", ] @@ -2130,9 +2128,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "security-framework" -version = "3.5.0" +version = "3.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc198e42d9b7510827939c9a15f5062a0c913f3371d765977e586d2fe6c16f4a" +checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" dependencies = [ "bitflags", "core-foundation", @@ -2163,9 +2161,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.227" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80ece43fc6fbed4eb5392ab50c07334d3e577cbf40997ee896fe7af40bba4245" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ "serde_core", "serde_derive", @@ -2173,18 +2171,18 @@ dependencies = [ [[package]] name = "serde_core" -version = "1.0.227" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a576275b607a2c86ea29e410193df32bc680303c82f31e275bbfcafe8b33be5" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.227" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51e694923b8824cf0e9b382adf0f60d4e05f348f357b38833a3fa5ed7c2ede04" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", @@ -2416,11 +2414,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.16" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" dependencies = [ - "thiserror-impl 2.0.16", + "thiserror-impl 2.0.17", ] [[package]] @@ -2436,9 +2434,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.16" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", @@ -2528,9 +2526,9 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.26.3" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f63835928ca123f1bef57abbcd23bb2ba0ac9ae1235f1e65bda0d06e7786bd" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" dependencies = [ "rustls", "tokio", @@ -2549,9 +2547,9 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.26.2" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084" +checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" dependencies = [ "futures-util", "log", @@ -2742,7 +2740,7 @@ dependencies = [ "governor", "http", "pin-project", - "thiserror 2.0.16", + "thiserror 2.0.17", "tonic", "tower", "tracing", @@ -2818,9 +2816,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "tungstenite" -version = "0.26.2" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13" +checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" dependencies = [ "bytes", "data-encoding", @@ -2829,7 +2827,7 @@ dependencies = [ "log", "rand 0.9.2", "sha1", - "thiserror 2.0.16", + "thiserror 2.0.17", "utf-8", ] diff --git a/src/enterprise/handlers/desktop_client_mfa.rs b/src/enterprise/handlers/desktop_client_mfa.rs index e4e98faa..fc756143 100644 --- a/src/enterprise/handlers/desktop_client_mfa.rs +++ b/src/enterprise/handlers/desktop_client_mfa.rs @@ -21,23 +21,24 @@ pub(super) async fn mfa_auth_callback( ) -> Result { info!("Processing MFA authentication callback"); debug!( - "Received payload: state={}, flow_type={}", + "Received payload: state={}, flow_type={:?}", payload.state, payload.flow_type ); - let flow_type = payload.flow_type.parse::().map_err(|err| { - warn!("Failed to parse flow type '{}': {err:?}", payload.flow_type); - ApiError::BadRequest("Invalid flow type".into()) - })?; - - if flow_type != FlowType::Mfa { - warn!("Invalid flow type for MFA callback: {flow_type:?}"); - return Err(ApiError::BadRequest( - "Invalid flow type for MFA callback".into(), - )); + match payload.flow_type { + FlowType::Mfa => (), + FlowType::Enrollment => { + warn!( + "Invalid flow type for MFA callback: {:?}", + payload.flow_type + ); + return Err(ApiError::BadRequest( + "Invalid flow type for MFA callback".into(), + )); + } } - debug!("Flow type validation passed: {flow_type:?}"); + debug!("Flow type validation passed: {:?}", payload.flow_type); let nonce = private_cookies .get(NONCE_COOKIE_NAME) @@ -78,7 +79,7 @@ pub(super) async fn mfa_auth_callback( let request = ClientMfaOidcAuthenticateRequest { code: payload.code, nonce, - callback_url: state.callback_url(&flow_type).to_string(), + callback_url: state.callback_url(&payload.flow_type).to_string(), state: payload.state, }; diff --git a/src/enterprise/handlers/openid_login.rs b/src/enterprise/handlers/openid_login.rs index ea0af22b..49ad8385 100644 --- a/src/enterprise/handlers/openid_login.rs +++ b/src/enterprise/handlers/openid_login.rs @@ -44,29 +44,18 @@ impl AuthInfo { } } -#[derive(Deserialize, Debug, PartialEq, Eq)] +#[derive(Deserialize, Debug)] +#[serde(rename_all = "lowercase")] pub(crate) enum FlowType { Enrollment, Mfa, } -impl std::str::FromStr for FlowType { - type Err = (); - - fn from_str(s: &str) -> Result { - match s.to_lowercase().as_str() { - "enrollment" => Ok(FlowType::Enrollment), - "mfa" => Ok(FlowType::Mfa), - _ => Err(()), - } - } -} - #[derive(Deserialize, Debug)] -struct RequestData { +pub(crate) struct RequestData { state: Option, #[serde(rename = "type")] - flow_type: String, + flow_type: FlowType, } /// Request external OAuth2/OpenID provider details from Defguard Core. @@ -79,13 +68,8 @@ async fn auth_info( ) -> Result<(PrivateCookieJar, Json), ApiError> { debug!("Getting auth info for OAuth2/OpenID login"); - let flow_type = request_data - .flow_type - .parse::() - .map_err(|()| ApiError::BadRequest("Invalid flow type".into()))?; - let request = AuthInfoRequest { - redirect_url: state.callback_url(&flow_type).to_string(), + redirect_url: state.callback_url(&request_data.flow_type).to_string(), state: request_data.state, }; @@ -127,7 +111,7 @@ pub(super) struct AuthenticationResponse { pub(super) code: String, pub(super) state: String, #[serde(rename = "type")] - pub(super) flow_type: String, + pub(super) flow_type: FlowType, } #[derive(Serialize)] @@ -143,15 +127,13 @@ async fn auth_callback( mut private_cookies: PrivateCookieJar, Json(payload): Json, ) -> Result<(PrivateCookieJar, Json), ApiError> { - let flow_type = payload - .flow_type - .parse::() - .map_err(|()| ApiError::BadRequest("Invalid flow type".into()))?; - - if flow_type != FlowType::Enrollment { - return Err(ApiError::BadRequest( - "Invalid flow type for OpenID enrollment callback".into(), - )); + match payload.flow_type { + FlowType::Enrollment => (), + FlowType::Mfa => { + return Err(ApiError::BadRequest( + "Invalid flow type for OpenID enrollment callback".into(), + )); + } } let nonce = private_cookies @@ -176,7 +158,7 @@ async fn auth_callback( let request = AuthCallbackRequest { code: payload.code, nonce, - callback_url: state.callback_url(&flow_type).to_string(), + callback_url: state.callback_url(&payload.flow_type).to_string(), }; let rx = state diff --git a/src/error.rs b/src/error.rs index af5d2ebc..b67b35c3 100644 --- a/src/error.rs +++ b/src/error.rs @@ -28,11 +28,13 @@ pub enum ApiError { EnterpriseNotEnabled, #[error("Precondition required: {0}")] PreconditionRequired(String), + #[error("Bad request: {0}")] + NotFound(String), } impl IntoResponse for ApiError { fn into_response(self) -> Response { - error!("{}", self); + error!("{self}"); let (status, error_message) = match self { Self::Unauthorized(msg) => (StatusCode::UNAUTHORIZED, msg), Self::BadRequest(msg) => (StatusCode::BAD_REQUEST, msg), @@ -42,15 +44,14 @@ impl IntoResponse for ApiError { "Enterprise features are not enabled".to_string(), ), Self::PreconditionRequired(msg) => (StatusCode::PRECONDITION_REQUIRED, msg), + Self::NotFound(msg) => (StatusCode::NOT_FOUND, msg), _ => ( StatusCode::INTERNAL_SERVER_ERROR, "Internal server error".to_string(), ), }; - let body = Json(json!({ - "error": error_message, - })); + let body = Json(json!({"error": error_message})); (status, body).into_response() } @@ -78,6 +79,7 @@ impl From for ApiError { _ => ApiError::PreconditionRequired(status.message().to_string()), }, Code::Unavailable => ApiError::CoreTimeout, + Code::NotFound => ApiError::NotFound(status.to_string()), _ => ApiError::Unexpected(status.to_string()), } } diff --git a/src/handlers/desktop_client_mfa.rs b/src/handlers/desktop_client_mfa.rs index 4c333b61..26662a89 100644 --- a/src/handlers/desktop_client_mfa.rs +++ b/src/handlers/desktop_client_mfa.rs @@ -196,7 +196,7 @@ async fn finish_remote_mfa( if let Some(sender) = sender_option { let _ = sender.send(response.preshared_key); } - // If desktop stopped listening for the result, there will be no palce to send the + // If desktop stopped listening for the result, there will be no place to send the // result. else { error!("Remote MFA approve finished but session was not found."); diff --git a/src/handlers/mod.rs b/src/handlers/mod.rs index c01763c5..cf391008 100644 --- a/src/handlers/mod.rs +++ b/src/handlers/mod.rs @@ -55,18 +55,21 @@ where /// /// Waits for core response with a given timeout and returns the response payload. pub(crate) async fn get_core_response(rx: Receiver) -> Result { - debug!("Fetching core response..."); + debug!("Fetching core response."); if let Ok(core_response) = timeout(CORE_RESPONSE_TIMEOUT, rx).await { debug!("Got gRPC response from Defguard Core"); if let Ok(Payload::CoreError(core_error)) = core_response { if core_error.status_code == Code::FailedPrecondition as i32 && core_error.message == "no valid license" { - debug!("Tried to get core response related to an enterprise feature but the enterprise is not enabled, ignoring it..."); + debug!( + "Tried to get response from Core related to an enterprise feature but the \ + enterprise is not enabled, ignoring it." + ); return Err(ApiError::EnterpriseNotEnabled); } error!( - "Received an error response from the core service. | status code: {} message: {}", + "Received an error response from Core service. | status code: {} message: {}", core_error.status_code, core_error.message ); return Err(core_error.into()); @@ -74,7 +77,7 @@ pub(crate) async fn get_core_response(rx: Receiver) -> Result