hyperion_vault_core/
rbac.rs1pub const ACTIONS: [&str; 4] = ["create", "update", "delete", "rotate"];
2
3pub fn is_valid_action(action: &str) -> bool {
4 action == "*" || ACTIONS.contains(&action)
5}
6
7pub fn path_matches(pattern: &str, name: &str) -> bool {
8 match pattern.strip_suffix('*') {
9 Some(prefix) => name.starts_with(prefix),
10 None => pattern == name,
11 }
12}
13
14pub fn action_matches(rule_action: &str, action: &str) -> bool {
15 rule_action == "*" || rule_action == action
16}
17
18pub fn authorize(is_admin: bool, rules: &[(String, String)], action: &str, name: &str) -> bool {
19 is_admin
20 || rules
21 .iter()
22 .any(|(a, p)| action_matches(a, action) && path_matches(p, name))
23}
24
25pub fn visible(is_admin: bool, rules: &[(String, String)], name: &str) -> bool {
26 is_admin || rules.iter().any(|(_, p)| path_matches(p, name))
27}
28
29#[cfg(test)]
30mod tests {
31 use super::*;
32
33 fn rules(v: &[(&str, &str)]) -> Vec<(String, String)> {
34 v.iter()
35 .map(|(a, p)| (a.to_string(), p.to_string()))
36 .collect()
37 }
38
39 #[test]
40 fn glob_prefix_matches_under_path() {
41 assert!(path_matches("stripe/*", "stripe/secret-key"));
42 assert!(!path_matches("stripe/*", "db/root"));
43 }
44
45 #[test]
46 fn exact_pattern_requires_exact_name() {
47 assert!(path_matches("db/pw", "db/pw"));
48 assert!(!path_matches("db/pw", "db/pw2"));
49 }
50
51 #[test]
52 fn lone_star_matches_everything() {
53 assert!(path_matches("*", "anything/at/all"));
54 }
55
56 #[test]
57 fn action_wildcard_matches_any_action() {
58 assert!(action_matches("*", "create"));
59 assert!(action_matches("create", "create"));
60 assert!(!action_matches("create", "delete"));
61 }
62
63 #[test]
64 fn admin_bypasses_all_rules() {
65 assert!(authorize(true, &[], "delete", "anything"));
66 }
67
68 #[test]
69 fn payment_role_scoped_to_stripe() {
70 let r = rules(&[("create", "stripe/*"), ("rotate", "stripe/*")]);
71 assert!(authorize(false, &r, "create", "stripe/secret-key"));
72 assert!(authorize(false, &r, "rotate", "stripe/webhook"));
73 assert!(!authorize(false, &r, "delete", "stripe/secret-key"));
74 assert!(!authorize(false, &r, "create", "db/root"));
75 }
76
77 #[test]
78 fn visibility_follows_patterns() {
79 let r = rules(&[("create", "stripe/*")]);
80 assert!(visible(false, &r, "stripe/a"));
81 assert!(!visible(false, &r, "db/a"));
82 assert!(visible(true, &[], "db/a"));
83 }
84
85 #[test]
86 fn action_validation() {
87 assert!(is_valid_action("create"));
88 assert!(is_valid_action("*"));
89 assert!(!is_valid_action("read"));
90 assert!(!is_valid_action(""));
91 }
92}