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
524 lines
23 KiB
PHP
524 lines
23 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Filament\Pages\TenantConfiguration\CoverageV2Readiness;
|
|
use App\Models\OperationRun;
|
|
use App\Models\ProviderConnection;
|
|
use App\Models\TenantConfigurationResource;
|
|
use App\Models\TenantConfigurationResourceEvidence;
|
|
use App\Models\TenantConfigurationResourceType;
|
|
use App\Models\User;
|
|
use App\Services\Auth\ManagedEnvironmentAccessDecision;
|
|
use App\Services\Auth\ManagedEnvironmentAccessScopeResolver;
|
|
use App\Services\Graph\GraphClientInterface;
|
|
use App\Services\Graph\GraphResponse;
|
|
use App\Services\TenantConfiguration\CoverageV2ReadinessReadModel;
|
|
use App\Services\TenantConfiguration\GenericContentEvidenceCaptureService;
|
|
use App\Services\TenantConfiguration\ResourceTypeRegistry;
|
|
use App\Services\TenantConfiguration\StartTenantConfigurationCapture;
|
|
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;
|
|
use App\Support\TenantConfiguration\RestoreTier;
|
|
use App\Support\TenantConfiguration\SourceClass;
|
|
use App\Support\TenantConfiguration\SupportState;
|
|
use App\Support\Workspaces\WorkspaceContext;
|
|
use Filament\Facades\Filament;
|
|
use Illuminate\Auth\Access\AuthorizationException;
|
|
use Illuminate\Support\Facades\Queue;
|
|
|
|
it('Spec424 syncs Security Defaults as one active graph v1 fallback registry row', function (): void {
|
|
app(ResourceTypeRegistry::class)->syncDefaults();
|
|
|
|
$rows = TenantConfigurationResourceType::query()
|
|
->active()
|
|
->where('canonical_type', 'securityDefaults')
|
|
->get();
|
|
$type = $rows->sole();
|
|
|
|
expect($rows)->toHaveCount(1)
|
|
->and($type->source_class)->toBe(SourceClass::GraphV1Fallback)
|
|
->and($type->support_state)->toBe(SupportState::FallbackSupported)
|
|
->and($type->default_coverage_level)->toBe(CoverageLevel::Renderable)
|
|
->and($type->default_evidence_state)->toBe(EvidenceState::NotCaptured)
|
|
->and($type->default_identity_state)->toBe(IdentityState::Stable)
|
|
->and($type->default_claim_state)->toBe(ClaimState::InternalOnly)
|
|
->and($type->restore_tier)->toBe(RestoreTier::NotRestorable)
|
|
->and((bool) $type->allows_beta_claims)->toBeFalse()
|
|
->and((bool) $type->allows_graph_fallback_claims)->toBeTrue()
|
|
->and((bool) $type->allows_certified_claims)->toBeFalse()
|
|
->and($type->metadata['registry_only'])->toBeFalse()
|
|
->and($type->metadata['source_contract_key'])->toBe('securityDefaults')
|
|
->and($type->metadata['source_version'])->toBe('v1.0')
|
|
->and($type->metadata['customer_claims_allowed'])->toBeFalse()
|
|
->and($type->metadata['certification_allowed'])->toBeFalse()
|
|
->and($type->metadata['restore_allowed'])->toBeFalse();
|
|
|
|
expect(TenantConfigurationResourceType::query()
|
|
->active()
|
|
->where('canonical_type', 'securityDefaults')
|
|
->where('source_class', SourceClass::Tcm->value)
|
|
->count())->toBe(0);
|
|
});
|
|
|
|
it('Spec424 default sync command deactivates stale Security Defaults TCM planning rows', function (): void {
|
|
TenantConfigurationResourceType::query()->updateOrCreate(
|
|
[
|
|
'canonical_type' => 'securityDefaults',
|
|
'source_class' => SourceClass::Tcm->value,
|
|
],
|
|
[
|
|
'display_name' => 'Security defaults legacy planning row',
|
|
'description' => 'Legacy Security Defaults planning row.',
|
|
'workload' => 'entra',
|
|
'resource_class' => 'configuration',
|
|
'support_state' => SupportState::OutOfScope->value,
|
|
'default_coverage_level' => CoverageLevel::Detected->value,
|
|
'default_evidence_state' => EvidenceState::NotCaptured->value,
|
|
'default_identity_state' => IdentityState::Derived->value,
|
|
'default_claim_state' => ClaimState::InternalOnly->value,
|
|
'restore_tier' => RestoreTier::NotRestorable->value,
|
|
'allows_beta_claims' => false,
|
|
'allows_graph_fallback_claims' => false,
|
|
'allows_certified_claims' => false,
|
|
'is_active' => true,
|
|
'metadata' => ['catalog_import_batch' => 'spec_419_seeded_representative_manifest'],
|
|
],
|
|
);
|
|
|
|
$this->artisan('tenant-configuration:sync-defaults')
|
|
->assertSuccessful();
|
|
|
|
$activeRows = TenantConfigurationResourceType::query()
|
|
->active()
|
|
->where('canonical_type', 'securityDefaults')
|
|
->get();
|
|
$activeType = $activeRows->sole();
|
|
|
|
expect($activeRows)->toHaveCount(1)
|
|
->and($activeType->source_class)->toBe(SourceClass::GraphV1Fallback)
|
|
->and(TenantConfigurationResourceType::query()
|
|
->where('canonical_type', 'securityDefaults')
|
|
->where('source_class', SourceClass::Tcm->value)
|
|
->where('is_active', true)
|
|
->exists())->toBeFalse();
|
|
});
|
|
|
|
it('Spec424 captures singleton Security Defaults evidence with internal-only renderable posture', function (): void {
|
|
app(ResourceTypeRegistry::class)->syncDefaults();
|
|
|
|
[$user, $environment] = createMinimalUserWithTenant(role: 'owner');
|
|
$connection = ProviderConnection::factory()->withCredential()->create([
|
|
'workspace_id' => (int) $environment->workspace_id,
|
|
'managed_environment_id' => (int) $environment->getKey(),
|
|
'scopes_granted' => ['Policy.Read.All'],
|
|
]);
|
|
$graph = spec424CaptureGraphClient([
|
|
spec424CaptureSecurityDefaultsPayload([
|
|
'@odata.context' => 'https://graph.microsoft.com/v1.0/$metadata#policies/identitySecurityDefaultsEnforcementPolicy/$entity',
|
|
'clientSecret' => 'spec424-provider-secret',
|
|
]),
|
|
]);
|
|
app()->instance(GraphClientInterface::class, $graph);
|
|
|
|
$result = app(GenericContentEvidenceCaptureService::class)->capture(
|
|
tenant: $environment,
|
|
providerConnection: $connection,
|
|
operationRun: spec424CaptureRun($user, $environment, $connection),
|
|
canonicalTypes: ['securityDefaults'],
|
|
);
|
|
|
|
expect($graph->calls)->toHaveCount(1)
|
|
->and($graph->calls[0]['policy_type'])->toBe('securityDefaults')
|
|
->and($graph->calls[0]['options'])->toHaveKey('client_request_id')
|
|
->and($graph->calls[0]['options'])->not->toHaveKey('top')
|
|
->and($result['summary_counts'])->toMatchArray([
|
|
'total' => 1,
|
|
'processed' => 1,
|
|
'succeeded' => 1,
|
|
'skipped' => 0,
|
|
'failed' => 0,
|
|
])
|
|
->and($result['outcomes'][0]['outcome'])->toBe(CaptureOutcome::Captured->value)
|
|
->and($result['outcomes'][0]['item_count'])->toBe(1)
|
|
->and($result['outcomes'][0]['source_contract_key'])->toBe('securityDefaults');
|
|
|
|
$resource = TenantConfigurationResource::query()->sole();
|
|
$evidence = TenantConfigurationResourceEvidence::query()->sole();
|
|
|
|
expect($resource->canonical_type)->toBe('securityDefaults')
|
|
->and($resource->source_class)->toBe(SourceClass::GraphV1Fallback)
|
|
->and($resource->canonical_key_kind)->toBe(CanonicalKeyKind::GraphObjectId)
|
|
->and($resource->source_resource_id)->toBe('securityDefaults')
|
|
->and($resource->source_display_name)->toBe('Security Defaults')
|
|
->and($resource->latest_identity_state)->toBe(IdentityState::Stable)
|
|
->and($resource->latest_claim_state)->toBe(ClaimState::InternalOnly)
|
|
->and($resource->source_identity['strategy_identifier'])->toBe('graph.security_defaults.v1')
|
|
->and($resource->source_metadata['source_contract_key'])->toBe('securityDefaults')
|
|
->and($resource->source_metadata['registry_source_class'])->toBe('graph_v1_fallback')
|
|
->and($resource->source_metadata['registry_support_state'])->toBe('fallback_supported');
|
|
|
|
expect($evidence->source_contract_key)->toBe('securityDefaults')
|
|
->and($evidence->source_endpoint)->toBe('/policies/identitySecurityDefaultsEnforcementPolicy')
|
|
->and($evidence->source_version)->toBe('v1.0')
|
|
->and($evidence->coverage_level)->toBe(CoverageLevel::Renderable)
|
|
->and($evidence->evidence_state)->toBe(EvidenceState::ContentBacked)
|
|
->and($evidence->capture_outcome)->toBe(CaptureOutcome::Captured)
|
|
->and($evidence->raw_payload['clientSecret'])->toBe('spec424-provider-secret')
|
|
->and($evidence->normalized_payload)->not->toHaveKey('@odata.context')
|
|
->and($evidence->normalized_payload['clientSecret'])->toBe('[redacted]')
|
|
->and($evidence->permission_context['scopes_granted'])->toBe(['Policy.Read.All'])
|
|
->and($evidence->payload_hash)->toBeString()->toHaveLength(64);
|
|
});
|
|
|
|
it('Spec424 creates no fake evidence when Security Defaults capture is permission blocked', function (): void {
|
|
app(ResourceTypeRegistry::class)->syncDefaults();
|
|
|
|
[$user, $environment] = createMinimalUserWithTenant(role: 'owner');
|
|
$connection = ProviderConnection::factory()->withCredential()->create([
|
|
'workspace_id' => (int) $environment->workspace_id,
|
|
'managed_environment_id' => (int) $environment->getKey(),
|
|
'scopes_granted' => [],
|
|
]);
|
|
$graph = spec424CaptureGraphClient([
|
|
new GraphResponse(false, [], 403, [['message' => 'Forbidden']]),
|
|
]);
|
|
app()->instance(GraphClientInterface::class, $graph);
|
|
|
|
$result = app(GenericContentEvidenceCaptureService::class)->capture(
|
|
tenant: $environment,
|
|
providerConnection: $connection,
|
|
operationRun: spec424CaptureRun($user, $environment, $connection),
|
|
canonicalTypes: ['securityDefaults'],
|
|
);
|
|
|
|
expect($graph->calls)->toHaveCount(1)
|
|
->and($result['outcomes'][0]['outcome'])->toBe(CaptureOutcome::BlockedPermission->value)
|
|
->and($result['outcomes'][0]['reason_code'])->toBe('graph_permission_blocked')
|
|
->and(TenantConfigurationResource::query()->count())->toBe(0)
|
|
->and(TenantConfigurationResourceEvidence::query()->count())->toBe(0);
|
|
});
|
|
|
|
it('Spec424 blocks mismatched operation run scope before provider work', function (): void {
|
|
app(ResourceTypeRegistry::class)->syncDefaults();
|
|
|
|
[$user, $environment] = createMinimalUserWithTenant(role: 'owner');
|
|
$connection = ProviderConnection::factory()->withCredential()->create([
|
|
'workspace_id' => (int) $environment->workspace_id,
|
|
'managed_environment_id' => (int) $environment->getKey(),
|
|
]);
|
|
$graph = spec424CaptureGraphClient([
|
|
spec424CaptureSecurityDefaultsPayload(),
|
|
]);
|
|
app()->instance(GraphClientInterface::class, $graph);
|
|
|
|
$run = spec424CaptureRun($user, $environment, $connection, [
|
|
'securityDefaults',
|
|
], [
|
|
'provider_connection_id' => (int) $connection->getKey() + 1000,
|
|
]);
|
|
|
|
expect(fn () => app(GenericContentEvidenceCaptureService::class)->capture(
|
|
tenant: $environment,
|
|
providerConnection: $connection,
|
|
operationRun: $run,
|
|
canonicalTypes: ['securityDefaults'],
|
|
))->toThrow(InvalidArgumentException::class, 'target scope');
|
|
|
|
expect($graph->calls)->toBe([])
|
|
->and(TenantConfigurationResourceEvidence::query()->count())->toBe(0);
|
|
});
|
|
|
|
it('Spec424 renders Security Defaults inspect summaries and compare details from DB only', function (): void {
|
|
app(ResourceTypeRegistry::class)->syncDefaults();
|
|
|
|
[$user, $environment] = createMinimalUserWithTenant(role: 'owner');
|
|
$connection = ProviderConnection::factory()->withCredential()->create([
|
|
'workspace_id' => (int) $environment->workspace_id,
|
|
'managed_environment_id' => (int) $environment->getKey(),
|
|
'scopes_granted' => ['Policy.Read.All'],
|
|
]);
|
|
app()->instance(GraphClientInterface::class, spec424CaptureGraphClient([
|
|
spec424CaptureSecurityDefaultsPayload(['isEnabled' => false]),
|
|
spec424CaptureSecurityDefaultsPayload(['isEnabled' => true, 'clientSecret' => 'spec424-render-secret']),
|
|
]));
|
|
|
|
app(GenericContentEvidenceCaptureService::class)->capture(
|
|
tenant: $environment,
|
|
providerConnection: $connection,
|
|
operationRun: spec424CaptureRun($user, $environment, $connection),
|
|
canonicalTypes: ['securityDefaults'],
|
|
);
|
|
app(GenericContentEvidenceCaptureService::class)->capture(
|
|
tenant: $environment,
|
|
providerConnection: $connection,
|
|
operationRun: spec424CaptureRun($user, $environment, $connection),
|
|
canonicalTypes: ['securityDefaults'],
|
|
);
|
|
|
|
$resource = TenantConfigurationResource::query()->sole();
|
|
app()->instance(GraphClientInterface::class, spec424FailingGraphClient());
|
|
|
|
$details = assertNoOutboundHttp(fn (): array => app(CoverageV2ReadinessReadModel::class)
|
|
->inspectDetails($resource, $environment, $user));
|
|
$summary = $details['typed_render_summary'] ?? null;
|
|
$encodedSummary = json_encode($summary, JSON_THROW_ON_ERROR);
|
|
|
|
expect($summary)->toBeArray()
|
|
->and($summary['resource_type'])->toBe('Security Defaults')
|
|
->and($summary['state'])->toBe('Enabled')
|
|
->and($summary['compare_summary']['status'])->toBe('Material changes detected')
|
|
->and($summary['compare_summary']['changed'])->toBeTrue()
|
|
->and(collect($summary['compare_summary']['changes'])->pluck('label')->all())->toContain('Enabled State')
|
|
->and($encodedSummary)->not->toContain('spec424-render-secret')
|
|
->and($encodedSummary)->not->toContain('source_endpoint')
|
|
->and($encodedSummary)->not->toContain('identitySecurityDefaultsEnforcementPolicy');
|
|
});
|
|
|
|
it('Spec424 denies Security Defaults inspect and page access outside the actor scope', function (): void {
|
|
app(ResourceTypeRegistry::class)->syncDefaults();
|
|
|
|
[$user, $environment] = createMinimalUserWithTenant(role: 'owner');
|
|
[, $foreignEnvironment] = createMinimalUserWithTenant(role: 'owner');
|
|
$connection = ProviderConnection::factory()->withCredential()->create([
|
|
'workspace_id' => (int) $environment->workspace_id,
|
|
'managed_environment_id' => (int) $environment->getKey(),
|
|
]);
|
|
app()->instance(GraphClientInterface::class, spec424CaptureGraphClient([
|
|
spec424CaptureSecurityDefaultsPayload(),
|
|
]));
|
|
|
|
app(GenericContentEvidenceCaptureService::class)->capture(
|
|
tenant: $environment,
|
|
providerConnection: $connection,
|
|
operationRun: spec424CaptureRun($user, $environment, $connection),
|
|
canonicalTypes: ['securityDefaults'],
|
|
);
|
|
|
|
$resource = TenantConfigurationResource::query()->sole();
|
|
$outsider = User::factory()->create();
|
|
$outsiderDetails = app(CoverageV2ReadinessReadModel::class)->inspectDetails($resource, $environment, $outsider);
|
|
|
|
expect(app(CoverageV2ReadinessReadModel::class)->inspectDetails($resource, $foreignEnvironment, $user))->toBe([])
|
|
->and($outsiderDetails['operation_run_url'] ?? null)->toBeNull();
|
|
|
|
$this->actingAs($outsider)
|
|
->get(CoverageV2Readiness::getUrl(tenant: $environment))
|
|
->assertNotFound();
|
|
});
|
|
|
|
it('Spec424 returns forbidden for Security Defaults readiness when view capability is denied', function (): void {
|
|
app(ResourceTypeRegistry::class)->syncDefaults();
|
|
|
|
[$user, $environment] = createMinimalUserWithTenant(role: 'owner');
|
|
spec424CoverageActingAs($user, $environment);
|
|
|
|
app()->instance(ManagedEnvironmentAccessScopeResolver::class, new class
|
|
{
|
|
public function decision(User $user, $environment, ?string $requiredCapability = null): ManagedEnvironmentAccessDecision
|
|
{
|
|
return new ManagedEnvironmentAccessDecision(
|
|
workspaceId: (int) $environment->workspace_id,
|
|
managedEnvironmentId: (int) $environment->getKey(),
|
|
userId: (int) $user->getKey(),
|
|
workspaceMember: true,
|
|
workspaceRole: 'owner',
|
|
explicitScopeRowsPresent: false,
|
|
managedEnvironmentAllowed: true,
|
|
failedBoundary: 'capability',
|
|
requiredCapability: $requiredCapability,
|
|
capabilityAllowed: false,
|
|
denialHttpStatus: 403,
|
|
);
|
|
}
|
|
});
|
|
|
|
try {
|
|
$this->get(CoverageV2Readiness::getUrl(tenant: $environment))
|
|
->assertForbidden();
|
|
} finally {
|
|
app()->forgetInstance(ManagedEnvironmentAccessScopeResolver::class);
|
|
}
|
|
});
|
|
|
|
it('Spec424 readonly users cannot start Security Defaults capture', function (): void {
|
|
Queue::fake();
|
|
|
|
[$user, $environment] = createMinimalUserWithTenant(role: 'readonly', workspaceRole: 'readonly');
|
|
$connection = ProviderConnection::factory()->create([
|
|
'workspace_id' => (int) $environment->workspace_id,
|
|
'managed_environment_id' => (int) $environment->getKey(),
|
|
]);
|
|
|
|
expect(fn () => app(StartTenantConfigurationCapture::class)->start($environment, $connection, $user, [
|
|
'securityDefaults',
|
|
]))->toThrow(AuthorizationException::class);
|
|
|
|
Queue::assertNothingPushed();
|
|
});
|
|
|
|
it('Spec424 adds no tenant id ownership, mini-platform, route, Filament resource, or customer output file', function (): void {
|
|
$newPlatformFiles = collect([
|
|
'apps/platform/app/Filament',
|
|
'apps/platform/app/Models',
|
|
'apps/platform/routes',
|
|
'apps/platform/database/migrations',
|
|
])
|
|
->flatMap(fn (string $path): array => glob(repo_path($path).'/**/*424*') ?: [])
|
|
->map(fn (string $path): string => str_replace(repo_path().DIRECTORY_SEPARATOR, '', $path))
|
|
->values()
|
|
->all();
|
|
|
|
$runtimeFiles = [
|
|
app_path('Services/TenantConfiguration/ClaimGuard.php'),
|
|
app_path('Services/TenantConfiguration/CoverageIdentityStrategyRegistry.php'),
|
|
app_path('Services/TenantConfiguration/CoverageSourceContractResolver.php'),
|
|
app_path('Services/TenantConfiguration/EntraComparablePayloadNormalizer.php'),
|
|
app_path('Services/TenantConfiguration/EntraCoverageComparator.php'),
|
|
app_path('Services/TenantConfiguration/EntraRenderableSummaryBuilder.php'),
|
|
app_path('Services/TenantConfiguration/ResourceTypeRegistry.php'),
|
|
app_path('Services/Graph/MicrosoftGraphClient.php'),
|
|
config_path('graph_contracts.php'),
|
|
];
|
|
$joinedRuntime = implode("\n", array_map(static fn (string $path): string => file_get_contents($path) ?: '', $runtimeFiles));
|
|
|
|
expect($newPlatformFiles)->toBe([])
|
|
->and($joinedRuntime)->not->toContain('tenant_id')
|
|
->and($joinedRuntime)->not->toContain('ReviewPack')
|
|
->and($joinedRuntime)->not->toContain('customer-ready')
|
|
->and($joinedRuntime)->not->toContain('certification-ready')
|
|
->and($joinedRuntime)->not->toContain('restore-ready');
|
|
});
|
|
|
|
function spec424CaptureRun(
|
|
$user,
|
|
$environment,
|
|
ProviderConnection $connection,
|
|
array $resourceTypes = ['securityDefaults'],
|
|
array $targetScopeOverrides = [],
|
|
): OperationRun {
|
|
return OperationRun::factory()->withUser($user)->forTenant($environment)->create([
|
|
'type' => OperationRunType::TenantConfigurationCapture->value,
|
|
'status' => OperationRunStatus::Queued->value,
|
|
'outcome' => OperationRunOutcome::Pending->value,
|
|
'context' => [
|
|
'target_scope' => array_replace([
|
|
'workspace_id' => (int) $environment->workspace_id,
|
|
'managed_environment_id' => (int) $environment->getKey(),
|
|
'provider_connection_id' => (int) $connection->getKey(),
|
|
], $targetScopeOverrides),
|
|
'resource_types' => $resourceTypes,
|
|
'required_capability' => 'evidence.manage',
|
|
],
|
|
]);
|
|
}
|
|
|
|
function spec424CaptureGraphClient(array $responses): GraphClientInterface
|
|
{
|
|
return new class($responses) implements GraphClientInterface
|
|
{
|
|
public array $calls = [];
|
|
|
|
public function __construct(private array $responses) {}
|
|
|
|
public function listPolicies(string $policyType, array $options = []): GraphResponse
|
|
{
|
|
$this->calls[] = ['policy_type' => $policyType, 'options' => $options];
|
|
$response = array_shift($this->responses);
|
|
|
|
if ($response instanceof GraphResponse) {
|
|
return $response;
|
|
}
|
|
|
|
return new GraphResponse(true, is_array($response) ? $response : []);
|
|
}
|
|
|
|
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);
|
|
}
|
|
};
|
|
}
|
|
|
|
function spec424CoverageActingAs(User $user, $environment): void
|
|
{
|
|
test()->actingAs($user);
|
|
$environment->makeCurrent();
|
|
Filament::setTenant($environment, true);
|
|
|
|
session()->put(WorkspaceContext::SESSION_KEY, (int) $environment->workspace_id);
|
|
}
|
|
|
|
function spec424CaptureSecurityDefaultsPayload(array $overrides = []): array
|
|
{
|
|
return array_replace([
|
|
'id' => 'securityDefaults',
|
|
'displayName' => 'Security Defaults',
|
|
'description' => 'Tenant-wide Security Defaults policy.',
|
|
'isEnabled' => true,
|
|
], $overrides);
|
|
}
|
|
|
|
function spec424FailingGraphClient(): GraphClientInterface
|
|
{
|
|
return new class implements GraphClientInterface
|
|
{
|
|
public function listPolicies(string $policyType, array $options = []): GraphResponse
|
|
{
|
|
throw new RuntimeException('Spec424 render path must not call provider clients.');
|
|
}
|
|
|
|
public function getPolicy(string $policyType, string $policyId, array $options = []): GraphResponse
|
|
{
|
|
throw new RuntimeException('Spec424 render path must not call provider clients.');
|
|
}
|
|
|
|
public function getOrganization(array $options = []): GraphResponse
|
|
{
|
|
throw new RuntimeException('Spec424 render path must not call provider clients.');
|
|
}
|
|
|
|
public function applyPolicy(string $policyType, string $policyId, array $payload, array $options = []): GraphResponse
|
|
{
|
|
throw new RuntimeException('Spec424 render path must not call provider clients.');
|
|
}
|
|
|
|
public function getServicePrincipalPermissions(array $options = []): GraphResponse
|
|
{
|
|
throw new RuntimeException('Spec424 render path must not call provider clients.');
|
|
}
|
|
|
|
public function request(string $method, string $path, array $options = []): GraphResponse
|
|
{
|
|
throw new RuntimeException('Spec424 render path must not call provider clients.');
|
|
}
|
|
};
|
|
}
|