fix permissions bug in table owner assignment
This commit is contained in:
parent
efd6cf1fb3
commit
5a3d6eabf9
2 changed files with 43 additions and 10 deletions
|
|
@ -73,7 +73,7 @@ pub(super) async fn post(
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// For authorization only.
|
// For authorization only.
|
||||||
let _portal = PortalAccessor::new()
|
PortalAccessor::new()
|
||||||
.id(portal_id)
|
.id(portal_id)
|
||||||
.as_actor(Actor::User(user.id))
|
.as_actor(Actor::User(user.id))
|
||||||
.verify_workspace_id(workspace_id)
|
.verify_workspace_id(workspace_id)
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ use phono_backends::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use sqlx::query;
|
use sqlx::{Acquire as _, query};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
@ -87,14 +87,47 @@ create table {0}.{1} (
|
||||||
))
|
))
|
||||||
.execute(root_client.get_conn())
|
.execute(root_client.get_conn())
|
||||||
.await?;
|
.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!(
|
query(&format!(
|
||||||
"alter table {nsp}.{tbl} owner to {rol}",
|
"alter table {nsp}.{tbl} owner to {rol}",
|
||||||
nsp = escape_identifier(PHONO_TABLE_NAMESPACE),
|
nsp = escape_identifier(PHONO_TABLE_NAMESPACE),
|
||||||
tbl = escape_identifier(&table_name),
|
tbl = escape_identifier(&table_name),
|
||||||
rol = escape_identifier(&rolname_table_owner),
|
rol = escape_identifier(&rolname_table_owner),
|
||||||
))
|
))
|
||||||
.execute(root_client.get_conn())
|
.execute(&mut *txn)
|
||||||
.await?;
|
.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!(
|
query(&format!(
|
||||||
"grant select on {nsp}.{tbl} to {rol}",
|
"grant select on {nsp}.{tbl} to {rol}",
|
||||||
nsp = escape_identifier(PHONO_TABLE_NAMESPACE),
|
nsp = escape_identifier(PHONO_TABLE_NAMESPACE),
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue