auth/cell_authorize.ts

Cell view + edit + manage authorization helpers.

Pure functions over (auth, cell, grants). No DB I/O; the caller is responsible for loading the cell row and the cell's grant list out-of- band, then handing both to the predicate.

Unauthenticated callers pass null for auth — public pages can resolve cell.visibility === 'public' cells without a session, and that branch is the only path that admits them. Callers may pass null for grants in that case (and any case where they know they don't need to consider grants); grant_admits short-circuits on null.

Three tiers:

  • can_view_cell — admin / public / owner / any viewer+ grant.
  • can_edit_cell — admin / owner / editor grant. NULL created_by is admin-only (system-origin defense-in-depth).
  • can_manage_cell — admin / owner only. Not delegable, not a grant level. Gates the manage tier: visibility writes and all grant management (cell_grant_create / _list / _revoke).

Grant-admit branches: per-actor_id grants admit the matching actor; (role, scope_id?) grants admit any holder of an active role_grant matching those literals (scope_id IS NULL admits any-scope role_grant). The grant's level (viewer / editor) gates which predicate it satisfies.

view source

Declarations
#

3 declarations

can_edit_cell
#

auth/cell_authorize.ts view source

(auth: RequestContext | null, cell: CellRow, grants: readonly CellGrantRow[] | null): boolean import {can_edit_cell} from '@fuzdev/fuz_app/auth/cell_authorize.js';

Edit authorization for a cell.

Unauthenticated callers can never edit. Admin always allowed.

IMPORTANT: the cell.created_by === null branch is explicit defense-in-depth. NULL created_by means system origin (well-known cells seeded by migration, future daemon/agent cells). Non-admin edits MUST be denied — editor-level grants do NOT bypass this guard, because system cells are policy-controlled at admin level. Do NOT collapse into a single equality check that would silently return false for NULL via JS equality semantics — the explicit branch survives refactors and reads as a load-bearing security property.

auth

request context, or null for unauthenticated callers

type RequestContext | null

cell

the cell row

type CellRow

grants

the cell's grant list, or null to skip the grant branch

type readonly CellGrantRow[] | null

returns

boolean

whether the caller may edit the cell

can_manage_cell
#

auth/cell_authorize.ts view source

(auth: RequestContext | null, cell: CellRow): boolean import {can_manage_cell} from '@fuzdev/fuz_app/auth/cell_authorize.js';

Manage authorization for a cell — admin || owner.

The implicit tier above editor: gates visibility writes and all grant management (cell_grant_create / _list / _revoke). NOT delegable and NOT a grant level — an editor-grant holder is never a manager. Grants are not consulted.

NULL created_by (system origin) has no owner, so manage falls to admin only — the explicit NULL guard lives in is_owner.

auth

request context, or null for unauthenticated callers

type RequestContext | null

cell

the cell row

type CellRow

returns

boolean

whether the caller is in the manage tier for the cell

can_view_cell
#

auth/cell_authorize.ts view source

(auth: RequestContext | null, cell: CellRow, grants: readonly CellGrantRow[] | null): boolean import {can_view_cell} from '@fuzdev/fuz_app/auth/cell_authorize.js';

View authorization for a cell.

  • Admin: always allowed.
  • cell.visibility === 'public': allowed for everyone, including unauthenticated callers (e.g. a public landing cell).
  • Owner (cell.created_by === auth.actor.id): allowed.
  • Any active grant on the cell admits the caller (actor-shaped: match on actor_id; role-shaped: match on (role, scope_id?) against an active role_grant).
  • Otherwise: false.

auth

request context, or null for unauthenticated callers

type RequestContext | null

cell

the cell row

type CellRow

grants

the cell's grant list, or null to skip the grant branch

type readonly CellGrantRow[] | null

returns

boolean

whether the caller may view the cell

Depends on
#

Imported by
#