139 lines
4.5 KiB
TypeScript
139 lines
4.5 KiB
TypeScript
import { describe, expect, test } from "bun:test";
|
|
import {
|
|
createGovernance,
|
|
createMachineIdentityManager,
|
|
createSamlFederation,
|
|
createScimHandler,
|
|
discoverOidc,
|
|
memoryScimStore,
|
|
oidcAuthorizationUrl,
|
|
syncDirectory,
|
|
} from "../src/index.ts";
|
|
|
|
describe("enterprise identity", () => {
|
|
test("discovers OIDC safely and creates PKCE authorization URLs", async () => {
|
|
const metadata = {
|
|
issuer: "https://id.test",
|
|
authorization_endpoint: "https://id.test/auth",
|
|
token_endpoint: "https://id.test/token",
|
|
jwks_uri: "https://id.test/jwks",
|
|
};
|
|
const discovered = await discoverOidc(metadata.issuer, {
|
|
fetch: (async () => Response.json(metadata)) as unknown as typeof fetch,
|
|
});
|
|
const url = new URL(
|
|
oidcAuthorizationUrl(discovered, {
|
|
clientId: "app",
|
|
redirectUri: "https://app.test/callback",
|
|
state: "state",
|
|
nonce: "nonce",
|
|
codeChallenge: "challenge",
|
|
}),
|
|
);
|
|
expect(url.searchParams.get("code_challenge_method")).toBe("S256");
|
|
});
|
|
|
|
test("validates signed SAML boundaries and prevents replay", async () => {
|
|
const assertion = {
|
|
id: "assertion-1",
|
|
issuer: "https://id.test",
|
|
audience: "app",
|
|
recipient: "https://app.test/saml",
|
|
expiresAt: 200,
|
|
identity: { externalId: "u1", username: "u1", groups: [], active: true },
|
|
};
|
|
const federation = createSamlFederation({
|
|
adapter: { createLoginRequest: () => "request", verifySignedResponse: async () => assertion },
|
|
issuer: assertion.issuer,
|
|
audience: assertion.audience,
|
|
recipient: assertion.recipient,
|
|
now: () => 100,
|
|
});
|
|
expect((await federation.callback("signed")).externalId).toBe("u1");
|
|
await expect(federation.callback("signed")).rejects.toThrow("WRN-SAML-REPLAY");
|
|
});
|
|
|
|
test("syncs LDAP/AD adapters and provisions SCIM users", async () => {
|
|
const values: string[] = [];
|
|
expect(
|
|
(
|
|
await syncDirectory(
|
|
{
|
|
kind: "active-directory",
|
|
search: async () => [
|
|
{ externalId: "u1", username: "user", groups: ["staff"], active: true },
|
|
],
|
|
},
|
|
{
|
|
baseDn: "dc=test",
|
|
upsert: (identity) => {
|
|
values.push(identity.externalId);
|
|
},
|
|
},
|
|
)
|
|
).synchronized,
|
|
).toBe(1);
|
|
const handler = createScimHandler({ store: memoryScimStore(), bearerToken: "a".repeat(32) });
|
|
const created = await handler(
|
|
new Request("https://app.test/scim/v2/Users", {
|
|
method: "POST",
|
|
headers: { authorization: `Bearer ${"a".repeat(32)}` },
|
|
body: JSON.stringify({ userName: "person@example.test", active: true }),
|
|
}),
|
|
);
|
|
expect(created.status).toBe(201);
|
|
expect(
|
|
(
|
|
await handler(
|
|
new Request("https://app.test/scim/v2/Users", {
|
|
headers: { authorization: `Bearer ${"a".repeat(32)}` },
|
|
}),
|
|
)
|
|
).status,
|
|
).toBe(200);
|
|
expect(values).toEqual(["u1"]);
|
|
});
|
|
|
|
test("issues scoped machine identities without exposing hashes", async () => {
|
|
const manager = createMachineIdentityManager(() => 100);
|
|
const issued = await manager.issue({
|
|
ownerId: "org",
|
|
name: "deploy",
|
|
kind: "service-account",
|
|
scopes: ["deploy:write"],
|
|
});
|
|
expect(issued.credential.secretHash).toBe("[REDACTED]");
|
|
expect((await manager.authenticate(issued.secret, "deploy:write"))?.kind).toBe(
|
|
"service-account",
|
|
);
|
|
expect(await manager.authenticate(issued.secret, "admin")).toBeNull();
|
|
});
|
|
|
|
test("tracks consent, approvals, export/deletion, retention and audit", async () => {
|
|
const audit: string[] = [];
|
|
const deleted: string[] = [];
|
|
const governance = createGovernance({
|
|
now: () => 100,
|
|
audit: (event) => {
|
|
audit.push(event.type);
|
|
},
|
|
exportSubject: (id) => ({ id }),
|
|
deleteSubject: (id) => {
|
|
deleted.push(id);
|
|
},
|
|
});
|
|
await governance.consent("u1", "analytics", true, "v2");
|
|
const exportRequest = await governance.request("u1", "export");
|
|
expect((await governance.decide(exportRequest.id, "admin", true)).result).toEqual({ id: "u1" });
|
|
const deleteRequest = await governance.request("u1", "delete");
|
|
await governance.decide(deleteRequest.id, "admin", true);
|
|
expect(
|
|
await governance.enforceRetention([{ subjectId: "u2", createdAt: 0 }], 50, (record) => {
|
|
deleted.push(record.subjectId);
|
|
}),
|
|
).toBe(1);
|
|
expect(deleted).toEqual(["u1", "u2"]);
|
|
expect(audit).toContain("consent.changed");
|
|
});
|
|
});
|