· 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.

Manage Laravel Filament Spatie permissions on Multi-Tenant panel - picture

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.

Avatar of yebor974
yebor974
Freelance - French IT Engineer passionate about Laravel and Filament PHP, creating innovative solutions to simplify web development.
React
Share post

Stay ahead in the Filament Mastery adventure

Join our community of Filament PHP enthusiasts! Get exclusive tips, tutorials, and updates delivered straight to your inbox.