mockforge_http/handlers/
risk_simulation.rs

1//! Risk simulation API handlers
2//!
3//! This module provides API endpoints for simulating risk scenarios
4//! and managing risk engine configuration.
5
6use axum::{extract::State, http::StatusCode, response::Json};
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9use std::sync::Arc;
10
11use crate::auth::risk_engine::{RiskEngine, RiskEngineConfig};
12
13/// Risk simulation state
14#[derive(Clone)]
15pub struct RiskSimulationState {
16    /// Risk engine
17    pub risk_engine: Arc<RiskEngine>,
18}
19
20/// Set risk score request
21#[derive(Debug, Deserialize)]
22pub struct SetRiskScoreRequest {
23    /// User ID
24    pub user_id: String,
25    /// Risk score (0.0 - 1.0)
26    pub risk_score: f64,
27}
28
29/// Set risk factors request
30#[derive(Debug, Deserialize)]
31pub struct SetRiskFactorsRequest {
32    /// User ID
33    pub user_id: String,
34    /// Risk factors
35    pub risk_factors: HashMap<String, f64>,
36}
37
38/// Trigger MFA request
39#[derive(Debug, Deserialize)]
40pub struct TriggerMfaRequest {
41    /// User ID
42    pub user_id: String,
43    /// MFA type
44    pub mfa_type: String,
45}
46
47/// Block user request
48#[derive(Debug, Deserialize)]
49pub struct BlockUserRequest {
50    /// User ID
51    pub user_id: String,
52    /// Reason
53    pub reason: String,
54}
55
56/// Set simulated risk score
57pub async fn set_risk_score(
58    State(state): State<RiskSimulationState>,
59    Json(request): Json<SetRiskScoreRequest>,
60) -> Result<Json<serde_json::Value>, StatusCode> {
61    state
62        .risk_engine
63        .set_simulated_risk(request.user_id.clone(), Some(request.risk_score))
64        .await;
65
66    Ok(Json(serde_json::json!({
67        "success": true,
68        "user_id": request.user_id,
69        "risk_score": request.risk_score
70    })))
71}
72
73/// Set simulated risk factors
74pub async fn set_risk_factors(
75    State(state): State<RiskSimulationState>,
76    Json(request): Json<SetRiskFactorsRequest>,
77) -> Result<Json<serde_json::Value>, StatusCode> {
78    state
79        .risk_engine
80        .set_simulated_factors(request.user_id.clone(), request.risk_factors.clone())
81        .await;
82
83    Ok(Json(serde_json::json!({
84        "success": true,
85        "user_id": request.user_id,
86        "risk_factors": request.risk_factors
87    })))
88}
89
90/// Clear simulated risk
91pub async fn clear_risk(
92    State(state): State<RiskSimulationState>,
93    axum::extract::Path(user_id): axum::extract::Path<String>,
94) -> Result<Json<serde_json::Value>, StatusCode> {
95    state.risk_engine.clear_simulated_risk(&user_id).await;
96
97    Ok(Json(serde_json::json!({
98        "success": true,
99        "user_id": user_id,
100        "message": "Simulated risk cleared"
101    })))
102}
103
104/// Trigger MFA prompt
105pub async fn trigger_mfa(
106    State(state): State<RiskSimulationState>,
107    Json(request): Json<TriggerMfaRequest>,
108) -> Result<Json<serde_json::Value>, StatusCode> {
109    // Set risk score high enough to trigger MFA
110    state.risk_engine.set_simulated_risk(request.user_id.clone(), Some(0.8)).await;
111
112    Ok(Json(serde_json::json!({
113        "success": true,
114        "user_id": request.user_id,
115        "mfa_type": request.mfa_type,
116        "message": "MFA prompt triggered"
117    })))
118}
119
120/// Block user login
121pub async fn block_user(
122    State(state): State<RiskSimulationState>,
123    Json(request): Json<BlockUserRequest>,
124) -> Result<Json<serde_json::Value>, StatusCode> {
125    // Set risk score high enough to block
126    state.risk_engine.set_simulated_risk(request.user_id.clone(), Some(0.95)).await;
127
128    Ok(Json(serde_json::json!({
129        "success": true,
130        "user_id": request.user_id,
131        "reason": request.reason,
132        "message": "User login blocked"
133    })))
134}
135
136/// Get risk assessment for user
137pub async fn get_risk_assessment(
138    State(state): State<RiskSimulationState>,
139    axum::extract::Path(user_id): axum::extract::Path<String>,
140) -> Json<serde_json::Value> {
141    let risk_factors = HashMap::new();
142    let assessment = state.risk_engine.assess_risk(&user_id, &risk_factors).await;
143
144    Json(serde_json::json!({
145        "user_id": user_id,
146        "risk_score": assessment.risk_score,
147        "risk_factors": assessment.risk_factors,
148        "recommended_action": assessment.recommended_action
149    }))
150}
151
152/// Create risk simulation router
153pub fn risk_simulation_router(state: RiskSimulationState) -> axum::Router {
154    use axum::routing::{delete, get, post};
155
156    axum::Router::new()
157        .route("/risk/simulate", post(set_risk_score))
158        .route("/risk/factors", post(set_risk_factors))
159        .route("/risk/clear/{user_id}", delete(clear_risk))
160        .route("/risk/trigger-mfa", post(trigger_mfa))
161        .route("/risk/block", post(block_user))
162        .route("/risk/assessment/{user_id}", get(get_risk_assessment))
163        .with_state(state)
164}