Roles
Roles are spatie/laravel-permission roles, using the kit’s own model, App\Models\Role, which extends Spatie’s. A role is a named bundle of permissions, and a user can hold several roles.
System roles
Section titled “System roles”Three roles are built in, defined by App\Enums\RoleName:
enum RoleName: string{ case Superadmin = 'superadmin';
case Admin = 'admin';
case User = 'user';}| Role | Permissions | Notes |
|---|---|---|
superadmin |
Every permission, and more (see below) | Hidden from non-superadmins, can’t be deleted or deactivated. |
admin |
users.view, users.create, users.edit, users.delete, users.status |
Manages users. No role management and no activity log by default. |
user |
None | Assigned to everyone who signs up. |
Permissions are granted in database/seeders/RolePermissionSeeder.php.
System roles are identified by name. Role::isSystemRole() returns true for any name in RoleName. System roles:
- can’t be deleted, by anyone (
RolePolicy::delete), - can’t be edited by non-superadmins (
RolePolicy::update), - are always listed first, in enum order (
Role::systemRolesFirst()scope), - are marked with a “System” badge on the Roles page.
The superadmin role
Section titled “The superadmin role”Superadmins are the owners of the app. The kit gives them extra rules on top of “can do everything”:
| Rule | Enforced by |
|---|---|
| Pass every authorization check | Gate::before in AppServiceProvider (with exceptions) |
| Invisible to non-superadmins in lists, counts, filters and the activity log | User::visibleTo() scope. See Superadmin Visibility. |
| Can’t be deleted, individually or in bulk | UserPolicy::delete, UserController::bulkDestroy |
| Can’t be deactivated | UserPolicy::updateStatus |
| Read-only to non-superadmins | UserPolicy::update, and the edit page disables every input |
Only superadmins can grant superadmin |
StoreUserRequest / UpdateUserRequest validation |
A superadmin can’t remove their own superadmin role |
UserPolicy::update |
| Can’t be combined with other roles | The role picker on the user create and edit pages |
The superadmin role’s permissions can’t be edited |
The role edit page shows it read-only |
Create the first superadmin with php artisan tachi:superadmin. See Artisan Commands.
Custom roles
Section titled “Custom roles”Anyone with roles.create can add roles from the Roles page (/roles) and choose their permissions. Custom roles can be renamed, have their permissions changed (roles.edit), and be deleted (roles.delete) once no users hold them. See Role Management.
Custom roles are data, not code: they live in the database and aren’t in RoleName.
Customizing
Section titled “Customizing”Changing what admins can do
Section titled “Changing what admins can do”Edit the $admin->givePermissionTo([...]) list in RolePermissionSeeder, then re-run it:
$admin = Role::query()->firstOrCreate(['name' => RoleName::Admin->value]);$admin->givePermissionTo([ PermissionEnum::UsersView->value, PermissionEnum::UsersCreate->value, PermissionEnum::UsersEdit->value, PermissionEnum::UsersDelete->value, PermissionEnum::UsersStatus->value, PermissionEnum::ActivityView->value, // now admins can read the activity log too]);php artisan db:seed --class=RolePermissionSeederThe seeder only adds permissions. To take one away from an existing database, remove it on the Roles page (as a superadmin) or with $admin->revokePermissionTo(...).
Adding a system role
Section titled “Adding a system role”When a role must exist in every install and must never be deleted, make it a system role:
-
Add a case to
RoleName, e.g.case Manager = 'manager';. Case order controls list order. -
Create it and grant its permissions in
RolePermissionSeeder:$manager = Role::query()->firstOrCreate(['name' => RoleName::Manager->value]);$manager->givePermissionTo([PermissionEnum::UsersView->value]); -
Regenerate the frontend constants:
php artisan tachi:types. -
Add a badge style for it in
resources/js/utils/role-color.ts.FIXED_ROLE_STYLESmust cover every system role, andtscfails until it does.
For roles that admins should be able to manage themselves, use a custom role instead.