Tenancy: access tenant in `canAccessPanel` function

Hi, The canAccessPanel function on the User model is an ideal place to check if the users tenant has access to that functionality. Unfortunately the Tenant model is not loaded until after the firs invocation of canAccessPanel. User.php
public function canAccessPanel(Panel $panel): bool
{
return match ($panel->getId()) {
'app' => true,
'tickets' => Feature::activate(\App\Features\Tickets::class),
default => true,
};
}
public function canAccessPanel(Panel $panel): bool
{
return match ($panel->getId()) {
'app' => true,
'tickets' => Feature::activate(\App\Features\Tickets::class),
default => true,
};
}
AppServiceProvider.php
Feature::resolveScopeUsing(fn () => Filament::getTenant());
Feature::useMorphMap();
Feature::discover();
Feature::resolveScopeUsing(fn () => Filament::getTenant());
Feature::useMorphMap();
Feature::discover();
App\Features\Tickets.php
class Tickets
{
public function resolve(): mixed
{
return true;
}
}
class Tickets
{
public function resolve(): mixed
{
return true;
}
}
Is it possible to get the tenant loaded in the canAccessPanel method or is there another good way to achieve this?
No description
No description
Solution:
there is a function getCurrentPanel not sure if it will help
Jump to solution
3 Replies
Proculair
Proculair7mo ago
Hi, I've changed my approach somewhat. I have created a custom billing provider.
<?php

namespace App\Filament\AppPanel\Providers;

use App\Http\Middleware\VerifyTenantIsSubscribed;
use Closure;
use Filament\Billing\Providers\Contracts\Provider;
use Illuminate\Http\RedirectResponse;

class ManualBillingProvider implements Provider
{
public function getRouteAction(): Closure
{
return function (): RedirectResponse {
return redirect('https://example.com');
};
}

public function getSubscribedMiddleware(): string
{
return VerifyTenantIsSubscribed::class;
}
}
<?php

namespace App\Filament\AppPanel\Providers;

use App\Http\Middleware\VerifyTenantIsSubscribed;
use Closure;
use Filament\Billing\Providers\Contracts\Provider;
use Illuminate\Http\RedirectResponse;

class ManualBillingProvider implements Provider
{
public function getRouteAction(): Closure
{
return function (): RedirectResponse {
return redirect('https://example.com');
};
}

public function getSubscribedMiddleware(): string
{
return VerifyTenantIsSubscribed::class;
}
}
I've come across a new bug:
<?php

namespace App\Http\Middleware;

use App\Enums\PanelEnum;
use Closure;
use Filament\Facades\Filament;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class VerifyTenantIsSubscribed
{
public function handle(Request $request, Closure $next): Response
{
// BUG: the result of Filament::getPanel() is always app

$panel = PanelEnum::from(Filament::getPanel()->getId());

if (!$panel->tenantCanAccess(Filament::getTenant())) {
abort(402);
}

return $next($request);
}
}
<?php

namespace App\Http\Middleware;

use App\Enums\PanelEnum;
use Closure;
use Filament\Facades\Filament;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class VerifyTenantIsSubscribed
{
public function handle(Request $request, Closure $next): Response
{
// BUG: the result of Filament::getPanel() is always app

$panel = PanelEnum::from(Filament::getPanel()->getId());

if (!$panel->tenantCanAccess(Filament::getTenant())) {
abort(402);
}

return $next($request);
}
}
If I access the appPanelProvider or from a different provider the result of the getPanel call is always app
Solution
Lara Zeus
Lara Zeus7mo ago
there is a function getCurrentPanel not sure if it will help
Proculair
Proculair7mo ago
Awesome, this worked!!