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