fix(db): reconcile invitation admission with main

Adapt invitation helpers to the community-admitted transaction API and move the unreleased revocation migration after the current schema changes. Exercise upgrades from the latest pre-revocation schema.

Signed-off-by: Local Builder <1c476cb1127ea270cb900bdc3891060140bdd1173f91498030ee9b4db01c2f20@buzz.block.builderlab.xyz>
This commit is contained in:
Local Builder
2026-10-15 18:09:56 -04:00
committed by seiler
parent 2ab99e9b7e
commit a3b7b181a8
7 changed files with 31 additions and 31 deletions

View File

@@ -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#"

View File

@@ -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?;

View File

@@ -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<MembershipClaimResult> {
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,

View File

@@ -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(())
}

View File

@@ -5,7 +5,7 @@ async fn legacy_fixture() -> (PgPool, Vec<Uuid>) {
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();

View File

@@ -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