Skip to main content

uqa_core/
catalog_acl.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Durable access-control metadata for table-shaped relations.
8
9use serde::{Deserialize, Serialize};
10
11mod grantee;
12pub use grantee::AclGrantee;
13mod relation_security;
14pub use relation_security::{BoundRelationSecurity, LegacyRelationSecurity, RelationAclRevisions};
15
16/// Grantable privileges carried by one table-shaped relation ACL path.
17#[expect(
18    clippy::struct_excessive_bools,
19    reason = "models PostgreSQL's independently grantable table-shaped relation privileges"
20)]
21#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
22pub struct TablePrivileges {
23    #[serde(default)]
24    pub select: bool,
25    #[serde(default)]
26    pub insert: bool,
27    #[serde(default)]
28    pub update: bool,
29    #[serde(default)]
30    pub delete: bool,
31    #[serde(default)]
32    pub truncate: bool,
33    #[serde(default)]
34    pub references: bool,
35    #[serde(default)]
36    pub trigger: bool,
37    #[serde(default)]
38    pub maintain: bool,
39}
40
41impl TablePrivileges {
42    pub const ALL: Self = Self {
43        select: true,
44        insert: true,
45        update: true,
46        delete: true,
47        truncate: true,
48        references: true,
49        trigger: true,
50        maintain: true,
51    };
52
53    #[must_use]
54    pub const fn is_empty(self) -> bool {
55        !self.select
56            && !self.insert
57            && !self.update
58            && !self.delete
59            && !self.truncate
60            && !self.references
61            && !self.trigger
62            && !self.maintain
63    }
64
65    #[must_use]
66    pub const fn intersects(self, other: Self) -> bool {
67        self.select && other.select
68            || self.insert && other.insert
69            || self.update && other.update
70            || self.delete && other.delete
71            || self.truncate && other.truncate
72            || self.references && other.references
73            || self.trigger && other.trigger
74            || self.maintain && other.maintain
75    }
76
77    pub fn insert(&mut self, other: Self) {
78        self.select |= other.select;
79        self.insert |= other.insert;
80        self.update |= other.update;
81        self.delete |= other.delete;
82        self.truncate |= other.truncate;
83        self.references |= other.references;
84        self.trigger |= other.trigger;
85        self.maintain |= other.maintain;
86    }
87
88    pub fn remove(&mut self, other: Self) {
89        self.select &= !other.select;
90        self.insert &= !other.insert;
91        self.update &= !other.update;
92        self.delete &= !other.delete;
93        self.truncate &= !other.truncate;
94        self.references &= !other.references;
95        self.trigger &= !other.trigger;
96        self.maintain &= !other.maintain;
97    }
98}
99
100/// One explicit table-shaped relation ACL path. Legacy entries without an explicit grantor originate from the relation owner.
101#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
102pub struct TableAclEntry {
103    pub role: AclGrantee,
104    #[serde(default, skip_serializing_if = "Option::is_none")]
105    pub grantor: Option<String>,
106    #[serde(default)]
107    pub privileges: TablePrivileges,
108    #[serde(default)]
109    pub grant_options: TablePrivileges,
110}