Skip to content

Blog Roles

Problem

Model read, create, update, and delete permissions for a blog without scattering role checks through handlers.

Solution

Define immutable role rules once, then ask Ward for an explained decision at each authorization boundary.

ts
import { ANONYMOUS, createWard, owns } from '@vielzeug/ward';

const ward = createWard<'read' | 'create' | 'update' | 'delete', { authorId: string }>([
  { role: ANONYMOUS, resource: 'posts', action: 'read', effect: 'allow' },
  { role: 'viewer', resource: 'posts', action: 'read', effect: 'allow' },
  { role: 'editor', resource: 'posts', action: 'create', effect: 'allow' },
  { role: 'editor', resource: 'posts', action: 'update', effect: 'allow', when: owns('authorId') },
  { role: 'admin', resource: 'posts', action: 'delete', effect: 'allow' },
]);

ward.explain({ principal: null, resource: 'posts', action: 'read' }).allowed; // true
ward.explain({
  principal: { id: 'u1', roles: ['editor'] },
  resource: 'posts',
  action: 'update',
  data: { authorId: 'u1' },
}).allowed; // true
ward.explain({
  principal: { id: 'u1', roles: ['editor'] },
  resource: 'posts',
  action: 'update',
  data: { authorId: 'u2' },
}).allowed; // false

Pitfalls

  • Pass resource data for ownership predicates; omitted data cannot satisfy owns().
  • Model default-deny explicitly by adding only allowed rules.