Scope My shifts at the upstream, and give Route and NavNode one gate declaration
CI / full-gate (push) Successful in 2m56s
CI / full-gate (push) Successful in 2m56s
This commit is contained in:
+4
-2
@@ -5,10 +5,12 @@ 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;
|
||||
|
||||
// A route or nav node names exactly one of these; discovery refuses two. Omitting all three is the
|
||||
// same as `public`, which is why stating it outright makes an open gate a choice, not an oversight.
|
||||
export interface Gate {
|
||||
permission?: string | undefined; // the Keto Permission the caller must hold
|
||||
permission?: string | undefined; // the Keto Permission the caller must hold, `<resource>:<action>`
|
||||
public?: boolean | undefined; // anyone, signed in or not
|
||||
session?: boolean | undefined; // any signed-in user, no grant needed
|
||||
session?: boolean | undefined; // any signed-in user, no grant to hold; anonymous is sent to /login
|
||||
}
|
||||
|
||||
export function allows(gate: Gate, user: User | null): boolean {
|
||||
|
||||
@@ -39,6 +39,7 @@ export { CSRF_FIELD } from "../auth/csrf.ts";
|
||||
// reference consumer. The Ory client types + their error classes are re-exported so a system
|
||||
// plugin can type against them and `instanceof`-match their errors. See README → System capabilities.
|
||||
export type { SystemCapabilities } from "./system.ts";
|
||||
export type { Gate } from "../auth/gate.ts";
|
||||
export type { Identity, KratosAdmin, RecoveryCode } from "../auth/kratos-admin.ts";
|
||||
export type { ExpandTree, KetoClient, RelationQuery, RelationTuple, SubjectSet } from "../auth/keto-client.ts";
|
||||
export type { HydraAdmin, OAuth2Client } from "../auth/hydra-admin.ts";
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
// A plugin's identity is its folder under plugins/: folder name = `id` (isValidPluginId), mount =
|
||||
// `/<id>`. Neither is in the manifest — the host derives them, so they can't drift or be claimed twice.
|
||||
|
||||
import type { Gate } from "../auth/gate.ts";
|
||||
import type { RequestContext } from "../http/context.ts";
|
||||
import type { NavNode } from "../ui/nav.ts";
|
||||
import { envName, type SettingDecl, type SettingsOf } from "./settings.ts";
|
||||
@@ -24,17 +25,11 @@ export type RouteResult =
|
||||
|
||||
export type RouteHandler = (ctx: RequestContext) => Promise<RouteResult | void> | RouteResult | void;
|
||||
|
||||
export interface Route {
|
||||
// `Gate` carries `permission`/`public`/`session`, checked before the handler runs.
|
||||
export interface Route extends Gate {
|
||||
handler: RouteHandler;
|
||||
method: HttpMethod;
|
||||
path: string; // relative to the plugin's mount path `/<id>`; ":name" segments → ctx.params.name
|
||||
permission?: string; // coarse gate — the Keto Permission the caller must hold; checked before the handler runs
|
||||
// Same as omitting `permission`, but stated outright so public is a deliberate choice rather than
|
||||
// a forgotten gate. Mutually exclusive with `permission` (discovery refuses both).
|
||||
public?: boolean;
|
||||
// Any signed-in user, no grant to hold — for a plugin whose data is the visitor's own. Anonymous
|
||||
// is bounced to /login, never 403. Mutually exclusive with the other two (discovery refuses both).
|
||||
session?: boolean;
|
||||
}
|
||||
|
||||
// A Keto Permission this plugin gates on — declared for docs/seeding. Names are a shared global
|
||||
|
||||
+3
-5
@@ -3,12 +3,13 @@
|
||||
// A node is visible iff `allows` passes its gate; a gated header hides its whole subtree, and a pure
|
||||
// header left with no children is dropped.
|
||||
|
||||
import { allows } from "../auth/gate.ts";
|
||||
import { allows, type Gate } from "../auth/gate.ts";
|
||||
import type { User } from "../http/context.ts";
|
||||
import { ENGLISH } from "../i18n/english.ts";
|
||||
import type { Translate } from "../i18n/translate.ts";
|
||||
|
||||
export interface NavNode {
|
||||
// `Gate` carries `permission`/`public`/`session` — consumed by the filter, never rendered.
|
||||
export interface NavNode extends Gate {
|
||||
id?: string; // stable key for override targeting; stripped from the rendered tree
|
||||
children?: NavNode[];
|
||||
count?: number;
|
||||
@@ -17,9 +18,6 @@ export interface NavNode {
|
||||
icon?: string;
|
||||
label: string;
|
||||
open?: boolean;
|
||||
permission?: string; // required permission token; consumed by the filter, never rendered
|
||||
public?: boolean; // show to everyone, signed in or not — the blessed alias for "no permission", stated outright; consumed by the filter, never rendered. Mutually exclusive with permission (discovery refuses both).
|
||||
session?: boolean; // show to any signed-in user, no grant to hold; consumed by the filter, never rendered. Mutually exclusive with the other two (discovery refuses both).
|
||||
}
|
||||
|
||||
// Central override (config/menu.ts). Targets nodes by `id`; applied rename → group →
|
||||
|
||||
Reference in New Issue
Block a user