use super::{
context::TableGrantContext,
targets::{
validated_foreign_table_grant_targets, validated_table_grant_targets,
validated_view_grant_targets,
},
updates::{system_privilege_updates, table_privilege_updates, TablePrivilegeUpdate},
};
use crate::catalog::security::{
roles::dependencies::RoleDependencyCandidate, system_relations::SystemPrivilegeUpdate,
};
use uqa_sql::catalog::security::acl_command::{AclCommandRoles, ResolvedAclRoles};
use uqa_sql::{
ast::{GrantTableStmt, GrantTableTarget},
catalog::security::{
table::{requested_acl_privileges, RequestedTablePrivileges},
table_grants::{
foreign_table_privilege_updates, validate_table_acl_roles,
validate_table_grant_target_kinds, view_privilege_updates, ForeignTablePrivilegeUpdate,
ResolvedTableGrantTarget, TableGrantApplication, ViewPrivilegeUpdate,
},
},
SQLError,
};
pub(super) struct PreparedTableGrant<'a> {
pub updates: Vec<TablePrivilegeUpdate<'a>>,
pub view_updates: Vec<ViewPrivilegeUpdate>,
pub foreign_updates: Vec<ForeignTablePrivilegeUpdate>,
pub system_updates: Vec<SystemPrivilegeUpdate>,
pub acl_updates: Vec<super::tuples::RelationPrivilegeUpdate>,
pub notices: Vec<(&'static str, String)>,
}
pub(super) fn resolve_roles(
context: &TableGrantContext<'_>,
statement: &GrantTableStmt,
command_roles: &mut AclCommandRoles,
roles: &std::collections::BTreeMap<String, uqa_sql::catalog::roles::RoleDefinition>,
) -> Result<ResolvedAclRoles, SQLError> {
command_roles.resolve_validated(
context.names,
roles,
&statement.grantees,
statement.grantor.as_ref(),
|resolved| {
validate_table_acl_roles(
statement,
&resolved.grantees,
resolved.requested_grantor.as_deref(),
&resolved.current_user,
roles,
)
},
)
}
pub(super) fn prepare<'a>(
context: &'a TableGrantContext<'_>,
statement: &GrantTableStmt,
targets: &[ResolvedTableGrantTarget],
command_roles: &mut AclCommandRoles,
) -> Result<RoleDependencyCandidate<'a, PreparedTableGrant<'a>>, SQLError> {
let roles = context.roles.role_definitions();
let ResolvedAclRoles {
grantees,
current_user,
..
} = resolve_roles(context, statement, command_roles, &roles)?;
validate_table_grant_target_kinds(statement, targets)?;
let has_table_relations = targets.iter().any(|target| {
matches!(
target.kind,
"table" | "view" | "materialized view" | "foreign table"
)
});
let requested_privileges = if has_table_relations
|| matches!(
statement.target,
GrantTableTarget::AllTablesInSchemas { .. }
) {
requested_acl_privileges(&statement.privileges)?
} else {
RequestedTablePrivileges {
table: Vec::new(),
columns: Vec::new(),
}
};
let memberships = context.roles.role_memberships();
let table_targets = validated_table_grant_targets(context, targets, &requested_privileges)?;
let view_targets = validated_view_grant_targets(context, targets, &requested_privileges)?;
let foreign_targets =
validated_foreign_table_grant_targets(context, targets, &requested_privileges)?;
let mut notices = Vec::new();
let mut dependencies = std::collections::BTreeSet::new();
let application = TableGrantApplication {
statement,
grantees: &grantees,
requested: &requested_privileges,
current_user: ¤t_user,
roles: &roles,
memberships: &memberships,
};
let mut updates =
table_privilege_updates(table_targets, &application, &mut notices, &mut dependencies)?;
let mut view_updates =
view_privilege_updates(view_targets, &application, &mut notices, &mut dependencies)?;
let mut foreign_updates = foreign_table_privilege_updates(
foreign_targets,
&application,
&mut notices,
&mut dependencies,
)?;
let system_updates = system_privilege_updates(
context,
targets,
&application,
&mut notices,
&mut dependencies,
)?;
let acl_updates = super::tuples::prepare(
context,
targets,
&application,
&mut updates,
&mut view_updates,
&mut foreign_updates,
)?;
Ok(RoleDependencyCandidate {
value: PreparedTableGrant {
updates,
view_updates,
foreign_updates,
system_updates,
acl_updates,
notices,
},
memberships,
roles,
dependencies,
})
}