Bound Guard in UI Layer
Problem
A UI module renders conditional controls (edit button, delete button) based on the current user's permissions. You want to check multiple permissions for the same user without passing the principal repeatedly to every call.
Solution
Call forUser() once when the user context is established. It returns a BoundWard that snapshots the principal and binds all decision methods to it.
ts
import { createWard } from '@vielzeug/ward';
const ward = createWard([
{ role: 'viewer', resource: 'posts', action: 'read', effect: 'allow' },
{ role: 'editor', resource: 'posts', action: 'update', effect: 'allow' },
]);
const KNOWN_ACTIONS = ['read', 'update', 'delete'] as const;
export function usePostActions(user: { id: string; roles: string[] }) {
const bound = ward.forUser(user);
return {
// Returns the subset of KNOWN_ACTIONS the user is allowed to perform
actions: bound.allowedActions('posts', KNOWN_ACTIONS),
canRead: bound.can('posts', 'read'),
canUpdate: bound.can('posts', 'update'),
canDelete: bound.can('posts', 'delete'),
explainDelete: bound.explain('posts', 'delete'),
};
}Pitfalls
- The bound view snapshots the principal at call time. If you call
forUser(user)and then mutateuser.roles, the bound view will not reflect the change. Create a new bound view when the user's roles change. allowedActions()requiresknownActions. It does not discover action names from the rule set on its own.