Skip to main content

uqa_sql/catalog/security/
builtin_routines.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! EXECUTE ACLs of immutable catalog routines. Their owner and routine identity
8//! stay pinned; ACL endpoints retain role incarnations like user routine ACLs.
9
10pub mod initialization;
11pub mod selection;
12
13#[derive(Clone, Copy, Debug)]
14pub struct BuiltinRoutineIdentity {
15    pub oid: u32,
16    pub name: &'static str,
17    pub kind: char,
18    pub argument_types: &'static [i64],
19}
20
21use super::object_acl;
22use crate::catalog::roles::identity::RoleSubject;
23use crate::{
24    ast::RoutineAclEntry,
25    catalog::roles::{RoleDefinition, RoleIdentity},
26    SQLError,
27};
28use std::{collections::BTreeMap, ops::Deref};
29
30#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
31#[serde(deny_unknown_fields)]
32pub struct BuiltinRoutineSecurity {
33    pub revision: [u8; 16],
34    pub execute_acl: Vec<RoutineAclEntry>,
35}
36
37pub type BuiltinRoutineSecurities = BTreeMap<u32, BuiltinRoutineSecurity>;
38pub type BuiltinRoutineSecurityRead<'a> = Box<dyn Deref<Target = BuiltinRoutineSecurities> + 'a>;
39
40pub trait BuiltinRoutineSecurityCatalog {
41    fn builtin_routine_securities(&self) -> BuiltinRoutineSecurityRead<'_>;
42}
43
44/// Authorization of an already selected built-in. Analysis may retain a binding without executing it; planners invoke this only before evaluating a constant, and executors invoke it while preparing or entering the surviving call.
45pub trait BuiltinRoutineExecution: std::fmt::Debug + Send + Sync {
46    fn require_execute(&self, binding: &crate::ast::FunctionBinding) -> Result<(), SQLError>;
47}
48
49pub const METADATA_PREFIX: &str = "uqa.builtin_routine_security.v1:";
50
51#[must_use]
52pub fn metadata_key(oid: u32) -> String {
53    format!("{METADATA_PREFIX}{oid}")
54}
55
56pub fn validate(
57    entry: &BuiltinRoutineSecurity,
58    roles: &BTreeMap<String, RoleDefinition>,
59) -> Result<(), SQLError> {
60    if entry.revision == [0; 16] {
61        return Err(SQLError::Internal(
62            "invalid builtin routine ACL tuple identity".into(),
63        ));
64    }
65    object_acl::validate_acl(
66        RoleIdentity::BOOTSTRAP,
67        Some(&entry.execute_acl),
68        "builtin routine",
69    )?;
70    for role in object_acl::acl_roles(RoleIdentity::BOOTSTRAP, Some(&entry.execute_acl)) {
71        if role.role_name(roles).is_none() {
72            return Err(SQLError::Internal(
73                "builtin routine ACL references missing role incarnation".into(),
74            ));
75        }
76    }
77    Ok(())
78}
79
80#[cfg(test)]
81mod tests;