A cache consumer presents the login it was granted, checked over the catalogue; the two-node bed's baserow keeps its own cache and the bed asserts it answers (issue 081)
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* **A module that takes a shared-cache grant presents the login it was granted** (novox/hq 081).
|
||||
*
|
||||
* The cache provider scopes each consumer to an ACL user of its own, confined to keys under its
|
||||
* login (novox/hq 080). A consumer that hands its software the password and not the login logs in
|
||||
* as the server's default user: the grant is honoured by the provider and ignored by the consumer,
|
||||
* and nothing notices while the default user is open. The grant bed proves the provider's half
|
||||
* against a consumer written to the contract; this holds every catalogue module that asks for the
|
||||
* cache to its half — the login, as `${bound:redis-cache:as}`, somewhere it hands its software.
|
||||
*
|
||||
* What it cannot see is whether the software also keeps its keys under that login: that is the
|
||||
* software's, and a module whose software cannot (fixed key or channel names in its code) does not
|
||||
* take the shared cache at all — baserow runs its own, and n8n in its shipped mode needs none.
|
||||
*/
|
||||
import { test } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
||||
import { resolve } from "node:path";
|
||||
|
||||
import { catalogueIsPresent, catalogueDir } from "./integration/harness.ts";
|
||||
|
||||
const CACHE = "redis-cache";
|
||||
|
||||
/** Whether a manifest hands its software the login it is granted for the cache. */
|
||||
function presentsTheLogin(manifest: string): boolean {
|
||||
return manifest.includes(`\${bound:${CACHE}:as}`);
|
||||
}
|
||||
|
||||
test("the check sees a module that hands its software the password and not the login", () => {
|
||||
const passwordOnly = JSON.stringify({ module: "m", requires: [CACHE], resources: [
|
||||
{ id: "env", type: "file", path: "/x", content: `HOST=\${bound:${CACHE}:at}\nPASSWORD=\${secret:${CACHE}}\n` }] });
|
||||
const withLogin = JSON.stringify({ module: "m", requires: [CACHE], resources: [
|
||||
{ id: "env", type: "file", path: "/x", content: `USER=\${bound:${CACHE}:as}\nPASSWORD=\${secret:${CACHE}}\n` }] });
|
||||
assert.equal(presentsTheLogin(passwordOnly), false);
|
||||
assert.equal(presentsTheLogin(withLogin), true);
|
||||
});
|
||||
|
||||
test("every catalogue module that takes the shared cache presents the login it was granted", (t) => {
|
||||
const absent = catalogueIsPresent();
|
||||
if (absent) {
|
||||
t.skip(`cannot check — ${absent}`);
|
||||
return;
|
||||
}
|
||||
const offences: string[] = [];
|
||||
for (const d of readdirSync(catalogueDir(), { withFileTypes: true })) {
|
||||
const file = resolve(catalogueDir(), d.name, "module.json");
|
||||
if (!d.isDirectory() || !existsSync(file)) continue;
|
||||
const text = readFileSync(file, "utf8");
|
||||
const m = JSON.parse(text) as { requires?: string[] };
|
||||
if (!(m.requires ?? []).includes(CACHE)) continue;
|
||||
if (!presentsTheLogin(text)) offences.push(d.name);
|
||||
}
|
||||
assert.deepEqual(offences, [],
|
||||
`these take the shared cache and never hand their software the login (\${bound:${CACHE}:as}), so they ` +
|
||||
`log in as the server's default user — present the login, or run a cache of their own:\n ${offences.join("\n ")}`);
|
||||
});
|
||||
Reference in New Issue
Block a user