Some checks failed
PR Fast Feedback / fast-feedback (pull_request) Failing after 1m12s
Added BaselineSubjectResolution page and supporting logic to visualize missing identities, ambiguous matches, and skipped coverages per Spec 384.
323 lines
15 KiB
PHP
323 lines
15 KiB
PHP
<?php
|
|
|
|
namespace App\Providers\Filament;
|
|
|
|
use App\Filament\Clusters\Inventory\InventoryCluster;
|
|
use App\Filament\Pages\Auth\Login;
|
|
use App\Filament\Pages\BaselineCompareLanding;
|
|
use App\Filament\Pages\BaselineSubjectResolution;
|
|
use App\Filament\Pages\ChooseEnvironment;
|
|
use App\Filament\Pages\ChooseWorkspace;
|
|
use App\Filament\Pages\CrossEnvironmentComparePage;
|
|
use App\Filament\Pages\EnvironmentDashboard;
|
|
use App\Filament\Pages\EnvironmentRequiredPermissions;
|
|
use App\Filament\Pages\Findings\FindingsHygieneReport;
|
|
use App\Filament\Pages\Findings\FindingsIntakeQueue;
|
|
use App\Filament\Pages\Findings\MyFindingsInbox;
|
|
use App\Filament\Pages\Governance\DecisionRegister;
|
|
use App\Filament\Pages\Governance\GovernanceInbox;
|
|
use App\Filament\Pages\InventoryCoverage;
|
|
use App\Filament\Pages\Monitoring\FindingExceptionsQueue;
|
|
use App\Filament\Pages\NoAccess;
|
|
use App\Filament\Pages\Reviews\CustomerReviewWorkspace;
|
|
use App\Filament\Pages\Reviews\ReviewRegister;
|
|
use App\Filament\Pages\Settings\WorkspaceSettings;
|
|
use App\Filament\Resources\AlertDeliveryResource;
|
|
use App\Filament\Resources\AlertDestinationResource;
|
|
use App\Filament\Resources\AlertRuleResource;
|
|
use App\Filament\Resources\BaselineProfileResource;
|
|
use App\Filament\Resources\BaselineSnapshotResource;
|
|
use App\Filament\Resources\EntraGroupResource;
|
|
use App\Filament\Resources\EnvironmentReviewResource;
|
|
use App\Filament\Resources\InventoryItemResource;
|
|
use App\Filament\Resources\PolicyResource;
|
|
use App\Filament\Resources\ProviderConnectionResource;
|
|
use App\Filament\Resources\Workspaces\WorkspaceResource;
|
|
use App\Models\ManagedEnvironment;
|
|
use App\Models\User;
|
|
use App\Models\Workspace;
|
|
use App\Models\WorkspaceMembership;
|
|
use App\Services\Auth\WorkspaceCapabilityResolver;
|
|
use App\Services\Auth\WorkspaceRoleCapabilityMap;
|
|
use App\Support\Auth\Capabilities;
|
|
use App\Support\Filament\PanelThemeAsset;
|
|
use App\Support\Navigation\AdminSurfaceScope;
|
|
use App\Support\Navigation\NavigationScope;
|
|
use App\Support\Navigation\WorkspaceHubNavigation;
|
|
use App\Support\OperationRunLinks;
|
|
use App\Support\Workspaces\WorkspaceContext;
|
|
use Filament\Facades\Filament;
|
|
use Filament\FontProviders\LocalFontProvider;
|
|
use Filament\Http\Middleware\Authenticate;
|
|
use Filament\Http\Middleware\AuthenticateSession;
|
|
use Filament\Http\Middleware\DisableBladeIconComponents;
|
|
use Filament\Http\Middleware\DispatchServingFilamentEvent;
|
|
use Filament\Navigation\NavigationGroup;
|
|
use Filament\Navigation\NavigationItem;
|
|
use Filament\Panel;
|
|
use Filament\PanelProvider;
|
|
use Filament\Support\Colors\Color;
|
|
use Filament\View\PanelsRenderHook;
|
|
use Filament\Widgets\AccountWidget;
|
|
use Filament\Widgets\FilamentInfoWidget;
|
|
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
|
|
use Illuminate\Cookie\Middleware\EncryptCookies;
|
|
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken;
|
|
use Illuminate\Routing\Middleware\SubstituteBindings;
|
|
use Illuminate\Session\Middleware\StartSession;
|
|
use Illuminate\View\Middleware\ShareErrorsFromSession;
|
|
|
|
class AdminPanelProvider extends PanelProvider
|
|
{
|
|
public function panel(Panel $panel): Panel
|
|
{
|
|
$panel = $panel
|
|
->default()
|
|
->id('admin')
|
|
->path('admin')
|
|
->login(Login::class)
|
|
->brandName('TenantPilot')
|
|
->brandLogo(fn () => view('filament.admin.logo'))
|
|
->brandLogoHeight('2rem')
|
|
->homeUrl(fn (): string => route('admin.home'))
|
|
->favicon(asset('favicon.ico'))
|
|
->font(null, provider: LocalFontProvider::class, preload: [])
|
|
->authenticatedRoutes(function (Panel $panel): void {
|
|
ChooseWorkspace::registerRoutes($panel);
|
|
ChooseEnvironment::registerRoutes($panel);
|
|
NoAccess::registerRoutes($panel);
|
|
})
|
|
->colors([
|
|
'primary' => Color::Indigo,
|
|
])
|
|
->navigationGroups([
|
|
NavigationGroup::make('Inventory'),
|
|
NavigationGroup::make(__('localization.navigation.monitoring')),
|
|
NavigationGroup::make(__('localization.review.reporting')),
|
|
NavigationGroup::make(__('localization.navigation.settings')),
|
|
NavigationGroup::make(__('localization.navigation.governance')),
|
|
NavigationGroup::make('Backups & Restore'),
|
|
NavigationGroup::make('Directory'),
|
|
NavigationGroup::make(__('localization.navigation.workspace_wide')),
|
|
NavigationGroup::make(__('localization.navigation.workspace_admin')),
|
|
])
|
|
->navigationItems([
|
|
$this->overviewNavigationItem(),
|
|
NavigationItem::make('Items')
|
|
->url(fn (): string => InventoryCluster::getUrl(panel: 'admin'))
|
|
->icon('heroicon-o-squares-2x2')
|
|
->group('Inventory')
|
|
->sort(1)
|
|
->visible(fn (): bool => NavigationScope::shouldRegisterEnvironmentNavigation() && InventoryCluster::canAccess()),
|
|
NavigationItem::make('Coverage')
|
|
->url(fn (): string => InventoryCoverage::getUrl(panel: 'admin'))
|
|
->icon('heroicon-o-table-cells')
|
|
->group('Inventory')
|
|
->sort(3)
|
|
->visible(fn (): bool => NavigationScope::shouldRegisterEnvironmentNavigation() && InventoryCoverage::canAccess()),
|
|
NavigationItem::make('Groups')
|
|
->url(fn (): string => EntraGroupResource::getUrl(panel: 'admin'))
|
|
->icon('heroicon-o-user-group')
|
|
->group('Directory')
|
|
->sort(10)
|
|
->visible(fn (): bool => NavigationScope::shouldRegisterEnvironmentNavigation() && EntraGroupResource::canViewAny()),
|
|
NavigationItem::make(fn (): string => __('localization.navigation.integrations'))
|
|
->url(fn (): string => WorkspaceHubNavigation::environmentFilteredUrl(route('filament.admin.resources.provider-connections.index')))
|
|
->icon('heroicon-o-link')
|
|
->group(fn (): string => WorkspaceHubNavigation::workspaceAdminGroup())
|
|
->sort(15)
|
|
->visible(fn (): bool => ProviderConnectionResource::canViewAny()),
|
|
NavigationItem::make(fn (): string => __('localization.navigation.settings'))
|
|
->url(fn (): string => WorkspaceHubNavigation::cleanUrl(WorkspaceSettings::getUrl(panel: 'admin')))
|
|
->icon('heroicon-o-cog-6-tooth')
|
|
->group(fn (): string => WorkspaceHubNavigation::workspaceAdminGroup())
|
|
->sort(20)
|
|
->visible(function (): bool {
|
|
$user = auth()->user();
|
|
|
|
if (! $user instanceof User) {
|
|
return false;
|
|
}
|
|
|
|
$workspaceId = app(WorkspaceContext::class)->currentWorkspaceId(request());
|
|
|
|
if (! is_int($workspaceId)) {
|
|
return false;
|
|
}
|
|
|
|
$workspace = Workspace::query()->whereKey($workspaceId)->first();
|
|
|
|
if (! $workspace instanceof Workspace) {
|
|
return false;
|
|
}
|
|
|
|
/** @var WorkspaceCapabilityResolver $resolver */
|
|
$resolver = app(WorkspaceCapabilityResolver::class);
|
|
|
|
return $resolver->isMember($user, $workspace)
|
|
&& $resolver->can($user, $workspace, Capabilities::WORKSPACE_SETTINGS_VIEW);
|
|
}),
|
|
NavigationItem::make(fn (): string => __('localization.navigation.manage_workspaces'))
|
|
->url(function (): string {
|
|
return WorkspaceHubNavigation::cleanUrl(route('filament.admin.resources.workspaces.index'));
|
|
})
|
|
->icon('heroicon-o-squares-2x2')
|
|
->group(fn (): string => WorkspaceHubNavigation::workspaceAdminGroup())
|
|
->sort(10)
|
|
->visible(function (): bool {
|
|
$user = auth()->user();
|
|
|
|
if (! $user instanceof User) {
|
|
return false;
|
|
}
|
|
|
|
$roles = WorkspaceRoleCapabilityMap::rolesWithCapability(Capabilities::WORKSPACE_MEMBERSHIP_MANAGE);
|
|
|
|
return WorkspaceMembership::query()
|
|
->where('user_id', (int) $user->getKey())
|
|
->whereIn('role', $roles)
|
|
->exists();
|
|
}),
|
|
NavigationItem::make(fn (): string => __('localization.navigation.operations'))
|
|
->url(fn (): string => WorkspaceHubNavigation::environmentFilteredUrl(OperationRunLinks::index()))
|
|
->icon('heroicon-o-queue-list')
|
|
->group(fn (): string => WorkspaceHubNavigation::workspaceWideGroup(__('localization.navigation.monitoring')))
|
|
->sort(10),
|
|
NavigationItem::make('Alerts')
|
|
->url(fn (): string => WorkspaceHubNavigation::environmentFilteredUrl(route('filament.admin.alerts')))
|
|
->icon('heroicon-o-bell-alert')
|
|
->group(fn (): string => WorkspaceHubNavigation::workspaceWideGroup(__('localization.navigation.monitoring')))
|
|
->sort(23),
|
|
NavigationItem::make(fn (): string => __('localization.navigation.evidence_overview'))
|
|
->url(fn (): string => $this->workspaceEvidenceOverviewNavigationUrl())
|
|
->icon('heroicon-o-shield-check')
|
|
->group(fn (): string => WorkspaceHubNavigation::workspaceWideGroup(__('localization.navigation.monitoring')))
|
|
->sort(27),
|
|
NavigationItem::make(fn (): string => __('localization.navigation.audit_log'))
|
|
->url(fn (): string => WorkspaceHubNavigation::environmentFilteredUrl(route('admin.monitoring.audit-log')))
|
|
->icon('heroicon-o-clipboard-document-list')
|
|
->group(fn (): string => WorkspaceHubNavigation::workspaceWideGroup(__('localization.navigation.monitoring')))
|
|
->sort(30),
|
|
])
|
|
->renderHook(
|
|
PanelsRenderHook::HEAD_END,
|
|
fn () => view('filament.partials.livewire-intercept-shim')->render()
|
|
)
|
|
->renderHook(
|
|
PanelsRenderHook::TOPBAR_START,
|
|
fn () => view('filament.partials.context-bar')->render()
|
|
)
|
|
->renderHook(
|
|
PanelsRenderHook::SIDEBAR_NAV_START,
|
|
fn () => view('filament.partials.sidebar-scope-indicator')->render()
|
|
)
|
|
->renderHook(
|
|
PanelsRenderHook::PAGE_START,
|
|
fn (): string => AdminSurfaceScope::fromRequest(request()) === AdminSurfaceScope::OnboardingWorkflow
|
|
|| request()->routeIs('admin.workspace.managed-environments.index', 'filament.admin.pages.choose-environment')
|
|
? ''
|
|
: ((bool) config('tenantpilot.bulk_operations.progress_widget_enabled', true)
|
|
? view('livewire.bulk-operation-progress-wrapper')->render()
|
|
: '')
|
|
)
|
|
->resources([
|
|
PolicyResource::class,
|
|
ProviderConnectionResource::class,
|
|
InventoryItemResource::class,
|
|
AlertDestinationResource::class,
|
|
AlertRuleResource::class,
|
|
AlertDeliveryResource::class,
|
|
WorkspaceResource::class,
|
|
BaselineProfileResource::class,
|
|
BaselineSnapshotResource::class,
|
|
EnvironmentReviewResource::class,
|
|
])
|
|
->discoverClusters(in: app_path('Filament/Clusters'), for: 'App\\Filament\\Clusters')
|
|
->discoverResources(in: app_path('Filament/Resources'), for: 'App\\Filament\\Resources')
|
|
->pages([
|
|
BaselineCompareLanding::class,
|
|
BaselineSubjectResolution::class,
|
|
InventoryCoverage::class,
|
|
EnvironmentRequiredPermissions::class,
|
|
WorkspaceSettings::class,
|
|
CrossEnvironmentComparePage::class,
|
|
GovernanceInbox::class,
|
|
DecisionRegister::class,
|
|
FindingsHygieneReport::class,
|
|
FindingsIntakeQueue::class,
|
|
MyFindingsInbox::class,
|
|
FindingExceptionsQueue::class,
|
|
CustomerReviewWorkspace::class,
|
|
ReviewRegister::class,
|
|
])
|
|
->widgets([
|
|
AccountWidget::class,
|
|
FilamentInfoWidget::class,
|
|
])
|
|
->databaseNotifications()
|
|
->databaseNotificationsPolling(null)
|
|
->unsavedChangesAlerts()
|
|
->middleware([
|
|
EncryptCookies::class,
|
|
AddQueuedCookiesToResponse::class,
|
|
StartSession::class,
|
|
AuthenticateSession::class,
|
|
ShareErrorsFromSession::class,
|
|
VerifyCsrfToken::class,
|
|
SubstituteBindings::class,
|
|
'ensure-correct-guard:web',
|
|
'ensure-workspace-selected',
|
|
'ensure-environment-context-selected',
|
|
DisableBladeIconComponents::class,
|
|
DispatchServingFilamentEvent::class,
|
|
])
|
|
->middleware(['apply-resolved-locale:admin'], isPersistent: true)
|
|
->authMiddleware([
|
|
Authenticate::class,
|
|
]);
|
|
|
|
$theme = PanelThemeAsset::resolve('resources/css/filament/admin/theme.css');
|
|
|
|
if (is_string($theme)) {
|
|
$panel->theme($theme);
|
|
}
|
|
|
|
return $panel;
|
|
}
|
|
|
|
private function workspaceEvidenceOverviewNavigationUrl(): string
|
|
{
|
|
return WorkspaceHubNavigation::environmentFilteredUrl(route('admin.evidence.overview'));
|
|
}
|
|
|
|
private function overviewNavigationItem(): NavigationItem
|
|
{
|
|
return NavigationItem::make('Overview')
|
|
->url(function (): string {
|
|
$tenant = $this->environmentBoundNavigationTenant();
|
|
|
|
return $tenant instanceof ManagedEnvironment
|
|
? EnvironmentDashboard::getUrl(panel: 'admin', tenant: $tenant)
|
|
: route('admin.home');
|
|
})
|
|
->icon('heroicon-o-home')
|
|
->sort(-100)
|
|
->isActiveWhen(fn (): bool => request()->routeIs('admin.home', 'admin.workspace.environments.show'));
|
|
}
|
|
|
|
private function environmentBoundNavigationTenant(): ?ManagedEnvironment
|
|
{
|
|
if (! AdminSurfaceScope::fromRequest(request())->requiresExplicitEnvironment()) {
|
|
return null;
|
|
}
|
|
|
|
$tenant = Filament::getTenant();
|
|
|
|
if ($tenant instanceof ManagedEnvironment) {
|
|
return $tenant;
|
|
}
|
|
|
|
return app(WorkspaceContext::class)->rememberedEnvironment(request());
|
|
}
|
|
}
|