Skip to main content

uqa_sql/catalog/security/
sequence_inquiry.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Sequence privilege inquiry, target binding and access checks.
8
9use super::{
10    sequence::{self as acl, role_has_privilege, AclPrivilege, PrivilegeCheck},
11    BoundSequenceSecurity,
12};
13use crate::catalog::roles::identity::RoleSubject;
14use crate::{
15    catalog::{
16        resolution::RelationResolution,
17        roles::{guards::RoleCatalogGuards, RoleReferenceNames},
18    },
19    SQLError,
20};
21use std::{collections::BTreeMap, ops::Deref};
22use uqa_core::{RelationIdentity, Value};
23
24pub type SequenceSecurityRead<'a> =
25    Box<dyn Deref<Target = BTreeMap<RelationIdentity, BoundSequenceSecurity>> + 'a>;
26pub trait SequenceSecurityCatalog {
27    fn security_read(&self) -> SequenceSecurityRead<'_>;
28}
29pub trait SequencePrivilegeResolution {
30    fn visible_relation_kind(&self, reference: &str) -> Result<RelationResolution, SQLError>;
31    fn sequence_privilege_oid(
32        &self,
33        oid: i64,
34    ) -> Result<Option<(String, RelationIdentity)>, SQLError>;
35}
36pub trait SequenceTablePrivilegeInquiry {
37    fn sequence_table_privileges(
38        &self,
39        relation: &RelationIdentity,
40        subject: &dyn RoleSubject,
41        checks: &[super::table::TablePrivilegeCheck],
42    ) -> Result<bool, SQLError>;
43}
44
45pub struct SequencePrivilegeInquiry<'a> {
46    pub names: &'a dyn RoleReferenceNames,
47    pub roles: &'a dyn RoleCatalogGuards,
48    pub security: &'a dyn SequenceSecurityCatalog,
49    pub resolution: &'a dyn SequencePrivilegeResolution,
50}
51
52impl SequencePrivilegeInquiry<'_> {
53    pub fn ensure_sequence_nextval_privilege(
54        &self,
55        name: &str,
56        relation: &RelationIdentity,
57    ) -> Result<(), SQLError> {
58        self.ensure_sequence_value_privilege(
59            name,
60            relation,
61            &[AclPrivilege::Usage, AclPrivilege::Update],
62        )
63    }
64
65    pub fn ensure_sequence_currval_privilege(
66        &self,
67        name: &str,
68        relation: &RelationIdentity,
69    ) -> Result<(), SQLError> {
70        self.ensure_sequence_value_privilege(
71            name,
72            relation,
73            &[AclPrivilege::Usage, AclPrivilege::Select],
74        )
75    }
76
77    pub fn ensure_sequence_setval_privilege(
78        &self,
79        name: &str,
80        relation: &RelationIdentity,
81    ) -> Result<(), SQLError> {
82        self.ensure_sequence_value_privilege(name, relation, &[AclPrivilege::Update])
83    }
84
85    fn ensure_sequence_value_privilege(
86        &self,
87        name: &str,
88        relation: &RelationIdentity,
89        privileges: &[AclPrivilege],
90    ) -> Result<(), SQLError> {
91        let security = self
92            .security
93            .security_read()
94            .get(relation)
95            .cloned()
96            .ok_or_else(|| {
97                SQLError::Internal(format!("sequence `{name}` has no security metadata"))
98            })?;
99        let current_user = self.names.current_role();
100        let roles = self.roles.role_definitions();
101        let security = security.resolve(&roles).map_err(SQLError::Internal)?;
102        let memberships = self.roles.role_memberships();
103        if privileges.iter().any(|privilege| {
104            role_has_privilege(
105                &security,
106                &current_user,
107                PrivilegeCheck {
108                    privilege: *privilege,
109                    grant_option: false,
110                },
111                &roles,
112                &memberships,
113            )
114        }) {
115            return Ok(());
116        }
117        Err(SQLError::Routine {
118            sqlstate: "42501".into(),
119            message: format!("permission denied for sequence {}", relation.name),
120        })
121    }
122
123    pub fn has_sequence_privilege_value(&self, arguments: &[Value]) -> Result<Value, SQLError> {
124        let Some(arguments) = SequencePrivilegeArguments::parse(arguments)? else {
125            return Ok(Value::Null);
126        };
127        let request = arguments.bind(self.names, self.roles)?;
128        let Some((_, relation)) = request.target.resolve(self.resolution)? else {
129            return Ok(Value::Null);
130        };
131        request.evaluate(&relation, self.roles, self.security)
132    }
133
134    pub fn role_has_sequence_table_privilege(
135        &self,
136        relation: &RelationIdentity,
137        subject: &(impl RoleSubject + ?Sized),
138        privilege: super::table::TableAclPrivilege,
139        grant_option: bool,
140    ) -> Result<bool, SQLError> {
141        self.role_has_sequence_table_privileges(
142            relation,
143            subject,
144            &[super::table::TablePrivilegeCheck {
145                privilege,
146                grant_option,
147            }],
148        )
149    }
150
151    pub fn role_has_sequence_table_privileges(
152        &self,
153        relation: &RelationIdentity,
154        subject: &(impl RoleSubject + ?Sized),
155        checks: &[super::table::TablePrivilegeCheck],
156    ) -> Result<bool, SQLError> {
157        let mut checks = checks
158            .iter()
159            .filter_map(|check| {
160                let privilege = match check.privilege {
161                    super::table::TableAclPrivilege::Select => AclPrivilege::Select,
162                    super::table::TableAclPrivilege::Update => AclPrivilege::Update,
163                    super::table::TableAclPrivilege::Insert
164                    | super::table::TableAclPrivilege::Delete
165                    | super::table::TableAclPrivilege::Truncate
166                    | super::table::TableAclPrivilege::References
167                    | super::table::TableAclPrivilege::Trigger
168                    | super::table::TableAclPrivilege::Maintain => return None,
169                };
170                Some(PrivilegeCheck {
171                    privilege,
172                    grant_option: check.grant_option,
173                })
174            })
175            .peekable();
176        if checks.peek().is_none() {
177            return Ok(false);
178        }
179        let security = self
180            .security
181            .security_read()
182            .get(relation)
183            .cloned()
184            .ok_or_else(|| {
185                SQLError::Internal(format!(
186                    "sequence `{}` has no security metadata",
187                    relation.qualified_name()
188                ))
189            })?;
190        let roles = self.roles.role_definitions();
191        let security = security.resolve(&roles).map_err(SQLError::Internal)?;
192        let memberships = self.roles.role_memberships();
193        Ok(checks.any(|check| role_has_privilege(&security, subject, check, &roles, &memberships)))
194    }
195
196    pub fn ensure_sequence_owner(
197        &self,
198        name: &str,
199        relation: &RelationIdentity,
200    ) -> Result<String, SQLError> {
201        let security = self
202            .security
203            .security_read()
204            .get(relation)
205            .cloned()
206            .ok_or_else(|| {
207                SQLError::Internal(format!("sequence `{name}` has no security metadata"))
208            })?;
209        let roles = self.roles.role_definitions();
210        let owner = security.owner_reference(&roles)?;
211        crate::schema::sequences::ownership::require_sequence_ownership(&relation.name, {
212            let current = self.names.current_role();
213            let memberships = self.roles.role_memberships();
214            crate::catalog::roles::role_inherits(&roles, &memberships, &current, &owner)
215        })?;
216        let owner = owner.catalog_name(&roles)?;
217        Ok(owner)
218    }
219}
220
221impl SequenceTablePrivilegeInquiry for SequencePrivilegeInquiry<'_> {
222    fn sequence_table_privileges(
223        &self,
224        relation: &RelationIdentity,
225        subject: &dyn RoleSubject,
226        checks: &[super::table::TablePrivilegeCheck],
227    ) -> Result<bool, SQLError> {
228        self.role_has_sequence_table_privileges(relation, subject, checks)
229    }
230}
231
232mod value;
233pub use value::{
234    missing_sequence, SequencePrivilegeArguments, SequencePrivilegeRequest, SequencePrivilegeTarget,
235};
236
237#[cfg(test)]
238mod tests;