Implements spec 424 with comparable renderable capture/readiness changes and supporting tests/spec artifacts. Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #491
139 lines
5.7 KiB
PHP
139 lines
5.7 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Models\OperationRun;
|
|
use App\Models\ProviderConnection;
|
|
use App\Models\TenantConfigurationResource;
|
|
use App\Models\TenantConfigurationResourceEvidence;
|
|
use App\Services\Graph\GraphClientInterface;
|
|
use App\Services\Graph\GraphResponse;
|
|
use App\Services\TenantConfiguration\GenericContentEvidenceCaptureService;
|
|
use App\Services\TenantConfiguration\ResourceTypeRegistry;
|
|
use App\Support\OperationRunOutcome;
|
|
use App\Support\OperationRunStatus;
|
|
use App\Support\OperationRunType;
|
|
use App\Support\TenantConfiguration\CaptureOutcome;
|
|
use App\Support\TenantConfiguration\CoverageLevel;
|
|
|
|
it('Spec421 promotes current content-backed Entra evidence and keeps planning types blocked', function (): void {
|
|
app(ResourceTypeRegistry::class)->syncDefaults();
|
|
config()->set('graph_contracts.types.conditionalAccessPolicy.volatile_fields', ['modifiedDateTime']);
|
|
|
|
[$user, $environment] = createMinimalUserWithTenant(role: 'owner');
|
|
$connection = ProviderConnection::factory()->withCredential()->create([
|
|
'workspace_id' => (int) $environment->workspace_id,
|
|
'managed_environment_id' => (int) $environment->getKey(),
|
|
]);
|
|
$graph = spec421PromotionGraphClient();
|
|
app()->instance(GraphClientInterface::class, $graph);
|
|
|
|
$result = app(GenericContentEvidenceCaptureService::class)->capture(
|
|
tenant: $environment,
|
|
providerConnection: $connection,
|
|
operationRun: spec421PromotionRun($user, $environment, $connection, [
|
|
'conditionalAccessPolicy',
|
|
'securityDefaults',
|
|
'application',
|
|
'servicePrincipal',
|
|
'roleDefinition',
|
|
'administrativeUnit',
|
|
]),
|
|
canonicalTypes: [
|
|
'conditionalAccessPolicy',
|
|
'securityDefaults',
|
|
'application',
|
|
'servicePrincipal',
|
|
'roleDefinition',
|
|
'administrativeUnit',
|
|
],
|
|
);
|
|
$outcomes = collect($result['outcomes'])->keyBy('canonical_type');
|
|
$resources = TenantConfigurationResource::query()->get()->keyBy('canonical_type');
|
|
$evidenceByResourceId = TenantConfigurationResourceEvidence::query()->get()->keyBy('resource_id');
|
|
|
|
expect($graph->calls)->toBe(['conditionalAccessPolicy', 'securityDefaults'])
|
|
->and($resources)->toHaveCount(2)
|
|
->and($evidenceByResourceId)->toHaveCount(2)
|
|
->and($evidenceByResourceId[$resources['conditionalAccessPolicy']->getKey()]->coverage_level)->toBe(CoverageLevel::Renderable)
|
|
->and($evidenceByResourceId[$resources['securityDefaults']->getKey()]->coverage_level)->toBe(CoverageLevel::Renderable)
|
|
->and($outcomes['conditionalAccessPolicy']['outcome'])->toBe(CaptureOutcome::Captured->value)
|
|
->and($outcomes['securityDefaults']['outcome'])->toBe(CaptureOutcome::Captured->value)
|
|
->and($outcomes['application']['outcome'])->toBe(CaptureOutcome::BlockedUnsupported->value)
|
|
->and($outcomes['servicePrincipal']['outcome'])->toBe(CaptureOutcome::BlockedUnsupported->value)
|
|
->and($outcomes['roleDefinition']['outcome'])->toBe(CaptureOutcome::BlockedUnsupported->value)
|
|
->and($outcomes['administrativeUnit']['outcome'])->toBe(CaptureOutcome::BlockedUnsupported->value);
|
|
});
|
|
|
|
function spec421PromotionRun($user, $environment, ProviderConnection $connection, array $resourceTypes): OperationRun
|
|
{
|
|
return OperationRun::factory()->withUser($user)->forTenant($environment)->create([
|
|
'type' => OperationRunType::TenantConfigurationCapture->value,
|
|
'status' => OperationRunStatus::Queued->value,
|
|
'outcome' => OperationRunOutcome::Pending->value,
|
|
'context' => [
|
|
'target_scope' => [
|
|
'workspace_id' => (int) $environment->workspace_id,
|
|
'managed_environment_id' => (int) $environment->getKey(),
|
|
'provider_connection_id' => (int) $connection->getKey(),
|
|
],
|
|
'resource_types' => $resourceTypes,
|
|
'required_capability' => 'evidence.manage',
|
|
],
|
|
]);
|
|
}
|
|
|
|
function spec421PromotionGraphClient(): GraphClientInterface
|
|
{
|
|
return new class implements GraphClientInterface
|
|
{
|
|
public array $calls = [];
|
|
|
|
public function listPolicies(string $policyType, array $options = []): GraphResponse
|
|
{
|
|
$this->calls[] = $policyType;
|
|
|
|
if ($policyType === 'securityDefaults') {
|
|
return new GraphResponse(true, [
|
|
'id' => 'securityDefaults',
|
|
'displayName' => 'Security Defaults',
|
|
'isEnabled' => true,
|
|
]);
|
|
}
|
|
|
|
return new GraphResponse(true, [[
|
|
'id' => 'cap-1',
|
|
'displayName' => 'Require MFA',
|
|
'state' => 'enabled',
|
|
'conditions' => ['users' => ['includeUsers' => ['All']]],
|
|
'grantControls' => ['builtInControls' => ['mfa']],
|
|
]]);
|
|
}
|
|
|
|
public function getPolicy(string $policyType, string $policyId, array $options = []): GraphResponse
|
|
{
|
|
return new GraphResponse(false, [], 501);
|
|
}
|
|
|
|
public function getOrganization(array $options = []): GraphResponse
|
|
{
|
|
return new GraphResponse(false, [], 501);
|
|
}
|
|
|
|
public function applyPolicy(string $policyType, string $policyId, array $payload, array $options = []): GraphResponse
|
|
{
|
|
return new GraphResponse(false, [], 501);
|
|
}
|
|
|
|
public function getServicePrincipalPermissions(array $options = []): GraphResponse
|
|
{
|
|
return new GraphResponse(false, [], 501);
|
|
}
|
|
|
|
public function request(string $method, string $path, array $options = []): GraphResponse
|
|
{
|
|
return new GraphResponse(false, [], 501);
|
|
}
|
|
};
|
|
}
|