actionable/
permissions.rs1use std::{collections::HashMap, sync::Arc};
2
3use serde::{Deserialize, Serialize};
4
5use crate::{
6 statement::Configuration, Action, ActionNameList, Identifier, PermissionDenied, ResourceName,
7 Statement,
8};
9
10#[derive(Default, Debug, Clone, Serialize, Deserialize)]
13pub struct Permissions {
14 data: Arc<Data>,
15}
16
17#[derive(Default, Debug, Clone, Serialize, Deserialize)]
18struct Data {
19 children: Option<HashMap<Identifier<'static>, Data>>,
20 allowed: AllowedActions,
21 configuration: Option<HashMap<String, Configuration>>,
22}
23
24impl Permissions {
25 #[must_use]
28 pub fn allow_all() -> Self {
29 Self::from(vec![Statement::allow_all_for_any_resource()])
30 }
31
32 pub fn check<'a, R: AsRef<[Identifier<'a>]>, P: Action>(
39 &self,
40 resource_name: R,
41 action: &P,
42 ) -> Result<(), PermissionDenied> {
43 if self.data.allowed_to(resource_name.as_ref(), action) {
44 Ok(())
45 } else {
46 Err(PermissionDenied {
47 resource: ResourceName::from(resource_name.as_ref()).to_owned(),
48 action: action.name(),
49 })
50 }
51 }
52
53 pub fn allowed_to<'a, R: AsRef<[Identifier<'a>]>, P: Action>(
58 &self,
59 resource_name: R,
60 action: &P,
61 ) -> bool {
62 self.data.allowed_to(resource_name, action)
63 }
64
65 #[must_use]
67 pub fn get<'a: 's, 's, R: AsRef<[Identifier<'a>]>>(
68 &'s self,
69 resource_name: R,
70 key: &str,
71 ) -> Option<&'s Configuration> {
72 self.data.get(resource_name, key)
73 }
74
75 #[must_use]
78 pub fn merged<'a>(permissions: impl IntoIterator<Item = &'a Self>) -> Self {
79 let mut combined = Data::default();
80 for incoming in permissions {
81 combined.add_permissions(&incoming.data);
82 }
83 Self {
84 data: Arc::new(combined),
85 }
86 }
87}
88
89impl Data {
90 fn add_permissions(&mut self, permissions: &Self) {
91 if let Some(children) = &permissions.children {
92 let our_children = self.children.get_or_insert_with(HashMap::new);
93 for (name, permissions) in children {
94 let our_permissions = our_children.entry(name.clone()).or_default();
95 our_permissions.add_permissions(permissions);
96 }
97 }
98
99 self.allowed.add_allowed(&permissions.allowed);
100 if let Some(incoming_configuration) = &permissions.configuration {
101 if let Some(configuration) = &mut self.configuration {
102 for (key, value) in incoming_configuration {
103 configuration
104 .entry(key.clone())
105 .or_insert_with(|| value.clone());
106 }
107 } else {
108 self.configuration = permissions.configuration.clone();
109 }
110 }
111 }
112
113 fn allowed_to<'a, R: AsRef<[Identifier<'a>]>, P: Action>(
114 &self,
115 resource_name: R,
116 action: &P,
117 ) -> bool {
118 let resource_name = resource_name.as_ref();
119 if let Some(resource) = resource_name.first() {
124 if let Some(children) = &self.children {
125 let remaining_resource = &resource_name[1..resource_name.len()];
126 if let Some(permissions) = children.get(resource) {
128 if permissions.allowed_to(remaining_resource, action) {
129 return true;
130 }
131 }
132
133 if let Some(permissions) = children.get(&Identifier::Any) {
135 if permissions.allowed_to(remaining_resource, action) {
136 return true;
137 }
138 }
139 }
140 }
141
142 let mut allowed = &self.allowed;
149 for name in action.name().0 {
150 allowed = match allowed {
151 AllowedActions::None => return false,
152 AllowedActions::All => return true,
153 AllowedActions::Some(actions) => {
154 if let Some(children_allowed) = actions.get(name.as_ref()) {
155 children_allowed
156 } else {
157 return false;
158 }
159 }
160 };
161 }
162 matches!(allowed, AllowedActions::All)
163 }
164
165 #[must_use]
166 pub fn get<'a: 's, 's, R: AsRef<[Identifier<'a>]>>(
167 &'s self,
168 resource_name: R,
169 key: &str,
170 ) -> Option<&'s Configuration> {
171 let resource_name = resource_name.as_ref();
172 if let Some(resource) = resource_name.as_ref().first() {
177 if let Some(children) = &self.children {
178 let remaining_resource = &resource_name[1..resource_name.len()];
179 if let Some(permissions) = children.get(resource) {
181 if let Some(config) = permissions.get(remaining_resource, key) {
182 return Some(config);
183 }
184 }
185
186 if let Some(permissions) = children.get(&Identifier::Any) {
188 if let Some(config) = permissions.get(remaining_resource, key) {
189 return Some(config);
190 }
191 }
192 }
193 }
194
195 self.configuration
196 .as_ref()
197 .and_then(|configs| configs.get(key))
198 }
199}
200
201impl From<Statement> for Permissions {
202 fn from(stmt: Statement) -> Self {
203 Self::from(vec![stmt])
204 }
205}
206
207impl From<Vec<Statement>> for Permissions {
208 fn from(statements: Vec<Statement>) -> Self {
209 let mut permissions = Data::default();
210 for statement in statements {
211 for resource in statement.resources {
213 let mut current_permissions = &mut permissions;
214 for name in resource {
216 let permissions = current_permissions
217 .children
218 .get_or_insert_with(HashMap::default);
219 current_permissions = permissions.entry(name).or_default();
220 }
221
222 match &statement.actions {
224 Some(ActionNameList::List(actions)) =>
225 for action in actions {
226 let mut allowed = &mut current_permissions.allowed;
227 for name in &action.0 {
228 let action_map = match allowed {
229 AllowedActions::All | AllowedActions::None => {
230 *allowed = {
231 let mut action_map = HashMap::new();
232 action_map
233 .insert(name.to_string(), AllowedActions::None);
234 AllowedActions::Some(action_map)
235 };
236 if let AllowedActions::Some(action_map) = allowed {
237 action_map
238 } else {
239 unreachable!()
240 }
241 }
242 AllowedActions::Some(action_map) => action_map,
243 };
244 allowed = action_map.entry(name.to_string()).or_default();
245 }
246 *allowed = AllowedActions::All;
247 },
248 Some(ActionNameList::All) => {
249 current_permissions.allowed = AllowedActions::All;
250 }
251 None => {}
252 }
253
254 if let Some(incoming_configs) = &statement.configuration {
255 let configuration = current_permissions
256 .configuration
257 .get_or_insert_with(HashMap::default);
258 for (key, value) in incoming_configs {
259 configuration
260 .entry(key.clone())
261 .or_insert_with(|| value.clone());
262 }
263 }
264 }
265 }
266 Self {
267 data: Arc::new(permissions),
268 }
269 }
270}
271
272#[derive(Debug, Clone, Serialize, Deserialize)]
273enum AllowedActions {
274 None,
275 Some(HashMap<String, AllowedActions>),
276 All,
277}
278
279impl Default for AllowedActions {
280 fn default() -> Self {
281 Self::None
282 }
283}
284
285impl AllowedActions {
286 fn add_allowed(&mut self, other: &Self) {
287 match other {
288 Self::None => {}
289 Self::Some(actions) =>
290 if !matches!(self, Self::All) {
291 if let Self::Some(our_allowed) = self {
292 for (name, allowed) in actions {
293 let our_entry = our_allowed.entry(name.clone()).or_default();
294 our_entry.add_allowed(allowed);
295 }
296 } else {
297 *self = Self::Some(actions.clone());
298 }
299 },
300 Self::All => {
301 *self = Self::All;
302 }
303 }
304 }
305}