| 
						
						
							
								
							
						
						
					 | 
				
				 | 
				
					@ -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<DuoAuthContext> { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    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<DuoAuthContex | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    }; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    ctx.delete(conn).await.ok(); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    return Some(ret_ctx) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    Some(ret_ctx) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					} | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					// Task to clean up expired Duo authentication contexts that may have accumulated in the database.
 | 
				
			
			
		
	
	
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
				
				 | 
				
					@ -400,15 +396,17 @@ fn make_callback_url(client_name: &str) -> Result<String, Error> { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        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<String, Error> { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					pub async fn get_duo_auth_url( | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    email: &str, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    client_id: &String, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    device_identifier: &String, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    conn: &mut DbConn, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					) -> Result<String, Error> { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    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( | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            ) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					} | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					} | 
				
			
			
		
	
	
		
			
				
					| 
						
						
						
					 | 
				
				 | 
				
					
  |