1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
use std::collections::HashMap;
use crate::{Action, ActionNameList, Identifier, Statement};
/// A collection of allowed permissions. This is constructed from a `Vec<`[`Statement`]`>`. By default, no actions are allowed on any resources.
#[derive(Default, Debug)]
pub struct Permissions {
children: Option<HashMap<Identifier<'static>, Permissions>>,
allowed: AllowedActions,
}
impl Permissions {
/// Evaluate whether the `action` is allowed to be taken upon
/// `resource_name`. Returns true if the action should be allowed. If no
/// statements that match `resource_name` allow `action`, false will be
/// returned.
pub fn allowed_to<'a, R: AsRef<[Identifier<'a>]>, P: Action>(
&self,
resource_name: R,
action: &P,
) -> bool {
let resource_name = resource_name.as_ref();
// This function checks all possible matches of `resource_name` by using
// recursion to call itself for each entry in `resource_name`. This
// first block does the function call recursion. The second block checks
// `action`.
if let Some(resource) = resource_name.first() {
if let Some(children) = &self.children {
let remaining_resource = &resource_name[1..resource_name.len()];
// Check if there are entries for this resource segment.
if let Some(permissions) = children.get(resource) {
if permissions.allowed_to(remaining_resource, action) {
return true;
}
}
// Check if there are entries for `Any`.
if let Some(permissions) = children.get(&Identifier::Any) {
if permissions.allowed_to(remaining_resource, action) {
return true;
}
}
}
}
// When execution reaches here, either resource_name is empty, or none
// of the previous paths have reached an "allow" state. The purpose of
// this chunk of code is to determine if this action is allowed based on
// this node's list of approved actions. This is also evaluated
// recursively, but at any stage if we reach match (positive or
// negative), we we can return.
let mut allowed = &self.allowed;
for name in action.name().0 {
allowed = match allowed {
AllowedActions::None => return false,
AllowedActions::All => return true,
AllowedActions::Some(actions) => {
if let Some(children_allowed) = actions.get(name.as_ref()) {
children_allowed
} else {
return false;
}
}
};
}
matches!(allowed, AllowedActions::All)
}
}
impl From<Vec<Statement>> for Permissions {
fn from(statements: Vec<Statement>) -> Self {
let mut permissions = Self::default();
for statement in statements {
// Apply this statement to all resources
for resource in statement.resources {
let mut current_permissions = &mut permissions;
// Look up the permissions for the resource path
for name in resource {
let permissions = current_permissions
.children
.get_or_insert_with(HashMap::default);
current_permissions = permissions.entry(name).or_default();
}
// Apply the "allowed" status to each action in this resource.
let mut allowed = &mut current_permissions.allowed;
match &statement.actions {
ActionNameList::List(actions) => {
for action in actions {
for name in &action.0 {
let action_map = match allowed {
AllowedActions::All | AllowedActions::None => {
*allowed = {
let mut action_map = HashMap::new();
action_map
.insert(name.to_string(), AllowedActions::None);
AllowedActions::Some(action_map)
};
if let AllowedActions::Some(action_map) = allowed {
action_map
} else {
unreachable!()
}
}
AllowedActions::Some(action_map) => action_map,
};
allowed = action_map.entry(name.to_string()).or_default();
}
}
}
ActionNameList::All => {}
}
if statement.allowed {
*allowed = AllowedActions::All
} else {
*allowed = AllowedActions::None
}
}
}
permissions
}
}
#[derive(Debug)]
enum AllowedActions {
None,
Some(HashMap<String, AllowedActions>),
All,
}
impl Default for AllowedActions {
fn default() -> Self {
Self::None
}
}