spawn-access-control 0.1.12

A Rust library for access control management with WebAssembly support, including role-based access control (RBAC), permissions, and audit logging.
Documentation
use crate::audit_log::AuditLogEntry;
use crate::geo_analyzer::GeoAnomaly;
use chrono::{DateTime, Utc, Duration};
use serde::Serialize;
use std::collections::HashMap;

#[derive(Debug, Serialize)]
pub struct RiskScore {
    pub user: String,
    pub score: f64,  // 0.0 (düşük risk) - 1.0 (yüksek risk)
    pub factors: Vec<RiskFactor>,
    pub timestamp: DateTime<Utc>,
}

#[derive(Debug, Serialize)]
pub struct RiskFactor {
    pub factor_type: RiskFactorType,
    pub weight: f64,
    pub description: String,
}

#[derive(Debug, Serialize)]
pub enum RiskFactorType {
    LocationAnomaly,
    FailedAttempts,
    UnusualTime,
    UnusualDevice,
    SuspiciousIP,
    ResourceAccess,
}

pub struct RiskAnalyzer {
    user_history: HashMap<String, UserBehavior>,
    config: RiskConfig,
}

#[derive(Clone)]
pub struct RiskConfig {
    pub location_weight: f64,
    pub time_weight: f64,
    pub attempts_weight: f64,
    pub device_weight: f64,
    pub ip_weight: f64,
    pub resource_weight: f64,
    pub high_risk_threshold: f64,
}

#[derive(Default)]
struct UserBehavior {
    usual_locations: Vec<String>,
    usual_access_times: Vec<u32>, // Saat bazında (0-23)
    usual_devices: Vec<String>,
    access_patterns: Vec<AccessPattern>,
}

struct AccessPattern {
    timestamp: DateTime<Utc>,
    resource: String,
    success: bool,
}

impl RiskAnalyzer {
    pub fn new(config: RiskConfig) -> Self {
        Self {
            user_history: HashMap::new(),
            config,
        }
    }

    pub fn calculate_risk(&mut self, user: &str, context: &AccessContext) -> RiskScore {
        let mut factors = Vec::new();
        let mut total_score = 0.0;

        // Konum bazlı risk analizi
        if let Some(geo_anomaly) = &context.geo_anomaly {
            factors.push(RiskFactor {
                factor_type: RiskFactorType::LocationAnomaly,
                weight: self.config.location_weight,
                description: format!("Unusual location access from {}", geo_anomaly.current_location.country),
            });
            total_score += self.config.location_weight;
        }

        // Zaman bazlı risk analizi
        if self.is_unusual_time(user, context.timestamp) {
            factors.push(RiskFactor {
                factor_type: RiskFactorType::UnusualTime,
                weight: self.config.time_weight,
                description: "Access attempt at unusual time".to_string(),
            });
            total_score += self.config.time_weight;
        }

        // Cihaz bazlı risk analizi
        if !self.is_known_device(user, &context.user_agent) {
            factors.push(RiskFactor {
                factor_type: RiskFactorType::UnusualDevice,
                weight: self.config.device_weight,
                description: "Access from unknown device".to_string(),
            });
            total_score += self.config.device_weight;
        }

        RiskScore {
            user: user.to_string(),
            score: total_score.min(1.0),
            factors,
            timestamp: Utc::now(),
        }
    }

    pub fn update_behavior(&mut self, user: &str, entry: &AuditLogEntry) {
        let behavior = self.user_history.entry(user.to_string()).or_default();
        
        if let Some(location) = entry.metadata.ip_address.as_ref() {
            if !behavior.usual_locations.contains(location) {
                behavior.usual_locations.push(location.clone());
            }
        }

        let hour = entry.timestamp.hour();
        if !behavior.usual_access_times.contains(&hour) {
            behavior.usual_access_times.push(hour);
        }

        if let Some(device) = entry.metadata.user_agent.as_ref() {
            if !behavior.usual_devices.contains(device) {
                behavior.usual_devices.push(device.clone());
            }
        }

        behavior.access_patterns.push(AccessPattern {
            timestamp: entry.timestamp,
            resource: entry.resource.clone(),
            success: matches!(entry.result, ActionResult::Success),
        });
    }

    fn is_unusual_time(&self, user: &str, timestamp: DateTime<Utc>) -> bool {
        if let Some(behavior) = self.user_history.get(user) {
            !behavior.usual_access_times.contains(&timestamp.hour())
        } else {
            false
        }
    }

    fn is_known_device(&self, user: &str, device: &str) -> bool {
        if let Some(behavior) = self.user_history.get(user) {
            behavior.usual_devices.contains(device)
        } else {
            false
        }
    }
}

pub struct AccessContext {
    pub timestamp: DateTime<Utc>,
    pub ip_address: String,
    pub user_agent: String,
    pub geo_anomaly: Option<GeoAnomaly>,
    pub recent_failed_attempts: u32,
}