Files
multiverse/xcom-ultra/xcu-core/src/iam_matrix.rs
T

80 lines
2.6 KiB
Rust

// [TSM.ID].[11031972] — All Rights Reserved. Proprietary & Confidential.
use axum::{
extract::Request,
middleware::Next,
response::Response,
http::StatusCode,
};
use jsonwebtoken::{decode, DecodingKey, Validation, Algorithm};
use serde::{Deserialize, Serialize};
use tracing::warn;
use std::sync::OnceLock;
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct IamClaims {
pub sub: String, // User/Tenant ID
pub role: String, // "supreme_admin" or "tenant"
pub packages: Vec<String>, // ["phase-1", "phase-72", "billing"]
pub exp: usize,
}
/// JWT Secret WAJIB dari environment variable — DILARANG HARDCODE
/// Env: XCU_IAM_SECRET (harus sama dengan secret JUMPA.ID IAM)
static IAM_SECRET: OnceLock<String> = OnceLock::new();
fn get_iam_secret() -> &'static str {
IAM_SECRET.get_or_init(|| {
std::env::var("XCU_IAM_SECRET").unwrap_or_else(|_| {
warn!("[IAM] ⚠️ XCU_IAM_SECRET env var NOT SET! Using fallback. SET THIS IN PRODUCTION!");
"XCU_IAM_SECRET_NOT_CONFIGURED".to_string()
})
})
}
pub async fn xcu_gatekeeper(
mut req: Request,
next: Next,
) -> Result<Response, StatusCode> {
// 1. Ekstrak Token dari Header atau Query Params (untuk SSE)
let auth_header = req.headers().get("Authorization").and_then(|h| h.to_str().ok());
let token = if let Some(auth) = auth_header {
if auth.starts_with("Bearer ") {
Some(auth.trim_start_matches("Bearer "))
} else {
None
}
} else {
// Fallback ke query param (misal untuk SSE)
req.uri().query().and_then(|q| {
q.split('&').find(|kv| kv.starts_with("token=")).map(|kv| kv.trim_start_matches("token="))
})
};
if let Some(t) = token {
let validation = Validation::new(Algorithm::HS256);
// validate_exp = true (default) — expired tokens WAJIB ditolak
match decode::<IamClaims>(
t,
&DecodingKey::from_secret(get_iam_secret().as_bytes()),
&validation,
) {
Ok(token_data) => {
// Token Sah! Masukkan Identitas Lintas-Dimensi ke dalam mesin.
req.extensions_mut().insert(token_data.claims);
}
Err(e) => {
warn!("IAM Matrix Breach Detected: Invalid JWT Token. {:?}", e);
return Err(StatusCode::UNAUTHORIZED);
}
}
} else {
// TANPA TOKEN = REJECTED. Tidak ada guest bypass.
warn!("[IAM] No token provided — ACCESS DENIED");
return Err(StatusCode::UNAUTHORIZED);
}
Ok(next.run(req).await)
}