Blog Roles
Problem
You need to model a blog with multiple roles: anonymous visitors can read posts, editors can create and update their own posts, and admins can delete. Each role has different permissions and some require ownership checks.
Solution
Use createWard with per-role rules and owns() to gate the update action on authorship.
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' },
]);
// Anonymous visitor can read
ward.can(null, 'posts', 'read'); // true
// Editor can update their own post but not another's
ward.can({ id: 'u1', roles: ['editor'] }, 'posts', 'update', { authorId: 'u1' }); // true
ward.can({ id: 'u1', roles: ['editor'] }, 'posts', 'update', { authorId: 'u2' }); // false
// Admin can delete, editor cannot
ward.can({ id: 'u2', roles: ['admin'] }, 'posts', 'delete'); // true
ward.can({ id: 'u1', roles: ['editor'] }, 'posts', 'delete'); // falsePitfalls
- Forgetting to pass
datatocan()when the rule has awhenpredicate causes the predicate to receivedata: undefined.owns()returnsfalsein that case, so the action is denied. - Role names are matched exactly.
'Editor'does not match the rule withrole: 'editor'. - If you add an admin allow rule for all resources with
WILDCARD, place it at a higherprioritythan any specific deny rules if you want it to override them.