Skip to main content

uqa_sql/catalog/security/
sequence_binding.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Sequence owners, grantees and grantors retain their original role incarnations.
8
9use super::{role_bindings, SequenceSecurity};
10use crate::catalog::roles::{identity::RoleBinding, RoleDefinition, RoleIdentity, RoleReference};
11use std::collections::BTreeMap;
12use uqa_core::{
13    catalog_acl::AclGrantee,
14    catalog_role::BoundAclEntry,
15    catalog_sequence::{SequenceAclEntry, SequencePrivileges},
16};
17
18pub type BoundSequenceAclEntry = BoundAclEntry<SequencePrivileges>;
19
20#[derive(Debug, Clone, PartialEq, Eq)]
21pub struct BoundSequenceSecurity {
22    pub role_owner: RoleIdentity,
23    pub acl: Option<Vec<BoundSequenceAclEntry>>,
24}
25
26#[cfg(test)]
27mod tests;
28
29impl BoundSequenceSecurity {
30    pub fn from_row(row: uqa_core::catalog_sequence::BoundSequenceSecurity) -> Self {
31        Self {
32            role_owner: row.role_owner,
33            acl: row.acl,
34        }
35    }
36
37    pub fn row(&self) -> uqa_core::catalog_sequence::BoundSequenceSecurity {
38        uqa_core::catalog_sequence::BoundSequenceSecurity {
39            role_owner: self.role_owner,
40            acl: self.acl.clone(),
41        }
42    }
43
44    pub fn owner(role_owner: RoleIdentity) -> Self {
45        Self {
46            role_owner,
47            acl: None,
48        }
49    }
50
51    pub fn owner_reference(
52        &self,
53        roles: &BTreeMap<String, RoleDefinition>,
54    ) -> Result<RoleReference, crate::SQLError> {
55        let name = role_bindings::role_name(roles, self.role_owner, "sequence")
56            .map_err(crate::SQLError::Internal)?;
57        Ok(RoleReference::Bound(std::sync::Arc::new(
58            RoleBinding::from_definition(&roles[name])?,
59        )))
60    }
61
62    pub fn bind(
63        security: &SequenceSecurity,
64        roles: &BTreeMap<String, RoleDefinition>,
65    ) -> Result<Self, String> {
66        let bind = |name: &str| role_bindings::bind_role(roles, name, "sequence");
67        let entries = |entries: &[SequenceAclEntry]| {
68            entries
69                .iter()
70                .map(|entry| {
71                    Ok(BoundSequenceAclEntry {
72                        role: entry.role.role_name().map(bind).transpose()?,
73                        grantor: bind(entry.grantor.as_deref().unwrap_or(&security.role_owner))?,
74                        privileges: entry.privileges,
75                        grant_options: entry.grant_options,
76                    })
77                })
78                .collect::<Result<Vec<_>, String>>()
79        };
80        Ok(Self {
81            role_owner: bind(&security.role_owner)?,
82            acl: security.acl.as_deref().map(entries).transpose()?,
83        })
84    }
85
86    /// Resolve display names only through the role view accompanying this authority snapshot.
87    pub fn resolve(
88        &self,
89        roles: &BTreeMap<String, RoleDefinition>,
90    ) -> Result<SequenceSecurity, String> {
91        let name =
92            |identity| role_bindings::role_name(roles, identity, "sequence").map(str::to_owned);
93        let entries = |entries: &[BoundSequenceAclEntry]| {
94            entries
95                .iter()
96                .map(|entry| {
97                    Ok(SequenceAclEntry {
98                        role: entry
99                            .role
100                            .map(&name)
101                            .transpose()?
102                            .map_or(AclGrantee::Public, AclGrantee::Role),
103                        grantor: Some(name(entry.grantor)?),
104                        privileges: entry.privileges,
105                        grant_options: entry.grant_options,
106                    })
107                })
108                .collect::<Result<Vec<_>, String>>()
109        };
110        Ok(SequenceSecurity {
111            role_owner: name(self.role_owner)?,
112            acl: self.acl.as_deref().map(entries).transpose()?,
113        })
114    }
115
116    pub fn validate(&self, roles: &BTreeMap<String, RoleDefinition>) -> Result<(), String> {
117        super::sequence::validate_sequence_security_invariants(&self.resolve(roles)?, roles)
118    }
119
120    pub fn depends_on(&self, role: RoleIdentity) -> bool {
121        self.role_owner == role
122            || self
123                .acl
124                .iter()
125                .flatten()
126                .any(|entry| entry.role == Some(role) || entry.grantor == role)
127    }
128}