255 lines
9.4 KiB
PHP
255 lines
9.4 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Support\Governance;
|
|
|
|
use App\Support\Inventory\InventoryPolicyTypeMeta;
|
|
|
|
final class GovernanceSubjectTaxonomyRegistry
|
|
{
|
|
/**
|
|
* @var array<string, list<string>>
|
|
*/
|
|
private const DOMAIN_CLASSES = [
|
|
GovernanceDomainKey::Intune->value => [GovernanceSubjectClass::Policy->value],
|
|
GovernanceDomainKey::PlatformFoundation->value => [GovernanceSubjectClass::ConfigurationResource->value],
|
|
GovernanceDomainKey::Entra->value => [GovernanceSubjectClass::Control->value],
|
|
];
|
|
|
|
/**
|
|
* @return list<GovernanceSubjectType>
|
|
*/
|
|
public function all(): array
|
|
{
|
|
return array_values(array_merge(
|
|
$this->policySubjectTypes(),
|
|
$this->foundationSubjectTypes(),
|
|
));
|
|
}
|
|
|
|
/**
|
|
* @return list<GovernanceSubjectType>
|
|
*/
|
|
public function active(): array
|
|
{
|
|
return array_values(array_filter(
|
|
$this->all(),
|
|
static fn (GovernanceSubjectType $subjectType): bool => $subjectType->active,
|
|
));
|
|
}
|
|
|
|
/**
|
|
* @return list<string>
|
|
*/
|
|
public function activeLegacyBucketKeys(string $legacyBucket): array
|
|
{
|
|
$subjectTypes = array_filter(
|
|
$this->active(),
|
|
static fn (GovernanceSubjectType $subjectType): bool => $subjectType->legacyBucket === $legacyBucket,
|
|
);
|
|
|
|
$keys = array_map(
|
|
static fn (GovernanceSubjectType $subjectType): string => $subjectType->subjectTypeKey,
|
|
$subjectTypes,
|
|
);
|
|
|
|
sort($keys, SORT_STRING);
|
|
|
|
return array_values(array_unique($keys));
|
|
}
|
|
|
|
public function find(string $domainKey, string $subjectTypeKey): ?GovernanceSubjectType
|
|
{
|
|
foreach ($this->all() as $subjectType) {
|
|
if ($subjectType->domainKey->value !== trim($domainKey)) {
|
|
continue;
|
|
}
|
|
|
|
if ($subjectType->subjectTypeKey !== trim($subjectTypeKey)) {
|
|
continue;
|
|
}
|
|
|
|
return $subjectType;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public function findBySubjectTypeKey(string $subjectTypeKey, ?string $legacyBucket = null): ?GovernanceSubjectType
|
|
{
|
|
$subjectTypeKey = trim($subjectTypeKey);
|
|
$legacyBucket = is_string($legacyBucket) ? trim($legacyBucket) : null;
|
|
|
|
foreach ($this->all() as $subjectType) {
|
|
if ($subjectType->subjectTypeKey !== $subjectTypeKey) {
|
|
continue;
|
|
}
|
|
|
|
if ($legacyBucket !== null && $subjectType->legacyBucket !== $legacyBucket) {
|
|
continue;
|
|
}
|
|
|
|
return $subjectType;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* @return list<string>
|
|
*/
|
|
public function canonicalNouns(): array
|
|
{
|
|
return ['domain_key', 'subject_class', 'subject_type_key', 'subject_type_label'];
|
|
}
|
|
|
|
public function ownershipDescriptor(?PlatformVocabularyGlossary $glossary = null): RegistryOwnershipDescriptor
|
|
{
|
|
$glossary ??= app(PlatformVocabularyGlossary::class);
|
|
|
|
return $glossary->registry('governance_subject_taxonomy_registry')
|
|
?? RegistryOwnershipDescriptor::fromArray([
|
|
'registry_key' => 'governance_subject_taxonomy_registry',
|
|
'boundary_classification' => PlatformVocabularyGlossary::BOUNDARY_CROSS_DOMAIN_GOVERNANCE,
|
|
'owner_layer' => PlatformVocabularyGlossary::OWNER_PLATFORM_CORE,
|
|
'source_class_or_file' => self::class,
|
|
'canonical_nouns' => $this->canonicalNouns(),
|
|
'allowed_consumers' => ['baseline_scope', 'compare', 'snapshot', 'review'],
|
|
'compatibility_notes' => 'Governed-subject registry lookups remain the canonical bridge from legacy policy-type payloads to platform-safe descriptors.',
|
|
]);
|
|
}
|
|
|
|
public function isKnownDomain(string $domainKey): bool
|
|
{
|
|
return array_key_exists(trim($domainKey), self::DOMAIN_CLASSES);
|
|
}
|
|
|
|
public function allowsSubjectClass(string $domainKey, string $subjectClass): bool
|
|
{
|
|
$domainKey = trim($domainKey);
|
|
$subjectClass = trim($subjectClass);
|
|
|
|
return in_array($subjectClass, self::DOMAIN_CLASSES[$domainKey] ?? [], true);
|
|
}
|
|
|
|
public function supportsFilters(string $domainKey, string $subjectClass): bool
|
|
{
|
|
return false;
|
|
}
|
|
|
|
public function groupLabel(string $domainKey, string $subjectClass): string
|
|
{
|
|
return match ([trim($domainKey), trim($subjectClass)]) {
|
|
[GovernanceDomainKey::Intune->value, GovernanceSubjectClass::Policy->value] => 'Intune policies',
|
|
[GovernanceDomainKey::PlatformFoundation->value, GovernanceSubjectClass::ConfigurationResource->value] => 'Platform foundation configuration resources',
|
|
[GovernanceDomainKey::Entra->value, GovernanceSubjectClass::Control->value] => 'Entra controls',
|
|
default => trim($domainKey).' / '.trim($subjectClass),
|
|
};
|
|
}
|
|
|
|
/**
|
|
* @return list<GovernanceSubjectType>
|
|
*/
|
|
private function policySubjectTypes(): array
|
|
{
|
|
return array_values(array_map(
|
|
function (array $row): GovernanceSubjectType {
|
|
$type = (string) ($row['type'] ?? '');
|
|
$label = (string) ($row['label'] ?? $type);
|
|
$category = is_string($row['category'] ?? null) ? (string) $row['category'] : null;
|
|
$platform = is_string($row['platform'] ?? null) ? (string) $row['platform'] : null;
|
|
$contract = InventoryPolicyTypeMeta::baselineSupportContract($type);
|
|
|
|
return new GovernanceSubjectType(
|
|
domainKey: GovernanceDomainKey::Intune,
|
|
subjectClass: GovernanceSubjectClass::Policy,
|
|
subjectTypeKey: $type,
|
|
label: $label,
|
|
description: $this->descriptionFor($category, $platform),
|
|
captureSupported: in_array($contract['capture_capability'] ?? null, ['supported', 'limited'], true),
|
|
compareSupported: in_array($contract['compare_capability'] ?? null, ['supported', 'limited'], true),
|
|
inventorySupported: true,
|
|
active: true,
|
|
supportMode: $this->supportModeForContract($contract),
|
|
legacyBucket: 'policy_types',
|
|
);
|
|
},
|
|
array_values(array_filter(
|
|
InventoryPolicyTypeMeta::supported(),
|
|
static fn (array $row): bool => filled($row['type'] ?? null),
|
|
)),
|
|
));
|
|
}
|
|
|
|
/**
|
|
* @return list<GovernanceSubjectType>
|
|
*/
|
|
private function foundationSubjectTypes(): array
|
|
{
|
|
return array_values(array_map(
|
|
function (array $row): GovernanceSubjectType {
|
|
$type = (string) ($row['type'] ?? '');
|
|
$label = InventoryPolicyTypeMeta::baselineCompareLabel($type) ?? (string) ($row['label'] ?? $type);
|
|
$category = is_string($row['category'] ?? null) ? (string) $row['category'] : null;
|
|
$platform = is_string($row['platform'] ?? null) ? (string) $row['platform'] : null;
|
|
$contract = InventoryPolicyTypeMeta::baselineSupportContract($type);
|
|
$supported = (bool) data_get($row, 'baseline_compare.supported', false);
|
|
|
|
return new GovernanceSubjectType(
|
|
domainKey: GovernanceDomainKey::PlatformFoundation,
|
|
subjectClass: GovernanceSubjectClass::ConfigurationResource,
|
|
subjectTypeKey: $type,
|
|
label: $label,
|
|
description: $this->descriptionFor($category, $platform),
|
|
captureSupported: in_array($contract['capture_capability'] ?? null, ['supported', 'limited'], true),
|
|
compareSupported: in_array($contract['compare_capability'] ?? null, ['supported', 'limited'], true),
|
|
inventorySupported: in_array($contract['source_model_expected'] ?? null, ['inventory', 'policy'], true),
|
|
active: $supported,
|
|
supportMode: $this->supportModeForContract($contract),
|
|
legacyBucket: 'foundation_types',
|
|
);
|
|
},
|
|
array_values(array_filter(
|
|
InventoryPolicyTypeMeta::foundations(),
|
|
static fn (array $row): bool => filled($row['type'] ?? null),
|
|
)),
|
|
));
|
|
}
|
|
|
|
private function descriptionFor(?string $category, ?string $platform): ?string
|
|
{
|
|
$parts = array_values(array_filter([$category, $platform], static fn (?string $part): bool => filled($part)));
|
|
|
|
if ($parts === []) {
|
|
return null;
|
|
}
|
|
|
|
return implode(' | ', $parts);
|
|
}
|
|
|
|
/**
|
|
* @param array<string, mixed> $contract
|
|
*/
|
|
private function supportModeForContract(array $contract): string
|
|
{
|
|
$capabilities = [
|
|
(string) ($contract['capture_capability'] ?? 'unsupported'),
|
|
(string) ($contract['compare_capability'] ?? 'unsupported'),
|
|
];
|
|
|
|
if (! (bool) ($contract['runtime_valid'] ?? false) && (bool) ($contract['config_supported'] ?? false)) {
|
|
return 'invalid_support_config';
|
|
}
|
|
|
|
if (in_array('supported', $capabilities, true)) {
|
|
return 'supported';
|
|
}
|
|
|
|
if (in_array('limited', $capabilities, true)) {
|
|
return 'limited';
|
|
}
|
|
|
|
return 'excluded';
|
|
}
|
|
} |