TenantAtlas/apps/platform/tests/Support/TenantConfiguration/Spec425Fixtures.php
ahmido 33e496c182 feat: complete spec 425 enta certified compare pack (#492)
Implements spec 425 with Entra certified compare pack support, coverage, guards, evaluator, fixtures, and tests.

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #492
2026-07-01 23:27:16 +00:00

205 lines
8.5 KiB
PHP

<?php
declare(strict_types=1);
namespace Tests\Support\TenantConfiguration;
use App\Models\ManagedEnvironment;
use App\Models\OperationRun;
use App\Models\ProviderConnection;
use App\Models\TenantConfigurationResource;
use App\Models\TenantConfigurationResourceEvidence;
use App\Models\TenantConfigurationResourceType;
use App\Services\TenantConfiguration\CoveragePayloadRedactor;
use App\Services\TenantConfiguration\ResourceTypeRegistry;
use App\Services\TenantConfiguration\SupportedScopeResolver;
use App\Support\OperationRunOutcome;
use App\Support\OperationRunStatus;
use App\Support\OperationRunType;
use App\Support\TenantConfiguration\CanonicalKeyKind;
use App\Support\TenantConfiguration\CaptureOutcome;
use App\Support\TenantConfiguration\ClaimState;
use App\Support\TenantConfiguration\CoverageLevel;
use App\Support\TenantConfiguration\EvidenceState;
use App\Support\TenantConfiguration\IdentityState;
final class Spec425Fixtures
{
/**
* @return array<string, mixed>
*/
public static function fixture(string $family, string $name): array
{
$path = base_path("tests/Fixtures/TenantConfiguration/Spec425/{$family}/{$name}.json");
$json = file_get_contents($path);
if ($json === false) {
throw new \RuntimeException("Missing Spec425 fixture [{$family}/{$name}].");
}
$payload = json_decode($json, true, flags: JSON_THROW_ON_ERROR);
if (! is_array($payload)) {
throw new \RuntimeException("Invalid Spec425 fixture [{$family}/{$name}].");
}
return $payload;
}
public static function syncDefaults(): void
{
app(ResourceTypeRegistry::class)->syncDefaults();
app(SupportedScopeResolver::class)->syncDefaults();
}
public static function resourceType(string $canonicalType): TenantConfigurationResourceType
{
return TenantConfigurationResourceType::query()
->active()
->where('canonical_type', $canonicalType)
->orderBy('source_class')
->firstOrFail();
}
public static function createRun(ManagedEnvironment $environment, ProviderConnection $connection): OperationRun
{
return OperationRun::factory()
->forTenant($environment)
->create([
'type' => OperationRunType::TenantConfigurationCapture->value,
'status' => OperationRunStatus::Completed->value,
'outcome' => OperationRunOutcome::Succeeded->value,
'context' => [
'target_scope' => [
'workspace_id' => (int) $environment->workspace_id,
'managed_environment_id' => (int) $environment->getKey(),
'provider_connection_id' => (int) $connection->getKey(),
],
'resource_types' => ['conditionalAccessPolicy', 'securityDefaults'],
'required_capability' => 'evidence.manage',
],
]);
}
/**
* @param array<string, mixed> $payload
* @param array<string, mixed> $resourceOverrides
* @param array<string, mixed> $evidenceOverrides
*/
public static function createResourceWithEvidence(
ManagedEnvironment $environment,
ProviderConnection $connection,
string $canonicalType,
array $payload,
array $resourceOverrides = [],
array $evidenceOverrides = [],
): TenantConfigurationResource {
$resourceType = self::resourceType($canonicalType);
$normalizedPayload = self::normalizedPayload($payload);
$payloadHash = self::payloadHash($normalizedPayload);
$run = self::createRun($environment, $connection);
$sourceResourceId = (string) ($payload['id'] ?? $canonicalType);
/** @var TenantConfigurationResource $resource */
$resource = TenantConfigurationResource::factory()->create(array_replace([
'workspace_id' => (int) $environment->workspace_id,
'managed_environment_id' => (int) $environment->getKey(),
'provider_connection_id' => (int) $connection->getKey(),
'resource_type_id' => (int) $resourceType->getKey(),
'source_class' => $resourceType->source_class,
'canonical_type' => $canonicalType,
'canonical_resource_key' => "{$canonicalType}:graph_object_id:{$sourceResourceId}",
'canonical_key_kind' => CanonicalKeyKind::GraphObjectId->value,
'source_resource_id' => $sourceResourceId,
'source_display_name' => (string) ($payload['displayName'] ?? $payload['name'] ?? $canonicalType),
'source_metadata' => [
'source_contract_key' => self::sourceContractKey($canonicalType),
'source_endpoint' => self::sourceEndpoint($canonicalType),
'source_version' => 'v1.0',
],
'identity_strategy' => $canonicalType === 'securityDefaults'
? 'graph.security_defaults.v1'
: 'graph.conditional_access_policy.v1',
'source_identity' => [
'primary_value' => $sourceResourceId,
'strategy_identifier' => $canonicalType === 'securityDefaults'
? 'graph.security_defaults.v1'
: 'graph.conditional_access_policy.v1',
],
'secondary_identity_keys' => (object) [],
'identity_diagnostics' => (object) [],
'latest_evidence_state' => EvidenceState::ContentBacked->value,
'latest_identity_state' => IdentityState::Stable->value,
'latest_claim_state' => ClaimState::InternalOnly->value,
'latest_payload_hash' => $payloadHash,
'latest_captured_at' => now(),
], $resourceOverrides));
/** @var TenantConfigurationResourceEvidence $evidence */
$evidence = TenantConfigurationResourceEvidence::factory()->create(array_replace([
'resource_id' => (int) $resource->getKey(),
'workspace_id' => (int) $environment->workspace_id,
'managed_environment_id' => (int) $environment->getKey(),
'provider_connection_id' => (int) $connection->getKey(),
'resource_type_id' => (int) $resourceType->getKey(),
'operation_run_id' => (int) $run->getKey(),
'source_contract_key' => self::sourceContractKey($canonicalType),
'source_endpoint' => self::sourceEndpoint($canonicalType),
'source_version' => 'v1.0',
'source_schema_hash' => hash('sha256', $canonicalType),
'source_metadata' => [
'source_contract_key' => self::sourceContractKey($canonicalType),
'source_endpoint' => self::sourceEndpoint($canonicalType),
'source_version' => 'v1.0',
],
'raw_payload' => $payload,
'normalized_payload' => $normalizedPayload,
'payload_hash' => $payloadHash,
'permission_context' => ['scopes_granted' => ['Policy.Read.All']],
'evidence_state' => EvidenceState::ContentBacked->value,
'coverage_level' => CoverageLevel::Renderable->value,
'capture_outcome' => CaptureOutcome::Captured->value,
'captured_at' => now(),
], $evidenceOverrides));
$resource->forceFill([
'latest_evidence_id' => (int) $evidence->getKey(),
'latest_payload_hash' => (string) $evidence->payload_hash,
'latest_captured_at' => $evidence->captured_at,
])->save();
return $resource->refresh();
}
/**
* @param array<string, mixed> $payload
* @return array<string, mixed>
*/
public static function normalizedPayload(array $payload): array
{
$redacted = app(CoveragePayloadRedactor::class)->redact($payload);
return is_array($redacted) ? $redacted : [];
}
/**
* @param array<string, mixed> $payload
*/
public static function payloadHash(array $payload): string
{
return hash('sha256', json_encode($payload, JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR));
}
public static function sourceContractKey(string $canonicalType): string
{
return $canonicalType === 'securityDefaults' ? 'securityDefaults' : 'conditionalAccessPolicy';
}
public static function sourceEndpoint(string $canonicalType): string
{
return $canonicalType === 'securityDefaults'
? '/policies/identitySecurityDefaultsEnforcementPolicy'
: '/identity/conditionalAccess/policies';
}
}