fix: resolve review #004 findings W1-W4 + close review gate

W1 (call/protocol/abort-cascade-wiring): wire AbortCascade into
CallAdapter handle_stream for EVENT_ABORTED. Cascades with
AbortPolicy::AbortDependents, aborts root, no descendant frames on
wire (ADR-016 Decision 2). Two integration tests added.

W2 (core/endpoint-client-fingerprint): extract TLS client cert
fingerprint in dispatch_quinn (SHA256:<hex> of leaf cert DER via
peer_identity) and dispatch_iroh (ed25519:<hex> of peer NodeId).
Fingerprint format documented in auth.md. Server config change
(with_no_client_auth → request-but-don't-require) deferred to new
follow-up task core/endpoint-request-client-cert.

W3 (vault/mnemonic-debug-redaction): replace Mnemonic derive(Debug)
with manual redacting impl (phrase: "[REDACTED]"). Seed confirmed
no Debug impl. Redaction test added.

W4 (core/auth-apikey-resources): Option B — drop entry.resources from
spec. External identities (token/fingerprint) grant scopes only;
resource-scoped ACLs are composition-internal (ADR-015/022). auth.md
corrected + limitation documented. Two tests confirm empty resources.

review-post-impl-fixes: all 4 verified, workspace green (326 tests,
0 failures, 0 clippy warnings). Review #004 status → resolved.

Graph: 34 tasks, 12 gens.
This commit is contained in:
2026-06-24 11:00:54 +00:00
parent d149932e2a
commit 97216764ea
12 changed files with 492 additions and 32 deletions

View File

@@ -318,4 +318,52 @@ mod tests {
let s = format!("{e}");
assert!(s.starts_with("tls config error:"));
}
#[test]
fn resolve_api_key_returns_empty_resources() {
use sha2::{Digest, Sha256};
let token = "alk_test_secret";
let mut hasher = Sha256::new();
hasher.update(token.as_bytes());
let hash = format!("sha256:{}", hex::encode(hasher.finalize()));
let entry = ApiKeyEntry {
prefix: "alk_tes".to_string(),
hash,
scopes: vec!["admin".to_string()],
description: "test key".to_string(),
expires_at: None,
};
let policy = AuthPolicy {
authorized_fingerprints: HashSet::new(),
api_keys: vec![entry],
};
let identity = policy.resolve_api_key(token);
assert!(identity.is_some(), "api key with matching prefix and hash should resolve");
let identity = identity.unwrap();
assert_eq!(identity.id, "alk_tes");
assert_eq!(identity.scopes, vec!["admin"]);
assert!(
identity.resources.is_empty(),
"token-resolved identities must have empty resources (Option B — scopes only)"
);
}
#[test]
fn resolve_identity_from_fingerprint_returns_empty_resources() {
let policy = AuthPolicy {
authorized_fingerprints: HashSet::from(["SHA256:known".to_string()]),
api_keys: vec![],
};
let identity = policy
.resolve_identity_from_fingerprint("SHA256:known")
.expect("known fingerprint should resolve");
assert_eq!(identity.id, "SHA256:known");
assert!(
identity.resources.is_empty(),
"fingerprint-resolved identities must have empty resources (Option B — scopes only)"
);
}
}