Files
pikasTech-HWLAB/internal/cloud/access-control-preset-users.test.ts
2026-06-26 12:57:27 +08:00

77 lines
2.9 KiB
TypeScript

import assert from "node:assert/strict";
import { createHash } from "node:crypto";
import { test } from "bun:test";
import { createCloudApiServer } from "./server.ts";
test("preset user login keeps YAML nav claims when OpenFGA tuple sync is unavailable", async () => {
const password = "preset-user-password";
const server = createCloudApiServer({
env: {
HWLAB_ACCESS_CONTROL_REQUIRED: "1",
HWLAB_ACCESS_CONTROL_USERS_JSON: JSON.stringify([
{
id: "usr_v03_huiggao",
username: "huiggao@163.com",
displayName: "huiggao@163.com",
email: "huiggao@163.com",
role: "user",
status: "active",
navProfile: "code-mdtodo-only",
passwordHashEnv: "HWLAB_PRESET_USER_HUIGGAO_PASSWORD_HASH"
}
]),
HWLAB_ACCESS_CONTROL_NAV_PROFILES_JSON: JSON.stringify([
{ id: "code-mdtodo-only", allowedIds: ["workbench.code", "project.mdtodo"] }
]),
HWLAB_PRESET_USER_HUIGGAO_PASSWORD_HASH: stablePasswordHash(password)
},
openFgaAuthorizer: throwingOpenFgaAuthorizer()
});
await listen(server);
try {
const { port } = server.address() as { port: number };
const login = await fetch(`http://127.0.0.1:${port}/auth/login`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ username: "huiggao@163.com", password })
});
assert.equal(login.status, 200);
const loginBody = await login.json();
assert.deepEqual(loginBody.access.nav.allowedIds, ["workbench.code", "project.mdtodo"]);
const cookie = login.headers.get("set-cookie")?.split(";")[0] ?? "";
assert.match(cookie, /^hwlab_session=/u);
const session = await fetch(`http://127.0.0.1:${port}/auth/session`, { headers: { cookie } });
assert.equal(session.status, 200);
const sessionBody = await session.json();
assert.equal(sessionBody.actor.username, "huiggao@163.com");
assert.deepEqual(sessionBody.access.nav.allowedIds, ["workbench.code", "project.mdtodo"]);
} finally {
await close(server);
}
});
function stablePasswordHash(password: string) {
const salt = "preset-user-test-salt";
return `sha256:${salt}:${createHash("sha256").update(`${salt}:${password}`).digest("hex")}`;
}
function throwingOpenFgaAuthorizer() {
const error = Object.assign(new Error("openfga unavailable"), { code: "openfga_unavailable" });
return {
async describe() { return { ok: false, code: "openfga_unavailable" }; },
async check() { throw error; },
async writeTuple() { throw error; },
async deleteTuple() { throw error; }
};
}
function listen(server: any) {
return new Promise<void>((resolve) => server.listen(0, "127.0.0.1", resolve));
}
function close(server: any) {
return new Promise<void>((resolve, reject) => server.close((error: Error | undefined) => (error ? reject(error) : resolve())));
}