feat(swarm-authelia-bridge): report a heal as its own outcome
`EnsureAgentIdentity` answered `AlreadyExists` whether it had added the agent marker to an existing subject or done nothing at all, and the controller discarded the answer outright. So the one case worth telling a human about — a subject that was NOT an agent a moment ago — could not survive the socket, let alone reach a log. `Healed` is a variant rather than a field on `AlreadyExists` because a field is ignorable: adding a variant makes every existing match fail to compile until its author decides what a heal means. That is the property the old shape lacked. The store cannot tell a pre-marker agent identity from a human operator account created without a group — both are `groups: []`. So this is either the intended migration or an agent joining a person's live SSO account, and only the caller has the context to tell them apart. Gated by state/gate-3549-heal.sh (8 arms + mutation): the mutation collapses Healed back and reddens the discriminating arm while leaving the anti-noise arm green. The W' control asserts exactly one warn in the whole run, so a build that warned on every routine ensure would fail.
This commit is contained in:
parent
d58be6834b
commit
a248db1fa2
3 changed files with 60 additions and 12 deletions
|
|
@ -274,14 +274,25 @@ async fn handle(state: &AppState, name: String) -> Result<BridgeResponse> {
|
|||
|
||||
let mut user_store = store::load_store(&cfg.users_file)?;
|
||||
if user_store.users.contains_key(&name) {
|
||||
// The subject is there, so no password is minted — but it may
|
||||
// predate the marker that puts it in the roster, and running agent
|
||||
// creation again is the whole of the migration for those. Reported
|
||||
// as `AlreadyExists` either way: the wire answer is about the
|
||||
// subject existing, not about whether bytes moved.
|
||||
// The subject is there, so no password is minted. Whether it gained
|
||||
// the marker here is the caller's business, not an implementation
|
||||
// detail: an unmarked subject is either a pre-marker agent (the
|
||||
// intended migration) or a human created without a group, and this
|
||||
// process cannot tell them apart. So the two outcomes are reported
|
||||
// as different variants rather than folded into one.
|
||||
if store::mark_as_agent(&mut user_store, &name) {
|
||||
store::publish(&cfg.users_file, &mut user_store)?;
|
||||
tracing::info!(agent = %name, "marked an existing identity as an agent");
|
||||
// `warn`, not `info`: the uneventful path is silent, so anything
|
||||
// logged here is a subject that was *not* an agent a moment ago.
|
||||
// Names the account because "which one" is the entire question
|
||||
// an operator will have.
|
||||
tracing::warn!(
|
||||
subject = %name,
|
||||
"added the agent marker to an EXISTING identity — intended if this \
|
||||
predates the marker, but the store cannot distinguish that from a \
|
||||
human account created without a group"
|
||||
);
|
||||
return Ok(BridgeResponse::Healed);
|
||||
}
|
||||
return Ok(BridgeResponse::AlreadyExists);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue