mirror of
https://github.com/block/buzz.git
synced 2026-11-07 01:41:06 +00:00
fix(relay): preserve refusal accounting across live admission
Restore audio stored-owner failure accounting and keep live policy revalidation separate from deny-set revocation. Preserve denied-only priority over weak removal claims; cover denied and unavailable root closes. Co-authored-by: Honor <e0bf479eeebe47dde1e35705212a4df70616a7bc834d907ddfb18cf0405f8b1b@seiler-test.communities.public.builderlabstaging.xyz> Signed-off-by: Brad Seiler <seiler@squareup.com>
This commit is contained in:
@@ -1108,6 +1108,12 @@ async fn run_active_audio_connection(
|
||||
let admitted_owner = match admitted_owner {
|
||||
Ok(owner) => owner,
|
||||
Err(denial) => {
|
||||
crate::handlers::auth::record_enforce_refusal(
|
||||
&control,
|
||||
Some(&denial),
|
||||
nip_fi_assertion.as_ref(),
|
||||
);
|
||||
auth_line.finish(AuthResult::from_outcome(denial.outcome));
|
||||
shadow_attempt.refused();
|
||||
exit_authorization_refusal(
|
||||
&mut ws_send,
|
||||
|
||||
@@ -392,7 +392,13 @@ impl CommunityConnectionControl {
|
||||
CommunityDisconnectReason::AuthorizationDenied
|
||||
};
|
||||
let won = self.reason_tx.send_if_modified(|current| match current {
|
||||
None | Some(CommunityDisconnectReason::Removed) => {
|
||||
None => {
|
||||
*current = Some(reason);
|
||||
true
|
||||
}
|
||||
Some(CommunityDisconnectReason::Removed)
|
||||
if class == buzz_auth::DenialClass::AuthorizationDenied =>
|
||||
{
|
||||
*current = Some(reason);
|
||||
true
|
||||
}
|
||||
@@ -444,6 +450,7 @@ impl CommunityConnectionControl {
|
||||
self.manager_disconnect_nip_fi_with_class(
|
||||
frame_tx,
|
||||
buzz_auth::DenialClass::AuthorizationDenied,
|
||||
crate::nip_fi_shadow::EnforceSession::revoke,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -451,12 +458,13 @@ impl CommunityConnectionControl {
|
||||
&self,
|
||||
frame_tx: &mpsc::Sender<WsMessage>,
|
||||
class: buzz_auth::DenialClass,
|
||||
enforce: fn(&crate::nip_fi_shadow::EnforceSession),
|
||||
) {
|
||||
self.publish_nip_fi_denial(
|
||||
crate::nip_fi_session::NipFiWsRoute::Root,
|
||||
class,
|
||||
Some(frame_tx),
|
||||
crate::nip_fi_shadow::EnforceSession::revoke,
|
||||
enforce,
|
||||
);
|
||||
self.cancel.cancel();
|
||||
}
|
||||
@@ -1609,7 +1617,7 @@ impl ConnectionManager {
|
||||
if nip_fi {
|
||||
entry
|
||||
.community_control
|
||||
.manager_disconnect_nip_fi_with_class(&entry.terminal_ctrl_tx, class);
|
||||
.manager_disconnect_nip_fi_with_class(&entry.terminal_ctrl_tx, class, |_| {});
|
||||
} else {
|
||||
entry
|
||||
.community_control
|
||||
@@ -3663,6 +3671,74 @@ pub(crate) mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
/// Live policy failures close root sockets without attributing them to
|
||||
/// the NIP-FI deny set, just as the audio registry does.
|
||||
#[test]
|
||||
fn live_revalidation_does_not_record_deny_set_revocation() {
|
||||
for class in [
|
||||
buzz_auth::DenialClass::AuthorizationDenied,
|
||||
buzz_auth::DenialClass::AuthorizationUnavailable,
|
||||
] {
|
||||
let (records, lines, _, ()) =
|
||||
crate::nip_fi_shadow::tests::enforce_observed(async {}, |()| async move {
|
||||
let manager = ConnectionManager::new();
|
||||
let community = buzz_core::CommunityId::from_uuid(Uuid::new_v4());
|
||||
let conn_id = Uuid::new_v4();
|
||||
let key = nostr::Keys::generate().public_key();
|
||||
let (tx, _rx) = mpsc::channel(8);
|
||||
let (ctrl_tx, _ctrl_rx) = mpsc::channel(8);
|
||||
let (terminal_tx, mut terminal_rx) = mpsc::channel(1);
|
||||
let cancel = CancellationToken::new();
|
||||
let control = CommunityConnectionControl::new(cancel.clone());
|
||||
let reason = control.disconnect_reason();
|
||||
control.attach_nip_fi_enforce(crate::nip_fi_shadow::EnforceSession::new(
|
||||
"ws",
|
||||
crate::nip_fi_shadow::BOUND.into(),
|
||||
Some(key),
|
||||
));
|
||||
control.nip_fi_enforce().expect("attached").admit(key);
|
||||
manager.register(
|
||||
conn_id,
|
||||
tx,
|
||||
ctrl_tx,
|
||||
terminal_tx,
|
||||
None,
|
||||
cancel.clone(),
|
||||
community,
|
||||
Arc::new(AtomicU8::new(0)),
|
||||
Arc::new(Mutex::new(HashMap::new())),
|
||||
3,
|
||||
control,
|
||||
);
|
||||
manager.set_authenticated_identity(
|
||||
conn_id,
|
||||
key.to_bytes().to_vec(),
|
||||
Some(NipFiSession::new("test-issuer", None)),
|
||||
);
|
||||
assert!(manager.disconnect_live_authorization(community, conn_id, class));
|
||||
assert!(cancel.is_cancelled());
|
||||
assert_eq!(
|
||||
*reason.borrow(),
|
||||
Some(
|
||||
if class == buzz_auth::DenialClass::AuthorizationUnavailable {
|
||||
CommunityDisconnectReason::AuthorizationUnavailable
|
||||
} else {
|
||||
CommunityDisconnectReason::AuthorizationDenied
|
||||
}
|
||||
),
|
||||
);
|
||||
assert!(terminal_rx.try_recv().is_ok(), "terminal denial frame");
|
||||
});
|
||||
assert_eq!(records.iter().filter(|r| *r == "ws admit/admit").count(), 1);
|
||||
assert!(
|
||||
records
|
||||
.iter()
|
||||
.all(|r| !r.contains("end/") && !r.contains("deny/")),
|
||||
"{class:?} must not be attributed to the deny set: {lines:?}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nip_fi_disconnect_audio_is_issuer_scoped() {
|
||||
let registry = CommunityConnectionRegistry::new();
|
||||
|
||||
Reference in New Issue
Block a user