use std::sync::RwLock;
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::{SystemTime, UNIX_EPOCH};
use serde::{Deserialize, Serialize};
use tracing::{info, warn};
pub struct EmergencyState {
locked_down: AtomicBool,
lockdown_reason: RwLock<String>,
break_glass_key_path: Option<String>,
two_party: TwoPartyTracker,
}
struct TwoPartyTracker {
required_operations: Vec<String>,
pending: RwLock<std::collections::HashMap<String, (String, u64)>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TwoPartyConfig {
#[serde(default)]
pub require_for: Vec<String>,
}
impl Default for TwoPartyConfig {
fn default() -> Self {
Self {
require_for: vec![
"EMERGENCY LOCKDOWN".into(),
"IMPERSONATE".into(),
"DROP TENANT".into(),
],
}
}
}
impl EmergencyState {
pub fn new(break_glass_key_path: Option<String>, two_party_config: TwoPartyConfig) -> Self {
Self {
locked_down: AtomicBool::new(false),
lockdown_reason: RwLock::new(String::new()),
break_glass_key_path,
two_party: TwoPartyTracker {
required_operations: two_party_config.require_for,
pending: RwLock::new(std::collections::HashMap::new()),
},
}
}
pub fn lockdown(&self, reason: &str) {
self.locked_down.store(true, Ordering::SeqCst);
let mut r = self
.lockdown_reason
.write()
.unwrap_or_else(|p| p.into_inner());
*r = reason.to_string();
warn!(reason = %reason, "EMERGENCY LOCKDOWN ACTIVATED");
}
pub fn unlock(&self) {
self.locked_down.store(false, Ordering::SeqCst);
let mut r = self
.lockdown_reason
.write()
.unwrap_or_else(|p| p.into_inner());
r.clear();
info!("EMERGENCY LOCKDOWN DEACTIVATED");
}
pub fn is_locked_down(&self) -> bool {
self.locked_down.load(Ordering::SeqCst)
}
pub fn lockdown_reason(&self) -> String {
self.lockdown_reason
.read()
.unwrap_or_else(|p| p.into_inner())
.clone()
}
pub fn validate_break_glass(&self, provided_key: &str) -> bool {
let Some(ref path) = self.break_glass_key_path else {
return false;
};
let Ok(stored_key) = std::fs::read_to_string(path) else {
warn!(path = %path, "break-glass key file not readable");
return false;
};
let stored_trimmed = stored_key.trim();
if stored_trimmed.is_empty() {
return false;
}
provided_key.len() == stored_trimmed.len()
&& provided_key
.as_bytes()
.iter()
.zip(stored_trimmed.as_bytes())
.fold(0u8, |acc, (a, b)| acc | (a ^ b))
== 0
}
pub fn requires_two_party(&self, operation: &str) -> bool {
self.two_party
.required_operations
.iter()
.any(|op| operation.to_uppercase().starts_with(&op.to_uppercase()))
}
pub fn submit_two_party_approval(&self, operation: &str, approver: &str) -> bool {
let now = now_secs();
let op_key = operation.to_uppercase();
let mut pending = self
.two_party
.pending
.write()
.unwrap_or_else(|p| p.into_inner());
if let Some((first_approver, ts)) = pending.get(&op_key).cloned() {
if now - ts > 300 {
pending.remove(&op_key);
pending.insert(op_key, (approver.into(), now));
return true;
}
if first_approver == approver {
return true; }
pending.remove(&op_key);
info!(
operation = %operation,
first = %first_approver,
second = %approver,
"two-party authorization satisfied"
);
return false; }
pending.insert(op_key, (approver.into(), now));
true }
}
impl Default for EmergencyState {
fn default() -> Self {
Self::new(None, TwoPartyConfig::default())
}
}
fn now_secs() -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn lockdown_lifecycle() {
let state = EmergencyState::default();
assert!(!state.is_locked_down());
state.lockdown("security incident");
assert!(state.is_locked_down());
assert_eq!(state.lockdown_reason(), "security incident");
state.unlock();
assert!(!state.is_locked_down());
}
#[test]
fn two_party_auth() {
let state = EmergencyState::default();
assert!(state.submit_two_party_approval("EMERGENCY LOCKDOWN", "admin_alice"));
assert!(state.submit_two_party_approval("EMERGENCY LOCKDOWN", "admin_alice"));
assert!(!state.submit_two_party_approval("EMERGENCY LOCKDOWN", "admin_bob"));
}
#[test]
fn break_glass_no_file() {
let state = EmergencyState::default();
assert!(!state.validate_break_glass("any_key"));
}
#[test]
fn requires_two_party_check() {
let state = EmergencyState::default();
assert!(state.requires_two_party("EMERGENCY LOCKDOWN REASON 'test'"));
assert!(state.requires_two_party("IMPERSONATE AUTH USER 'x'"));
assert!(!state.requires_two_party("SELECT * FROM users"));
}
}