fix permissions bug in table owner assignment

This commit is contained in:
Brent Schroeter 2025-12-01 23:59:46 -08:00
parent efd6cf1fb3
commit 5a3d6eabf9
2 changed files with 43 additions and 10 deletions

View file

@ -73,7 +73,7 @@ pub(super) async fn post(
.await?;
// For authorization only.
let _portal = PortalAccessor::new()
PortalAccessor::new()
.id(portal_id)
.as_actor(Actor::User(user.id))
.verify_workspace_id(workspace_id)

View file

@ -10,7 +10,7 @@ use phono_backends::{
},
};
use serde::Deserialize;
use sqlx::query;
use sqlx::{Acquire as _, query};
use uuid::Uuid;
use crate::{
@ -87,14 +87,47 @@ create table {0}.{1} (
))
.execute(root_client.get_conn())
.await?;
query(&format!(
"alter table {nsp}.{tbl} owner to {rol}",
nsp = escape_identifier(PHONO_TABLE_NAMESPACE),
tbl = escape_identifier(&table_name),
rol = escape_identifier(&rolname_table_owner),
))
.execute(root_client.get_conn())
.await?;
// Postgres requires that a role have "CREATE" privileges on a schema when
// it is given ownership of a relation in that schema. This is at odds with
// our intent here, since the dedicated table owner role should never need
// nor want to have or impart the ability to create unrelated tables.
//
// While not strictly necessary, in order to keep user permissions as clean
// as possible, we run all three of the "GRANT", "ALTER", and "REVOKE"
// commands within a transaction. At least as of Postgres 18, emperical
// testing confirms that permissions updates behave similarly to
// conventional commands and queries executed within transactions, so for
// outside observers, the table owner role and its descendents should never
// appear to actually receive schema "CREATE" privileges, even momentarily,
// as a result of this code block.
{
let mut txn = root_client.get_conn().begin().await?;
query(&format!(
"grant create on schema {nsp} to {rol}",
nsp = escape_identifier(PHONO_TABLE_NAMESPACE),
rol = escape_identifier(&rolname_table_owner)
))
.execute(&mut *txn)
.await?;
query(&format!(
"alter table {nsp}.{tbl} owner to {rol}",
nsp = escape_identifier(PHONO_TABLE_NAMESPACE),
tbl = escape_identifier(&table_name),
rol = escape_identifier(&rolname_table_owner),
))
.execute(&mut *txn)
.await?;
query(&format!(
"revoke create on schema {nsp} from {rol}",
nsp = escape_identifier(PHONO_TABLE_NAMESPACE),
rol = escape_identifier(&rolname_table_owner)
))
.execute(&mut *txn)
.await?;
txn.commit().await?;
}
query(&format!(
"grant select on {nsp}.{tbl} to {rol}",
nsp = escape_identifier(PHONO_TABLE_NAMESPACE),