use std::collections::HashSet; use anyhow::Result; use interim_models::{ client::AppDbClient, workspace_user_perm::{self, WorkspaceUserPerm}, }; use interim_pgtypes::{ client::WorkspaceClient, pg_acl::PgPrivilegeType, pg_database::PgDatabase, pg_role::{PgRole, RoleTree, user_id_from_rolname}, }; use sqlx::query; use uuid::Uuid; /// Derive workspace access control permissions from the permission grants of /// a workspace's backing database. pub(crate) async fn sync_for_workspace( workspace_id: Uuid, app_db: &mut AppDbClient, workspace_client: &mut WorkspaceClient, db_role_prefix: &str, ) -> Result<()> { let db = PgDatabase::current().fetch_one(workspace_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(workspace_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(workspace_client) .await? { for implicit_role in role_tree.flatten_inherited() { all_roles.insert(implicit_role.clone()); } } } let user_ids: Vec = all_roles .iter() .filter_map(|role| user_id_from_rolname(&role.rolname, db_role_prefix).ok()) .collect(); query!( "delete from workspace_user_perms where workspace_id = $1 and not (user_id = any($2))", workspace_id, user_ids.as_slice(), ) .execute(app_db.get_conn()) .await?; for user_id in user_ids { WorkspaceUserPerm::insert() .workspace_id(workspace_id) .user_id(user_id) .perm(workspace_user_perm::PermissionValue::Connect) .build()? .execute(app_db) .await?; } Ok(()) }