Skip to main content

styrene_rbac/
warning.rs

1//! Policy warnings — every silent failure during config loading is reported.
2
3use core::fmt;
4
5/// A non-fatal issue discovered during policy config loading.
6///
7/// Warnings don't prevent the policy from loading — they report what was
8/// dropped, filtered, or normalized so the operator can fix their config.
9#[derive(Debug, Clone, PartialEq, Eq)]
10pub enum PolicyWarning {
11    /// A roster entry was dropped because the identity hash is not valid
12    /// (must be exactly 32 lowercase hex characters).
13    InvalidIdentityHash { identity_hash: String, label: String },
14    /// A blocked prefix was dropped because it's too short (minimum 8 hex
15    /// chars) or contains non-hex characters.
16    InvalidBlockedPrefix { prefix: String },
17    /// An identity hash was normalized from uppercase to lowercase.
18    NormalizedIdentityHash { original: String, normalized: String },
19    /// A blocked prefix was normalized from uppercase to lowercase.
20    NormalizedBlockedPrefix { original: String, normalized: String },
21    /// A grant was dropped from a roster entry because it's not a known
22    /// capability string (possible typo).
23    UnknownGrant { identity_hash: String, grant: String },
24    /// Duplicate roster entry for the same identity hash. The later entry
25    /// was kept and the earlier one was dropped.
26    DuplicateRosterEntry { identity_hash: String, kept_role: String, dropped_role: String },
27}
28
29impl fmt::Display for PolicyWarning {
30    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31        match self {
32            Self::InvalidIdentityHash { identity_hash, label } => write!(
33                f,
34                "dropped roster entry: invalid identity hash '{identity_hash}' \
35                 (must be 32 hex chars){label_suffix}",
36                label_suffix =
37                    if label.is_empty() { String::new() } else { format!(" [label: {label}]") }
38            ),
39            Self::InvalidBlockedPrefix { prefix } => {
40                write!(f, "dropped blocked prefix: '{prefix}' (must be >= 8 hex chars)")
41            }
42            Self::NormalizedIdentityHash { original, normalized } => {
43                write!(f, "normalized identity hash: '{original}' -> '{normalized}'")
44            }
45            Self::NormalizedBlockedPrefix { original, normalized } => {
46                write!(f, "normalized blocked prefix: '{original}' -> '{normalized}'")
47            }
48            Self::UnknownGrant { identity_hash, grant } => write!(
49                f,
50                "dropped unknown grant '{grant}' from identity '{identity_hash}' \
51                 (not a known capability — possible typo?)"
52            ),
53            Self::DuplicateRosterEntry { identity_hash, kept_role, dropped_role } => write!(
54                f,
55                "duplicate roster entry for '{identity_hash}': \
56                 kept role '{kept_role}', dropped role '{dropped_role}'"
57            ),
58        }
59    }
60}