· yebor974 · Advanced Techniques, Tutorials, Integration Guides
Manage Laravel Filament Spatie permissions on Multi-Tenant panel
Learn how to manage roles and permissions in a Filament multi-tenant panel using Laravel Spatie Permissions. Simplify tenant-specific access control.

Managing permissions in a multi-tenant Laravel application can be complex. However, with the help of Spatie’s Permissions package and proper middleware configuration, you can simplify this process. In this article, we’ll guide you through setting up and managing permissions for a multi-tenant panel in a Laravel application using Filament and Spatie’s Permissions.
Prerequisites
To follow along, you should have:
- A Laravel application set up.
- Filament installed and configured.
- Basic understanding of multi-tenancy in Laravel.
- Basic understanding of Spatie Permissions
You can follow this article to set up a member multi-tenant panel. You have to change the relation between User
and Entity
with a MorphToMany
using model_has_roles
Spatie table.
Step 1: Install Spatie Permissions
To manage roles and permissions, install Spatie’s Permissions package via Composer:
composer require spatie/laravel-permission
Next, publish the configuration file and migration:
php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider"
php artisan migrate
In the configuration file (config/permission.php), ensure that Spatie is set up to work with team-based permissions by enabling the teams mode. Add or adjust the following setting:
'teams' => true,
This ensures that permissions are scoped to the specific tenant, represented by the Entity model in this case.
Step 2: Define Basic Roles
For this implementation, we’ll define two basic roles: collaborator
and admin
. These roles can be created via a seeder. Here’s an example of a simple seeder:
use Spatie\Permission\Models\Role;
class RoleSeeder extends Seeder
{
public function run()
{
Role::create(['name' => 'collaborator']);
Role::create(['name' => 'admin']);
}
}
Run the seeder to populate your database:
php artisan db:seed --class=RoleSeeder
Step 3: Configure User model
We use the Spatie model_has_roles
table to retrieve the entities associated with a user.
use Filament\Models\Contracts\FilamentUser;
use Filament\Models\Contracts\HasDefaultTenant;
use Filament\Models\Contracts\HasTenants;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Spatie\Permission\Traits\HasRoles;
class User extends Authenticatable implements FilamentUser, HasTenants, HasDefaultTenant
{
use HasRoles;
public function entities(): MorphToMany
{
return $this->morphToMany(
Entity::class,
'model',
config('permission.table_names.model_has_roles'),
config('permission.column_names.model_morph_key'),
app(PermissionRegistrar::class)->teamsKey
)->withPivot(['role_id']);
}
public function canAccessTenant(Model $tenant): bool
{
return $this->entities->contains($tenant);
}
public function getTenants(Panel $panel): array|Collection
{
return $this->entities ?? [];
}
public function getDefaultTenant(Panel $panel): ?Model
{
return $this->entities()->first();
}
}
Step 4: Configure Multi-Tenant Entities and Middleware
Ensure your multi-tenant setup includes an Entity
model and is properly integrated with Filament. For example, you might register tenants using the following configuration on your Member panel provider:
->tenant(Entity::class)
->tenantRegistration(RegisterEntity::class)
->tenantProfile(EditEntity::class)
->tenantMiddleware([
EntitiesPermissionMiddleware::class,
], isPersistent: true)
In this setup:
Entity
represents the tenant model.RegisterEntity
handles tenant registration.EditEntity
manages tenant profile editing.
We’ll use a custom middleware, EntitiesPermissionMiddleware
, to dynamically set the permissions team ID based on the current tenant. Here’s the middleware implementation:
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Filament\FilamentManager;
class EntitiesPermissionMiddleware
{
public function handle(Request $request, Closure $next)
{
if (!empty(auth()->user())) {
setPermissionsTeamId(Filament::getTenant()->getKey());
}
return $next($request);
}
}
These operations will now automatically respect the tenant context, ensuring that permissions are scoped to the correct Entity.
Example with configuring Tenant Menu items
To enhance your multi-tenant panel, you can define tenant-specific menu items. Here’s an example configuration:
->tenantMenuItems([
'register' => MenuItem::make()->label('Register new entity'),
MenuItem::make()
->label('Manage collaborators')
->url(fn (): string => MemberResource::getUrl())
->icon('heroicon-o-users')
->visible(fn (): bool => auth()->user()->can('manageMembers', filament()->getTenant())),
MenuItem::make()
->label('Manage Stripe subscription')
->url(fn (): string => ManageSubscription::getUrl())
->icon('heroicon-o-currency-euro')
->visible(fn (): bool => auth()->user()->can('manageStripeInfos', filament()->getTenant())),
])
This configuration ensures that menu items are displayed based on the user’s permissions for the current tenant. An admin could setup tenant collaborators and tenant stripe billing informations but not collaborators for example.
Example of Entity Policy:
use App\Models\Entity;
use App\Models\User;
class EntityPolicy
{
//....
public function manageMembers(User $user, Entity $entity): bool
{
return $user->hasPermissionTo('manage-members entity', filament()->getAuthGuard());
}
public function manageStripeInfos(User $user, Entity $entity): bool
{
return $user->hasPermissionTo('update-stripe-infos entity', filament()->getAuthGuard());
}
}
By combining Laravel Filament, Spatie Permissions, and a multi-tenant middleware setup, you can manage roles and permissions effectively across tenants. This approach provides scalability and flexibility for your application.
If you found it useful, please like and share it with others who might benefit.