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