Initial Commit
This commit is contained in:
146
packages/identity/auth/permissions/tests/integration.test.ts
Normal file
146
packages/identity/auth/permissions/tests/integration.test.ts
Normal file
@@ -0,0 +1,146 @@
|
||||
import { describe, expect, it } from "@suaveplan/testing/runner";
|
||||
import { MemoryAdapter } from "../src/adapters/memory/memory.js";
|
||||
import { PermissionCache } from "../src/cache/permission-cache/permission-cache.js";
|
||||
import { type Ability, buildAbility } from "../src/core/ability/ability.js";
|
||||
import { PermissionManager } from "../src/core/permission-manager/permission-manager.js";
|
||||
import { PolicyEngine } from "../src/core/policy-engine/policy-engine.js";
|
||||
import { RoleManager } from "../src/core/role-manager/role-manager.js";
|
||||
import { CircularInheritanceError } from "../src/errors/permission-error.js";
|
||||
import {
|
||||
createAdminRole,
|
||||
createViewerRole,
|
||||
} from "../src/presets/role-presets/role-presets.js";
|
||||
import type { Permission } from "../src/types/permissions.types.js";
|
||||
|
||||
function makePerm(id: string, resource = "posts", action = "read"): Permission {
|
||||
return { id, resource, action };
|
||||
}
|
||||
|
||||
describe("Full permissions flow", () => {
|
||||
it("role inheritance transitivity", { timeout: 5000 }, async () => {
|
||||
const adapter = new MemoryAdapter();
|
||||
const roleManager = new RoleManager(adapter);
|
||||
const _manager = new PermissionManager(adapter);
|
||||
|
||||
const cPerm = makePerm("p-c", "comments", "read");
|
||||
const bPerm = makePerm("p-b", "posts", "write");
|
||||
const aPerm = makePerm("p-a", "posts", "read");
|
||||
|
||||
await adapter.createRole({ id: "c", name: "C", permissions: [cPerm] });
|
||||
await adapter.createRole({
|
||||
id: "b",
|
||||
name: "B",
|
||||
permissions: [bPerm],
|
||||
inherits: ["c"],
|
||||
});
|
||||
await adapter.createRole({
|
||||
id: "a",
|
||||
name: "A",
|
||||
permissions: [aPerm],
|
||||
inherits: ["b"],
|
||||
});
|
||||
|
||||
const perms = await roleManager.getEffectivePermissions("a");
|
||||
const permIds = perms.map((p) => p.id);
|
||||
expect(permIds).toContain("p-a");
|
||||
expect(permIds).toContain("p-b");
|
||||
expect(permIds).toContain("p-c");
|
||||
});
|
||||
|
||||
it("policy engine with cache", { timeout: 5000 }, async () => {
|
||||
const adapter = new MemoryAdapter();
|
||||
await adapter.createRole({
|
||||
id: "r1",
|
||||
name: "R",
|
||||
permissions: [makePerm("p1")],
|
||||
});
|
||||
await adapter.assignRole({
|
||||
id: "ur1",
|
||||
userId: "u1",
|
||||
roleId: "r1",
|
||||
assignedAt: new Date(),
|
||||
});
|
||||
|
||||
const manager = new PermissionManager(adapter);
|
||||
const cache = new PermissionCache({ ttl: 60_000 });
|
||||
const engine = new PolicyEngine(manager, { cache });
|
||||
|
||||
const r1 = await engine.evaluate("u1", "posts", "read");
|
||||
const r2 = await engine.evaluate("u1", "posts", "read");
|
||||
expect(r1.allowed).toBe(true);
|
||||
expect(r2.matchedRule).toBe("cached-allow");
|
||||
|
||||
// Invalidate cache and re-check
|
||||
cache.invalidate("u1");
|
||||
const r3 = await engine.evaluate("u1", "posts", "read");
|
||||
expect(r3.allowed).toBe(true);
|
||||
expect(r3.matchedRule).toBe("posts:read");
|
||||
});
|
||||
|
||||
it("circular inheritance detection", { timeout: 5000 }, async () => {
|
||||
const adapter = new MemoryAdapter();
|
||||
const roleManager = new RoleManager(adapter);
|
||||
|
||||
await adapter.createRole({
|
||||
id: "x",
|
||||
name: "X",
|
||||
permissions: [],
|
||||
inherits: ["y"],
|
||||
});
|
||||
await adapter.createRole({
|
||||
id: "y",
|
||||
name: "Y",
|
||||
permissions: [],
|
||||
inherits: ["x"],
|
||||
});
|
||||
|
||||
await expect(
|
||||
roleManager.getRoleInheritanceChain("x")
|
||||
).rejects.toBeInstanceOf(CircularInheritanceError);
|
||||
});
|
||||
|
||||
it("preset roles work with PermissionManager", {
|
||||
timeout: 5000,
|
||||
}, async () => {
|
||||
const adapter = new MemoryAdapter();
|
||||
await adapter.createRole(createAdminRole());
|
||||
await adapter.assignRole({
|
||||
id: "ur-admin",
|
||||
userId: "admin-user",
|
||||
roleId: "admin",
|
||||
assignedAt: new Date(),
|
||||
});
|
||||
|
||||
await adapter.createRole(createViewerRole());
|
||||
await adapter.assignRole({
|
||||
id: "ur-viewer",
|
||||
userId: "viewer-user",
|
||||
roleId: "viewer",
|
||||
assignedAt: new Date(),
|
||||
});
|
||||
|
||||
const manager = new PermissionManager(adapter);
|
||||
expect(await manager.hasPermission("admin-user", "any", "any")).toBe(
|
||||
true
|
||||
);
|
||||
expect(
|
||||
await manager.hasPermission("viewer-user", "posts", "read")
|
||||
).toBe(true);
|
||||
expect(
|
||||
await manager.hasPermission("viewer-user", "posts", "delete")
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Ability system end-to-end", () => {
|
||||
it("can/cannot consistency", { timeout: 5000 }, () => {
|
||||
const ability: Ability = buildAbility((b) => {
|
||||
b.can("read", "posts");
|
||||
b.cannot("delete", "posts");
|
||||
});
|
||||
expect(ability.can("read", "posts")).toBe(true);
|
||||
expect(ability.cannot("read", "posts")).toBe(false);
|
||||
expect(ability.can("delete", "posts")).toBe(false);
|
||||
expect(ability.cannot("delete", "posts")).toBe(true);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user