TenantAtlas/apps/platform/app/Services/TenantConfiguration/ExchangePowerShellEvidenceNormalizer.php
ahmido 30a6733f78 Spec 436: Exchange content-backed evidence promotion (#503)
Summary: Wire Exchange PowerShell evidence capture through Spec 435 structured envelopes, target normalizers, and hash builder for transportRule, remoteDomain, and inboundConnector; preserve append-only internal content-backed evidence with fail-closed readiness, output, identity, and redaction behavior; add Spec 436 artifacts and focused coverage. Validation: php artisan test --filter=Spec436 --compact PASS, 28 tests / 307 assertions; git diff --cached --check PASS before commit. Product/Ops: Livewire v4 unchanged; provider registration unchanged under apps/platform/bootstrap/providers.php; global search unchanged; no destructive/high-impact actions; no assets/browser/UI/deployment impact.
Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #503
2026-07-09 14:03:10 +00:00

68 lines
2.4 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Services\TenantConfiguration;
final class ExchangePowerShellEvidenceNormalizer
{
/**
* @var list<string>
*/
public const INCLUDED_TYPES = [
'transportRule',
'remoteDomain',
'inboundConnector',
];
public function __construct(
private readonly ExchangeTransportRuleEvidenceNormalizer $transportRules,
private readonly ExchangeRemoteDomainEvidenceNormalizer $remoteDomains,
private readonly ExchangeInboundConnectorEvidenceNormalizer $inboundConnectors,
) {}
public function evaluate(ExchangePowerShellStructuredOutputEnvelope $envelope): ExchangePowerShellNormalizerReadiness
{
return match ($envelope->resourceType) {
'transportRule' => $this->transportRules->evaluate($envelope),
'remoteDomain' => $this->remoteDomains->evaluate($envelope),
'inboundConnector' => $this->inboundConnectors->evaluate($envelope),
default => ExchangePowerShellNormalizerReadiness::blocked(
resourceType: $envelope->resourceType,
definition: [
'resource_type' => $envelope->resourceType,
'source_surface' => ExchangePowerShellCommandContracts::SOURCE_SURFACE,
'included_types' => self::INCLUDED_TYPES,
],
blockers: ['blocked_target_not_ready'],
itemCount: $envelope->itemCount,
),
};
}
/**
* @return list<array{raw_payload: array<string, mixed>, normalized_payload: array<string, mixed>, payload_hash: string}>
*/
public function normalizedEvidenceItems(ExchangePowerShellStructuredOutputEnvelope $envelope): array
{
return match ($envelope->resourceType) {
'transportRule' => $this->transportRules->normalizedEvidenceItems($envelope),
'remoteDomain' => $this->remoteDomains->normalizedEvidenceItems($envelope),
'inboundConnector' => $this->inboundConnectors->normalizedEvidenceItems($envelope),
default => [],
};
}
/**
* @return array<string, array<string, mixed>>
*/
public function definitions(): array
{
return [
'transportRule' => $this->transportRules->definition(),
'remoteDomain' => $this->remoteDomains->definition(),
'inboundConnector' => $this->inboundConnectors->definition(),
];
}
}