mockforge_intelligence/handlers/
risk_simulation.rs1use async_trait::async_trait;
6use axum::{extract::State, http::StatusCode, response::Json};
7use mockforge_foundation::auth_risk_types::RiskAssessment;
8use serde::Deserialize;
9use std::collections::HashMap;
10
11#[async_trait]
13pub trait RiskSimulationEngine: Send + Sync {
14 async fn set_simulated_risk(&self, user_id: String, score: Option<f64>);
16 async fn set_simulated_factors(&self, user_id: String, factors: HashMap<String, f64>);
18 async fn clear_simulated_risk(&self, user_id: &str);
20 async fn assess_risk(&self, user_id: &str, factors: &HashMap<String, f64>) -> RiskAssessment;
22}
23
24pub trait RiskSimulationStateProvider: Clone + Send + Sync + 'static {
26 type Engine: RiskSimulationEngine;
28 fn risk_engine(&self) -> &Self::Engine;
30}
31
32#[derive(Debug, Deserialize)]
34pub struct SetRiskScoreRequest {
35 pub user_id: String,
37 pub risk_score: f64,
39}
40
41#[derive(Debug, Deserialize)]
43pub struct SetRiskFactorsRequest {
44 pub user_id: String,
46 pub risk_factors: HashMap<String, f64>,
48}
49
50#[derive(Debug, Deserialize)]
52pub struct TriggerMfaRequest {
53 pub user_id: String,
55 pub mfa_type: String,
57}
58
59#[derive(Debug, Deserialize)]
61pub struct BlockUserRequest {
62 pub user_id: String,
64 pub reason: String,
66}
67
68pub async fn set_risk_score<S: RiskSimulationStateProvider>(
70 State(state): State<S>,
71 Json(request): Json<SetRiskScoreRequest>,
72) -> Result<Json<serde_json::Value>, StatusCode> {
73 state
74 .risk_engine()
75 .set_simulated_risk(request.user_id.clone(), Some(request.risk_score))
76 .await;
77
78 Ok(Json(serde_json::json!({
79 "success": true,
80 "user_id": request.user_id,
81 "risk_score": request.risk_score
82 })))
83}
84
85pub async fn set_risk_factors<S: RiskSimulationStateProvider>(
87 State(state): State<S>,
88 Json(request): Json<SetRiskFactorsRequest>,
89) -> Result<Json<serde_json::Value>, StatusCode> {
90 state
91 .risk_engine()
92 .set_simulated_factors(request.user_id.clone(), request.risk_factors.clone())
93 .await;
94
95 Ok(Json(serde_json::json!({
96 "success": true,
97 "user_id": request.user_id,
98 "risk_factors": request.risk_factors
99 })))
100}
101
102pub async fn clear_risk<S: RiskSimulationStateProvider>(
104 State(state): State<S>,
105 axum::extract::Path(user_id): axum::extract::Path<String>,
106) -> Result<Json<serde_json::Value>, StatusCode> {
107 state.risk_engine().clear_simulated_risk(&user_id).await;
108
109 Ok(Json(serde_json::json!({
110 "success": true,
111 "user_id": user_id,
112 "message": "Simulated risk cleared"
113 })))
114}
115
116pub async fn trigger_mfa<S: RiskSimulationStateProvider>(
118 State(state): State<S>,
119 Json(request): Json<TriggerMfaRequest>,
120) -> Result<Json<serde_json::Value>, StatusCode> {
121 state.risk_engine().set_simulated_risk(request.user_id.clone(), Some(0.8)).await;
123
124 Ok(Json(serde_json::json!({
125 "success": true,
126 "user_id": request.user_id,
127 "mfa_type": request.mfa_type,
128 "message": "MFA prompt triggered"
129 })))
130}
131
132pub async fn block_user<S: RiskSimulationStateProvider>(
134 State(state): State<S>,
135 Json(request): Json<BlockUserRequest>,
136) -> Result<Json<serde_json::Value>, StatusCode> {
137 state
139 .risk_engine()
140 .set_simulated_risk(request.user_id.clone(), Some(0.95))
141 .await;
142
143 Ok(Json(serde_json::json!({
144 "success": true,
145 "user_id": request.user_id,
146 "reason": request.reason,
147 "message": "User login blocked"
148 })))
149}
150
151pub async fn get_risk_assessment<S: RiskSimulationStateProvider>(
153 State(state): State<S>,
154 axum::extract::Path(user_id): axum::extract::Path<String>,
155) -> Json<serde_json::Value> {
156 let risk_factors = HashMap::new();
157 let assessment = state.risk_engine().assess_risk(&user_id, &risk_factors).await;
158
159 Json(serde_json::json!({
160 "user_id": user_id,
161 "risk_score": assessment.risk_score,
162 "risk_factors": assessment.risk_factors,
163 "recommended_action": assessment.recommended_action
164 }))
165}
166
167pub fn risk_simulation_router<S: RiskSimulationStateProvider>(state: S) -> axum::Router {
169 use axum::routing::{delete, get, post};
170
171 axum::Router::new()
172 .route("/risk/simulate", post(set_risk_score::<S>))
173 .route("/risk/factors", post(set_risk_factors::<S>))
174 .route("/risk/clear/{user_id}", delete(clear_risk::<S>))
175 .route("/risk/trigger-mfa", post(trigger_mfa::<S>))
176 .route("/risk/block", post(block_user::<S>))
177 .route("/risk/assessment/{user_id}", get(get_risk_assessment::<S>))
178 .with_state(state)
179}