uqa_sql/catalog/security/
view_authorization.rs1use crate::{
9 catalog::{
10 roles::guards::RoleCatalogGuards,
11 security::{
12 columns::role_has_column_privilege as column_privilege_check,
13 table::{role_has_privilege, TableAclPrivilege, TablePrivilegeCheck},
14 },
15 stored_view::StoredView,
16 view::StoredViewKind,
17 },
18 SQLError,
19};
20use uqa_core::RelationIdentity;
21pub struct ViewAuthorizationContext<'a> {
22 pub roles: &'a dyn RoleCatalogGuards,
23}
24impl ViewAuthorizationContext<'_> {
25 pub fn ensure_view_privilege_for(
26 &self,
27 name: &str,
28 view: &StoredView,
29 subject: &str,
30 privilege: TableAclPrivilege,
31 ) -> Result<(), SQLError> {
32 let relation = RelationIdentity::from_legacy_name(name).map_err(SQLError::Internal)?;
33 let security = view.security();
34 let roles = self.roles.role_definitions();
35 let memberships = self.roles.role_memberships();
36 if role_has_privilege(
37 &security,
38 subject,
39 TablePrivilegeCheck {
40 privilege,
41 grant_option: false,
42 },
43 &roles,
44 &memberships,
45 ) {
46 return Ok(());
47 }
48 Err(SQLError::Routine {
49 sqlstate: "42501".into(),
50 message: format!(
51 "permission denied for {} {}",
52 match view.kind {
53 StoredViewKind::View => "view",
54 StoredViewKind::Materialized => "materialized view",
55 },
56 relation.name
57 ),
58 })
59 }
60 pub fn ensure_view_column_privilege_for(
61 &self,
62 name: &str,
63 view: &StoredView,
64 column: &str,
65 subject: &str,
66 privilege: TableAclPrivilege,
67 ) -> Result<(), SQLError> {
68 let relation = RelationIdentity::from_legacy_name(name).map_err(SQLError::Internal)?;
69 let security = view.security();
70 let roles = self.roles.role_definitions();
71 let memberships = self.roles.role_memberships();
72 if column_privilege_check(
73 &security,
74 column,
75 subject,
76 TablePrivilegeCheck {
77 privilege,
78 grant_option: false,
79 },
80 &roles,
81 &memberships,
82 ) {
83 return Ok(());
84 }
85 Err(SQLError::Routine {
86 sqlstate: "42501".into(),
87 message: format!(
88 "permission denied for {} {}",
89 match view.kind {
90 StoredViewKind::View => "view",
91 StoredViewKind::Materialized => "materialized view",
92 },
93 relation.name
94 ),
95 })
96 }
97 pub fn ensure_any_view_column_privilege_for(
98 &self,
99 name: &str,
100 view: &StoredView,
101 subject: &str,
102 privilege: TableAclPrivilege,
103 ) -> Result<(), SQLError> {
104 let security = view.security();
105 let roles = self.roles.role_definitions();
106 let memberships = self.roles.role_memberships();
107 let check = TablePrivilegeCheck {
108 privilege,
109 grant_option: false,
110 };
111 let columns = view.output_columns.as_deref().ok_or_else(|| {
112 SQLError::Internal(format!(
113 "loaded view `{name}` has no durable public column metadata"
114 ))
115 })?;
116 if role_has_privilege(&security, subject, check, &roles, &memberships)
117 || columns.iter().any(|column| {
118 column_privilege_check(&security, column, subject, check, &roles, &memberships)
119 })
120 {
121 return Ok(());
122 }
123 drop(memberships);
124 drop(roles);
125 self.ensure_view_privilege_for(name, view, subject, privilege)
126 }
127}
128
129#[cfg(test)]
130mod tests;