Managing permissions
This guide covers assigning permissions directly to models. You will learn how to:
- Apply the withPermissions mixin to a User model
- Give, sync, and revoke direct permissions
- Combine direct permissions with role-based permissions
Overview
The recommended approach is to manage permissions through
roles. However, some applications need to assign extra permissions to specific users outside of their role. For example, a user might have the "editor" role but also need the billing.refund permission that editors do not normally have.
The withPermissions mixin can be applied to any Lucid model, including your User model, to store permissions directly as a JSON column.
Setup
The model needs a permissions column and the mixin applied using compose.
-
Create the migration
If your
userstable does not already have apermissionscolumn, generate a migration to add one.node ace make:migration add_permissions_to_users -
Define the migration
Open the generated migration and add the
permissionscolumn. This column stores a JSON array of permission keys assigned directly to the user.database/migrations/xxxx_add_permissions_to_users.tsimport { BaseSchema } from '@adonisjs/lucid/schema' export default class extends BaseSchema { protected tableName = 'users' async up() { this.schema.alterTable(this.tableName, (table) => { table.text('permissions').defaultTo('[]') }) } async down() { this.schema.alterTable(this.tableName, (table) => { table.dropColumn('permissions') }) } } -
Run the migration
node ace migration:run -
Apply the mixin
Add
withPermissionsto your User model alongsidewithRolesif you are using both.app/models/user.tsimport { UserSchema } from '#database/schema' import { compose } from '@adonisjs/core/helpers' import { withRoles, withPermissions } from '@adonisplus/permissions' import Role from '#models/role' export default class User extends compose( UserSchema, withPermissions(), withRoles({ roleModel: () => Role, pivotTable: 'user_roles', }) ) {}
Giving permissions
Use permissions.getKey() for type-safe permission keys. Duplicates are ignored automatically. Both methods persist the change immediately.
import { permissions } from '#start/permissions'
await user.givePermission(permissions.getKey('billing.refund'))
await user.givePermissions([
permissions.getKey('billing.refund'),
permissions.getKey('product.delete'),
])
Syncing permissions
Use syncPermissions to replace all direct permissions with a new list. Any permissions not in the provided list are removed.
await user.syncPermissions([permissions.getKey('billing.refund')])
Revoking permissions
await user.revokePermission(permissions.getKey('billing.refund'))
await user.revokePermissions([
permissions.getKey('billing.refund'),
permissions.getKey('product.delete'),
])
Combining with roles
When the User model has both withPermissions and withRoles, calling getPermissions() aggregates permissions from all assigned roles and direct assignments into a single deduplicated array.
// User has "editor" role (with product.create, product.update)
// User also has direct permission: billing.refund
const allPermissions = await user.getPermissions()
// ['product.create', 'product.update', 'billing.refund']