314 lines
11 KiB
PHP
314 lines
11 KiB
PHP
<?php
|
|
|
|
namespace App\Filament\Resources\ProviderConnectionResource\Pages;
|
|
|
|
use App\Filament\Resources\ProviderConnectionResource;
|
|
use App\Jobs\ProviderComplianceSnapshotJob;
|
|
use App\Jobs\ProviderInventorySyncJob;
|
|
use App\Models\OperationRun;
|
|
use App\Models\ProviderConnection;
|
|
use App\Models\Tenant;
|
|
use App\Models\User;
|
|
use App\Services\Auth\CapabilityResolver;
|
|
use App\Services\Intune\AuditLogger;
|
|
use App\Services\Providers\ProviderConnectionMutationService;
|
|
use App\Services\Providers\ProviderOperationStartGate;
|
|
use App\Services\Verification\StartVerification;
|
|
use App\Support\Auth\Capabilities;
|
|
use App\Support\OperationRunLinks;
|
|
use App\Support\OpsUx\OperationUxPresenter;
|
|
use App\Support\OpsUx\OpsUxBrowserEvents;
|
|
use App\Support\Providers\ProviderConnectionType;
|
|
use App\Support\Rbac\UiEnforcement;
|
|
use Filament\Actions;
|
|
use Filament\Actions\Action;
|
|
use Filament\Forms\Components\TextInput;
|
|
use Filament\Notifications\Notification;
|
|
use Filament\Resources\Pages\EditRecord;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
|
|
class EditProviderConnection extends EditRecord
|
|
{
|
|
protected static string $resource = ProviderConnectionResource::class;
|
|
|
|
public ?string $scopedTenantExternalId = null;
|
|
|
|
protected bool $shouldMakeDefault = false;
|
|
|
|
protected bool $defaultWasChanged = false;
|
|
|
|
public function mount($record): void
|
|
{
|
|
parent::mount($record);
|
|
|
|
$recordTenant = $this->record instanceof ProviderConnection
|
|
? ProviderConnectionResource::resolveTenantForRecord($this->record)
|
|
: null;
|
|
|
|
if ($recordTenant instanceof Tenant) {
|
|
$this->scopedTenantExternalId = (string) $recordTenant->external_id;
|
|
|
|
return;
|
|
}
|
|
|
|
$tenantIdFromQuery = request()->query('tenant_id');
|
|
|
|
if (is_string($tenantIdFromQuery) && $tenantIdFromQuery !== '') {
|
|
$this->scopedTenantExternalId = $tenantIdFromQuery;
|
|
|
|
return;
|
|
}
|
|
|
|
$tenant = request()->route('tenant');
|
|
|
|
if ($tenant instanceof Tenant) {
|
|
$this->scopedTenantExternalId = (string) $tenant->external_id;
|
|
|
|
return;
|
|
}
|
|
|
|
if (is_string($tenant) && $tenant !== '') {
|
|
$this->scopedTenantExternalId = $tenant;
|
|
}
|
|
}
|
|
|
|
protected function mutateFormDataBeforeSave(array $data): array
|
|
{
|
|
$this->shouldMakeDefault = (bool) ($data['is_default'] ?? false);
|
|
unset($data['is_default']);
|
|
|
|
return $data;
|
|
}
|
|
|
|
protected function afterSave(): void
|
|
{
|
|
$record = $this->getRecord();
|
|
|
|
$tenant = $record instanceof ProviderConnection
|
|
? ($record->tenant ?? $this->currentTenant())
|
|
: $this->currentTenant();
|
|
|
|
if (! $tenant instanceof Tenant) {
|
|
return;
|
|
}
|
|
|
|
$changedFields = array_values(array_diff(array_keys($record->getChanges()), ['updated_at']));
|
|
|
|
if ($this->shouldMakeDefault && ! $record->is_default) {
|
|
$record->makeDefault();
|
|
$this->defaultWasChanged = true;
|
|
}
|
|
|
|
$hasDefault = $tenant->providerConnections()
|
|
->where('provider', $record->provider)
|
|
->where('is_default', true)
|
|
->exists();
|
|
|
|
if (! $hasDefault) {
|
|
$record->makeDefault();
|
|
$this->defaultWasChanged = true;
|
|
}
|
|
|
|
$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;
|
|
|
|
if ($changedFields !== []) {
|
|
app(AuditLogger::class)->log(
|
|
tenant: $tenant,
|
|
action: 'provider_connection.updated',
|
|
context: [
|
|
'metadata' => [
|
|
'provider' => $record->provider,
|
|
'entra_tenant_id' => $record->entra_tenant_id,
|
|
'fields' => $changedFields,
|
|
],
|
|
],
|
|
actorId: $actorId,
|
|
actorEmail: $actorEmail,
|
|
actorName: $actorName,
|
|
resourceType: 'provider_connection',
|
|
resourceId: (string) $record->getKey(),
|
|
status: 'success',
|
|
);
|
|
}
|
|
|
|
if ($this->defaultWasChanged) {
|
|
app(AuditLogger::class)->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',
|
|
);
|
|
}
|
|
}
|
|
|
|
protected function getHeaderActions(): array
|
|
{
|
|
$tenant = $this->currentTenant();
|
|
|
|
return [
|
|
Actions\DeleteAction::make()
|
|
->visible(false),
|
|
|
|
Actions\ActionGroup::make([
|
|
UiEnforcement::forAction(
|
|
Action::make('view_last_check_run')
|
|
->label('View last check run')
|
|
->icon('heroicon-o-eye')
|
|
->color('gray')
|
|
->visible(fn (ProviderConnection $record): bool => $tenant instanceof Tenant
|
|
&& OperationRun::query()
|
|
->where('tenant_id', $tenant->getKey())
|
|
->where('type', 'provider.connection.check')
|
|
->where('context->provider_connection_id', (int) $record->getKey())
|
|
->exists())
|
|
->url(function (ProviderConnection $record): ?string {
|
|
$tenant = $this->currentTenant();
|
|
|
|
if (! $tenant instanceof Tenant) {
|
|
return null;
|
|
}
|
|
|
|
$run = OperationRun::query()
|
|
->where('tenant_id', $tenant->getKey())
|
|
->where('type', 'provider.connection.check')
|
|
->where('context->provider_connection_id', (int) $record->getKey())
|
|
->orderByDesc('id')
|
|
->first();
|
|
|
|
if (! $run instanceof OperationRun) {
|
|
return null;
|
|
}
|
|
|
|
return OperationRunLinks::view($run, $tenant);
|
|
})
|
|
)
|
|
->requireCapability(Capabilities::PROVIDER_VIEW)
|
|
->tooltip('You do not have permission to view provider connections.')
|
|
->preserveVisibility()
|
|
->apply(),
|
|
ProviderConnectionResource::makeCheckConnectionAction(),
|
|
ProviderConnectionResource::makeInventorySyncAction(),
|
|
ProviderConnectionResource::makeComplianceSnapshotAction(),
|
|
ProviderConnectionResource::makeSetDefaultAction(),
|
|
ProviderConnectionResource::makeEnableDedicatedOverrideAction(
|
|
source: 'provider_connection.edit_page',
|
|
modalDescription: 'Dedicated credentials are stored encrypted and reset consent to the dedicated app registration.',
|
|
),
|
|
ProviderConnectionResource::makeRotateDedicatedCredentialAction(
|
|
modalDescription: 'Stores a replacement dedicated client secret and refreshes dedicated identity state.',
|
|
),
|
|
ProviderConnectionResource::makeDeleteDedicatedCredentialAction(
|
|
modalDescription: 'Deletes the dedicated credential and leaves the connection blocked until a replacement is added or the type is reverted.',
|
|
),
|
|
ProviderConnectionResource::makeRevertToPlatformAction(
|
|
source: 'provider_connection.edit_page',
|
|
modalDescription: 'Reverts the connection to the platform-managed identity and removes any dedicated credential.',
|
|
),
|
|
ProviderConnectionResource::makeEnableConnectionAction(),
|
|
ProviderConnectionResource::makeDisableConnectionAction(),
|
|
])
|
|
->label('Actions')
|
|
->icon('heroicon-o-ellipsis-vertical')
|
|
->color('gray'),
|
|
];
|
|
}
|
|
|
|
protected function getFormActions(): array
|
|
{
|
|
$tenant = $this->currentTenant();
|
|
|
|
$user = auth()->user();
|
|
|
|
if (! $tenant instanceof Tenant || ! $user instanceof User) {
|
|
return [
|
|
$this->getCancelFormAction(),
|
|
];
|
|
}
|
|
|
|
$capabilityResolver = app(CapabilityResolver::class);
|
|
|
|
if ($capabilityResolver->can($user, $tenant, Capabilities::PROVIDER_MANAGE)) {
|
|
return parent::getFormActions();
|
|
}
|
|
|
|
return [
|
|
$this->getCancelFormAction(),
|
|
];
|
|
}
|
|
|
|
protected function handleRecordUpdate(Model $record, array $data): Model
|
|
{
|
|
$tenant = $record instanceof ProviderConnection
|
|
? ($record->tenant ?? $this->currentTenant())
|
|
: $this->currentTenant();
|
|
|
|
$user = auth()->user();
|
|
|
|
if (! $tenant instanceof Tenant || ! $user instanceof User) {
|
|
abort(404);
|
|
}
|
|
|
|
$capabilityResolver = app(CapabilityResolver::class);
|
|
|
|
if (! $capabilityResolver->isMember($user, $tenant)) {
|
|
abort(404);
|
|
}
|
|
|
|
if (! $capabilityResolver->can($user, $tenant, Capabilities::PROVIDER_MANAGE)) {
|
|
abort(403);
|
|
}
|
|
|
|
return parent::handleRecordUpdate($record, $data);
|
|
}
|
|
|
|
private function currentTenant(): ?Tenant
|
|
{
|
|
if (isset($this->record) && $this->record instanceof ProviderConnection) {
|
|
$recordTenant = ProviderConnectionResource::resolveTenantForRecord($this->record);
|
|
|
|
if ($recordTenant instanceof Tenant) {
|
|
return $recordTenant;
|
|
}
|
|
}
|
|
|
|
if (is_string($this->scopedTenantExternalId) && $this->scopedTenantExternalId !== '') {
|
|
return Tenant::query()
|
|
->where('external_id', $this->scopedTenantExternalId)
|
|
->first();
|
|
}
|
|
|
|
$tenant = request()->route('tenant');
|
|
|
|
if ($tenant instanceof Tenant) {
|
|
return $tenant;
|
|
}
|
|
|
|
if (is_string($tenant) && $tenant !== '') {
|
|
return Tenant::query()
|
|
->where('external_id', $tenant)
|
|
->first();
|
|
}
|
|
|
|
$tenantFromCreateResolution = ProviderConnectionResource::resolveTenantForCreate();
|
|
|
|
if ($tenantFromCreateResolution instanceof Tenant) {
|
|
return $tenantFromCreateResolution;
|
|
}
|
|
|
|
return Tenant::current();
|
|
}
|
|
}
|