TenantAtlas/tests/Feature/Filament/FindingViewRbacEvidenceTest.php
ahmido ef41c9193a feat: add Intune RBAC baseline compare support (#156)
## 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
2026-03-09 18:49:20 +00:00

151 lines
5.7 KiB
PHP

<?php
declare(strict_types=1);
use App\Filament\Resources\FindingResource;
use App\Filament\Widgets\Dashboard\RecentDriftFindings;
use App\Models\Finding;
use App\Models\InventoryItem;
use App\Support\Baselines\BaselineSubjectKey;
use Filament\Facades\Filament;
use Livewire\Livewire;
it('renders readable role definition evidence and no-restore messaging on the finding detail page', function (): void {
[$user, $tenant] = createUserWithTenant(role: 'owner');
$subjectExternalId = BaselineSubjectKey::workspaceSafeSubjectExternalIdForPolicy(
'intuneRoleDefinition',
'Security Reader',
'rbac-role-1',
);
$finding = Finding::factory()->create([
'tenant_id' => (int) $tenant->getKey(),
'finding_type' => Finding::FINDING_TYPE_DRIFT,
'source' => 'baseline.compare',
'subject_type' => 'policy',
'subject_external_id' => (string) $subjectExternalId,
'evidence_fidelity' => 'content',
'severity' => Finding::SEVERITY_LOW,
'evidence_jsonb' => [
'change_type' => 'different_version',
'policy_type' => 'intuneRoleDefinition',
'subject_key' => hash('sha256', 'intuneRoleDefinition|rbac-role-1'),
'display_name' => 'Security Reader',
'summary' => [
'kind' => 'rbac_role_definition',
],
'baseline' => [
'policy_version_id' => 10,
'hash' => 'baseline',
],
'current' => [
'policy_version_id' => 11,
'hash' => 'current',
],
'rbac_role_definition' => [
'diff_kind' => 'metadata_only',
'diff_fingerprint' => 'rbac-diff-1',
'changed_keys' => ['Role definition > Description'],
'metadata_keys' => ['Role definition > Description'],
'permission_keys' => [],
'baseline' => [
'normalized' => [
'Role definition > Display name' => 'Security Reader',
'Role definition > Description' => 'Baseline description',
'Role definition > Role source' => 'Custom',
],
'is_built_in' => false,
'role_permission_count' => 1,
],
'current' => [
'normalized' => [
'Role definition > Display name' => 'Security Reader',
'Role definition > Description' => 'Updated description',
'Role definition > Role source' => 'Custom',
],
'is_built_in' => false,
'role_permission_count' => 1,
],
],
'fidelity' => 'content',
'provenance' => [
'baseline_profile_id' => 1,
'baseline_snapshot_id' => 1,
'compare_operation_run_id' => 1,
'inventory_sync_run_id' => 1,
],
],
]);
$this->actingAs($user)
->get(FindingResource::getUrl('view', ['record' => $finding], tenant: $tenant))
->assertOk()
->assertSee('Intune RBAC Role Definition drift')
->assertSee('Metadata-only change')
->assertSee('Changed fields')
->assertSee('Role definition > Description')
->assertSee('Baseline description')
->assertSee('Updated description')
->assertSee('Role Assignments are not included')
->assertSee('RBAC restore is not supported');
});
it('shows RBAC labels and display-name fallback in the recent drift findings widget', function (): void {
[$user, $tenant] = createUserWithTenant(role: 'owner');
$this->actingAs($user);
$tenant->makeCurrent();
Filament::setCurrentPanel(Filament::getPanel('tenant'));
Filament::setTenant($tenant, true);
$subjectExternalId = 'rbac-role-1';
InventoryItem::factory()->create([
'tenant_id' => (int) $tenant->getKey(),
'workspace_id' => (int) $tenant->workspace_id,
'external_id' => $subjectExternalId,
'policy_type' => 'intuneRoleDefinition',
'display_name' => 'Security Reader',
'meta_jsonb' => ['etag' => 'E1'],
'last_seen_at' => now(),
]);
$finding = Finding::factory()->create([
'tenant_id' => (int) $tenant->getKey(),
'finding_type' => Finding::FINDING_TYPE_DRIFT,
'source' => 'baseline.compare',
'subject_type' => 'policy',
'subject_external_id' => (string) $subjectExternalId,
'severity' => Finding::SEVERITY_HIGH,
'status' => Finding::STATUS_NEW,
'evidence_jsonb' => [
'change_type' => 'missing_policy',
'policy_type' => 'intuneRoleDefinition',
'subject_key' => hash('sha256', 'intuneRoleDefinition|rbac-role-1'),
'display_name' => 'Security Reader',
'summary' => [
'kind' => 'rbac_role_definition',
],
'baseline' => ['policy_version_id' => 10],
'current' => ['policy_version_id' => null],
'rbac_role_definition' => [
'diff_kind' => 'missing',
],
'fidelity' => 'mixed',
'provenance' => [
'baseline_profile_id' => 1,
'baseline_snapshot_id' => 1,
'compare_operation_run_id' => 1,
'inventory_sync_run_id' => null,
],
],
]);
Livewire::actingAs($user)->test(RecentDriftFindings::class)
->assertCanSeeTableRecords([$finding])
->assertSee('Security Reader')
->assertSee('Intune RBAC Role Definition drift');
});