mirror of
https://github.com/cozystack/cozystack.git
synced 2026-10-23 03:27:50 +09:00
fix(dashboard): re-arm the 401 latch once the session recovers
unauthorizedHandled was set once per client and never cleared, so it
collapsed every later 401 too, not just the concurrent ones it was meant
to. Harmless for the shipped default handler, which navigates away and
takes the client with it, but the onUnauthorized hook exists precisely so
a caller can re-auth in place, and such a handler would fire exactly once
for the client's lifetime and then go silent.
Clear the latch on any 2xx from either fetch site: a success means the
session is alive again, so the next 401 is a new episode. The storm guard
still holds, since concurrent 401s have no success between them.
Assisted-by: LLM
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
(cherry picked from commit b6c33926dc)
This commit is contained in:
1 parent
6b296d7810
commit
a7c149c860
2 files changed
+30
No files matched your search
+25
@@ -52,6 +52,31 @@ describe("K8sClient onUnauthorized (401) handling", () => {
|
||||
expect(onUnauthorized).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it("re-arms the latch after a success so a later 401 handles again", async () => {
|
||||
const onUnauthorized = vi.fn()
|
||||
const responses = [
|
||||
unauthorized(),
|
||||
fakeResponse({ body: JSON.stringify({ kind: "Pod" }) }),
|
||||
unauthorized(),
|
||||
]
|
||||
let call = 0
|
||||
vi.stubGlobal("fetch", vi.fn(async () => responses[call++]))
|
||||
const client = new K8sClient({ onUnauthorized })
|
||||
|
||||
// A 2xx in between means the session recovered, so the next expiry is a
|
||||
// new episode. Without the re-arm a custom in-page handler would fire
|
||||
// once for the client's whole lifetime.
|
||||
await expect(client.get("", "v1", "pods", "p", "ns")).rejects.toThrow(
|
||||
K8sApiError,
|
||||
)
|
||||
await client.get("", "v1", "pods", "p", "ns")
|
||||
await expect(client.get("", "v1", "pods", "p", "ns")).rejects.toThrow(
|
||||
K8sApiError,
|
||||
)
|
||||
|
||||
expect(onUnauthorized).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
|
||||
it("does not invoke onUnauthorized on a non-401 error", async () => {
|
||||
const onUnauthorized = vi.fn()
|
||||
vi.stubGlobal(
|
||||
|
||||
@@ -86,6 +86,10 @@ export class K8sClient {
|
||||
throw new K8sApiError(res.status, body)
|
||||
}
|
||||
|
||||
// A 2xx means the session is alive again, so re-arm the latch: the default
|
||||
// handler navigates away, but a custom one may re-auth in place.
|
||||
this.unauthorizedHandled = false
|
||||
|
||||
if (res.status === 204) return undefined as T
|
||||
// Some endpoints (e.g. KubeVirt action subresources like
|
||||
// virtualmachines/{name}/restart) return 2xx with an empty body;
|
||||
@@ -282,6 +286,7 @@ export class K8sClient {
|
||||
await res.json().catch(() => res.statusText),
|
||||
)
|
||||
}
|
||||
this.unauthorizedHandled = false
|
||||
if (!res.body) throw new Error("No response body for watch")
|
||||
|
||||
const reader = res.body.getReader()
|
||||
|
||||
Reference in new issue
Block a user