swarm: tests and docs for the queue-credential revocation
Pins the three properties the revocation rests on and cannot check against a store: that only `Destroyed` revokes (a revocation on `Offline` or `Paused` would give an agent that stops and never restarts), that a 404 is absence while a 403 stays a failure, and that the delete addresses `secret/metadata/` -- the path that takes every version, which is the string the grant has to match. docs/swarm/credentials.md gains the revocation section and its table cell stops describing the deletion as something an operator does by hand.
This commit is contained in:
parent
8caf688ee4
commit
c21ec7719d
4 changed files with 101 additions and 16 deletions
|
|
@ -364,6 +364,39 @@ mod tests {
|
|||
assert!(!a.contains('='), "{a}");
|
||||
}
|
||||
|
||||
/// Revocation undoes step 3 of the mint and nothing else, and both ends
|
||||
/// spell the path through one function — the property that keeps a
|
||||
/// revocation from missing the object it is meant to remove.
|
||||
///
|
||||
/// The second half is what stops this being a tautology: the agent's
|
||||
/// other published object, the mTLS leaf, is at a different path and is
|
||||
/// deliberately left alone. Revoking both would take away the identity a
|
||||
/// re-created agent is re-minted under, for a credential problem that is
|
||||
/// only about the queue.
|
||||
#[test]
|
||||
fn revocation_names_the_path_the_mint_published_and_not_the_leaf_beside_it() {
|
||||
use swarm_secret_client::{mtls, queue};
|
||||
|
||||
assert_eq!(
|
||||
queue::agent_queue_path("atlas").expect("a plain name is legal"),
|
||||
"swarm/agents/atlas/queue"
|
||||
);
|
||||
assert_ne!(
|
||||
queue::agent_queue_path("atlas").expect("legal"),
|
||||
mtls::identity_path("atlas").expect("legal"),
|
||||
);
|
||||
}
|
||||
|
||||
/// A name the store must never be asked to delete under. `revoke_queue_credential`
|
||||
/// builds its path with the same validating function the mint does, so a
|
||||
/// traversal is refused before a request is made rather than addressing
|
||||
/// some other principal's secret.
|
||||
#[test]
|
||||
fn a_traversal_never_becomes_a_revocation() {
|
||||
swarm_secret_client::queue::agent_queue_path("../pr1ma")
|
||||
.expect_err("a traversal is not a legal agent name");
|
||||
}
|
||||
|
||||
fn both(k: &str) -> Option<String> {
|
||||
match k {
|
||||
ENV_AGENT_PKI_MOUNT => Some("pki-agents".to_owned()),
|
||||
|
|
|
|||
|
|
@ -3308,6 +3308,32 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
/// `Destroyed` is the one declaration that ends an agent's life, so it is
|
||||
/// the one that revokes. The credential is a bearer secret: left behind,
|
||||
/// anything that recovers it still authenticates as an agent that no
|
||||
/// longer exists.
|
||||
#[test]
|
||||
fn declaring_an_agent_destroyed_revokes_its_queue_credential() {
|
||||
assert!(crate::revokes_queue_credential(
|
||||
swarm_queue_client::wanted::AgentState::Destroyed
|
||||
));
|
||||
}
|
||||
|
||||
/// The control, and the more expensive half to get wrong: a revocation on
|
||||
/// `Offline` or `Paused` would give an agent that can be stopped and never
|
||||
/// restarted. Every non-terminal state is listed rather than sampled, so
|
||||
/// a state added later shows up here as well as in the match itself.
|
||||
#[test]
|
||||
fn no_state_an_agent_comes_back_from_revokes() {
|
||||
use swarm_queue_client::wanted::AgentState;
|
||||
for state in [AgentState::Up, AgentState::Offline, AgentState::Paused] {
|
||||
assert!(
|
||||
!crate::revokes_queue_credential(state),
|
||||
"{state:?} is a state an agent returns from"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// The whole point of the node: a freshly created agent is declared
|
||||
/// `Paused`, so it does not start driving turns the moment its hive
|
||||
/// brings it up. Any other state here is the bug this fixes.
|
||||
|
|
|
|||
Loading…
Reference in a new issue