satisfy(ActionSurfaceSlot::ListHeader, 'Header actions include capability-gated create.') ->satisfy(ActionSurfaceSlot::InspectAffordance, ActionSurfaceInspectAffordance::ClickableRow->value) ->satisfy(ActionSurfaceSlot::ListRowMoreMenu, 'Secondary row actions are grouped under "More".') ->exempt(ActionSurfaceSlot::ListBulkMoreGroup, 'Provider connections intentionally omit bulk actions.') ->satisfy(ActionSurfaceSlot::ListEmptyState, 'List page defines empty-state guidance and CTA.') ->exempt(ActionSurfaceSlot::DetailHeader, 'View page has no additional header mutations in this resource.'); } public static function canCreate(): bool { $tenant = static::resolveTenantForCreate(); $user = auth()->user(); if (! $tenant instanceof Tenant || ! $user instanceof User) { return false; } /** @var CapabilityResolver $resolver */ $resolver = app(CapabilityResolver::class); return $resolver->isMember($user, $tenant) && $resolver->can($user, $tenant, Capabilities::PROVIDER_MANAGE); } protected static function hasTenantCapability(string $capability): bool { $tenant = static::resolveScopedTenant(); $user = auth()->user(); if (! $tenant instanceof Tenant || ! $user instanceof User) { return false; } /** @var CapabilityResolver $resolver */ $resolver = app(CapabilityResolver::class); return $resolver->isMember($user, $tenant) && $resolver->can($user, $tenant, $capability); } protected static function resolveScopedTenant(): ?Tenant { $tenantExternalId = static::resolveRequestedTenantExternalId(); if (is_string($tenantExternalId) && $tenantExternalId !== '') { return static::resolveTenantByExternalId($tenantExternalId); } $routeTenant = request()->route('tenant'); if ($routeTenant instanceof Tenant) { return $routeTenant; } if (is_string($routeTenant) && $routeTenant !== '') { return Tenant::query() ->where('external_id', $routeTenant) ->first(); } $recordTenant = static::resolveTenantFromRouteRecord(); if ($recordTenant instanceof Tenant) { return $recordTenant; } $contextTenantExternalId = static::resolveContextTenantExternalId(); if (is_string($contextTenantExternalId) && $contextTenantExternalId !== '') { return static::resolveTenantByExternalId($contextTenantExternalId); } $filamentTenant = Filament::getTenant(); return $filamentTenant instanceof Tenant ? $filamentTenant : null; } public static function resolveTenantForRecord(?ProviderConnection $record = null): ?Tenant { if ($record instanceof ProviderConnection) { $tenant = $record->tenant; if (! $tenant instanceof Tenant && is_numeric($record->tenant_id)) { $tenant = Tenant::query()->whereKey((int) $record->tenant_id)->first(); } if ($tenant instanceof Tenant) { return $tenant; } } return static::resolveScopedTenant(); } public static function resolveRequestedTenantExternalId(): ?string { $queryTenant = request()->query('tenant_id'); if (is_string($queryTenant) && $queryTenant !== '') { return $queryTenant; } return static::resolveTenantExternalIdFromLivewireRequest(); } public static function resolveContextTenantExternalId(): ?string { $workspaceId = app(WorkspaceContext::class)->currentWorkspaceId(request()); $contextTenantId = app(WorkspaceContext::class)->lastTenantId(request()); if ($workspaceId !== null && $contextTenantId !== null) { $tenant = Tenant::query() ->whereKey($contextTenantId) ->where('workspace_id', (int) $workspaceId) ->first(); if ($tenant instanceof Tenant) { return (string) $tenant->external_id; } } $filamentTenant = Filament::getTenant(); if ($filamentTenant instanceof Tenant) { return (string) $filamentTenant->external_id; } return null; } public static function resolveTenantForCreate(): ?Tenant { $tenantExternalId = static::resolveRequestedTenantExternalId() ?? static::resolveContextTenantExternalId(); if (! is_string($tenantExternalId) || $tenantExternalId === '') { return null; } $tenant = static::resolveTenantByExternalId($tenantExternalId); $user = auth()->user(); $workspaceId = app(WorkspaceContext::class)->currentWorkspaceId(request()); if (! $tenant instanceof Tenant || ! $user instanceof User || $workspaceId === null) { return null; } if ((int) $tenant->workspace_id !== (int) $workspaceId) { return null; } if (! $user->canAccessTenant($tenant)) { return null; } return $tenant; } private static function resolveTenantExternalIdFromLivewireRequest(): ?string { if (! request()->headers->has('x-livewire') && ! request()->headers->has('x-livewire-navigate')) { return null; } try { $url = \Livewire\Livewire::originalUrl(); if (is_string($url) && $url !== '') { $externalId = static::extractTenantExternalIdFromUrl($url); if (is_string($externalId) && $externalId !== '') { return $externalId; } } } catch (\Throwable) { // Ignore and fall back to referer. } $referer = request()->headers->get('referer'); if (! is_string($referer) || $referer === '') { return null; } return static::extractTenantExternalIdFromUrl($referer); } private static function extractTenantExternalIdFromUrl(string $url): ?string { $query = parse_url($url, PHP_URL_QUERY); if (is_string($query) && $query !== '') { parse_str($query, $queryParams); $tenantExternalId = $queryParams['tenant_id'] ?? null; if (is_string($tenantExternalId) && $tenantExternalId !== '') { return $tenantExternalId; } } $path = parse_url($url, PHP_URL_PATH); if (! is_string($path) || $path === '') { $path = $url; } if (preg_match('~/(?:admin)/(?:tenants|t)/([0-9a-fA-F-]{36})(?:/|$)~', $path, $matches) !== 1) { return null; } return (string) $matches[1]; } private static function resolveTenantByExternalId(?string $externalId): ?Tenant { if (! is_string($externalId) || $externalId === '') { return null; } return Tenant::query() ->where('external_id', $externalId) ->first(); } private static function resolveTenantFromRouteRecord(): ?Tenant { $record = request()->route('record'); if ($record instanceof ProviderConnection) { return static::resolveTenantForRecord($record); } if (! is_numeric($record)) { return null; } $providerConnection = ProviderConnection::query() ->with('tenant') ->whereKey((int) $record) ->first(); if (! $providerConnection instanceof ProviderConnection) { return null; } return static::resolveTenantForRecord($providerConnection); } private static function applyMembershipScope(Builder $query): Builder { $workspaceId = app(WorkspaceContext::class)->currentWorkspaceId(request()); $user = auth()->user(); if (! is_int($workspaceId)) { $filamentTenant = Filament::getTenant(); if ($filamentTenant instanceof Tenant) { $workspaceId = (int) $filamentTenant->workspace_id; } } if (! is_int($workspaceId) || ! $user instanceof User) { return $query->whereRaw('1 = 0'); } return $query ->where('provider_connections.workspace_id', $workspaceId) ->whereExists(function ($membershipScope) use ($user, $workspaceId): void { $membershipScope ->selectRaw('1') ->from('tenants as scoped_tenants') ->join('tenant_memberships as scoped_memberships', function (JoinClause $join) use ($user): void { $join->on('scoped_memberships.tenant_id', '=', 'scoped_tenants.id') ->where('scoped_memberships.user_id', '=', (int) $user->getKey()); }) ->whereColumn('scoped_tenants.id', 'provider_connections.tenant_id') ->where('scoped_tenants.workspace_id', '=', $workspaceId); }); } /** * @return array */ private static function tenantFilterOptions(): array { $workspaceId = app(WorkspaceContext::class)->currentWorkspaceId(request()); $user = auth()->user(); if (! is_int($workspaceId) || ! $user instanceof User) { return []; } return Tenant::query() ->select(['tenants.external_id', 'tenants.name', 'tenants.environment']) ->join('tenant_memberships as filter_memberships', function (JoinClause $join) use ($user): void { $join->on('filter_memberships.tenant_id', '=', 'tenants.id') ->where('filter_memberships.user_id', '=', (int) $user->getKey()); }) ->where('tenants.workspace_id', $workspaceId) ->orderBy('tenants.name') ->get() ->mapWithKeys(function (Tenant $tenant): array { $environment = strtoupper((string) ($tenant->environment ?? '')); $label = $environment !== '' ? "{$tenant->name} ({$environment})" : (string) $tenant->name; return [(string) $tenant->external_id => $label]; }) ->all(); } private static function sanitizeErrorMessage(?string $value): ?string { if (! is_string($value) || trim($value) === '') { return null; } $normalized = preg_replace('/\s+/', ' ', strip_tags($value)); $normalized = is_string($normalized) ? trim($normalized) : ''; if ($normalized === '') { return null; } return Str::limit($normalized, 120); } public static function form(Schema $schema): Schema { return $schema ->schema([ Section::make('Connection') ->schema([ TextInput::make('display_name') ->label('Display name') ->required() ->disabled(fn (): bool => ! static::hasTenantCapability(Capabilities::PROVIDER_MANAGE)) ->maxLength(255), TextInput::make('entra_tenant_id') ->label('Entra tenant ID') ->required() ->maxLength(255) ->disabled(fn (): bool => ! static::hasTenantCapability(Capabilities::PROVIDER_MANAGE)) ->rules(['uuid']), Toggle::make('is_default') ->label('Default connection') ->disabled(fn (): bool => ! static::hasTenantCapability(Capabilities::PROVIDER_MANAGE)) ->helperText('Exactly one default connection is required per tenant/provider.'), ]) ->columns(2) ->columnSpanFull(), Section::make('Status') ->schema([ TextInput::make('status') ->label('Status') ->disabled() ->dehydrated(false), TextInput::make('health_status') ->label('Health') ->disabled() ->dehydrated(false), ]) ->columns(2) ->columnSpanFull(), ]); } public static function table(Table $table): Table { return $table ->modifyQueryUsing(function (Builder $query): Builder { $query->with('tenant'); $tenantExternalId = static::resolveRequestedTenantExternalId(); if (! is_string($tenantExternalId) || $tenantExternalId === '') { return $query; } return $query->whereHas('tenant', function (Builder $tenantQuery) use ($tenantExternalId): void { $tenantQuery->where('external_id', $tenantExternalId); }); }) ->defaultSort('display_name') ->recordUrl(fn (ProviderConnection $record): string => static::getUrl('view', ['record' => $record])) ->columns([ Tables\Columns\TextColumn::make('tenant.name') ->label('Tenant') ->description(function (ProviderConnection $record): ?string { $environment = $record->tenant?->environment; if (! is_string($environment) || trim($environment) === '') { return null; } return strtoupper($environment); }) ->url(function (ProviderConnection $record): ?string { $tenant = static::resolveTenantForRecord($record); if (! $tenant instanceof Tenant) { return null; } return TenantResource::getUrl('view', ['record' => $tenant], panel: 'admin'); }), Tables\Columns\TextColumn::make('display_name')->label('Name')->searchable(), Tables\Columns\TextColumn::make('provider')->label('Provider')->toggleable(), Tables\Columns\TextColumn::make('entra_tenant_id')->label('Entra tenant ID')->copyable()->toggleable(), Tables\Columns\IconColumn::make('is_default')->label('Default')->boolean(), Tables\Columns\TextColumn::make('status') ->label('Status') ->badge() ->formatStateUsing(BadgeRenderer::label(BadgeDomain::ProviderConnectionStatus)) ->color(BadgeRenderer::color(BadgeDomain::ProviderConnectionStatus)) ->icon(BadgeRenderer::icon(BadgeDomain::ProviderConnectionStatus)) ->iconColor(BadgeRenderer::iconColor(BadgeDomain::ProviderConnectionStatus)), Tables\Columns\TextColumn::make('health_status') ->label('Health') ->badge() ->formatStateUsing(BadgeRenderer::label(BadgeDomain::ProviderConnectionHealth)) ->color(BadgeRenderer::color(BadgeDomain::ProviderConnectionHealth)) ->icon(BadgeRenderer::icon(BadgeDomain::ProviderConnectionHealth)) ->iconColor(BadgeRenderer::iconColor(BadgeDomain::ProviderConnectionHealth)), Tables\Columns\TextColumn::make('last_health_check_at')->label('Last check')->since()->toggleable(), Tables\Columns\TextColumn::make('last_error_reason_code') ->label('Last error reason') ->toggleable(), Tables\Columns\TextColumn::make('last_error_message') ->label('Last error message') ->formatStateUsing(fn (?string $state): ?string => static::sanitizeErrorMessage($state)) ->toggleable(), ]) ->filters([ SelectFilter::make('tenant') ->label('Tenant') ->default(static::resolveScopedTenant()?->external_id) ->options(static::tenantFilterOptions()) ->query(function (Builder $query, array $data): Builder { $value = $data['value'] ?? null; if (! is_string($value) || $value === '') { return $query; } return $query->whereHas('tenant', function (Builder $tenantQuery) use ($value): void { $tenantQuery->where('external_id', $value); }); }), SelectFilter::make('provider') ->label('Provider') ->options([ 'microsoft' => 'Microsoft', ]) ->query(function (Builder $query, array $data): Builder { $value = $data['value'] ?? null; if (! is_string($value) || $value === '') { return $query; } return $query->where('provider_connections.provider', $value); }), SelectFilter::make('status') ->label('Status') ->options([ 'connected' => 'Connected', 'needs_consent' => 'Needs consent', 'error' => 'Error', 'disabled' => 'Disabled', ]) ->query(function (Builder $query, array $data): Builder { $value = $data['value'] ?? null; if (! is_string($value) || $value === '') { return $query; } return $query->where('provider_connections.status', $value); }), SelectFilter::make('health_status') ->label('Health') ->options([ 'ok' => 'OK', 'degraded' => 'Degraded', 'down' => 'Down', 'unknown' => 'Unknown', ]) ->query(function (Builder $query, array $data): Builder { $value = $data['value'] ?? null; if (! is_string($value) || $value === '') { return $query; } return $query->where('provider_connections.health_status', $value); }), Filter::make('default_only') ->label('Default only') ->query(fn (Builder $query): Builder => $query->where('provider_connections.is_default', true)), ]) ->actions([ Actions\ActionGroup::make([ UiEnforcement::forAction( Actions\EditAction::make() ) ->requireCapability(Capabilities::PROVIDER_MANAGE) ->apply(), UiEnforcement::forAction( Actions\Action::make('check_connection') ->label('Check connection') ->icon('heroicon-o-check-badge') ->color('success') ->visible(fn (ProviderConnection $record): bool => $record->status !== 'disabled') ->action(function (ProviderConnection $record, StartVerification $verification): void { $tenant = static::resolveTenantForRecord($record); $user = auth()->user(); if (! $tenant instanceof Tenant) { abort(404); } if (! $user instanceof User) { abort(403); } $result = $verification->providerConnectionCheck( tenant: $tenant, connection: $record, initiator: $user, ); if ($result->status === 'scope_busy') { Notification::make() ->title('Scope busy') ->body('Another provider operation is already running for this connection.') ->warning() ->actions([ Actions\Action::make('view_run') ->label('View run') ->url(OperationRunLinks::view($result->run, $tenant)), Actions\Action::make('manage_connections') ->label('Manage Provider Connections') ->url(static::getUrl('index', tenant: $tenant)), ]) ->send(); return; } if ($result->status === 'deduped') { Notification::make() ->title('Run already queued') ->body('A connection check is already queued or running.') ->warning() ->actions([ Actions\Action::make('view_run') ->label('View run') ->url(OperationRunLinks::view($result->run, $tenant)), Actions\Action::make('manage_connections') ->label('Manage Provider Connections') ->url(static::getUrl('index', tenant: $tenant)), ]) ->send(); return; } if ($result->status === 'blocked') { $reasonCode = is_string($result->run->context['reason_code'] ?? null) ? (string) $result->run->context['reason_code'] : 'unknown_error'; Notification::make() ->title('Connection check blocked') ->body("Blocked by provider configuration ({$reasonCode}).") ->warning() ->actions([ Actions\Action::make('view_run') ->label('View run') ->url(OperationRunLinks::view($result->run, $tenant)), Actions\Action::make('manage_connections') ->label('Manage Provider Connections') ->url(static::getUrl('index', tenant: $tenant)), ]) ->send(); return; } Notification::make() ->title('Connection check queued') ->body('Health check was queued and will run in the background.') ->success() ->actions([ Actions\Action::make('view_run') ->label('View run') ->url(OperationRunLinks::view($result->run, $tenant)), ]) ->send(); }) ) ->preserveVisibility() ->requireCapability(Capabilities::PROVIDER_RUN) ->apply(), UiEnforcement::forAction( Actions\Action::make('inventory_sync') ->label('Inventory sync') ->icon('heroicon-o-arrow-path') ->color('info') ->visible(fn (ProviderConnection $record): bool => $record->status !== 'disabled') ->action(function (ProviderConnection $record, ProviderOperationStartGate $gate): void { $tenant = static::resolveTenantForRecord($record); $user = auth()->user(); if (! $tenant instanceof Tenant || ! $user instanceof User) { return; } $initiator = $user; $result = $gate->start( tenant: $tenant, connection: $record, operationType: 'inventory_sync', dispatcher: function (OperationRun $operationRun) use ($tenant, $initiator, $record): void { ProviderInventorySyncJob::dispatch( tenantId: (int) $tenant->getKey(), userId: (int) $initiator->getKey(), providerConnectionId: (int) $record->getKey(), operationRun: $operationRun, ); }, initiator: $initiator, ); if ($result->status === 'scope_busy') { Notification::make() ->title('Scope is busy') ->body('Another provider operation is already running for this connection.') ->danger() ->actions([ Actions\Action::make('view_run') ->label('View run') ->url(OperationRunLinks::view($result->run, $tenant)), ]) ->send(); return; } if ($result->status === 'deduped') { Notification::make() ->title('Run already queued') ->body('An inventory sync is already queued or running.') ->warning() ->actions([ Actions\Action::make('view_run') ->label('View run') ->url(OperationRunLinks::view($result->run, $tenant)), ]) ->send(); return; } if ($result->status === 'blocked') { $reasonCode = is_string($result->run->context['reason_code'] ?? null) ? (string) $result->run->context['reason_code'] : 'unknown_error'; Notification::make() ->title('Inventory sync blocked') ->body("Blocked by provider configuration ({$reasonCode}).") ->warning() ->actions([ Actions\Action::make('view_run') ->label('View run') ->url(OperationRunLinks::view($result->run, $tenant)), ]) ->send(); return; } Notification::make() ->title('Inventory sync queued') ->body('Inventory sync was queued and will run in the background.') ->success() ->actions([ Actions\Action::make('view_run') ->label('View run') ->url(OperationRunLinks::view($result->run, $tenant)), ]) ->send(); }) ) ->preserveVisibility() ->requireCapability(Capabilities::PROVIDER_RUN) ->apply(), UiEnforcement::forAction( Actions\Action::make('compliance_snapshot') ->label('Compliance snapshot') ->icon('heroicon-o-shield-check') ->color('info') ->visible(fn (ProviderConnection $record): bool => $record->status !== 'disabled') ->action(function (ProviderConnection $record, ProviderOperationStartGate $gate): void { $tenant = static::resolveTenantForRecord($record); $user = auth()->user(); if (! $tenant instanceof Tenant || ! $user instanceof User) { return; } $initiator = $user; $result = $gate->start( tenant: $tenant, connection: $record, operationType: 'compliance.snapshot', dispatcher: function (OperationRun $operationRun) use ($tenant, $initiator, $record): void { ProviderComplianceSnapshotJob::dispatch( tenantId: (int) $tenant->getKey(), userId: (int) $initiator->getKey(), providerConnectionId: (int) $record->getKey(), operationRun: $operationRun, ); }, initiator: $initiator, ); if ($result->status === 'scope_busy') { Notification::make() ->title('Scope is busy') ->body('Another provider operation is already running for this connection.') ->danger() ->actions([ Actions\Action::make('view_run') ->label('View run') ->url(OperationRunLinks::view($result->run, $tenant)), ]) ->send(); return; } if ($result->status === 'deduped') { Notification::make() ->title('Run already queued') ->body('A compliance snapshot is already queued or running.') ->warning() ->actions([ Actions\Action::make('view_run') ->label('View run') ->url(OperationRunLinks::view($result->run, $tenant)), ]) ->send(); return; } if ($result->status === 'blocked') { $reasonCode = is_string($result->run->context['reason_code'] ?? null) ? (string) $result->run->context['reason_code'] : 'unknown_error'; Notification::make() ->title('Compliance snapshot blocked') ->body("Blocked by provider configuration ({$reasonCode}).") ->warning() ->actions([ Actions\Action::make('view_run') ->label('View run') ->url(OperationRunLinks::view($result->run, $tenant)), ]) ->send(); return; } Notification::make() ->title('Compliance snapshot queued') ->body('Compliance snapshot was queued and will run in the background.') ->success() ->actions([ Actions\Action::make('view_run') ->label('View run') ->url(OperationRunLinks::view($result->run, $tenant)), ]) ->send(); }) ) ->preserveVisibility() ->requireCapability(Capabilities::PROVIDER_RUN) ->apply(), UiEnforcement::forAction( Actions\Action::make('set_default') ->label('Set as default') ->icon('heroicon-o-star') ->color('primary') ->requiresConfirmation() ->visible(fn (ProviderConnection $record): bool => $record->status !== 'disabled' && ! $record->is_default) ->action(function (ProviderConnection $record, AuditLogger $auditLogger): void { $tenant = static::resolveTenantForRecord($record); if (! $tenant instanceof Tenant) { return; } $record->makeDefault(); $user = auth()->user(); $actorId = $user instanceof User ? (int) $user->getKey() : null; $actorEmail = $user instanceof User ? $user->email : null; $actorName = $user instanceof User ? $user->name : null; $auditLogger->log( tenant: $tenant, action: 'provider_connection.default_set', context: [ 'metadata' => [ 'provider' => $record->provider, 'entra_tenant_id' => $record->entra_tenant_id, ], ], actorId: $actorId, actorEmail: $actorEmail, actorName: $actorName, resourceType: 'provider_connection', resourceId: (string) $record->getKey(), status: 'success', ); Notification::make() ->title('Default connection updated') ->success() ->send(); }) ) ->preserveVisibility() ->requireCapability(Capabilities::PROVIDER_MANAGE) ->apply(), UiEnforcement::forAction( Actions\Action::make('update_credentials') ->label('Update credentials') ->icon('heroicon-o-key') ->color('primary') ->requiresConfirmation() ->modalDescription('Client secret is stored encrypted and will never be shown again.') ->form([ TextInput::make('client_id') ->label('Client ID') ->required() ->maxLength(255), TextInput::make('client_secret') ->label('Client secret') ->password() ->required() ->maxLength(255), ]) ->action(function (array $data, ProviderConnection $record, CredentialManager $credentials, AuditLogger $auditLogger): void { $tenant = static::resolveTenantForRecord($record); if (! $tenant instanceof Tenant) { return; } $credentials->upsertClientSecretCredential( connection: $record, clientId: (string) $data['client_id'], clientSecret: (string) $data['client_secret'], ); $user = auth()->user(); $actorId = $user instanceof User ? (int) $user->getKey() : null; $actorEmail = $user instanceof User ? $user->email : null; $actorName = $user instanceof User ? $user->name : null; $auditLogger->log( tenant: $tenant, action: 'provider_connection.credentials_updated', context: [ 'metadata' => [ 'provider' => $record->provider, 'entra_tenant_id' => $record->entra_tenant_id, 'client_id' => (string) $data['client_id'], ], ], actorId: $actorId, actorEmail: $actorEmail, actorName: $actorName, resourceType: 'provider_connection', resourceId: (string) $record->getKey(), status: 'success', ); Notification::make() ->title('Credentials updated') ->success() ->send(); }) ) ->requireCapability(Capabilities::PROVIDER_MANAGE) ->apply(), UiEnforcement::forAction( Actions\Action::make('enable_connection') ->label('Enable connection') ->icon('heroicon-o-play') ->color('success') ->requiresConfirmation() ->visible(fn (ProviderConnection $record): bool => $record->status === 'disabled') ->action(function (ProviderConnection $record, AuditLogger $auditLogger): void { $tenant = static::resolveTenantForRecord($record); if (! $tenant instanceof Tenant) { return; } $hadCredentials = $record->credential()->exists(); $previousStatus = (string) $record->status; $status = $hadCredentials ? 'connected' : 'error'; $errorReasonCode = $hadCredentials ? null : ProviderReasonCodes::ProviderCredentialMissing; $errorMessage = $hadCredentials ? null : 'Provider connection credentials are missing.'; $record->update([ 'status' => $status, 'health_status' => 'unknown', 'last_health_check_at' => null, 'last_error_reason_code' => $errorReasonCode, 'last_error_message' => $errorMessage, ]); $user = auth()->user(); $actorId = $user instanceof User ? (int) $user->getKey() : null; $actorEmail = $user instanceof User ? $user->email : null; $actorName = $user instanceof User ? $user->name : null; $auditLogger->log( tenant: $tenant, action: 'provider_connection.enabled', context: [ 'metadata' => [ 'provider' => $record->provider, 'entra_tenant_id' => $record->entra_tenant_id, 'from_status' => $previousStatus, 'to_status' => $status, 'credentials_present' => $hadCredentials, ], ], actorId: $actorId, actorEmail: $actorEmail, actorName: $actorName, resourceType: 'provider_connection', resourceId: (string) $record->getKey(), status: 'success', ); if (! $hadCredentials) { Notification::make() ->title('Connection enabled (credentials missing)') ->body('Add credentials before running checks or operations.') ->warning() ->send(); return; } Notification::make() ->title('Provider connection enabled') ->success() ->send(); }) ) ->preserveVisibility() ->requireCapability(Capabilities::PROVIDER_MANAGE) ->apply(), UiEnforcement::forAction( Actions\Action::make('disable_connection') ->label('Disable connection') ->icon('heroicon-o-archive-box-x-mark') ->color('danger') ->requiresConfirmation() ->visible(fn (ProviderConnection $record): bool => $record->status !== 'disabled') ->action(function (ProviderConnection $record, AuditLogger $auditLogger): void { $tenant = static::resolveTenantForRecord($record); if (! $tenant instanceof Tenant) { return; } $previousStatus = (string) $record->status; $record->update([ 'status' => 'disabled', ]); $user = auth()->user(); $actorId = $user instanceof User ? (int) $user->getKey() : null; $actorEmail = $user instanceof User ? $user->email : null; $actorName = $user instanceof User ? $user->name : null; $auditLogger->log( tenant: $tenant, action: 'provider_connection.disabled', context: [ 'metadata' => [ 'provider' => $record->provider, 'entra_tenant_id' => $record->entra_tenant_id, 'from_status' => $previousStatus, ], ], actorId: $actorId, actorEmail: $actorEmail, actorName: $actorName, resourceType: 'provider_connection', resourceId: (string) $record->getKey(), status: 'success', ); Notification::make() ->title('Provider connection disabled') ->warning() ->send(); }) ) ->preserveVisibility() ->requireCapability(Capabilities::PROVIDER_MANAGE) ->apply(), ]) ->label('More') ->icon('heroicon-o-ellipsis-vertical') ->color('gray'), ]) ->bulkActions([]); } public static function getEloquentQuery(): Builder { $query = parent::getEloquentQuery() ->with('tenant'); return static::applyMembershipScope($query) ->latest('provider_connections.id'); } public static function getPages(): array { return [ 'index' => Pages\ListProviderConnections::route('/'), 'create' => Pages\CreateProviderConnection::route('/create'), 'view' => Pages\ViewProviderConnection::route('/{record}'), 'edit' => Pages\EditProviderConnection::route('/{record}/edit'), ]; } private static function normalizeTenantExternalId(mixed $tenant): ?string { if ($tenant instanceof Tenant) { return (string) $tenant->external_id; } if (is_string($tenant) && $tenant !== '') { return $tenant; } if (is_numeric($tenant)) { $tenantModel = Tenant::query()->whereKey((int) $tenant)->first(); if ($tenantModel instanceof Tenant) { return (string) $tenantModel->external_id; } } return null; } /** * @param array $parameters */ public static function getUrl(?string $name = null, array $parameters = [], bool $isAbsolute = true, ?string $panel = null, ?Model $tenant = null, bool $shouldGuessMissingParameters = false): string { $panel ??= 'admin'; $tenantExternalId = null; if (array_key_exists('tenant', $parameters)) { $tenantExternalId = static::normalizeTenantExternalId($parameters['tenant']); unset($parameters['tenant']); } if ($tenantExternalId === null && $tenant instanceof Tenant) { $tenantExternalId = (string) $tenant->external_id; } if ($tenantExternalId === null) { $record = $parameters['record'] ?? null; if ($record instanceof ProviderConnection) { $tenantExternalId = static::resolveTenantForRecord($record)?->external_id; } } if ($tenantExternalId === null) { $tenantExternalId = static::resolveScopedTenant()?->external_id; } if (! array_key_exists('tenant_id', $parameters) && is_string($tenantExternalId) && $tenantExternalId !== '') { $parameters['tenant_id'] = $tenantExternalId; } return parent::getUrl($name, $parameters, $isAbsolute, $panel, null, $shouldGuessMissingParameters); } }