Skip to content
AzuAzu

Permissions

You already decide who can do what in your Bouncer policies. Azu asks the same policies, on every request. A missing method means denied.

Here’s the demo’s product policy:

app/policies/product_policy.ts
import { BasePolicy } from '@adonisjs/bouncer'
import type User from '#models/user'
import type Product from '#models/product'
export default class ProductPolicy extends BasePolicy {
viewList(user: User) {
return user.role !== 'customer'
}
view(_user: User, _product: Product) {
return true
}
create(user: User) {
return user.role === 'admin'
}
update(user: User, _product: Product) {
return user.role === 'admin' || user.role === 'staff'
}
delete(user: User) {
return user.role === 'admin'
}
publish(user: User) {
return user.role === 'admin'
}
restock(user: User, _product: Product) {
return user.role === 'admin'
}
/** The reusable action in app/admin/actions/feature.ts */
feature(user: User, _product: Product) {
return user.role === 'admin'
}
}
Method Controls
viewList The resource in the navigation, and its list.
view Opening a record’s detail page.
create The New button and the create form.
update The Edit button, the edit form, and actions without .ability().
delete The built-in Delete row and bulk actions.
Your own, such as publish Actions and fields that name it with .ability('publish').

view, update, and delete receive the record. A custom method receives it for actions and for fields on edit and detail pages. On create, the field check runs before a record exists.

Azu hides buttons, fields, and actions a user can’t use. But hiding is a convenience. The server always checks the policy, so a crafted request gets the same answer as a click.

That includes each record in a bulk action. Records the user can’t touch are skipped and reported.

.ability('publish') on a field shows it only to users your policy’s publish method allows. Azu checks it with the record on edit. A field the user can’t see is never read from the request, either.

Keep sensitive attributes out of columns and title. A field’s .ability() applies to the field, while Azu sends the resource’s columns separately. Those columns can also appear on a detail page when the user has no visible fields.

On an action, .ability() names the policy method checked for each record. See Actions.

Before any policy runs, a user must pass accessAdmin, the Bouncer ability in app/abilities/main.ts. See Installation.

Screens and navigation links use Bouncer abilities instead of policies. See Screens.

Azu denies, rather than allows, when anything is missing:

  • no Bouncer installed,
  • no accessAdmin ability,
  • a missing policy method,
  • an ability that throws.

The reason goes to your log, never to the user. Run node ace azu:doctor to list the policy methods your resources rely on and don’t have. Troubleshooting has more.