Managing roles
This guide covers how to create roles, assign them to users, and check role membership. You will learn how to:
- Set up the Role model and pivot table
- Create roles and assign permissions to them
- Assign, sync, and revoke roles on users
- Aggregate permissions from a user's roles
Overview
Roles are the recommended way to manage permissions. Instead of assigning individual permissions to each user, you create roles like "editor", "admin", or "billing manager", assign permissions to those roles, and then assign roles to users. When a user's responsibilities change, you update their role assignments rather than editing individual permissions.
The permissions package provides two mixins for this workflow:
withPermissionsgoes on the Role model and adds a JSON column for storing permission strings.withRolesgoes on the User model and adds a many-to-many relationship to the Role model through a pivot table.
Setup
You need a Role model with a permissions column, a pivot table linking users to roles, and both mixins applied to their respective models.
-
Create the migrations
Run the following commands to generate migration files for the roles table and the pivot table.
node ace make:migration rolesnode ace make:migration user_roles -
Define the roles table
Open the generated roles migration and define the table schema. The
permissionscolumn stores a JSON array of permission keys assigned to the role.database/migrations/xxxx_create_roles.tsimport { BaseSchema } from '@adonisjs/lucid/schema' export default class extends BaseSchema { protected tableName = 'roles' async up() { this.schema.createTable(this.tableName, (table) => { table.increments('id') table.string('name').unique().notNullable() table.text('permissions').defaultTo('[]') table.timestamp('created_at') table.timestamp('updated_at') }) } async down() { this.schema.dropTable(this.tableName) } } -
Define the user_roles pivot table
Open the generated user_roles migration and define the pivot table schema. The unique constraint on
user_idandrole_idprevents duplicate role assignments.database/migrations/xxxx_create_user_roles.tsimport { BaseSchema } from '@adonisjs/lucid/schema' export default class extends BaseSchema { protected tableName = 'user_roles' async up() { this.schema.createTable(this.tableName, (table) => { table.increments('id') table.integer('user_id').notNullable().unsigned() table.integer('role_id').notNullable().unsigned() table.unique(['user_id', 'role_id']) }) } async down() { this.schema.dropTable(this.tableName) } } -
Run the migrations
node ace migration:run -
Apply the mixins
Apply
withPermissionsto your Role model. This mixin adds methods for reading and writing thepermissionsJSON column.app/models/role.tsimport { RoleSchema } from '#database/schema' import { compose } from '@adonisjs/core/helpers' import { withPermissions } from '@adonisplus/permissions' export default class Role extends compose( RoleSchema, withPermissions() ) {}Apply
withRolesto your User model. The config requires the Role model and the pivot table name.app/models/user.tsimport { UserSchema } from '#database/schema' import { compose } from '@adonisjs/core/helpers' import { withRoles } from '@adonisplus/permissions' import Role from '#models/role' export default class User extends compose( UserSchema, withRoles({ roleModel: () => Role, pivotTable: 'user_roles', }) ) {}
roleModel
A function that returns the Role model class.
pivotTable
The name of the pivot table linking users to roles.
pivotForeignKey
The foreign key column on the pivot table for the user. Defaults to Lucid's convention (user_id).
pivotRelatedForeignKey
The foreign key column on the pivot table for the role. Defaults to Lucid's convention (role_id).
Creating roles with permissions
Create a role and assign permissions to it. Use permissions.getKey() for type-safe permission keys. Your editor will autocomplete the available keys and catch typos at compile time.
import { permissions } from '#start/permissions'
const editor = await Role.create({ name: 'editor' })
await editor.givePermissions([
permissions.getKey('product.create'),
permissions.getKey('product.update'),
])
const admin = await Role.create({ name: 'admin' })
await admin.syncPermissions(permissions.active())
For a full reference on managing the permissions column (giving, syncing, revoking), see the Managing permissions guide.
Assigning roles to users
You can pass role instances or role IDs to all assignment methods.
await user.assignRole(editorRole)
await user.assignRoles([editorRole, moderatorRole])
Syncing roles
Use syncRoles to replace all of a user's role assignments with a new list. Roles not in the provided list are removed.
await user.syncRoles([adminRole])
Revoking roles
await user.revokeRole(editorRole)
await user.revokeRoles([editorRole, moderatorRole])
Checking role membership
await user.hasRole('editor') // true or false
Getting a user's roles
const roles = await user.getRoles()
If roles are already preloaded on the model, getRoles returns them directly without an additional query.
Aggregating permissions
The getPermissions method on the user collects permissions from all assigned roles into a single deduplicated array. This is the method used by the Access class and Bouncer abilities to determine what a user can do.
const allPermissions = await user.getPermissions()
// Combines permissions from all roles