Skip to main content

uqa_sql/catalog/security/database/
binding.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Database ownership and ACL references retain role incarnations independently of names.
8
9use super::super::role_bindings;
10use super::{DatabaseAclEntry, DatabasePrivileges, DatabaseSecurity};
11use crate::catalog::roles::{RoleDefinition, RoleIdentity};
12use serde::{Deserialize, Serialize};
13use std::collections::BTreeMap;
14use uqa_core::catalog_acl::AclGrantee;
15
16pub type BoundDatabaseAclEntry = uqa_core::catalog_role::BoundAclEntry<DatabasePrivileges>;
17
18#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
19pub struct BoundDatabaseSecurity {
20    pub role_owner: RoleIdentity,
21    pub acl: Option<Vec<BoundDatabaseAclEntry>>,
22}
23
24fn role_name(
25    roles: &BTreeMap<String, RoleDefinition>,
26    identity: RoleIdentity,
27) -> Result<&str, String> {
28    role_bindings::role_name(roles, identity, "database")
29}
30
31impl BoundDatabaseSecurity {
32    pub fn bootstrap() -> Self {
33        Self {
34            role_owner: RoleDefinition::bootstrap().identity(),
35            acl: None,
36        }
37    }
38
39    pub fn depends_on(&self, role: RoleIdentity) -> bool {
40        self.role_owner == role
41            || self.acl.as_ref().is_some_and(|entries| {
42                entries
43                    .iter()
44                    .any(|entry| entry.role == Some(role) || entry.grantor == role)
45            })
46    }
47
48    pub fn validate(&self, roles: &BTreeMap<String, RoleDefinition>) -> Result<(), String> {
49        role_name(roles, self.role_owner)?;
50        for entry in self.acl.iter().flatten() {
51            if let Some(grantee) = entry.role {
52                role_name(roles, grantee)?;
53            }
54            role_name(roles, entry.grantor)?;
55        }
56        Ok(())
57    }
58
59    pub fn bind(
60        security: &DatabaseSecurity,
61        roles: &BTreeMap<String, RoleDefinition>,
62    ) -> Result<Self, String> {
63        super::validate_stored_database_security(security, roles)?;
64        let bind = |name: &str| role_bindings::bind_role(roles, name, "database");
65        Ok(Self {
66            role_owner: bind(&security.role_owner)?,
67            acl: security
68                .acl
69                .as_ref()
70                .map(|entries| {
71                    entries
72                        .iter()
73                        .map(|entry| {
74                            Ok(BoundDatabaseAclEntry {
75                                role: entry.role.role_name().map(bind).transpose()?,
76                                grantor: bind(
77                                    entry.grantor.as_deref().unwrap_or(&security.role_owner),
78                                )?,
79                                privileges: entry.privileges,
80                                grant_options: entry.grant_options,
81                            })
82                        })
83                        .collect::<Result<Vec<_>, String>>()
84                })
85                .transpose()?,
86        })
87    }
88
89    /// Project current names without rewriting or rebinding the stored references.
90    pub fn resolve(
91        &self,
92        roles: &BTreeMap<String, RoleDefinition>,
93    ) -> Result<DatabaseSecurity, String> {
94        let name = |identity| role_name(roles, identity).map(str::to_owned);
95        Ok(DatabaseSecurity {
96            role_owner: name(self.role_owner)?,
97            acl: self
98                .acl
99                .as_ref()
100                .map(|entries| {
101                    entries
102                        .iter()
103                        .map(|entry| {
104                            Ok(DatabaseAclEntry {
105                                role: entry
106                                    .role
107                                    .map(&name)
108                                    .transpose()?
109                                    .map_or(AclGrantee::Public, AclGrantee::Role),
110                                grantor: Some(name(entry.grantor)?),
111                                privileges: entry.privileges,
112                                grant_options: entry.grant_options,
113                            })
114                        })
115                        .collect::<Result<Vec<_>, String>>()
116                })
117                .transpose()?,
118        })
119    }
120}
121
122#[cfg(test)]
123mod tests;