tiny-counter 0.1.0

Track event counts across time windows with fixed memory and fast queries
Documentation
// Security patterns and attack prevention.
//
// This example shows how to:
// - Implement brute force protection with progressive backoff
// - Detect anomalous activity patterns
// - Use cooldowns after failed attempts
// - Combine multiple security constraints

use chrono::Duration;
use tiny_counter::{EventId, EventStore, TimeUnit};

// Security-related events
#[derive(Debug, Clone, Copy)]
enum SecurityEvent {
    LoginAttempt,
    LoginSuccess,
    LoginFailed,
    ApiCall,
}

impl AsRef<str> for SecurityEvent {
    fn as_ref(&self) -> &str {
        match self {
            SecurityEvent::LoginAttempt => "security:login:attempt",
            SecurityEvent::LoginSuccess => "security:login:success",
            SecurityEvent::LoginFailed => "security:login:failed",
            SecurityEvent::ApiCall => "security:api:call",
        }
    }
}

impl EventId for SecurityEvent {}

fn main() {
    println!("=== Security Patterns Examples ===\n");

    // 1. Basic brute force protection
    println!("1. Brute force protection:");
    let store = EventStore::new();

    // Allow 5 attempts per minute, 20 per hour
    for i in 1..=8 {
        let result = store
            .limit()
            .at_most(SecurityEvent::LoginAttempt, 5, TimeUnit::Minutes)
            .at_most(SecurityEvent::LoginAttempt, 20, TimeUnit::Hours)
            .check_and_record(SecurityEvent::LoginAttempt);

        match result {
            Ok(()) => println!("  Login attempt {}: ✓ Allowed", i),
            Err(e) => println!("  Login attempt {}: ✗ Rate limited - {}", i, e),
        }
    }

    // 2. Progressive backoff
    println!("\n2. Progressive backoff after failures:");
    let store = EventStore::new();

    // Simulate failed login attempts
    for _ in 0..4 {
        store.record(SecurityEvent::LoginFailed);
    }

    let failed_attempts = store
        .query(SecurityEvent::LoginFailed)
        .last_minutes(15)
        .sum()
        .unwrap_or(0);

    println!("  Failed attempts in last 15 minutes: {}", failed_attempts);

    // Progressive cooldown based on failures
    let cooldown_seconds = match failed_attempts {
        0..=2 => 0,
        3..=5 => 30,
        6..=10 => 300, // 5 min
        _ => 1800,     // 30 min
    };

    println!("  Required cooldown: {} seconds", cooldown_seconds);

    if cooldown_seconds > 0 {
        let result = store
            .limit()
            .cooldown(
                SecurityEvent::LoginAttempt,
                Duration::seconds(cooldown_seconds),
            )
            .check_and_record(SecurityEvent::LoginAttempt);

        match result {
            Ok(()) => println!("  Next login attempt: ✓ Allowed (cooldown expired)"),
            Err(_) => println!("  Next login attempt: ✗ Cooldown active"),
        }
    }

    // 3. Anomaly detection
    println!("\n3. Anomaly detection:");
    let store = EventStore::new();

    // Establish baseline (simulate 30 days of normal activity)
    store.record_count(SecurityEvent::ApiCall, 300); // ~10/day

    let baseline = store
        .query(SecurityEvent::ApiCall)
        .last_days(30)
        .average()
        .unwrap_or(10.0);

    println!("  Baseline: {:.1} calls/day", baseline);

    // Simulate sudden spike
    store.record_count(SecurityEvent::ApiCall, 100);

    let current = store
        .query(SecurityEvent::ApiCall)
        .last_hours(1)
        .sum()
        .unwrap_or(0) as f64;

    println!("  Current hour: {} calls", current);

    // Alert if activity exceeds 5x baseline
    let threshold = baseline * 5.0;
    if current > threshold {
        println!(
            "  ⚠️ ALERT: Activity {} exceeds threshold {:.1}",
            current, threshold
        );
        println!("  ({}x normal rate)", current / baseline);
    } else {
        println!("  Activity within normal range");
    }

    // 4. Multi-factor security constraints
    println!("\n4. Multi-layer security:");
    let store = EventStore::new();

    // Try a sensitive operation with multiple constraints
    let result = store
        .limit()
        .at_most(SecurityEvent::LoginAttempt, 3, TimeUnit::Minutes)
        .at_most(SecurityEvent::LoginAttempt, 10, TimeUnit::Hours)
        .cooldown(SecurityEvent::LoginFailed, Duration::seconds(60))
        .check_and_record(SecurityEvent::LoginAttempt);

    println!("  Checking multiple security constraints:");
    println!("    - Max 3 attempts per minute");
    println!("    - Max 10 attempts per hour");
    println!("    - 60-second cooldown after failures");

    match result {
        Ok(()) => println!("  Result: ✓ All constraints passed"),
        Err(e) => println!("  Result: ✗ Constraint violated - {}", e),
    }

    // 5. Authentication workflow
    println!("\n5. Complete authentication workflow:");
    let store = EventStore::new();

    fn attempt_login(store: &EventStore, password_correct: bool) -> bool {
        // Check rate limits
        let can_attempt = store
            .limit()
            .at_most(SecurityEvent::LoginAttempt, 5, TimeUnit::Minutes)
            .at_most(SecurityEvent::LoginAttempt, 20, TimeUnit::Hours)
            .check_and_record(SecurityEvent::LoginAttempt);

        if can_attempt.is_err() {
            println!("    Rate limited");
            return false;
        }

        if password_correct {
            store.record(SecurityEvent::LoginSuccess);
            println!("    ✓ Login successful");
            true
        } else {
            store.record(SecurityEvent::LoginFailed);
            println!("    ✗ Login failed");
            false
        }
    }

    println!("  Attempting logins:");

    println!("  Attempt 1 (wrong password):");
    attempt_login(&store, false);

    println!("  Attempt 2 (wrong password):");
    attempt_login(&store, false);

    println!("  Attempt 3 (correct password):");
    attempt_login(&store, true);

    let success_rate = store
        .query_ratio(SecurityEvent::LoginSuccess, SecurityEvent::LoginAttempt)
        .last_hours(1);

    if let Some(rate) = success_rate {
        println!("  Success rate: {:.1}%", rate * 100.0);
    }

    // 6. Account lockout
    println!("\n6. Account lockout after repeated failures:");
    let store = EventStore::new();

    // Simulate multiple failed attempts
    for _ in 0..6 {
        store.record(SecurityEvent::LoginFailed);
    }

    let failures = store
        .query(SecurityEvent::LoginFailed)
        .last_hours(1)
        .sum()
        .unwrap_or(0);

    println!("  Failed attempts in last hour: {}", failures);

    if failures >= 5 {
        println!("  ⚠️ Account locked");
        println!("  Action required: Admin review or wait 1 hour");

        // Enforce lockout
        let result = store
            .limit()
            .at_most(SecurityEvent::LoginAttempt, 0, TimeUnit::Hours) // Block all
            .check_and_record(SecurityEvent::LoginAttempt);

        match result {
            Ok(()) => println!("  Login: ✓ Allowed"),
            Err(_) => println!("  Login: ✗ Blocked (account locked)"),
        }
    } else {
        println!("  Account status: Active");
    }

    println!("\n✓ Security patterns complete!");
}