diff --git a/crates/buzz-db/src/store/relay_admin_actions.rs b/crates/buzz-db/src/store/relay_admin_actions.rs index 01c2b97236..d118704465 100644 --- a/crates/buzz-db/src/store/relay_admin_actions.rs +++ b/crates/buzz-db/src/store/relay_admin_actions.rs @@ -524,7 +524,8 @@ pub async fn execute_ban_with_marker( .execute(tx.conn()) .await?; - crate::relay_invite::revoke_banned_issuer_invites(tx.conn(), community_id, target_pubkey).await?; + crate::relay_invite::revoke_banned_issuer_invites(tx.conn(), community_id, target_pubkey) + .await?; let marker = sqlx::query( r#" @@ -620,7 +621,8 @@ pub async fn execute_timeout_with_marker( .execute(tx.conn()) .await?; - crate::relay_invite::revoke_banned_issuer_invites(tx.conn(), community_id, target_pubkey).await?; + crate::relay_invite::revoke_banned_issuer_invites(tx.conn(), community_id, target_pubkey) + .await?; let marker = sqlx::query( r#" diff --git a/crates/buzz-db/src/store/relay_invite.rs b/crates/buzz-db/src/store/relay_invite.rs index 27c5cf83c7..736205bfa9 100644 --- a/crates/buzz-db/src/store/relay_invite.rs +++ b/crates/buzz-db/src/store/relay_invite.rs @@ -361,8 +361,10 @@ pub async fn claim_relay_invite_with_owner( ) .await?; - lock_admission(&mut tx, community).await?; - if claim_owner::claimant_restricted(&mut tx, community, claimer_pubkey, verified_owner).await? { + lock_admission(tx.conn(), community).await?; + if claim_owner::claimant_restricted(tx.conn(), community, claimer_pubkey, verified_owner) + .await? + { tx.rollback().await?; log_claim_outcome(community, None, "restricted", None, None); return Ok(ClaimOutcome::Restricted.into()); @@ -411,7 +413,7 @@ pub async fn claim_relay_invite_with_owner( let issuer: String = invite.try_get("created_by")?; let denial = if revoked_at.is_some() { Some((ClaimOutcome::Revoked, "revoked")) - } else if principal_restricted(&mut tx, community, &issuer).await? { + } else if principal_restricted(tx.conn(), community, &issuer).await? { Some((ClaimOutcome::IssuerRestricted, "issuer_restricted")) } else { None @@ -446,7 +448,7 @@ pub async fn claim_relay_invite_with_owner( .await?; } let owner = - claim_owner::persist_claim_owner(&mut tx, community, claimer_pubkey, verified_owner) + claim_owner::persist_claim_owner(tx.conn(), community, claimer_pubkey, verified_owner) .await?; tx.commit().await?; log_claim_outcome( @@ -512,7 +514,7 @@ pub async fn claim_relay_invite_with_owner( if !inserted { let owner = - claim_owner::persist_claim_owner(&mut tx, community, claimer_pubkey, verified_owner) + claim_owner::persist_claim_owner(tx.conn(), community, claimer_pubkey, verified_owner) .await?; tx.commit().await?; log_claim_outcome( @@ -543,7 +545,7 @@ pub async fn claim_relay_invite_with_owner( // 11. Commit membership, evidence, consumption, and verified ownership together. let owner = - claim_owner::persist_claim_owner(&mut tx, community, claimer_pubkey, verified_owner) + claim_owner::persist_claim_owner(tx.conn(), community, claimer_pubkey, verified_owner) .await?; tx.commit().await?; diff --git a/crates/buzz-db/src/store/relay_invite/claim_owner.rs b/crates/buzz-db/src/store/relay_invite/claim_owner.rs index e7f96a48f9..c1d7683e31 100644 --- a/crates/buzz-db/src/store/relay_invite/claim_owner.rs +++ b/crates/buzz-db/src/store/relay_invite/claim_owner.rs @@ -1,6 +1,6 @@ use crate::error::Result; use crate::{CommunityId, DbError}; -use sqlx::{PgPool, Postgres, Transaction}; +use sqlx::PgPool; /// Result of a committed legacy invitation claim. pub struct MembershipClaimResult { @@ -14,7 +14,7 @@ pub struct MembershipClaimResult { /// Check existing and presented owner identities without creating a user row. pub(super) async fn claimant_restricted( - tx: &mut Transaction<'_, Postgres>, + tx: &mut sqlx::PgConnection, community: CommunityId, claimant: &str, verified_owner: Option<&str>, @@ -36,7 +36,7 @@ pub(super) async fn claimant_restricted( hex::decode(claimant) .map_err(|_| DbError::InvalidData("invalid invitation principal".into()))?, ) - .fetch_optional(&mut **tx) + .fetch_optional(&mut *tx) .await?; let owner_bytes = hex::decode(owner).map_err(|_| DbError::InvalidData("invalid invitation owner".into()))?; @@ -57,7 +57,7 @@ pub(super) struct OwnerPersistence { /// Called only on an admitted claim, while the admission lock is still held. pub(super) async fn persist_claim_owner( - tx: &mut Transaction<'_, Postgres>, + tx: &mut sqlx::PgConnection, community: CommunityId, claimant: &str, verified_owner: Option<&str>, @@ -76,12 +76,12 @@ pub(super) async fn persist_claim_owner( ) .bind(community.as_uuid()) .bind(key) - .execute(&mut **tx) + .execute(&mut *tx) .await? .rows_affected(); } let link_created = sqlx::query("UPDATE users SET agent_owner_pubkey=$3 WHERE community_id=$1 AND pubkey=$2 AND agent_owner_pubkey IS NULL") - .bind(community.as_uuid()).bind(&claimant_bytes).bind(&owner_bytes).execute(&mut **tx).await?.rows_affected() > 0; + .bind(community.as_uuid()).bind(&claimant_bytes).bind(&owner_bytes).execute(&mut *tx).await?.rows_affected() > 0; // Defensive parity with other owner writers; admission already rejects banned owners. if link_created { super::revoke_restricted_agent_invites(tx, community, &claimant_bytes, &owner_bytes) @@ -102,17 +102,14 @@ pub async fn claim_relay_membership_with_owner( policy_version: Option<&str>, verified_owner: Option<&str>, ) -> Result { - let connection = crate::observability::acquire_writer( + let mut tx = crate::begin_community_write_transaction( pool, + community, crate::observability::WriterOperation::Authorization, ) .await?; - let mut tx = sqlx::Transaction::begin(connection, None).await?; - crate::deletion::DeletionStore::new(pool.clone()) - .guard_transaction(&mut tx, community) - .await?; - super::lock_admission(&mut tx, community).await?; - if claimant_restricted(&mut tx, community, pubkey, verified_owner).await? { + super::lock_admission(tx.conn(), community).await?; + if claimant_restricted(tx.conn(), community, pubkey, verified_owner).await? { return Err(DbError::InviteRestricted); } let inserted = sqlx::query( @@ -123,7 +120,7 @@ pub async fn claim_relay_membership_with_owner( .bind(community.as_uuid()) .bind(pubkey) .bind(role) - .execute(&mut *tx) + .execute(tx.conn()) .await? .rows_affected() > 0; @@ -136,11 +133,11 @@ pub async fn claim_relay_membership_with_owner( .bind(community.as_uuid()) .bind(pubkey) .bind(version) - .execute(&mut *tx) + .execute(tx.conn()) .await?; } - let owner = persist_claim_owner(&mut tx, community, pubkey, verified_owner).await?; + let owner = persist_claim_owner(tx.conn(), community, pubkey, verified_owner).await?; tx.commit().await?; Ok(MembershipClaimResult { inserted, diff --git a/crates/buzz-db/src/store/relay_invite/revocation.rs b/crates/buzz-db/src/store/relay_invite/revocation.rs index adf0846db0..c9e17cb836 100644 --- a/crates/buzz-db/src/store/relay_invite/revocation.rs +++ b/crates/buzz-db/src/store/relay_invite/revocation.rs @@ -1,11 +1,10 @@ //! Application-owned revocation, under the admission lock and in the writer's commit. use crate::{error::Result, CommunityId}; -use sqlx::{Postgres, Transaction}; /// Durably revoke an active banned principal's invites and its verified agents' invites. /// The caller holds the admission lock before writing restriction or invite rows. pub(crate) async fn revoke_banned_issuer_invites( - tx: &mut Transaction<'_, Postgres>, + tx: &mut sqlx::PgConnection, community: CommunityId, pubkey: &[u8], ) -> Result<()> { @@ -22,7 +21,7 @@ pub(crate) async fn revoke_banned_issuer_invites( ) .bind(community.as_uuid()) .bind(pubkey) - .execute(&mut **tx) + .execute(&mut *tx) .await?; Ok(()) } @@ -30,7 +29,7 @@ pub(crate) async fn revoke_banned_issuer_invites( /// Durably revoke an agent's invites when its newly verified owner is actively banned. /// Timeouts block admission temporarily but never durably revoke invites. pub(crate) async fn revoke_restricted_agent_invites( - tx: &mut Transaction<'_, Postgres>, + tx: &mut sqlx::PgConnection, community: CommunityId, agent_pubkey: &[u8], owner_pubkey: &[u8], @@ -46,7 +45,7 @@ pub(crate) async fn revoke_restricted_agent_invites( .bind(community.as_uuid()) .bind(agent_pubkey) .bind(owner_pubkey) - .execute(&mut **tx) + .execute(&mut *tx) .await?; Ok(()) } diff --git a/crates/buzz-db/src/store/relay_invite/upgrade_postgres_tests.rs b/crates/buzz-db/src/store/relay_invite/upgrade_postgres_tests.rs index 74ad798865..23ee2f6425 100644 --- a/crates/buzz-db/src/store/relay_invite/upgrade_postgres_tests.rs +++ b/crates/buzz-db/src/store/relay_invite/upgrade_postgres_tests.rs @@ -5,7 +5,7 @@ async fn legacy_fixture() -> (PgPool, Vec) { let pool = PgPool::connect(&crate::test_support::database_url()) .await .unwrap(); - crate::migration::run_migrations_through(&pool, 60) + crate::migration::run_migrations_through(&pool, 64) .await .unwrap(); let mut communities = Vec::new(); diff --git a/migrations/0061_invite_revocation.sql b/migrations/0065_invite_revocation.sql similarity index 100% rename from migrations/0061_invite_revocation.sql rename to migrations/0065_invite_revocation.sql diff --git a/scripts/reconcile-schema-after-pgschema.sql b/scripts/reconcile-schema-after-pgschema.sql index f3f46f37da..575fa1d221 100644 --- a/scripts/reconcile-schema-after-pgschema.sql +++ b/scripts/reconcile-schema-after-pgschema.sql @@ -234,7 +234,7 @@ BEGIN END $$; -- Invitation revocation is historical DML; pgschema does not execute it. --- Use the same tenant-fenced, idempotent repair as migration0061. +-- Use the same tenant-fenced, idempotent repair as migration 0065. SELECT backfill_invite_revocations(); DO $$ BEGIN