alibi_api/plugins/admin/
access.rs1use super::AdminConfig;
4use std::collections::HashMap;
5
6#[derive(Debug, Clone, Default, PartialEq, Eq)]
8pub struct RolePermissions {
9 pub permissions: HashMap<String, Vec<String>>,
11}
12
13impl RolePermissions {
14 #[must_use]
16 pub fn new() -> Self {
17 Self::default()
18 }
19
20 #[must_use]
22 pub fn allow<I, S>(mut self, resource: impl Into<String>, actions: I) -> Self
23 where
24 I: IntoIterator<Item = S>,
25 S: Into<String>,
26 {
27 drop(self.permissions.insert(
28 resource.into(),
29 actions.into_iter().map(Into::into).collect(),
30 ));
31 self
32 }
33
34 fn allows(&self, requested: &HashMap<String, Vec<String>>) -> bool {
35 use crate::plugins::access::{Connector, authorize};
36 authorize(
37 |resource| self.permissions.get(resource).map(Vec::as_slice),
38 requested
39 .iter()
40 .map(|(resource, actions)| (resource.as_str(), actions.as_slice(), Connector::And)),
41 Connector::And,
42 )
43 .success()
44 }
45}
46
47pub(super) fn default_roles() -> HashMap<String, RolePermissions> {
48 HashMap::from([
49 (
50 "admin".to_owned(),
51 RolePermissions::new()
52 .allow(
53 "user",
54 [
55 "create",
56 "list",
57 "set-role",
58 "ban",
59 "impersonate",
60 "delete",
61 "set-password",
62 "get",
63 "update",
64 ],
65 )
66 .allow("session", ["list", "revoke", "delete"]),
67 ),
68 ("user".to_owned(), RolePermissions::new()),
69 ])
70}
71
72fn configured_roles(config: &AdminConfig) -> HashMap<String, RolePermissions> {
73 config.roles.clone().unwrap_or_else(default_roles)
74}
75
76fn role_names<'a>(role: Option<&'a str>, default_role: &'a str) -> Vec<&'a str> {
77 role.filter(|role| !role.is_empty())
78 .or_else(|| (!default_role.is_empty()).then_some(default_role))
79 .unwrap_or("user")
80 .split(',')
81 .collect()
82}
83
84pub(super) fn is_admin_user_id(user_id: Option<&str>, config: &AdminConfig) -> bool {
85 user_id.is_some_and(|user_id| {
86 config
87 .admin_user_ids
88 .as_ref()
89 .is_some_and(|ids| ids.iter().any(|id| id == user_id))
90 })
91}
92
93pub(super) fn has_permission(
94 user_id: Option<&str>,
95 role: Option<&str>,
96 config: &AdminConfig,
97 requested: &HashMap<String, Vec<String>>,
98) -> bool {
99 if is_admin_user_id(user_id, config) {
100 return true;
101 }
102
103 let roles = configured_roles(config);
104 role_names(role, &config.default_role)
105 .into_iter()
106 .filter_map(|role| roles.get(role))
107 .any(|role| role.allows(requested))
108}
109
110pub(super) fn is_admin_role(role: Option<&str>, config: &AdminConfig) -> bool {
111 role_names(role, &config.default_role)
112 .into_iter()
113 .any(|role| {
114 config.admin_roles.as_ref().map_or_else(
115 || role == "admin",
116 |admins| admins.iter().any(|admin| admin.trim() == role),
117 )
118 })
119}