use std::collections::HashSet; use anyhow::{Context as _, Result}; use interim_pgtypes::{ pg_acl::PgPrivilegeType, pg_database::PgDatabase, pg_role::{PgRole, RoleTree, user_id_from_rolname}, }; use sqlx::{PgConnection, query}; use uuid::Uuid; use crate::bases::Base; pub struct BaseUserPerm { pub id: Uuid, pub base_id: Uuid, pub user_id: Uuid, pub perm: String, } pub async fn sync_perms_for_base( base_id: Uuid, app_db: &mut PgConnection, client: &mut PgConnection, ) -> Result<()> { let db = PgDatabase::fetch_current(&mut *client).await?; let explicit_roles = PgRole::fetch_by_names_any( db.datacl .unwrap_or(vec![]) .into_iter() .filter(|item| { item.privileges .iter() .any(|privilege| privilege.privilege == PgPrivilegeType::Connect) }) .map(|item| item.grantee) .collect(), &mut *client, ) .await?; let mut all_roles: HashSet = HashSet::new(); for explicit_role in explicit_roles { if let Some(role_tree) = RoleTree::fetch_members(explicit_role.oid, &mut *client).await? { for implicit_role in role_tree.flatten_inherited() { all_roles.insert(implicit_role.clone()); } } } let base = Base::fetch_by_id(base_id, &mut *app_db) .await? .context("base with that id not found")?; let user_ids: Vec = all_roles .iter() .filter_map(|role| user_id_from_rolname(&role.rolname, &base.user_role_prefix).ok()) .collect(); query!( "delete from base_user_perms where base_id = $1 and not (user_id = any($2))", base_id, user_ids.as_slice(), ) .execute(&mut *app_db) .await?; for user_id in user_ids { query!( " insert into base_user_perms (id, base_id, user_id, perm) values ($1, $2, $3, 'connect') on conflict (base_id, user_id, perm) do nothing ", Uuid::now_v7(), base.id, user_id ) .execute(&mut *app_db) .await?; } Ok(()) }