Skip to content

Permissions

Every resource brings its own permissions, named by its slug. They are listed in the role editor (Access → Roles) and checked on every page and every request of the endpoint.

Permission Allows
<slug>.view navigation entry, list and detail pages, selects and counts of the records
<slug>.create create page and saving new records
<slug>.update edit page, saving changes, attachments, actions and relation actions by default
<slug>.delete deleting single and selected records
<slug>.history the change history on the detail page
<slug>.export exports, only with $exportable = true

Resources and pages are denied by default: a role sees nothing until it is given permissions. Own permissions of actions (e.g. orders.approve) are listed in the role editor when they are in Config\Anvil::$permissions.

Method Checks Used for
canViewAny($gate) <slug>.view navigation, list, global search, selects
canView($gate, $record) <slug>.view with policy detail page, list rows, search hits, links
canCreate($gate) <slug>.create create button and saving
canUpdate($gate, $record) <slug>.update with policy, not locked by a workflow edit page, saving, attachments
canDelete($gate, $record) <slug>.delete with policy, not locked by a workflow delete button and deleting
canViewHistory($gate, $record) showsHistory() and <slug>.history with policy history section
canExport($gate) $exportable and <slug>.export export button

Override a check for a special rule; call the parent so the permission stays:

public function canDelete(Gate $gate, ?object $record = null): bool
{
// Paid invoices are never deleted.
return parent::canDelete($gate, $record) && ($record?->paid_at ?? null) === null;
}

A check that fails on a page answers 403 (“no access”, audit event access.denied); a button that would fail is not shown.

Record level rules – “only the owner may edit”, “only open requests” – are policies. They apply to the detail page, saving, deleting, actions, lists, selects, counts and the global search alike: a record a policy hides is not listed anywhere.

Rows a user should never see belong into scopeQuery() (see Loading and saving): they are not found at all (404), are not counted and cannot be selected. Policies decide about records that exist for the user but may not be opened or changed.

With a workflow, records in a locked state cannot be updated or deleted (isLocked()); only transitions change them. Transitions have permissions of their own.