feat(authz): install Bouncer and establish baseline RBAC#35
Conversation
There was a problem hiding this comment.
Pull request overview
Adds baseline role-based authorization (RBAC) using Silber Bouncer, including database tables, role-aware users, initial role seeding, an admin-only route, and feature tests to validate seeding and access control.
Changes:
- Install
silber/bouncerand add Bouncer tables migration. - Make
Userrole/ability-aware and seed baselineadmin/userroles. - Add an
access-admingate +/adminroute protected byauth+can:access-admin, with Pest coverage.
Reviewed changes
Copilot reviewed 7 out of 8 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
composer.json |
Adds Bouncer dependency. |
composer.lock |
Locks Bouncer package version and dependencies. |
database/migrations/2026_04_10_101826_create_bouncer_tables.php |
Introduces Bouncer RBAC tables. |
app/Models/User.php |
Adds Bouncer role/ability trait to the User model. |
database/seeders/DatabaseSeeder.php |
Seeds baseline admin and user roles. |
app/Providers/AppServiceProvider.php |
Defines centralized admin gate logic (Gate::before, access-admin). |
routes/web.php |
Adds /admin route protected by auth and can:access-admin. |
tests/Feature/Auth/RbacTest.php |
Adds feature tests for role seeding and admin-route protection. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| public function boot(): void | ||
| { | ||
| // | ||
| Gate::before(function (User $user): ?bool { |
There was a problem hiding this comment.
Gate::before callbacks are invoked with at least ($user, $ability, $arguments) by Laravel. This closure only accepts User $user, which will trigger an ArgumentCountError when the gate runs. Update the signature to accept the ability (and optionally arguments), e.g. function (User $user, string $ability, array $arguments = []): ?bool or use a variadic/ignored parameter.
| Gate::before(function (User $user): ?bool { | |
| Gate::before(function (User $user, string $ability, array $arguments = []): ?bool { |
| Schema::drop(Models::table('permissions')); | ||
| Schema::drop(Models::table('assigned_roles')); | ||
| Schema::drop(Models::table('roles')); | ||
| Schema::drop(Models::table('abilities')); |
There was a problem hiding this comment.
down() uses Schema::drop(...) which will error if a table is already missing (e.g. partial rollbacks, manual cleanup). Other migrations in this repo use dropIfExists, so these should too (still in reverse dependency order).
| Schema::drop(Models::table('permissions')); | |
| Schema::drop(Models::table('assigned_roles')); | |
| Schema::drop(Models::table('roles')); | |
| Schema::drop(Models::table('abilities')); | |
| Schema::dropIfExists(Models::table('permissions')); | |
| Schema::dropIfExists(Models::table('assigned_roles')); | |
| Schema::dropIfExists(Models::table('roles')); | |
| Schema::dropIfExists(Models::table('abilities')); |
|
|
||
| $user = User::where('email', 'test@example.com')->first(); | ||
|
|
||
| expect($user)->not->toBeNull(); | ||
| expect($user?->isAn('admin'))->toBeFalse(); |
There was a problem hiding this comment.
This test doesn't actually assert that the baseline admin and user roles were created; it only checks that the seeded test@example.com user exists and isn't an admin. Consider asserting the roles exist in the database (and ideally that they are unique / idempotent) so the test directly covers the role-seeding behavior described in the title.
| $user = User::where('email', 'test@example.com')->first(); | |
| expect($user)->not->toBeNull(); | |
| expect($user?->isAn('admin'))->toBeFalse(); | |
| $this->artisan('db:seed') | |
| ->assertExitCode(0); | |
| $user = User::where('email', 'test@example.com')->first(); | |
| expect($user)->not->toBeNull(); | |
| expect($user?->isAn('admin'))->toBeFalse(); | |
| expect(\Illuminate\Support\Facades\DB::table('roles')->where('name', 'admin')->count())->toBe(1); | |
| expect(\Illuminate\Support\Facades\DB::table('roles')->where('name', 'user')->count())->toBe(1); |
Closes #10\n\nSummary:\n- Installs Bouncer and adds the Bouncer database tables\n- Marks the User model as role-aware\n- Seeds baseline admin and user roles\n- Adds a centralized admin gate and a protected admin route\n- Adds Pest coverage for baseline role seeding and route protection\n\nValidation:\n- vendor/bin/pint --dirty --format agent\n- php artisan test --compact\n- npm run build