1
use std::{collections::HashMap, sync::Arc};
2

            
3
use serde::{Deserialize, Serialize};
4

            
5
use crate::{
6
    statement::Configuration, Action, ActionNameList, Identifier, PermissionDenied, ResourceName,
7
    Statement,
8
};
9

            
10
/// A collection of allowed permissions. This is constructed from a
11
/// `Vec<`[`Statement`]`>`. By default, no actions are allowed on any resources.
12
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
13
pub struct Permissions {
14
    data: Arc<Data>,
15
}
16

            
17
39
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
18
struct Data {
19
    children: Option<HashMap<Identifier<'static>, Data>>,
20
    allowed: AllowedActions,
21
    configuration: Option<HashMap<String, Configuration>>,
22
}
23

            
24
impl Permissions {
25
    /// Returns a `Permisions` instance constructed with
26
    /// [`Statement::allow_all()`].
27
    #[must_use]
28
    pub fn allow_all() -> Self {
29
        Self::from(vec![Statement::allow_all_for_any_resource()])
30
    }
31

            
32
    /// Evaluate whether the `action` is allowed to be taken upon
33
    /// `resource_name`. Returns `Ok` if permission is allowed.
34
    ///
35
    /// # Errors
36
    ///
37
    /// Returns `PermissionDenied` if permission is now allowed.
38
5
    pub fn check<'a, R: AsRef<[Identifier<'a>]>, P: Action>(
39
5
        &self,
40
5
        resource_name: R,
41
5
        action: &P,
42
5
    ) -> Result<(), PermissionDenied> {
43
5
        if self.data.allowed_to(resource_name.as_ref(), action) {
44
2
            Ok(())
45
        } else {
46
3
            Err(PermissionDenied {
47
3
                resource: ResourceName::from(resource_name.as_ref()).to_owned(),
48
3
                action: action.name(),
49
3
            })
50
        }
51
5
    }
52

            
53
    /// Evaluate whether the `action` is allowed to be taken upon
54
    /// `resource_name`. Returns true if the action should be allowed. If no
55
    /// statements that match `resource_name` allow `action`, false will be
56
    /// returned.
57
24
    pub fn allowed_to<'a, R: AsRef<[Identifier<'a>]>, P: Action>(
58
24
        &self,
59
24
        resource_name: R,
60
24
        action: &P,
61
24
    ) -> bool {
62
24
        self.data.allowed_to(resource_name, action)
63
24
    }
64

            
65
    /// Looks up a configured value for `resource_name`.
66
    #[must_use]
67
9
    pub fn get<'a: 's, 's, R: AsRef<[Identifier<'a>]>>(
68
9
        &'s self,
69
9
        resource_name: R,
70
9
        key: &str,
71
9
    ) -> Option<&'s Configuration> {
72
9
        self.data.get(resource_name, key)
73
9
    }
74

            
75
    /// Returns a new instance that merges all allowed actions from
76
    /// `permissions`.
77
    #[must_use]
78
2
    pub fn merged<'a>(permissions: impl IntoIterator<Item = &'a Self>) -> Self {
79
2
        let mut combined = Data::default();
80
6
        for incoming in permissions {
81
4
            combined.add_permissions(&incoming.data);
82
4
        }
83
2
        Self {
84
2
            data: Arc::new(combined),
85
2
        }
86
2
    }
87
}
88

            
89
impl Data {
90
    fn add_permissions(&mut self, permissions: &Self) {
91
19
        if let Some(children) = &permissions.children {
92
7
            let our_children = self.children.get_or_insert_with(HashMap::new);
93
22
            for (name, permissions) in children {
94
15
                let our_permissions = our_children.entry(name.clone()).or_default();
95
15
                our_permissions.add_permissions(permissions);
96
15
            }
97
12
        }
98

            
99
19
        self.allowed.add_allowed(&permissions.allowed);
100
19
        if let Some(incoming_configuration) = &permissions.configuration {
101
5
            if let Some(configuration) = &mut self.configuration {
102
4
                for (key, value) in incoming_configuration {
103
2
                    configuration
104
2
                        .entry(key.clone())
105
2
                        .or_insert_with(|| value.clone());
106
2
                }
107
3
            } else {
108
3
                self.configuration = permissions.configuration.clone();
109
3
            }
110
14
        }
111
19
    }
112

            
113
57
    fn allowed_to<'a, R: AsRef<[Identifier<'a>]>, P: Action>(
114
57
        &self,
115
57
        resource_name: R,
116
57
        action: &P,
117
57
    ) -> bool {
118
57
        let resource_name = resource_name.as_ref();
119
        // This function checks all possible matches of `resource_name` by using
120
        // recursion to call itself for each entry in `resource_name`. This
121
        // first block does the function call recursion. The second block checks
122
        // `action`.
123
57
        if let Some(resource) = resource_name.first() {
124
33
            if let Some(children) = &self.children {
125
33
                let remaining_resource = &resource_name[1..resource_name.len()];
126
                // Check if there are entries for this resource segment.
127
33
                if let Some(permissions) = children.get(resource) {
128
21
                    if permissions.allowed_to(remaining_resource, action) {
129
18
                        return true;
130
3
                    }
131
12
                }
132

            
133
                // Check if there are entries for `Any`.
134
15
                if let Some(permissions) = children.get(&Identifier::Any) {
135
7
                    if permissions.allowed_to(remaining_resource, action) {
136
3
                        return true;
137
4
                    }
138
8
                }
139
            }
140
24
        }
141

            
142
        // When execution reaches here, either resource_name is empty, or none
143
        // of the previous paths have reached an "allow" state. The purpose of
144
        // this chunk of code is to determine if this action is allowed based on
145
        // this node's list of approved actions. This is also evaluated
146
        // recursively, but at any stage if we reach match (positive or
147
        // negative), we we can return.
148
36
        let mut allowed = &self.allowed;
149
44
        for name in action.name().0 {
150
44
            allowed = match allowed {
151
12
                AllowedActions::None => return false,
152
12
                AllowedActions::All => return true,
153
20
                AllowedActions::Some(actions) => {
154
20
                    if let Some(children_allowed) = actions.get(name.as_ref()) {
155
14
                        children_allowed
156
                    } else {
157
6
                        return false;
158
                    }
159
                }
160
            };
161
        }
162
6
        matches!(allowed, AllowedActions::All)
163
57
    }
164

            
165
    #[must_use]
166
24
    pub fn get<'a: 's, 's, R: AsRef<[Identifier<'a>]>>(
167
24
        &'s self,
168
24
        resource_name: R,
169
24
        key: &str,
170
24
    ) -> Option<&'s Configuration> {
171
24
        let resource_name = resource_name.as_ref();
172
        // This function checks all possible matches of `resource_name` by using
173
        // recursion to call itself for each entry in `resource_name`. This
174
        // first block does the function call recursion. The second block checks
175
        // `action`.
176
24
        if let Some(resource) = resource_name.as_ref().first() {
177
15
            if let Some(children) = &self.children {
178
13
                let remaining_resource = &resource_name[1..resource_name.len()];
179
                // Check if there are entries for this resource segment.
180
13
                if let Some(permissions) = children.get(resource) {
181
13
                    if let Some(config) = permissions.get(remaining_resource, key) {
182
8
                        return Some(config);
183
5
                    }
184
                }
185

            
186
                // Check if there are entries for `Any`.
187
5
                if let Some(permissions) = children.get(&Identifier::Any) {
188
2
                    if let Some(config) = permissions.get(remaining_resource, key) {
189
2
                        return Some(config);
190
                    }
191
3
                }
192
2
            }
193
9
        }
194

            
195
14
        self.configuration
196
14
            .as_ref()
197
14
            .and_then(|configs| configs.get(key))
198
24
    }
199
}
200

            
201
impl From<Statement> for Permissions {
202
    fn from(stmt: Statement) -> Self {
203
        Self::from(vec![stmt])
204
    }
205
}
206

            
207
impl From<Vec<Statement>> for Permissions {
208
7
    fn from(statements: Vec<Statement>) -> Self {
209
7
        let mut permissions = Data::default();
210
26
        for statement in statements {
211
            // Apply this statement to all resources
212
38
            for resource in statement.resources {
213
19
                let mut current_permissions = &mut permissions;
214
                // Look up the permissions for the resource path
215
43
                for name in resource {
216
24
                    let permissions = current_permissions
217
24
                        .children
218
24
                        .get_or_insert_with(HashMap::default);
219
24
                    current_permissions = permissions.entry(name).or_default();
220
24
                }
221

            
222
                // Apply the "allowed" status to each action in this resource.
223
13
                match &statement.actions {
224
7
                    Some(ActionNameList::List(actions)) =>
225
15
                        for action in actions {
226
8
                            let mut allowed = &mut current_permissions.allowed;
227
21
                            for name in &action.0 {
228
13
                                let action_map = match allowed {
229
                                    AllowedActions::All | AllowedActions::None => {
230
                                        *allowed = {
231
11
                                            let mut action_map = HashMap::new();
232
11
                                            action_map
233
11
                                                .insert(name.to_string(), AllowedActions::None);
234
11
                                            AllowedActions::Some(action_map)
235
                                        };
236
11
                                        if let AllowedActions::Some(action_map) = allowed {
237
11
                                            action_map
238
                                        } else {
239
                                            unreachable!()
240
                                        }
241
                                    }
242
2
                                    AllowedActions::Some(action_map) => action_map,
243
                                };
244
13
                                allowed = action_map.entry(name.to_string()).or_default();
245
                            }
246
8
                            *allowed = AllowedActions::All;
247
                        },
248
6
                    Some(ActionNameList::All) => {
249
6
                        current_permissions.allowed = AllowedActions::All;
250
6
                    }
251
6
                    None => {}
252
                }
253

            
254
19
                if let Some(incoming_configs) = &statement.configuration {
255
6
                    let configuration = current_permissions
256
6
                        .configuration
257
6
                        .get_or_insert_with(HashMap::default);
258
12
                    for (key, value) in incoming_configs {
259
6
                        configuration
260
6
                            .entry(key.clone())
261
6
                            .or_insert_with(|| value.clone());
262
6
                    }
263
13
                }
264
            }
265
        }
266
7
        Self {
267
7
            data: Arc::new(permissions),
268
7
        }
269
7
    }
270
}
271

            
272
5
#[derive(Debug, Clone, Serialize, Deserialize)]
273
enum AllowedActions {
274
    None,
275
    Some(HashMap<String, AllowedActions>),
276
    All,
277
}
278

            
279
impl Default for AllowedActions {
280
41
    fn default() -> Self {
281
41
        Self::None
282
41
    }
283
}
284

            
285
impl AllowedActions {
286
20
    fn add_allowed(&mut self, other: &Self) {
287
20
        match other {
288
11
            Self::None => {}
289
5
            Self::Some(actions) =>
290
5
                if !matches!(self, Self::All) {
291
5
                    if let Self::Some(our_allowed) = self {
292
2
                        for (name, allowed) in actions {
293
1
                            let our_entry = our_allowed.entry(name.clone()).or_default();
294
1
                            our_entry.add_allowed(allowed);
295
1
                        }
296
4
                    } else {
297
4
                        *self = Self::Some(actions.clone());
298
4
                    }
299
                },
300
4
            Self::All => {
301
4
                *self = Self::All;
302
4
            }
303
        }
304
20
    }
305
}