Name the cause when a watch stops on an auth failure
A 401/403 was handled as one thing, so watchpr re-minted on every rejection and reported "token expired" for a permission boundary or an anonymous run that had no token to expire, sending the reader after the wrong problem. Classify a 401/403 as a rejected credential, a permission denial, or a request that carried no token, and re-mint only the first. Reject a re-minted empty token instead of replaying anonymously. Report the classified cause from --once as well as from the watch loop. Document watchpr's exit behaviour per cause.
This commit is contained in:
@@ -175,11 +175,22 @@ wrapped per stage (login / read denied / write denied) via `ErrVaultDenied`.
|
||||
## Gotchas
|
||||
|
||||
- `watchpr` exits 0 with no output changes on `--once` (just prints state).
|
||||
- Gitea tokens expire in ~1h, shorter than a watch: the client re-mints once on a
|
||||
401/403 and replays the request. If the fresh token is rejected too, `watchpr`
|
||||
exits non-zero rather than polling blind.
|
||||
- Gitea tokens expire in ~1h, shorter than a watch: the client re-mints once when
|
||||
the credential it sent was rejected and replays the request. If the fresh token
|
||||
is rejected too, `watchpr` exits non-zero rather than polling blind.
|
||||
- A 401/403 is classified before anything is re-minted, because only one of the
|
||||
three cases is a stale token: `ErrNoCredential` (the request carried no token —
|
||||
anonymous access to something not public), `IsPermissionDenied` (a 403 whose
|
||||
body names no credential, so the identity is simply not allowed) and
|
||||
`IsCredentialRejected` (any 401, or a 403 mentioning a token/scope/sign-in,
|
||||
which is what Gitea returns for a token missing a scope). Only the last
|
||||
re-mints; the others abort immediately, since a fresh token cannot fix them and
|
||||
blaming one hides the real cause. `IsAuthError` stays "any 401/403" — all three
|
||||
end a watch.
|
||||
- `watchpr` polls anonymously when no token can be minted (public repos work
|
||||
fine); only a real 401/403 reaches for Vault.
|
||||
fine); an anonymous run never reaches for Vault, on any status code.
|
||||
- A re-mint that hands back an empty token is an error: replaying with it would
|
||||
drop the Authorization header and silently continue as an anonymous watcher.
|
||||
- The token cache is process-wide (mutex-guarded); `RefreshGiteaToken` replaces
|
||||
it. Tests call the unexported `fetchGiteaToken` to avoid the cache.
|
||||
- `agentvault` never puts a secret in an error string: Vault decode failures and
|
||||
|
||||
Reference in New Issue
Block a user