Skip to main content

auth_framework/api/
security_simple.rs

1//! Simple Security Management API Endpoints
2//!
3//! This module provides basic endpoints for managing security features:
4//! - IP blacklisting and whitelisting
5//! - Security statistics and monitoring
6
7use crate::api::{ApiResponse, ApiState};
8use axum::{
9    Form,
10    extract::{Path, State},
11};
12use lazy_static::lazy_static;
13use serde::{Deserialize, Serialize};
14use serde_json::json;
15use std::collections::HashSet;
16use std::net::IpAddr;
17use std::sync::RwLock;
18
19// Global IP blacklist (in production, this should be persistent storage)
20lazy_static! {
21    static ref IP_BLACKLIST: RwLock<HashSet<IpAddr>> = RwLock::new(HashSet::new());
22    static ref SECURITY_STATS: RwLock<SecurityStats> = RwLock::new(SecurityStats::default());
23}
24
25#[derive(Debug, Default, Clone, Serialize)]
26struct SecurityStats {
27    blocked_requests: u64,
28    failed_auth_attempts: u64,
29    suspicious_activity: u64,
30    last_updated: Option<i64>,
31}
32
33#[derive(Debug, Deserialize)]
34pub struct BlacklistIpForm {
35    pub ip: String,
36    pub reason: Option<String>,
37}
38
39#[derive(Debug, Serialize)]
40pub struct SecurityStatsResponse {
41    pub blocked_requests: u64,
42    pub failed_auth_attempts: u64,
43    pub suspicious_activity: u64,
44    pub blacklisted_ips: usize,
45    pub last_updated: Option<i64>,
46}
47
48/// POST /api/v1/security/blacklist
49/// Add an IP address to the blacklist
50pub async fn blacklist_ip_endpoint(
51    State(_state): State<ApiState>,
52    Form(form): Form<BlacklistIpForm>,
53) -> ApiResponse<serde_json::Value> {
54    let ip: IpAddr = match form.ip.parse() {
55        Ok(ip) => ip,
56        Err(_) => return ApiResponse::error_typed("invalid_ip", "Invalid IP address format"),
57    };
58
59    {
60        let Ok(mut blacklist) = IP_BLACKLIST.write() else {
61            return ApiResponse::error_typed("internal_error", "Security subsystem unavailable");
62        };
63        blacklist.insert(ip);
64    }
65
66    // Update security stats
67    {
68        if let Ok(mut stats) = SECURITY_STATS.write() {
69            stats.blocked_requests += 1;
70            stats.last_updated = Some(chrono::Utc::now().timestamp());
71        }
72    }
73
74    let data = json!({
75        "ip": ip.to_string(),
76        "reason": form.reason.unwrap_or_else(|| "Manual blacklist".to_string())
77    });
78
79    ApiResponse::success_with_message(data, format!("IP {} added to blacklist", ip))
80}
81
82/// DELETE /api/v1/security/blacklist/{ip}
83/// Remove an IP address from the blacklist
84pub async fn unblock_ip_endpoint(
85    State(_state): State<ApiState>,
86    Path(ip_str): Path<String>,
87) -> ApiResponse<serde_json::Value> {
88    let ip: IpAddr = match ip_str.parse() {
89        Ok(ip) => ip,
90        Err(_) => return ApiResponse::error_typed("invalid_ip", "Invalid IP address format"),
91    };
92
93    let removed = {
94        let Ok(mut blacklist) = IP_BLACKLIST.write() else {
95            return ApiResponse::error_typed("internal_error", "Security subsystem unavailable");
96        };
97        blacklist.remove(&ip)
98    };
99
100    if removed {
101        // Update security stats
102        {
103            if let Ok(mut stats) = SECURITY_STATS.write() {
104                stats.last_updated = Some(chrono::Utc::now().timestamp());
105            }
106        }
107
108        let data = json!({
109            "ip": ip.to_string(),
110            "status": "unblocked"
111        });
112
113        ApiResponse::success_with_message(data, format!("IP {} removed from blacklist", ip))
114    } else {
115        let data = json!({
116            "ip": ip.to_string(),
117            "status": "not_found"
118        });
119
120        ApiResponse::success_with_message(data, format!("IP {} was not in blacklist", ip))
121    }
122}
123
124/// GET /api/v1/security/stats
125/// Get security statistics
126pub async fn stats_endpoint(State(_state): State<ApiState>) -> ApiResponse<SecurityStatsResponse> {
127    let stats = SECURITY_STATS.read().map(|s| s.clone()).unwrap_or_default();
128    let blacklist_size = IP_BLACKLIST.read().map(|b| b.len()).unwrap_or(0);
129
130    let response_data = SecurityStatsResponse {
131        blocked_requests: stats.blocked_requests,
132        failed_auth_attempts: stats.failed_auth_attempts,
133        suspicious_activity: stats.suspicious_activity,
134        blacklisted_ips: blacklist_size,
135        last_updated: stats.last_updated,
136    };
137
138    ApiResponse::success(response_data)
139}
140
141/// Check if an IP address is blacklisted (for middleware use)
142pub fn is_ip_blacklisted(ip: &IpAddr) -> bool {
143    IP_BLACKLIST.read().map(|b| b.contains(ip)).unwrap_or(false)
144}
145
146/// Increment failed authentication attempts (for middleware use)
147pub fn increment_failed_auth() {
148    if let Ok(mut stats) = SECURITY_STATS.write() {
149        stats.failed_auth_attempts += 1;
150        stats.last_updated = Some(chrono::Utc::now().timestamp());
151    }
152}
153
154/// Increment suspicious activity counter (for middleware use)
155pub fn increment_suspicious_activity() {
156    if let Ok(mut stats) = SECURITY_STATS.write() {
157        stats.suspicious_activity += 1;
158        stats.last_updated = Some(chrono::Utc::now().timestamp());
159    }
160}