Skip to main content

uqa_storage/catalog/
relation_security.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! The stored discriminator separates captured role identities from legacy names.
8
9use serde::{Deserialize, Deserializer, Serialize, Serializer};
10pub use uqa_core::catalog_acl::{BoundRelationSecurity, LegacyRelationSecurity};
11
12#[derive(Debug, Clone, PartialEq, Eq)]
13pub enum RelationSecurityRow {
14    Bound(BoundRelationSecurity),
15    Legacy(LegacyRelationSecurity),
16}
17
18impl RelationSecurityRow {
19    pub fn bootstrap() -> Self {
20        Self::Bound(BoundRelationSecurity::owner(
21            uqa_core::catalog_role::RoleIdentity::BOOTSTRAP,
22        ))
23    }
24
25    pub fn legacy(role_owner: impl Into<String>) -> Self {
26        Self::Legacy(LegacyRelationSecurity::owner(role_owner))
27    }
28}
29
30impl From<BoundRelationSecurity> for RelationSecurityRow {
31    fn from(row: BoundRelationSecurity) -> Self {
32        Self::Bound(row)
33    }
34}
35
36impl Serialize for RelationSecurityRow {
37    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
38        #[derive(Serialize)]
39        struct Bound<'a> {
40            relation_security_format: u32,
41            #[serde(flatten)]
42            security: &'a BoundRelationSecurity,
43        }
44        match self {
45            Self::Bound(security) => Bound {
46                relation_security_format: 1,
47                security,
48            }
49            .serialize(serializer),
50            Self::Legacy(security) => security.serialize(serializer),
51        }
52    }
53}
54
55impl<'de> Deserialize<'de> for RelationSecurityRow {
56    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
57        use serde::de::Error;
58        let mut value = serde_json::Value::deserialize(deserializer)?;
59        if let Some(version) = value.get("relation_security_format") {
60            if version.as_u64() != Some(1) {
61                return Err(D::Error::custom("unsupported relation security format"));
62            }
63            value
64                .as_object_mut()
65                .expect("version belongs to an object")
66                .remove("relation_security_format");
67            serde_json::from_value(value)
68                .map(Self::Bound)
69                .map_err(D::Error::custom)
70        } else {
71            serde_json::from_value(value)
72                .map(Self::Legacy)
73                .map_err(D::Error::custom)
74        }
75    }
76}
77
78#[cfg(test)]
79mod tests;