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::error::AccessError;
use ring::hmac;
use std::time::{SystemTime, UNIX_EPOCH};

#[derive(Debug, Clone)]
pub struct MFAConfig {
    secret_key: Vec<u8>,
    token_validity: u64,  // seconds
    max_attempts: u32,
    attempt_window: u64,  // seconds
}

pub struct MFAManager {
    config: MFAConfig,
    failed_attempts: std::collections::HashMap<String, Vec<u64>>,
}

impl MFAManager {
    pub fn new(config: MFAConfig) -> Self {
        Self {
            config,
            failed_attempts: std::collections::HashMap::new(),
        }
    }

    pub fn generate_totp(&self, user: &str) -> Result<String, AccessError> {
        let current_time = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap()
            .as_secs() / 30; // 30-second window

        let key = hmac::Key::new(hmac::HMAC_SHA256, &self.config.secret_key);
        let tag = hmac::sign(&key, format!("{}{}", user, current_time).as_bytes());

        // 6-digit TOTP
        let code = format!("{:06}", tag.as_ref()[0..4].iter()
            .fold(0u32, |acc, &x| acc * 256 + x as u32) % 1_000_000);

        Ok(code)
    }

    pub fn verify_totp(&mut self, user: &str, token: &str) -> Result<bool, AccessError> {
        // Rate limiting kontrolü
        if self.is_rate_limited(user) {
            return Err(AccessError::TooManyAttempts);
        }

        let expected = self.generate_totp(user)?;
        let valid = token == expected;

        if !valid {
            self.record_failed_attempt(user);
        }

        Ok(valid)
    }

    fn is_rate_limited(&self, user: &str) -> bool {
        if let Some(attempts) = self.failed_attempts.get(user) {
            let now = SystemTime::now()
                .duration_since(UNIX_EPOCH)
                .unwrap()
                .as_secs();

            let recent_attempts = attempts.iter()
                .filter(|&&timestamp| now - timestamp < self.config.attempt_window)
                .count();

            recent_attempts >= self.config.max_attempts as usize
        } else {
            false
        }
    }

    fn record_failed_attempt(&mut self, user: &str) {
        let now = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap()
            .as_secs();

        self.failed_attempts
            .entry(user.to_string())
            .or_insert_with(Vec::new)
            .push(now);
    }
}