uqa_sql/catalog/security/
acl_command.rs1use crate::{
10 ast::{AclRoleSpecification, RoleSpecification},
11 catalog::roles::{
12 identity::RoleBinding, resolve_acl_role_specification, resolve_role_specification,
13 RoleDefinition, RoleReference, RoleReferenceNames,
14 },
15 SQLError,
16};
17use std::collections::BTreeMap;
18use uqa_core::catalog_acl::AclGrantee;
19
20#[derive(Clone, Default)]
21pub struct AclCommandRoles {
22 bound: Option<BoundAclRoles>,
23}
24
25#[derive(Clone)]
26struct BoundAclRoles {
27 grantees: Vec<Option<RoleBinding>>,
28 grantor: Option<RoleBinding>,
29 current_user: RoleReference,
30}
31
32pub struct ResolvedAclRoles {
33 pub grantees: Vec<AclGrantee>,
34 pub requested_grantor: Option<String>,
35 pub current_user: RoleReference,
36}
37
38impl AclCommandRoles {
39 pub fn resolve_validated(
41 &mut self,
42 names: &dyn RoleReferenceNames,
43 roles: &BTreeMap<String, RoleDefinition>,
44 grantees: &[AclRoleSpecification],
45 grantor: Option<&RoleSpecification>,
46 validate: impl FnOnce(&ResolvedAclRoles) -> Result<(), SQLError>,
47 ) -> Result<ResolvedAclRoles, SQLError> {
48 let resolved = if let Some(bound) = &self.bound {
49 bound.resolve(roles)?
50 } else {
51 ResolvedAclRoles {
52 grantees: grantees
53 .iter()
54 .map(|role| resolve_acl_role_specification(names, role, roles))
55 .collect::<Result<_, _>>()?,
56 requested_grantor: grantor
57 .map(|role| resolve_role_specification(names, role).catalog_name(roles))
58 .transpose()?,
59 current_user: names.current_role(),
60 }
61 };
62 validate(&resolved)?;
63 if self.bound.is_none() {
64 self.bound = Some(BoundAclRoles {
65 grantees: resolved
66 .grantees
67 .iter()
68 .map(|grantee| {
69 grantee
70 .role_name()
71 .map(|name| RoleReference::from(name).bind(roles))
72 .transpose()
73 })
74 .collect::<Result<_, _>>()?,
75 grantor: resolved
76 .requested_grantor
77 .as_deref()
78 .map(|name| RoleReference::from(name).bind(roles))
79 .transpose()?,
80 current_user: match &resolved.current_user {
81 RoleReference::Bound(_) => resolved.current_user.clone(),
82 RoleReference::Named(_) => RoleReference::Bound(std::sync::Arc::new(
83 resolved.current_user.bind(roles)?,
84 )),
85 },
86 });
87 }
88 Ok(resolved)
89 }
90}
91
92impl BoundAclRoles {
93 fn resolve(
94 &self,
95 roles: &BTreeMap<String, RoleDefinition>,
96 ) -> Result<ResolvedAclRoles, SQLError> {
97 let name = |binding: &RoleBinding| {
98 binding.revalidate(roles)?;
99 binding.require_name(roles).map(str::to_owned)
100 };
101 Ok(ResolvedAclRoles {
102 grantees: self
103 .grantees
104 .iter()
105 .map(|binding| {
106 binding.as_ref().map_or_else(
107 || Ok(AclGrantee::Public),
108 |binding| name(binding).map(AclGrantee::Role),
109 )
110 })
111 .collect::<Result<_, _>>()?,
112 requested_grantor: self.grantor.as_ref().map(name).transpose()?,
113 current_user: self.current_user.clone(),
114 })
115 }
116}
117
118#[cfg(test)]
119mod tests;