Gate a route or nav node on a session, not only a permission
CI / full-gate (push) Successful in 3m6s

This commit is contained in:
2026-09-02 07:36:08 +02:00
parent 4ad8653a06
commit 8da75b4ca7
21 changed files with 250 additions and 75 deletions
+24
View File
@@ -0,0 +1,24 @@
// The coarse gate a route or nav node declares. One home for the rule, so the router and the menu
// can never disagree about what a visitor may reach.
import type { User } from "../http/context.ts";
// Widest first: whoever passes an earlier gate passes it without holding anything.
const GATES = ["public", "session", "permission"] as const;
export interface Gate {
permission?: string | undefined; // the Keto Permission the caller must hold
public?: boolean | undefined; // anyone, signed in or not
session?: boolean | undefined; // any signed-in user, no grant needed
}
export function allows(gate: Gate, user: User | null): boolean {
if (gate.public === true) return true;
if (gate.session === true) return user !== null;
return gate.permission == null || (user?.permissions.includes(gate.permission) ?? false);
}
// Which gates a declaration sets — discovery refuses more than one, since they contradict.
export function gatesSet(gate: Gate | null | undefined): string[] {
if (gate == null) return [];
return GATES.filter((name) => (name === "permission" ? gate.permission != null : gate[name] === true));
}