1
0
Fork 0
forked from 2sys/phonograph
phonograph/interim-server/src/workspace_user_perms.rs
2025-10-09 08:01:01 +00:00

72 lines
2.2 KiB
Rust

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<PgRole> = 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<Uuid> = 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(())
}