## Summary - add Intune RBAC Role Definition baseline scope support, capture references, compare classification, findings evidence, and landing/detail UI labels - keep Intune Role Assignments explicitly excluded from baseline compare scope, summaries, findings, and restore messaging - add focused Pest coverage for baseline scope selection, capture, compare behavior, recurrence, isolation, findings rendering, inventory anchoring, and RBAC summaries ## Verification - `vendor/bin/sail bin pint --dirty --format agent` - `vendor/bin/sail artisan test --compact tests/Unit/Inventory/InventoryPolicyTypeMetaBaselineSupportTest.php tests/Unit/Baselines/BaselinePolicyVersionResolverTest.php tests/Unit/Baselines/BaselineScopeTest.php tests/Unit/IntuneRoleDefinitionNormalizerTest.php tests/Feature/Baselines/BaselineCaptureRbacRoleDefinitionsTest.php tests/Feature/Baselines/BaselineCompareRbacRoleDefinitionsTest.php tests/Feature/Baselines/BaselineCompareDriftEvidenceContractRbacTest.php tests/Feature/Baselines/BaselineCompareCoverageGuardTest.php tests/Feature/Baselines/BaselineCompareCrossTenantMatchTest.php tests/Feature/Baselines/BaselineCompareFindingRecurrenceKeyTest.php tests/Feature/Baselines/BaselineCompareWhyNoFindingsReasonCodeTest.php tests/Feature/Filament/BaselineProfileFoundationScopeTest.php tests/Feature/Filament/BaselineSnapshotRbacRoleDefinitionsTest.php tests/Feature/Filament/BaselineCompareLandingRbacLabelsTest.php tests/Feature/Filament/FindingViewRbacEvidenceTest.php tests/Feature/Findings/FindingRecurrenceTest.php tests/Feature/Findings/DriftStaleAutoResolveTest.php tests/Feature/Inventory/InventorySyncButtonTest.php tests/Feature/Inventory/InventorySyncServiceTest.php tests/Feature/RunAuthorizationTenantIsolationTest.php` - result: `71 passed (467 assertions)` ## Filament / Platform Notes - Livewire compliance: unchanged and compatible with Livewire v4.0+ - Provider registration: no panel/provider changes; `bootstrap/providers.php` remains the registration location - Global search: no new globally searchable resource added; existing global search behavior is unchanged - Destructive actions: no new destructive actions introduced; existing confirmed actions remain unchanged - Assets: no new Filament assets introduced; deploy asset handling remains unchanged, including `php artisan filament:assets` - Testing plan covered: baseline profile scope, snapshot detail, compare job, findings recurrence, findings detail, compare landing labels, inventory sync anchoring, and tenant isolation Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #156
119 lines
4.8 KiB
PHP
119 lines
4.8 KiB
PHP
@php
|
|
$payload = $getState() ?? [];
|
|
$changedKeys = is_array($payload['changed_keys'] ?? null) ? $payload['changed_keys'] : [];
|
|
$baseline = is_array($payload['baseline'] ?? null) ? $payload['baseline'] : [];
|
|
$current = is_array($payload['current'] ?? null) ? $payload['current'] : [];
|
|
$baselineNormalized = is_array($baseline['normalized'] ?? null) ? $baseline['normalized'] : [];
|
|
$currentNormalized = is_array($current['normalized'] ?? null) ? $current['normalized'] : [];
|
|
$diffKind = is_string($payload['diff_kind'] ?? null) ? (string) $payload['diff_kind'] : 'permission_change';
|
|
|
|
$stringify = static function (mixed $value): string {
|
|
if ($value === null) {
|
|
return '-';
|
|
}
|
|
|
|
if (is_bool($value)) {
|
|
return $value ? 'Yes' : 'No';
|
|
}
|
|
|
|
if (is_array($value)) {
|
|
return implode(', ', array_map(static fn (mixed $item): string => (string) $item, $value));
|
|
}
|
|
|
|
return (string) $value;
|
|
};
|
|
|
|
$roleSourceLabel = static function (mixed $isBuiltIn): string {
|
|
return match ($isBuiltIn) {
|
|
true => __('findings.rbac.built_in'),
|
|
false => __('findings.rbac.custom'),
|
|
default => '-',
|
|
};
|
|
};
|
|
|
|
$sideRows = static function (array $normalized, array $side) use ($roleSourceLabel): array {
|
|
$rows = [];
|
|
|
|
foreach ($normalized as $key => $value) {
|
|
if (! is_string($key) || $key === '') {
|
|
continue;
|
|
}
|
|
|
|
$rows[$key] = $value;
|
|
}
|
|
|
|
if (! array_key_exists('Role definition > Role source', $rows)) {
|
|
$rows['Role definition > Role source'] = $roleSourceLabel($side['is_built_in'] ?? null);
|
|
}
|
|
|
|
if (! array_key_exists('Role definition > Permission blocks', $rows) && is_numeric($side['role_permission_count'] ?? null)) {
|
|
$rows['Role definition > Permission blocks'] = (int) $side['role_permission_count'];
|
|
}
|
|
|
|
ksort($rows);
|
|
|
|
return $rows;
|
|
};
|
|
|
|
$baselineRows = $sideRows($baselineNormalized, $baseline);
|
|
$currentRows = $sideRows($currentNormalized, $current);
|
|
@endphp
|
|
|
|
<div class="space-y-4">
|
|
<x-filament::section
|
|
:heading="__('findings.rbac.detail_heading')"
|
|
:description="__('findings.rbac.' . $diffKind)"
|
|
>
|
|
<div class="flex flex-wrap gap-2">
|
|
<x-filament::badge color="gray">
|
|
{{ __('findings.rbac.' . $diffKind) }}
|
|
</x-filament::badge>
|
|
@if ($changedKeys !== [])
|
|
<x-filament::badge color="warning">
|
|
{{ __('findings.rbac.changed_fields') }}: {{ count($changedKeys) }}
|
|
</x-filament::badge>
|
|
@endif
|
|
</div>
|
|
|
|
@if ($changedKeys !== [])
|
|
<div class="mt-4 space-y-2">
|
|
<div class="text-sm font-semibold text-gray-950 dark:text-white">{{ __('findings.rbac.changed_fields') }}</div>
|
|
<div class="flex flex-wrap gap-2">
|
|
@foreach ($changedKeys as $changedKey)
|
|
<x-filament::badge color="gray">{{ $changedKey }}</x-filament::badge>
|
|
@endforeach
|
|
</div>
|
|
</div>
|
|
@endif
|
|
|
|
<div class="mt-4 grid gap-4 lg:grid-cols-2">
|
|
@foreach ([
|
|
['heading' => __('findings.rbac.baseline'), 'rows' => $baselineRows],
|
|
['heading' => __('findings.rbac.current'), 'rows' => $currentRows],
|
|
] as $section)
|
|
<div class="rounded-xl border border-gray-200 bg-white p-4 shadow-sm dark:border-white/10 dark:bg-gray-900">
|
|
<div class="text-sm font-semibold text-gray-950 dark:text-white">{{ $section['heading'] }}</div>
|
|
|
|
@if ($section['rows'] === [])
|
|
<div class="mt-3 text-sm text-gray-500 dark:text-gray-400">{{ __('findings.rbac.absent') }}</div>
|
|
@else
|
|
<dl class="mt-3 space-y-3">
|
|
@foreach ($section['rows'] as $label => $value)
|
|
<div>
|
|
<dt class="text-xs font-medium uppercase tracking-wide text-gray-500 dark:text-gray-400">{{ $label }}</dt>
|
|
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">{{ $stringify($value) }}</dd>
|
|
</div>
|
|
@endforeach
|
|
</dl>
|
|
@endif
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
|
|
<div class="mt-4 rounded-lg border border-gray-200 bg-gray-50 p-3 text-sm text-gray-700 dark:border-white/10 dark:bg-gray-950 dark:text-gray-300">
|
|
{{ __('findings.rbac.assignments_excluded') }}
|
|
{{ __('findings.rbac.restore_unsupported') }}
|
|
</div>
|
|
</x-filament::section>
|
|
</div>
|