Role-Based Access Control (RBAC) for Filament Admin
This document explains how to implement and use role-based access control in the EasyOTC Filament admin panel.
Where this lives
- Role enum:
app/Enums/RoleEnum.php(canonical source for role values) - Setup commands:
php artisan roles:setupandphp artisan roles:permissions - Trait:
app/Traits/HasRoleAccess.php - Middleware:
App\Http\Middleware\RoleBasedAccess(aliasrole.access)
Where to access the admin panel
| Environment | URL | Credentials |
|---|---|---|
| Local | http://localhost:8000/admin | Seeded otc_one_admin user (see seeders) |
| Staging | https://stage-api.easyotc.com/admin | See /access |
Only users with the otc_one_admin, carrier_admin, or agent role can log in to /admin. member users authenticate against the storefront.
Overview
The system supports four roles (defined in app/Enums/RoleEnum.php):
- OTC One Admin (
otc_one_admin): Full access to all resources and actions - Carrier Admin (
carrier_admin): Carrier-scoped admin; read-only access to products and orders for their carrier - Agent (
agent): Most limited admin access, read-only for most resources - Member (
member): Storefront customer; no admin panel access
Setup
1. Run the Setup Commands
# Setup roles
php artisan roles:setup
# Setup permissions
php artisan roles:permissions2. Assign Roles to Users
Users can be assigned roles through the admin panel or programmatically:
$user = User::find(1);
$user->assignRole('carrier_admin');Navigation
The navigation is automatically filtered based on user roles:
- Super Admin: Sees all navigation items
- Carrier Admin: Sees only Products and Orders
- Agent: Sees only Products and Orders
Resource Access Control
Using the BaseResource Class
Extend BaseResource for automatic role checking:
use App\Filament\Resources\BaseResource;
class ProductResource extends BaseResource
{
// Override methods for custom access logic
public static function canAccess(): bool
{
$user = auth()->user();
if (!$user) {
return false;
}
// Super admin can access everything
if ($user->hasRole(RoleEnum::OTC_ONE_ADMIN->value)) {
return true;
}
// Carrier admin and agents can access products
return $user->hasAnyRole([
RoleEnum::CARRIER_ADMIN->value,
RoleEnum::AGENT->value
]);
}
}Manual Implementation
For existing resources, add these methods:
use App\Traits\HasRoleAccess;
use App\Enums\RoleEnum;
class OrderResource extends Resource
{
use HasRoleAccess;
public static function canAccess(): bool
{
// Your custom logic here
}
public static function canCreate(): bool
{
// Your custom logic here
}
public static function canEdit(Model $record): bool
{
// Your custom logic here
}
public static function canDelete(Model $record): bool
{
// Your custom logic here
}
public static function canView(Model $record): bool
{
// Your custom logic here
}
}Permission Matrix
| Resource | Action | Super Admin | Carrier Admin | Agent |
|---|---|---|---|---|
| Products | View | ✅ (All) | ✅ (Own Carrier Only) | ✅ |
| Products | Create | ✅ | ❌ (Read-only) | ❌ |
| Products | Edit | ✅ | ❌ (Read-only) | ❌ |
| Products | Delete | ✅ | ❌ | ❌ |
| Orders | View | ✅ (All) | ✅ (Own Carrier Only) | ✅ |
| Orders | Create | ✅ | ❌ (Read-only) | ❌ |
| Orders | Edit | ✅ | ❌ (Read-only) | ✅ |
| Orders | Delete | ✅ | ❌ | ❌ |
| Members | View | ✅ | ✅ | ✅ |
| Members | Create | ✅ | ❌ | ❌ |
| Members | Edit | ✅ | ❌ | ❌ |
| Members | Delete | ✅ | ❌ | ❌ |
| Carriers | View | ✅ | ✅ | ❌ |
| Carriers | Create | ✅ | ❌ | ❌ |
| Carriers | Edit | ✅ | ✅ | ❌ |
| Carriers | Delete | ✅ | ❌ | ❌ |
Carrier-Specific Filtering
Carrier Admin users have additional restrictions:
- Can only view Products and Orders that belong to their specific
carrier_id - Read-only access - cannot create, edit, or delete records
- Navigation shows only Products and Orders sections
Helper Methods
The HasRoleAccess trait provides several helper methods:
// Check if user has a specific role
$this->hasRole('carrier_admin');
$this->hasRole(RoleEnum::CARRIER_ADMIN);
// Check if user has any of the specified roles
$this->hasAnyRole(['carrier_admin', 'agent']);
// Check if user is a super admin
$this->isSuperAdmin();
// Check if user is a carrier admin
$this->isCarrierAdmin();
// Check if user is an agent
$this->isAgent();
// Check if user can access a specific resource
$this->canAccessResource('products');
// Check if user can perform a specific action on a resource
$this->canPerformAction('products', 'edit');Middleware
The RoleBasedAccess middleware is automatically applied to the admin panel. You can also use it on specific routes:
Route::middleware(['role.access:carrier_admin'])->group(function () {
// Routes only accessible by carrier admins
});Customizing Access Control
Adding New Roles
- Add the role to
RoleEnum - Update the
SetupRolesCommand - Update the
RoleBasedNavigationclass - Update the
HasRoleAccesstrait
Modifying Permissions
- Update the
SetupRolePermissionsCommand - Modify the permission matrix in the relevant resource classes
- Update the navigation items in
RoleBasedNavigation
Testing
To test role-based access:
- Create test users with different roles
- Log in as each user
- Verify that navigation items and actions are properly restricted
- Test that unauthorized access returns 403 errors
Troubleshooting
Common Issues
- Navigation not updating: Clear the application cache
- Permissions not working: Run
php artisan roles:permissionsagain - Users can't access admin: Ensure they have the correct role assigned
Debugging
Use the helper methods to debug access issues:
// Check user's roles
dd(auth()->user()->getRoleNames());
// Check if user can access a resource
dd($this->canAccessResource('products'));
// Check if user can perform an action
dd($this->canPerformAction('products', 'edit'));