Organize src/ into concern folders (http, auth, admin, plugin-host, ui); co-locate tests, move plugin-api barrel into plugin-host, sync docs + AGENTS layout

This commit is contained in:
2026-06-24 00:23:55 +02:00
parent 6d316c4888
commit de22f51c12
113 changed files with 282 additions and 265 deletions
+37
View File
@@ -0,0 +1,37 @@
import assert from "node:assert/strict";
import { test } from "node:test";
import { createDenylist } from "./denylist.ts";
test("createDenylist: revokes a subject's pre-revoke tokens, lets a fresh re-login through", () => {
let clock = 1000;
const dl = createDenylist({ now: () => clock, ttlSec: 600 });
// An un-revoked subject is never revoked.
assert.equal(dl.isRevoked("u1", 990), false);
// Revoke at t=1000. A token minted at/before the revoke is rejected; one minted after passes
// (a fresh re-login, whose JWT already reflects the new Keto state).
dl.revoke("u1");
assert.equal(dl.isRevoked("u1", 990), true); // before
assert.equal(dl.isRevoked("u1", 1000), true); // exactly at the revoke instant
assert.equal(dl.isRevoked("u1", 1001), false); // after → fresh token, not revoked
assert.equal(dl.isRevoked("u2", 990), false); // a different subject is unaffected
// A missing iat fails closed (better to force a re-mint than honour a maybe-revoked token).
assert.equal(dl.isRevoked("u1", undefined), true);
});
test("createDenylist: a later revoke advances the cutoff; entries self-evict after the TTL", () => {
let clock = 1000;
const dl = createDenylist({ now: () => clock, ttlSec: 600 });
dl.revoke("u1"); // cutoff = 1000
clock = 1500;
dl.revoke("u1"); // cutoff advances to 1500
assert.equal(dl.isRevoked("u1", 1400), true); // minted before the latest revoke
assert.equal(dl.isRevoked("u1", 1600), false); // minted after
// Past the TTL the entry is gone — any pre-revoke token has long since expired anyway.
clock = 1500 + 601;
assert.equal(dl.isRevoked("u1", 1400), false);
});