mirror of
https://github.com/block/buzz.git
synced 2026-11-06 17:31:19 +00:00
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:
@@ -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#"
|
||||
|
||||
@@ -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?;
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user