uqa_sql/catalog/security/
sequence_inquiry.rs1use super::{
10 sequence::{self as acl, role_has_privilege, AclPrivilege, PrivilegeCheck},
11 SequenceSecurity,
12};
13use crate::{
14 catalog::{
15 resolution::RelationResolution,
16 roles::{guards::RoleCatalogGuards, RoleDefinition, RoleReferenceNames},
17 },
18 SQLError,
19};
20use std::{collections::BTreeMap, ops::Deref};
21use uqa_core::{RelationIdentity, Value};
22
23pub type SequenceSecurityRead<'a> =
24 Box<dyn Deref<Target = BTreeMap<RelationIdentity, SequenceSecurity>> + 'a>;
25pub trait SequenceSecurityCatalog {
26 fn security_read(&self) -> SequenceSecurityRead<'_>;
27}
28pub trait SequencePrivilegeResolution {
29 fn visible_relation_kind(&self, reference: &str) -> Result<RelationResolution, SQLError>;
30 fn sequence_privilege_oid(
31 &self,
32 oid: i64,
33 ) -> Result<Option<(String, RelationIdentity)>, SQLError>;
34}
35pub struct SequencePrivilegeInquiry<'a> {
36 pub names: &'a dyn RoleReferenceNames,
37 pub roles: &'a dyn RoleCatalogGuards,
38 pub security: &'a dyn SequenceSecurityCatalog,
39 pub resolution: &'a dyn SequencePrivilegeResolution,
40}
41
42impl SequencePrivilegeInquiry<'_> {
43 pub fn ensure_sequence_nextval_privilege(
44 &self,
45 name: &str,
46 relation: &RelationIdentity,
47 ) -> Result<(), SQLError> {
48 self.ensure_sequence_value_privilege(
49 name,
50 relation,
51 &[AclPrivilege::Usage, AclPrivilege::Update],
52 )
53 }
54
55 pub fn ensure_sequence_currval_privilege(
56 &self,
57 name: &str,
58 relation: &RelationIdentity,
59 ) -> Result<(), SQLError> {
60 self.ensure_sequence_value_privilege(
61 name,
62 relation,
63 &[AclPrivilege::Usage, AclPrivilege::Select],
64 )
65 }
66
67 pub fn ensure_sequence_setval_privilege(
68 &self,
69 name: &str,
70 relation: &RelationIdentity,
71 ) -> Result<(), SQLError> {
72 self.ensure_sequence_value_privilege(name, relation, &[AclPrivilege::Update])
73 }
74
75 fn ensure_sequence_value_privilege(
76 &self,
77 name: &str,
78 relation: &RelationIdentity,
79 privileges: &[AclPrivilege],
80 ) -> Result<(), SQLError> {
81 let security = self
82 .security
83 .security_read()
84 .get(relation)
85 .cloned()
86 .ok_or_else(|| {
87 SQLError::Internal(format!("sequence `{name}` has no security metadata"))
88 })?;
89 let current_user = self.names.current_user_name();
90 let roles = self.roles.role_definitions();
91 let memberships = self.roles.role_memberships();
92 if privileges.iter().any(|privilege| {
93 role_has_privilege(
94 &security,
95 ¤t_user,
96 PrivilegeCheck {
97 privilege: *privilege,
98 grant_option: false,
99 },
100 &roles,
101 &memberships,
102 )
103 }) {
104 return Ok(());
105 }
106 Err(SQLError::Routine {
107 sqlstate: "42501".into(),
108 message: format!("permission denied for sequence {}", relation.name),
109 })
110 }
111
112 pub fn has_sequence_privilege_value(&self, arguments: &[Value]) -> Result<Value, SQLError> {
113 if arguments.iter().any(|argument| argument == &Value::Null) {
114 return Ok(Value::Null);
115 }
116 let (subject_value, sequence_value, privilege_value) = match arguments {
117 [sequence, privilege] => (None, sequence, privilege),
118 [subject, sequence, privilege] => (Some(subject), sequence, privilege),
119 _ => {
120 return Err(SQLError::BadArity {
121 name: "has_sequence_privilege".into(),
122 expected: "2 or 3".into(),
123 actual: arguments.len(),
124 })
125 }
126 };
127 let current_user = subject_value
128 .is_none()
129 .then(|| self.names.current_user_name());
130 let subject = {
131 let roles = self.roles.role_definitions();
132 subject_value.map_or_else(
133 || Ok(current_user),
134 |value| resolve_sequence_privilege_role(value, &roles),
135 )?
136 };
137 let Some((_name, relation)) = self.resolve_sequence_privilege_target(sequence_value)?
138 else {
139 return Ok(Value::Null);
140 };
141 let privilege = match privilege_value {
142 Value::Str(privilege) | Value::FixedChar(privilege) => privilege,
143 other => {
144 return Err(SQLError::TypeMismatch(format!(
145 "has_sequence_privilege privilege must be text, got {other:?}"
146 )))
147 }
148 };
149 let checks = acl::parse_privilege_checks(privilege)?;
150 let Some(subject) = subject else {
151 return Ok(Value::Bool(false));
152 };
153 let security = self
154 .security
155 .security_read()
156 .get(&relation)
157 .cloned()
158 .ok_or_else(|| {
159 SQLError::Internal(format!(
160 "sequence `{}` has no security metadata",
161 relation.qualified_name()
162 ))
163 })?;
164 let roles = self.roles.role_definitions();
165 let memberships = self.roles.role_memberships();
166 Ok(Value::Bool(checks.into_iter().any(|check| {
167 role_has_privilege(&security, &subject, check, &roles, &memberships)
168 })))
169 }
170
171 pub fn role_has_sequence_table_privilege(
172 &self,
173 relation: &RelationIdentity,
174 subject: &str,
175 privilege: super::table::TableAclPrivilege,
176 grant_option: bool,
177 ) -> Result<bool, SQLError> {
178 let privilege = match privilege {
179 super::table::TableAclPrivilege::Select => AclPrivilege::Select,
180 super::table::TableAclPrivilege::Update => AclPrivilege::Update,
181 super::table::TableAclPrivilege::Insert
182 | super::table::TableAclPrivilege::Delete
183 | super::table::TableAclPrivilege::Truncate
184 | super::table::TableAclPrivilege::References
185 | super::table::TableAclPrivilege::Trigger
186 | super::table::TableAclPrivilege::Maintain => return Ok(false),
187 };
188 let security = self
189 .security
190 .security_read()
191 .get(relation)
192 .cloned()
193 .ok_or_else(|| {
194 SQLError::Internal(format!(
195 "sequence `{}` has no security metadata",
196 relation.qualified_name()
197 ))
198 })?;
199 let roles = self.roles.role_definitions();
200 let memberships = self.roles.role_memberships();
201 Ok(role_has_privilege(
202 &security,
203 subject,
204 PrivilegeCheck {
205 privilege,
206 grant_option,
207 },
208 &roles,
209 &memberships,
210 ))
211 }
212
213 fn resolve_sequence_privilege_target(
214 &self,
215 value: &Value,
216 ) -> Result<Option<(String, RelationIdentity)>, SQLError> {
217 match value {
218 Value::Str(reference) | Value::FixedChar(reference) => {
219 let (name, kind) = match self.resolution.visible_relation_kind(reference)? {
220 RelationResolution::Found(name, kind) => (name, kind),
221 RelationResolution::MissingSchema(schema) => {
222 return Err(SQLError::Routine {
223 sqlstate: "3F000".into(),
224 message: format!("schema \"{schema}\" does not exist"),
225 });
226 }
227 RelationResolution::MissingRelation => {
228 return Err(SQLError::Routine {
229 sqlstate: "42P01".into(),
230 message: format!("relation \"{reference}\" does not exist"),
231 });
232 }
233 };
234 if kind != "sequence" {
235 return Err(SQLError::Routine {
236 sqlstate: "42809".into(),
237 message: format!("\"{reference}\" is not a sequence"),
238 });
239 }
240 let relation = RelationIdentity::from_legacy_name(&name).map_err(|error| {
241 SQLError::Internal(format!("resolve sequence `{name}`: {error}"))
242 })?;
243 Ok(Some((name, relation)))
244 }
245 Value::Int(oid) => self.resolution.sequence_privilege_oid(*oid),
246 other => Err(SQLError::TypeMismatch(format!(
247 "has_sequence_privilege sequence must be text or oid, got {other:?}"
248 ))),
249 }
250 }
251
252 pub fn ensure_sequence_owner(
253 &self,
254 name: &str,
255 relation: &RelationIdentity,
256 ) -> Result<String, SQLError> {
257 let owner = self
258 .security
259 .security_read()
260 .get(relation)
261 .map(|security| security.role_owner.clone())
262 .ok_or_else(|| {
263 SQLError::Internal(format!("sequence `{name}` has no security metadata"))
264 })?;
265 crate::schema::sequences::ownership::require_sequence_ownership(&relation.name, {
266 let current = self.names.current_user_name();
267 let roles = self.roles.role_definitions();
268 let memberships = self.roles.role_memberships();
269 crate::catalog::roles::role_inherits(&roles, &memberships, ¤t, &owner)
270 })?;
271 Ok(owner)
272 }
273}
274
275fn resolve_sequence_privilege_role(
276 value: &Value,
277 roles: &BTreeMap<String, RoleDefinition>,
278) -> Result<Option<String>, SQLError> {
279 match value {
280 Value::Str(name) | Value::FixedChar(name) => {
281 if roles.contains_key(name) {
282 Ok(Some(name.clone()))
283 } else {
284 Err(SQLError::Routine {
285 sqlstate: "42704".into(),
286 message: format!("role \"{name}\" does not exist"),
287 })
288 }
289 }
290 Value::Int(oid) => Ok(roles
291 .values()
292 .find(|role| role.oid == *oid)
293 .map(|role| role.name.clone())),
294 other => Err(SQLError::TypeMismatch(format!(
295 "has_sequence_privilege role must be name or oid, got {other:?}"
296 ))),
297 }
298}