use std::collections::HashSet; use anyhow::Result; use interim_models::{base::Base, client::AppDbClient}; use interim_pgtypes::{ client::BaseClient, pg_acl::PgPrivilegeType, pg_database::PgDatabase, pg_role::{PgRole, RoleTree, user_id_from_rolname}, }; use sqlx::query; use uuid::Uuid; 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 AppDbClient, base_client: &mut BaseClient, ) -> Result<()> { let db = PgDatabase::current().fetch_one(base_client).await?; let explicit_roles = PgRole::with_name_in( db.datacl .unwrap_or_default() .into_iter() .filter(|item| { item.privileges .iter() .any(|privilege| privilege.privilege == PgPrivilegeType::Connect) }) .map(|item| item.grantee) .collect(), ) .fetch_all(base_client) .await?; let mut all_roles: HashSet = HashSet::new(); for explicit_role in explicit_roles { if let Some(role_tree) = RoleTree::members_of_oid(explicit_role.oid) .fetch_tree(base_client) .await? { for implicit_role in role_tree.flatten_inherited() { all_roles.insert(implicit_role.clone()); } } } let base = Base::with_id(base_id).fetch_one(app_db).await?; 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(app_db.get_conn()) .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(app_db.get_conn()) .await?; } Ok(()) }