Skip to main content

ferrous_forge/config/locking/
validator.rs

1//! Configuration change validator that checks locks before allowing changes
2//!
3//! @task T015
4//! @epic T014
5
6use crate::config::locking::{HierarchicalLockManager, audit_log};
7use crate::{Error, Result};
8
9/// Configuration change validator that checks locks before allowing changes
10pub struct ConfigValidator;
11
12impl ConfigValidator {
13    /// Validate a configuration change against locks
14    ///
15    /// # Errors
16    ///
17    /// Returns an error if the change violates a lock.
18    #[allow(clippy::collapsible_if)]
19    pub async fn validate_change(key: &str, new_value: &str) -> Result<()> {
20        let lock_manager = HierarchicalLockManager::load().await?;
21
22        if let Some((level, entry)) = lock_manager.is_locked(key) {
23            if entry.value != new_value {
24                // Log the blocked attempt
25                audit_log::log_blocked_attempt(
26                    key,
27                    new_value,
28                    level,
29                    "Attempted change while locked",
30                )
31                .await?;
32
33                return Err(Error::config(format!(
34                    "Cannot change '{}' - it is locked at {} level\nCurrent value: {}\nAttempted value: {}\nLock reason: {}\n\nTo unlock, run:\n  ferrous-forge config unlock {} --level={} --reason=\"...\"",
35                    key,
36                    level.display_name(),
37                    entry.value,
38                    new_value,
39                    entry.reason,
40                    key,
41                    level.display_name().to_lowercase()
42                )));
43            }
44        }
45
46        Ok(())
47    }
48
49    /// Check if a key can be modified (not locked or same value)
50    pub async fn can_modify(key: &str, new_value: &str) -> bool {
51        matches!(Self::validate_change(key, new_value).await, Ok(()))
52    }
53}