import { ANONYMOUS_PRINCIPAL, createAuthenticatedPrincipal, createDevicePrincipal, parsePermissionId, } from "@ekklesia/contracts"; import { describe, expect, it, vi } from "vitest"; import { createAuthorizationEvaluator } from "../src/authorization/evaluator.js"; /** * Device authority, decided by the **same** evaluator that decides everything else. * * The point of these tests is mostly negative: a device must not acquire authority from being a * principal, from being paired, from an `authenticated` baseline, or from platform ownership. The * only thing that ever makes a device allowed is an explicit permission granted to that exact * device (ADR 0036). */ const displayPermission = parsePermissionId("announcements.slideshow.view"); const otherPermission = parsePermissionId("calendar.display.view"); const deviceId = "3f1c8a4e-1d2b-4c3a-9e5f-6a7b8c9d0e1f"; function evaluator(options: { readonly devicePermissions?: readonly string[] } = {}) { const granted = new Set(options.devicePermissions ?? []); const hasActivePermission = vi.fn(async (_deviceId: string, permission: string) => granted.has(permission), ); // Deliberately always true: if the evaluator ever consulted these for a device, every assertion // below would flip, which is exactly what must not happen. const isPlatformOwner = vi.fn(async () => true); const hasActiveGrant = vi.fn(async () => true); return { evaluator: createAuthorizationEvaluator({ devices: { hasActivePermission } as never, grants: { hasActiveGrant } as never, platformOwners: { isPlatformOwner } as never, }), hasActiveGrant, hasActivePermission, isPlatformOwner, }; } describe("device principals go through the ordinary evaluator", () => { it("allows a device holding the exact restricted permission", async () => { const harness = evaluator({ devicePermissions: [displayPermission] }); await expect( harness.evaluator.authorize({ baseline: "restricted", permission: displayPermission, principal: createDevicePrincipal(deviceId), }), ).resolves.toEqual({ allowed: true }); expect(harness.hasActivePermission).toHaveBeenCalledWith(deviceId, displayPermission); }); it("denies a device any permission it was not granted", async () => { const harness = evaluator({ devicePermissions: [displayPermission] }); // Pairing a screen for one display is not authority over another module's. await expect( harness.evaluator.authorize({ baseline: "restricted", permission: otherPermission, principal: createDevicePrincipal(deviceId), }), ).resolves.toEqual({ allowed: false }); }); it("never satisfies an authenticated baseline, however many permissions it holds", async () => { const harness = evaluator({ devicePermissions: [displayPermission] }); // "A person is signed in" is not true of a television. Every route that settles for the // `authenticated` baseline — the module UI inventory among them — stays closed. await expect( harness.evaluator.authorize({ baseline: "authenticated", permission: displayPermission, principal: createDevicePrincipal(deviceId), }), ).resolves.toEqual({ allowed: false }); expect(harness.hasActivePermission).not.toHaveBeenCalled(); }); it("never consults platform ownership or user grants for a device", async () => { const harness = evaluator(); await expect( harness.evaluator.authorize({ baseline: "restricted", permission: displayPermission, principal: createDevicePrincipal(deviceId), }), ).resolves.toEqual({ allowed: false }); // Both ports would have answered "yes". A device is denied anyway, because neither is a path a // device can reach — there is no device that is a platform owner. expect(harness.isPlatformOwner).not.toHaveBeenCalled(); expect(harness.hasActiveGrant).not.toHaveBeenCalled(); }); it("denies every device when the deployment composed no device support at all", async () => { const withoutDevices = createAuthorizationEvaluator({ grants: { hasActiveGrant: async () => true } as never, platformOwners: { isPlatformOwner: async () => true } as never, }); await expect( withoutDevices.authorize({ baseline: "restricted", permission: displayPermission, principal: createDevicePrincipal(deviceId), }), ).resolves.toEqual({ allowed: false }); }); it("denies a scoped check for a device rather than treating it as unscoped", async () => { const harness = evaluator({ devicePermissions: [displayPermission] }); await expect( harness.evaluator.authorize({ baseline: "restricted", permission: displayPermission, principal: createDevicePrincipal(deviceId), scope: "event-1", }), ).resolves.toEqual({ allowed: false }); }); it("still allows a device through a public baseline, like any other principal", async () => { const harness = evaluator(); await expect( harness.evaluator.authorize({ baseline: "public", permission: displayPermission, principal: createDevicePrincipal(deviceId), }), ).resolves.toEqual({ allowed: true }); }); it("leaves human and anonymous decisions exactly as they were", async () => { const harness = evaluator(); await expect( harness.evaluator.authorize({ baseline: "restricted", permission: displayPermission, principal: createAuthenticatedPrincipal("user-1"), }), ).resolves.toEqual({ allowed: true }); await expect( harness.evaluator.authorize({ baseline: "restricted", permission: displayPermission, principal: ANONYMOUS_PRINCIPAL, }), ).resolves.toEqual({ allowed: false }); await expect( harness.evaluator.authorize({ baseline: "authenticated", permission: displayPermission, principal: createAuthenticatedPrincipal("user-1"), }), ).resolves.toEqual({ allowed: true }); }); it("propagates a device lookup failure rather than reporting it as denied", async () => { const failing = createAuthorizationEvaluator({ devices: { hasActivePermission: async () => { throw new Error("database unavailable"); }, } as never, grants: { hasActiveGrant: async () => false } as never, platformOwners: { isPlatformOwner: async () => false } as never, }); // A caller must never be told "no permission" when the true answer is "could not be // determined", exactly as for a human. await expect( failing.authorize({ baseline: "restricted", permission: displayPermission, principal: createDevicePrincipal(deviceId), }), ).rejects.toMatchObject({ code: "AUTHORIZATION_EVALUATION_FAILED" }); }); });