uqa_sql/catalog/security/
sequence_grants.rs1use super::{
10 sequence::{grant_acl, revoke_acl, select_acl_grantor, AclPrivilege},
11 sequence_inquiry::SequencePrivilegeResolution,
12 SequenceSecurity,
13};
14use crate::catalog::roles::identity::RoleSubject;
15use crate::{
16 ast::{GrantSequenceStmt, SequenceRevokeBehavior},
17 catalog::{
18 resolution::RelationResolution,
19 roles::{RoleDefinition, RoleMembership, RoleMembershipKey},
20 },
21 SQLError,
22};
23use std::collections::BTreeMap;
24use uqa_core::catalog_acl::AclGrantee;
25use uqa_core::RelationIdentity;
26
27pub use super::grants::{
28 bind_grant_schemas as bind_sequence_grant_schemas, GrantNamespace as SequenceGrantNamespace,
29};
30
31pub struct ResolvedSequenceGrantTarget {
32 pub requested: String,
33 pub name: String,
34 pub relation: RelationIdentity,
35 pub kind: &'static str,
36}
37
38pub fn bind_named_sequence_grants(
39 resolution: &dyn SequencePrivilegeResolution,
40 names: &[String],
41) -> Result<Vec<ResolvedSequenceGrantTarget>, SQLError> {
42 let mut resolved = Vec::with_capacity(names.len());
43 for requested in names {
44 let (name, kind) = match resolution.visible_relation_kind(requested)? {
45 RelationResolution::Found(name, kind) => (name, kind),
46 RelationResolution::MissingSchema(schema) => {
47 return Err(SQLError::Routine {
48 sqlstate: "3F000".into(),
49 message: format!("schema \"{schema}\" does not exist"),
50 });
51 }
52 RelationResolution::MissingRelation => {
53 return Err(SQLError::Routine {
54 sqlstate: "42P01".into(),
55 message: format!("relation \"{requested}\" does not exist"),
56 });
57 }
58 };
59 let relation = RelationIdentity::from_legacy_name(&name)
60 .map_err(|error| SQLError::Internal(format!("resolve sequence `{name}`: {error}")))?;
61 resolved.push(ResolvedSequenceGrantTarget {
62 requested: requested.clone(),
63 name,
64 relation,
65 kind,
66 });
67 }
68 Ok(resolved)
69}
70
71pub fn sequence_grants_in_schemas<'a>(
72 resolved_schemas: &[String],
73 sequences: impl Iterator<Item = &'a RelationIdentity>,
74) -> Vec<ResolvedSequenceGrantTarget> {
75 let mut targets = sequences
76 .filter(|relation| resolved_schemas.contains(&relation.schema))
77 .map(|relation| ResolvedSequenceGrantTarget {
78 requested: relation.qualified_name(),
79 name: relation.qualified_name(),
80 relation: relation.clone(),
81 kind: "sequence",
82 })
83 .collect::<Vec<_>>();
84 targets.sort_by(|left, right| left.relation.cmp(&right.relation));
85 targets
86}
87
88pub fn apply_sequence_acl(
89 statement: &GrantSequenceStmt,
90 grantees: &[AclGrantee],
91 privileges: &[AclPrivilege],
92 current_user: &(impl RoleSubject + ?Sized),
93 roles: &BTreeMap<String, RoleDefinition>,
94 memberships: &BTreeMap<RoleMembershipKey, RoleMembership>,
95 current: &SequenceSecurity,
96) -> Result<(SequenceSecurity, usize), SQLError> {
97 let grantors = privileges
98 .iter()
99 .map(|privilege| {
100 (
101 *privilege,
102 select_acl_grantor(current, *privilege, current_user, roles, memberships),
103 )
104 })
105 .collect::<Vec<_>>();
106 let grantable = grantors
107 .iter()
108 .filter(|(_, grantor)| grantor.is_some())
109 .count();
110 let mut next = current.clone();
111 for (privilege, grantor) in grantors {
112 let Some(grantor) = grantor else {
113 continue;
114 };
115 if statement.is_grant {
116 grant_acl(
117 &mut next,
118 privilege,
119 grantees,
120 &grantor,
121 statement.grant_option,
122 );
123 } else {
124 revoke_acl(
125 &mut next,
126 privilege,
127 grantees,
128 &grantor,
129 statement.grant_option_only,
130 statement.revoke_behavior == SequenceRevokeBehavior::Cascade,
131 )?;
132 }
133 }
134 Ok((next, grantable))
135}
136
137pub fn validate_sequence_acl_roles(
138 statement: &GrantSequenceStmt,
139 grantees: &[AclGrantee],
140 requested_grantor: Option<&str>,
141 current_user: &(impl RoleSubject + ?Sized),
142 roles: &BTreeMap<String, RoleDefinition>,
143) -> Result<(), SQLError> {
144 for role in grantees {
145 if role
146 .role_name()
147 .is_some_and(|name| !roles.contains_key(name))
148 {
149 return Err(SQLError::Routine {
150 sqlstate: "42704".into(),
151 message: format!("role \"{role}\" does not exist"),
152 });
153 }
154 }
155 if statement.is_grant && statement.grant_option && grantees.iter().any(AclGrantee::is_public) {
156 return Err(SQLError::Routine {
157 sqlstate: "0LP01".into(),
158 message: "grant options can only be granted to roles".into(),
159 });
160 }
161 if let Some(requested_grantor) = requested_grantor {
162 if !roles.contains_key(requested_grantor) {
163 return Err(SQLError::Routine {
164 sqlstate: "42704".into(),
165 message: format!("role \"{requested_grantor}\" does not exist"),
166 });
167 }
168 if current_user.role_name(roles) != Some(requested_grantor) {
169 return Err(SQLError::Routine {
170 sqlstate: "0A000".into(),
171 message: "grantor must be current user".into(),
172 });
173 }
174 }
175 Ok(())
176}
177
178pub fn validate_sequence_grant_target_kinds(
179 targets: &[ResolvedSequenceGrantTarget],
180) -> Result<(), SQLError> {
181 for target in targets {
182 if target.kind == "sequence" {
183 continue;
184 }
185 return Err(SQLError::Routine {
186 sqlstate: "42809".into(),
187 message: format!("\"{}\" is not a sequence", target.requested),
188 });
189 }
190 Ok(())
191}
192
193pub fn sequence_acl_warning(is_grant: bool, partial: bool, name: &str) -> (&'static str, String) {
194 let message = match (is_grant, partial) {
195 (true, true) => format!("not all privileges were granted for \"{name}\""),
196 (true, false) => format!("no privileges were granted for \"{name}\""),
197 (false, true) => format!("not all privileges could be revoked for \"{name}\""),
198 (false, false) => format!("no privileges could be revoked for \"{name}\""),
199 };
200 ("WARNING", message)
201}