TenantAtlas/app/Support/Middleware/EnsureFilamentTenantSelected.php

177 lines
4.7 KiB
PHP

<?php
namespace App\Support\Middleware;
use App\Models\Tenant;
use App\Models\User;
use App\Models\Workspace;
use App\Services\Auth\CapabilityResolver;
use App\Support\Workspaces\WorkspaceContext;
use Closure;
use Filament\Facades\Filament;
use Filament\Models\Contracts\HasTenants;
use Filament\Navigation\NavigationBuilder;
use Filament\Navigation\NavigationItem;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class EnsureFilamentTenantSelected
{
/**
* @param Closure(Request): Response $next
*/
public function handle(Request $request, Closure $next): Response
{
$panel = Filament::getCurrentOrDefaultPanel();
if ($request->route()?->hasParameter('tenant')) {
$user = $request->user();
if ($user === null) {
return $next($request);
}
if (! $user instanceof HasTenants) {
abort(404);
}
if (! $panel->hasTenancy()) {
return $next($request);
}
$tenantParameter = $request->route()->parameter('tenant');
$tenant = $panel->getTenant($tenantParameter);
if (! $tenant instanceof Tenant) {
abort(404);
}
$workspaceContext = app(WorkspaceContext::class);
$workspaceId = $workspaceContext->currentWorkspaceId($request);
if ($workspaceId === null) {
abort(404);
}
if ((int) $tenant->workspace_id !== (int) $workspaceId) {
abort(404);
}
$workspace = Workspace::query()->whereKey($workspaceId)->first();
if (! $workspace instanceof Workspace) {
abort(404);
}
if (! $user instanceof User || ! $workspaceContext->isMember($user, $workspace)) {
abort(404);
}
if (! $user->canAccessTenant($tenant)) {
abort(404);
}
Filament::setTenant($tenant, true);
$this->configureNavigationForRequest($panel);
return $next($request);
}
if (filled(Filament::getTenant())) {
$this->configureNavigationForRequest($panel);
return $next($request);
}
$user = $request->user();
if (! $user instanceof User) {
$this->configureNavigationForRequest($panel);
return $next($request);
}
$tenant = null;
$workspaceId = app(WorkspaceContext::class)->currentWorkspaceId($request);
if ($workspaceId !== null) {
$tenant = $user->tenants()
->where('workspace_id', $workspaceId)
->where('status', 'active')
->first();
if (! $tenant) {
$tenant = $user->tenants()
->where('workspace_id', $workspaceId)
->first();
}
if (! $tenant) {
$tenant = $user->tenants()
->withTrashed()
->where('workspace_id', $workspaceId)
->first();
}
}
if (! $tenant) {
try {
$tenant = Tenant::current();
} catch (\RuntimeException) {
$tenant = null;
}
if ($tenant instanceof Tenant && ! app(CapabilityResolver::class)->isMember($user, $tenant)) {
$tenant = null;
}
}
if (! $tenant) {
$tenant = $user->tenants()
->where('status', 'active')
->first();
}
if (! $tenant) {
$tenant = $user->tenants()->first();
}
if (! $tenant) {
$tenant = $user->tenants()->withTrashed()->first();
}
if ($tenant) {
Filament::setTenant($tenant, true);
}
$this->configureNavigationForRequest($panel);
return $next($request);
}
private function configureNavigationForRequest(\Filament\Panel $panel): void
{
if (! $panel->hasTenancy()) {
return;
}
if (filled(Filament::getTenant())) {
$panel->navigation(true);
return;
}
$panel->navigation(function (): NavigationBuilder {
return app(NavigationBuilder::class)
->item(
NavigationItem::make('Workspaces')
->url(fn (): string => route('filament.admin.resources.workspaces.index'))
->icon('heroicon-o-squares-2x2')
->group('Settings')
->sort(10),
);
});
}
}