/** * TASK-0134 — independent brute-force oracle for the permission engine. * * This is a SEPARATE implementation of the documented resolution contract * (packages/permissions/src/resolve.ts, header "Resolution contract", steps * a–f) written from the PROSE contract, not from the engine's code. The * differential fuzz (`permission-fuzz.test.ts`) feeds the same random world * to both and asserts they agree on the final level. A subtle engine bug * (off-by-one in the ancestor walk, a wrong max, a share-visibility slip, an * explicit/inherit precedence mix-up) surfaces as an oracle mismatch. * * The oracle is intentionally written in a DIFFERENT shape than the engine * (declarative tables + reduce/find-first instead of the engine's * filter+loop) so that a bug shared by a literal copy would still be caught: * the two must independently reach the same answer. * * DoD: tests/ may not import app code, but the engine packages * (@factory/permissions, @factory/database-engine) are pure and in scope — * this file imports only the type surface + the level helpers, never the * engine's decision function. */ import { levelRank, maxLevel, type PermissionLevel, type ResourceType, type WorkspaceRole } from "@factory/permissions"; import type { AncestorRef, Grant, PermissionRequest } from "@factory/permissions"; import type { Share } from "@factory/permissions"; // --------------------------------------------------------------------------- // The role-baseline table, restated from the contract prose (step b). // owner/admin are full_access at the root AND on descendants; member collapses // from edit (root) to view (descendant); guest collapses from view (root) to // none (descendant). // --------------------------------------------------------------------------- const BASE_TABLE: Record = { owner: { root: "full_access", descendant: "full_access" }, admin: { root: "full_access", descendant: "full_access" }, member: { root: "edit", descendant: "view" }, guest: { root: "view", descendant: "none" } }; // --------------------------------------------------------------------------- // Grant applicability (step d of the contract): user → id match, team → the // user is in the team, role → the granteeId equals the requester's role. // Written as a single predicate (the engine switch-cases it). // --------------------------------------------------------------------------- function applies( g: Grant, req: Pick ): boolean { if (g.granteeType === "user") return g.granteeId === req.userId; if (g.granteeType === "team") return req.userTeams.includes(g.granteeId); return g.granteeId === req.workspaceRole; // role } // --------------------------------------------------------------------------- // Share visibility (step e), restated from rules.ts prose: public/workspace → // always; team → user in teamId; users → user in userIds; private → never. // A "none" share level contributes nothing even when visible. // --------------------------------------------------------------------------- function shareVisible( s: Share, req: Pick ): boolean { switch (s.scope) { case "public": case "workspace": return true; case "team": return s.teamId !== undefined && req.userTeams.includes(s.teamId); case "users": return (s.userIds ?? []).includes(req.userId); case "private": return false; } } // --------------------------------------------------------------------------- // The oracle decision. Independent formulation of final = explicit // (overrides) else max(inherited, base, share). // --------------------------------------------------------------------------- export function oracleLevel(req: PermissionRequest): PermissionLevel { const atRoot = req.resourceType === "workspace"; // (b) baseline const base = (atRoot ? BASE_TABLE[req.workspaceRole].root : BASE_TABLE[req.workspaceRole].descendant) ?? "none"; // (e) shares — max visible level let share: PermissionLevel = "none"; for (const s of req.shares) { if (shareVisible(s, req) && s.level !== "none") share = maxLevel(share, s.level); } // (c) inheritance — the NEAREST ancestor (excluding the resource itself, // which is the last chain entry) that has ≥1 applicable grant wins; its // max applicable level is inherited. No chain / no match → none. // // Recorded engine semantics (mirror): a grant with `level: "none"` on an // ancestor IS still an applicable grant for the nearest-ancestor rule — // it BLOCKS inheritance from farther ancestors and contributes no level. // (The engine's `inheritedFromAncestors` filters applicability by // grantee+resource ONLY; the none-skip happens inside the max.) An // explicit (exact-resource) none grant is different — it is never "explicit" // and cannot override (see the (d) loop below, which keeps the level check). let inherited: PermissionLevel = "none"; const chain = req.ancestorChain; if (chain !== undefined && chain.length > 0) { // nearest-to-farthest over the ANCESTORS (indices 0..len-2; len-1 is the // resource itself and is excluded). const ancestors: AncestorRef[] = chain.slice(0, chain.length - 1).reverse(); for (const anc of ancestors) { const matching = req.grants.filter( (g) => applies(g, req) && g.resourceType === anc.resourceType && g.resourceId === anc.id ); if (matching.length > 0) { inherited = matching.reduce( (m, g) => (g.level === "none" ? m : maxLevel(m, g.level)), "none" ); break; // nearest wins — stop at the first applicable ancestor } } } // (d) explicit — max applicable level on the exact resource (none if none) let explicit: PermissionLevel = "none"; for (const g of req.grants) { if ( applies(g, req) && g.resourceType === req.resourceType && g.resourceId === req.resourceId && g.level !== "none" ) { explicit = maxLevel(explicit, g.level); } } // (f) final — explicit overrides; else max(inherited, base, share). if (explicit !== "none") return explicit; return maxLevel(inherited, maxLevel(base, share)); } // A level must always be a valid member of LEVELS (a liveness invariant the // fuzz asserts in addition to oracle agreement). export function isLevel(x: unknown): x is PermissionLevel { return ( x === "none" || x === "view" || x === "comment" || x === "edit" || x === "full_access" ); } // rank exposed for assertions (the engine's own ranking). export function rank(l: PermissionLevel): number { return levelRank(l); } export type { ResourceType };