Skip to main content

qa_spec/
secrets.rs

1use globset::Glob;
2
3use crate::spec::form::SecretsPolicy;
4
5/// Secret access modes.
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub enum SecretAction {
8    Read,
9    Write,
10}
11
12/// Result of evaluating a secret policy for a key.
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum SecretAccessResult {
15    Allowed,
16    Denied(&'static str),
17    HostUnavailable,
18}
19
20fn matches_pattern(pattern: &str, key: &str) -> bool {
21    match Glob::new(pattern) {
22        Ok(glob) => glob.compile_matcher().is_match(key),
23        Err(_) => false,
24    }
25}
26
27fn matches_any(patterns: &[String], key: &str) -> bool {
28    patterns.iter().any(|pattern| matches_pattern(pattern, key))
29}
30
31pub fn evaluate(
32    policy: Option<&SecretsPolicy>,
33    key: &str,
34    action: SecretAction,
35    host_available: bool,
36) -> SecretAccessResult {
37    let policy = match policy {
38        Some(policy) if policy.enabled => policy,
39        _ => return SecretAccessResult::Denied("secret_access_denied"),
40    };
41
42    let enabled = match action {
43        SecretAction::Read => policy.read_enabled,
44        SecretAction::Write => policy.write_enabled,
45    };
46
47    if !enabled {
48        return SecretAccessResult::Denied("secret_access_denied");
49    }
50
51    if matches_any(&policy.deny, key) {
52        return SecretAccessResult::Denied("secret_access_denied");
53    }
54
55    if policy.allow.is_empty() || !matches_any(&policy.allow, key) {
56        return SecretAccessResult::Denied("secret_access_denied");
57    }
58
59    if !host_available {
60        return SecretAccessResult::HostUnavailable;
61    }
62
63    SecretAccessResult::Allowed
64}
65
66#[cfg(test)]
67mod tests {
68    use super::*;
69    use crate::spec::form::SecretsPolicy;
70
71    fn policy() -> SecretsPolicy {
72        SecretsPolicy {
73            enabled: true,
74            read_enabled: true,
75            write_enabled: true,
76            allow: vec!["aws/*".into()],
77            deny: vec!["aws/secret-deny".into()],
78        }
79    }
80
81    #[test]
82    fn allowed_key_using_pattern() {
83        assert_eq!(
84            evaluate(Some(&policy()), "aws/key", SecretAction::Read, true),
85            SecretAccessResult::Allowed
86        );
87    }
88
89    #[test]
90    fn denied_key_due_to_deny_list() {
91        assert_eq!(
92            evaluate(Some(&policy()), "aws/secret-deny", SecretAction::Read, true),
93            SecretAccessResult::Denied("secret_access_denied")
94        );
95    }
96
97    #[test]
98    fn host_unavailable_when_disabled() {
99        assert_eq!(
100            evaluate(Some(&policy()), "aws/key", SecretAction::Read, false),
101            SecretAccessResult::HostUnavailable
102        );
103    }
104}