TenantAtlas/apps/platform/app/Support/Providers/Capabilities/ProviderCapabilityRegistry.php
ahmido 9374260ae1 feat: add Exchange PowerShell invocation gate (#498)
## Summary
- Adds the Spec 431 Exchange PowerShell invocation gate and operation registration slice.
- Introduces trusted provider operation start handling and read-only Exchange PowerShell runner abstractions.
- Updates provider capability/readiness evaluation and coverage for Exchange PowerShell invocation safety.

## Verification
- `cd apps/platform && ./vendor/bin/sail artisan test tests/Feature/Providers/ProviderCapabilityEvaluationTest.php tests/Feature/Providers/ProviderOperationCapabilityGateTest.php tests/Feature/TenantConfiguration/Spec431ExchangePowerShellInvocationGateTest.php tests/Unit/Providers/ProviderCapabilityRegistryTest.php tests/Unit/Providers/ProviderOperationStartGateTest.php tests/Unit/Support/TenantConfiguration/Spec430ExchangePowerShellCommandAllowlistTest.php tests/Unit/Support/TenantConfiguration/Spec431ExchangePowerShellOperationRegistrationTest.php tests/Unit/Verification/ManagedEnvironmentPermissionCapabilityMappingTest.php`
- Result: 80 passed, 685 assertions.

## Product Surface / Ops
- Rendered UI surface changed: N/A.
- Filament/Livewire surface changed: N/A.
- Deployment impact: config/runtime service changes only; no migrations, queues, or assets added.

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #498
2026-07-07 15:23:29 +00:00

122 lines
4.6 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Support\Providers\Capabilities;
use App\Support\Providers\ProviderReasonCodes;
use InvalidArgumentException;
final class ProviderCapabilityRegistry
{
/**
* @return array<string, ProviderCapabilityDefinition>
*/
public function definitions(): array
{
return [
'provider_connection_check' => new ProviderCapabilityDefinition(
key: 'provider_connection_check',
label: 'Provider connection check',
operationTypes: ['provider.connection.check'],
providerRequirementKeys: ['permissions.admin_consent'],
defaultReasonCode: ProviderReasonCodes::ProviderConsentMissing,
),
'inventory_read' => new ProviderCapabilityDefinition(
key: 'inventory_read',
label: 'Inventory read',
operationTypes: ['inventory.sync'],
providerRequirementKeys: ['permissions.intune_configuration', 'permissions.intune_apps'],
defaultReasonCode: ProviderReasonCodes::ProviderPermissionMissing,
),
'configuration_read' => new ProviderCapabilityDefinition(
key: 'configuration_read',
label: 'Configuration read',
operationTypes: ['compliance.snapshot'],
providerRequirementKeys: ['permissions.intune_configuration'],
defaultReasonCode: ProviderReasonCodes::ProviderPermissionMissing,
),
'restore_execute' => new ProviderCapabilityDefinition(
key: 'restore_execute',
label: 'Restore execute',
operationTypes: ['restore.execute'],
providerRequirementKeys: ['permissions.intune_configuration', 'permissions.intune_rbac_assignments'],
defaultReasonCode: ProviderReasonCodes::IntuneRbacPermissionMissing,
),
'directory_groups_read' => new ProviderCapabilityDefinition(
key: 'directory_groups_read',
label: 'Directory groups read',
operationTypes: ['directory.groups.sync'],
providerRequirementKeys: ['permissions.directory_groups'],
defaultReasonCode: ProviderReasonCodes::ProviderPermissionMissing,
),
'directory_role_definitions_read' => new ProviderCapabilityDefinition(
key: 'directory_role_definitions_read',
label: 'Directory role definitions read',
operationTypes: ['directory.role_definitions.sync'],
providerRequirementKeys: ['provider.directory_role_definitions', 'permissions.admin_consent'],
defaultReasonCode: ProviderReasonCodes::ProviderPermissionMissing,
),
'exchange_powershell_invoke' => new ProviderCapabilityDefinition(
key: 'exchange_powershell_invoke',
label: 'Exchange PowerShell invocation',
operationTypes: ['tenant_configuration.exchange_powershell_invocation'],
providerRequirementKeys: ['provider.exchange_powershell_invocation', 'permissions.admin_consent'],
defaultReasonCode: ProviderReasonCodes::ProviderPermissionMissing,
),
];
}
/**
* @return array<string, ProviderCapabilityDefinition>
*/
public function all(): array
{
return $this->definitions();
}
/**
* @return array<int, string>
*/
public function keys(): array
{
return array_keys($this->definitions());
}
public function get(string $key): ProviderCapabilityDefinition
{
$key = trim($key);
$definition = $this->definitions()[$key] ?? null;
if (! $definition instanceof ProviderCapabilityDefinition) {
throw new InvalidArgumentException("Unknown provider capability key: {$key}");
}
return $definition;
}
/**
* @return array<int, ProviderCapabilityDefinition>
*/
public function forOperationType(string $operationType): array
{
$operationType = trim($operationType);
return array_values(array_filter(
$this->definitions(),
static fn (ProviderCapabilityDefinition $definition): bool => in_array($operationType, $definition->operationTypes, true),
));
}
/**
* @return array<int, string>
*/
public function keysForOperationType(string $operationType): array
{
return array_map(
static fn (ProviderCapabilityDefinition $definition): string => $definition->key,
$this->forOperationType($operationType),
);
}
}