1use super::RoleDefinition;
10use crate::SQLError;
11use serde::{Deserialize, Serialize};
12use std::{collections::BTreeMap, sync::Arc};
13pub use uqa_core::catalog_role::RoleIdentity;
14
15#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
17pub struct RoleBinding {
18 pub name: String,
19 pub oid: u32,
20 pub object_id: [u8; 16],
21}
22
23#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
25#[serde(untagged)]
26pub enum RoleReference {
27 Named(String),
28 Bound(Arc<RoleBinding>),
29}
30
31impl From<String> for RoleReference {
32 fn from(name: String) -> Self {
33 Self::Named(name)
34 }
35}
36
37impl From<&str> for RoleReference {
38 fn from(name: &str) -> Self {
39 Self::Named(name.into())
40 }
41}
42
43impl RoleReference {
44 pub fn from_identity(
45 identity: RoleIdentity,
46 roles: &BTreeMap<String, RoleDefinition>,
47 ) -> Result<Self, SQLError> {
48 let name = identity.role_name(roles).ok_or_else(|| {
49 SQLError::Internal("role identity references a missing or replaced incarnation".into())
50 })?;
51 Ok(Self::Bound(Arc::new(RoleBinding::from_definition(
52 &roles[name],
53 )?)))
54 }
55
56 pub fn require_name<'a>(
57 &'a self,
58 roles: &'a BTreeMap<String, RoleDefinition>,
59 ) -> Result<&'a str, SQLError> {
60 match self {
61 Self::Named(name) => {
62 super::require_role_exists(roles, name)?;
63 Ok(name)
64 }
65 Self::Bound(role) => role.require_name(roles),
66 }
67 }
68
69 pub fn catalog_name(
70 &self,
71 roles: &BTreeMap<String, RoleDefinition>,
72 ) -> Result<String, SQLError> {
73 match self {
74 Self::Named(name) => Ok(name.clone()),
75 Self::Bound(role) => role.require_name(roles).map(str::to_owned),
76 }
77 }
78
79 pub fn bind(&self, roles: &BTreeMap<String, RoleDefinition>) -> Result<RoleBinding, SQLError> {
80 let name = self.require_name(roles)?;
81 RoleBinding::from_definition(&roles[name])
82 }
83}
84
85impl RoleSubject for RoleReference {
86 fn role_name<'a>(&'a self, roles: &'a BTreeMap<String, RoleDefinition>) -> Option<&'a str> {
87 match self {
88 Self::Named(name) => name.role_name(roles),
89 Self::Bound(role) => role.role_name(roles),
90 }
91 }
92
93 fn role_definition<'a>(
94 &self,
95 roles: &'a BTreeMap<String, RoleDefinition>,
96 ) -> Option<&'a RoleDefinition> {
97 match self {
98 Self::Named(name) => name.role_definition(roles),
99 Self::Bound(role) => role.role_definition(roles),
100 }
101 }
102}
103
104impl RoleBinding {
105 pub fn identity(&self) -> RoleIdentity {
106 RoleIdentity {
107 oid: i64::from(self.oid),
108 object_id: self.object_id,
109 }
110 }
111
112 pub fn from_definition(role: &RoleDefinition) -> Result<Self, SQLError> {
113 if role.oid <= 0 {
114 return Err(SQLError::Internal("invalid role OID".into()));
115 }
116 if role.object_id == [0; 16] {
117 return Err(SQLError::Internal("role has no object identity".into()));
118 }
119 Ok(Self {
120 name: role.name.clone(),
121 oid: u32::try_from(role.oid)
122 .map_err(|_| SQLError::Internal("invalid role OID".into()))?,
123 object_id: role.object_id,
124 })
125 }
126
127 pub fn revalidate(&self, roles: &BTreeMap<String, RoleDefinition>) -> Result<(), SQLError> {
129 if self.role_definition(roles).is_some() {
130 Ok(())
131 } else {
132 Err(SQLError::Routine {
133 sqlstate: "42704".into(),
134 message: format!("role {} was concurrently dropped", self.oid),
135 })
136 }
137 }
138
139 fn matches_definition(&self, role: &RoleDefinition) -> bool {
140 role.oid == i64::from(self.oid) && role.object_id == self.object_id
141 }
142
143 pub fn require_name<'a>(
144 &self,
145 roles: &'a BTreeMap<String, RoleDefinition>,
146 ) -> Result<&'a str, SQLError> {
147 self.role_definition(roles)
148 .map(|role| role.name.as_str())
149 .ok_or_else(|| SQLError::Routine {
150 sqlstate: "42704".into(),
151 message: format!("invalid role OID: {}", self.oid),
152 })
153 }
154}
155
156pub trait RoleSubject {
158 fn role_name<'a>(&'a self, roles: &'a BTreeMap<String, RoleDefinition>) -> Option<&'a str>;
159
160 fn role_definition<'a>(
161 &self,
162 roles: &'a BTreeMap<String, RoleDefinition>,
163 ) -> Option<&'a RoleDefinition>;
164}
165
166impl<T: RoleSubject + ?Sized> RoleSubject for Arc<T> {
167 fn role_name<'a>(&'a self, roles: &'a BTreeMap<String, RoleDefinition>) -> Option<&'a str> {
168 self.as_ref().role_name(roles)
169 }
170 fn role_definition<'a>(
171 &self,
172 roles: &'a BTreeMap<String, RoleDefinition>,
173 ) -> Option<&'a RoleDefinition> {
174 self.as_ref().role_definition(roles)
175 }
176}
177
178impl<T: RoleSubject + ?Sized> RoleSubject for &T {
179 fn role_name<'a>(&'a self, roles: &'a BTreeMap<String, RoleDefinition>) -> Option<&'a str> {
180 (*self).role_name(roles)
181 }
182
183 fn role_definition<'a>(
184 &self,
185 roles: &'a BTreeMap<String, RoleDefinition>,
186 ) -> Option<&'a RoleDefinition> {
187 (*self).role_definition(roles)
188 }
189}
190
191impl RoleSubject for str {
192 fn role_name<'a>(&'a self, _roles: &'a BTreeMap<String, RoleDefinition>) -> Option<&'a str> {
193 Some(self)
194 }
195
196 fn role_definition<'a>(
197 &self,
198 roles: &'a BTreeMap<String, RoleDefinition>,
199 ) -> Option<&'a RoleDefinition> {
200 roles.get(self)
201 }
202}
203
204impl RoleSubject for String {
205 fn role_name<'a>(&'a self, roles: &'a BTreeMap<String, RoleDefinition>) -> Option<&'a str> {
206 self.as_str().role_name(roles)
207 }
208
209 fn role_definition<'a>(
210 &self,
211 roles: &'a BTreeMap<String, RoleDefinition>,
212 ) -> Option<&'a RoleDefinition> {
213 self.as_str().role_definition(roles)
214 }
215}
216
217impl RoleSubject for uqa_core::catalog_acl::AclGrantee {
218 fn role_name<'a>(&'a self, _roles: &'a BTreeMap<String, RoleDefinition>) -> Option<&'a str> {
219 self.role_name()
220 }
221
222 fn role_definition<'a>(
223 &self,
224 roles: &'a BTreeMap<String, RoleDefinition>,
225 ) -> Option<&'a RoleDefinition> {
226 self.role_name().and_then(|name| roles.get(name))
227 }
228}
229
230impl RoleSubject for RoleBinding {
231 fn role_name<'a>(&'a self, roles: &'a BTreeMap<String, RoleDefinition>) -> Option<&'a str> {
232 self.role_definition(roles).map(|role| role.name.as_str())
233 }
234
235 fn role_definition<'a>(
236 &self,
237 roles: &'a BTreeMap<String, RoleDefinition>,
238 ) -> Option<&'a RoleDefinition> {
239 roles
240 .get(&self.name)
241 .filter(|role| self.matches_definition(role))
242 .or_else(|| roles.values().find(|role| self.matches_definition(role)))
243 }
244}
245
246impl RoleSubject for RoleIdentity {
247 fn role_name<'a>(&'a self, roles: &'a BTreeMap<String, RoleDefinition>) -> Option<&'a str> {
248 self.role_definition(roles).map(|role| role.name.as_str())
249 }
250
251 fn role_definition<'a>(
252 &self,
253 roles: &'a BTreeMap<String, RoleDefinition>,
254 ) -> Option<&'a RoleDefinition> {
255 if !self.is_valid() {
256 return None;
257 }
258 roles.values().find(|role| role.identity() == *self)
259 }
260}
261
262#[cfg(test)]
263mod tests;