Some checks failed
PR Fast Feedback / fast-feedback (pull_request) Failing after 1m12s
Replaced legacy tenant and environment bindings in the BaselineDriftEngine with the new ProviderResourceIdentity framework as defined in Spec 382.
318 lines
11 KiB
PHP
318 lines
11 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 App\Support\Baselines\SubjectClass;
|
|
use Filament\Facades\Filament;
|
|
use Livewire\Livewire;
|
|
|
|
it('renders shared summary badges and RBAC row states on the finding detail page', function (): void {
|
|
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
|
|
|
$finding = findingViewRbacFinding($tenant);
|
|
|
|
$this->actingAs($user)
|
|
->get(FindingResource::getUrl('view', ['record' => $finding], tenant: $tenant))
|
|
->assertOk()
|
|
->assertSee('Intune RBAC Role Definition drift')
|
|
->assertSee('Metadata-only change')
|
|
->assertSee('2 changed')
|
|
->assertSee('1 added')
|
|
->assertSee('1 removed')
|
|
->assertSee('4 unchanged')
|
|
->assertSee('Changed')
|
|
->assertSee('Added')
|
|
->assertSee('Removed')
|
|
->assertSee('Unchanged')
|
|
->assertSee('Changed value')
|
|
->assertSee('No material change')
|
|
->assertSee('Role definition > Description')
|
|
->assertSee('Permission block 1 > Denied actions')
|
|
->assertSee('Permission block 1 > Conditions')
|
|
->assertSee('Baseline description')
|
|
->assertSee('Updated description')
|
|
->assertSee('border-warning-200')
|
|
->assertSee('text-gray-500')
|
|
->assertSee('Role Assignments are not included')
|
|
->assertSee('RBAC restore is not supported');
|
|
});
|
|
|
|
it('renders Allowed Actions as inline added removed and unchanged list chips', function (): void {
|
|
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
|
|
|
$finding = findingViewRbacFinding($tenant);
|
|
|
|
$this->actingAs($user)
|
|
->get(FindingResource::getUrl('view', ['record' => $finding], tenant: $tenant))
|
|
->assertOk()
|
|
->assertSee('Permission block 1 > Allowed actions')
|
|
->assertSee('Added items')
|
|
->assertSee('Removed items')
|
|
->assertSee('Unchanged items')
|
|
->assertSee('Microsoft.Intune/deviceConfigurations/create')
|
|
->assertSee('Microsoft.Intune/deviceConfigurations/delete')
|
|
->assertSee('Microsoft.Intune/deviceConfigurations/read')
|
|
->assertSee('@Resource[Microsoft.Intune/deviceConfigurations] Exists')
|
|
->assertSee('Microsoft.Intune/deviceConfigurations/wipe');
|
|
});
|
|
|
|
it('renders a no-change RBAC summary when the evidence only contains unchanged rows', function (): void {
|
|
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
|
|
|
$finding = findingViewRbacFinding($tenant, [
|
|
'changed_keys' => [],
|
|
'metadata_keys' => [],
|
|
'permission_keys' => [],
|
|
'baseline' => [
|
|
'normalized' => [
|
|
'Role definition > Display name' => 'Security Reader',
|
|
'Role definition > Description' => 'Baseline description',
|
|
],
|
|
'is_built_in' => false,
|
|
'role_permission_count' => 1,
|
|
],
|
|
'current' => [
|
|
'normalized' => [
|
|
'Role definition > Display name' => 'Security Reader',
|
|
'Role definition > Description' => 'Baseline description',
|
|
],
|
|
'is_built_in' => false,
|
|
'role_permission_count' => 1,
|
|
],
|
|
]);
|
|
|
|
$this->actingAs($user)
|
|
->get(FindingResource::getUrl('view', ['record' => $finding], tenant: $tenant))
|
|
->assertOk()
|
|
->assertSee('0 changed')
|
|
->assertSee('0 added')
|
|
->assertSee('0 removed')
|
|
->assertSee('4 unchanged')
|
|
->assertSee('No changes detected.');
|
|
});
|
|
|
|
it('renders a stable sparse fallback when the RBAC evidence payload is empty', function (): void {
|
|
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
|
|
|
$finding = findingViewRbacFinding($tenant, []);
|
|
$evidence = is_array($finding->evidence_jsonb) ? $finding->evidence_jsonb : [];
|
|
$evidence['rbac_role_definition'] = [];
|
|
|
|
$finding->forceFill([
|
|
'evidence_jsonb' => $evidence,
|
|
])->save();
|
|
|
|
$this->actingAs($user)
|
|
->get(FindingResource::getUrl('view', ['record' => $finding], tenant: $tenant))
|
|
->assertOk()
|
|
->assertSee('0 changed')
|
|
->assertSee('0 added')
|
|
->assertSee('0 removed')
|
|
->assertSee('0 unchanged')
|
|
->assertSee('No diff data available.')
|
|
->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();
|
|
setAdminPanelContext($tenant);
|
|
|
|
$rawSubjectExternalId = 'rbac-role-1';
|
|
$subjectKey = baselineProviderResourceSubjectKeyForTest(
|
|
'intuneRoleDefinition',
|
|
$rawSubjectExternalId,
|
|
SubjectClass::FoundationBacked,
|
|
);
|
|
$subjectExternalId = BaselineSubjectKey::workspaceSafeSubjectExternalId('intuneRoleDefinition', $subjectKey);
|
|
|
|
InventoryItem::factory()->create([
|
|
'managed_environment_id' => (int) $tenant->getKey(),
|
|
'workspace_id' => (int) $tenant->workspace_id,
|
|
'external_id' => $rawSubjectExternalId,
|
|
'policy_type' => 'intuneRoleDefinition',
|
|
'display_name' => 'Security Reader',
|
|
'meta_jsonb' => ['etag' => 'E1'],
|
|
'last_seen_at' => now(),
|
|
]);
|
|
|
|
$finding = Finding::factory()->create([
|
|
'managed_environment_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' => $subjectKey,
|
|
'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');
|
|
});
|
|
|
|
/**
|
|
* @param array<string, mixed> $rbacOverrides
|
|
*/
|
|
function findingViewRbacFinding(\App\Models\ManagedEnvironment $tenant, array $rbacOverrides = []): Finding
|
|
{
|
|
$subjectKey = baselineProviderResourceSubjectKeyForTest(
|
|
'intuneRoleDefinition',
|
|
'rbac-role-1',
|
|
SubjectClass::FoundationBacked,
|
|
);
|
|
$subjectExternalId = BaselineSubjectKey::workspaceSafeSubjectExternalId('intuneRoleDefinition', $subjectKey);
|
|
|
|
return Finding::factory()->create([
|
|
'managed_environment_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' => $subjectKey,
|
|
'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' => findingViewRbacEvidenceFixture($rbacOverrides),
|
|
'fidelity' => 'content',
|
|
'provenance' => [
|
|
'baseline_profile_id' => 1,
|
|
'baseline_snapshot_id' => 1,
|
|
'compare_operation_run_id' => 1,
|
|
'inventory_sync_run_id' => 1,
|
|
],
|
|
],
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* @param array<string, mixed> $overrides
|
|
* @return array<string, mixed>
|
|
*/
|
|
function findingViewRbacEvidenceFixture(array $overrides = []): array
|
|
{
|
|
return findingViewRbacFixtureMerge([
|
|
'diff_kind' => 'metadata_only',
|
|
'diff_fingerprint' => 'rbac-diff-1',
|
|
'changed_keys' => [
|
|
'Role definition > Description',
|
|
'Permission block 1 > Allowed actions',
|
|
],
|
|
'metadata_keys' => [
|
|
'Role definition > Description',
|
|
],
|
|
'permission_keys' => [
|
|
'Permission block 1 > Allowed actions',
|
|
],
|
|
'baseline' => [
|
|
'normalized' => [
|
|
'Role definition > Display name' => 'Security Reader',
|
|
'Role definition > Description' => 'Baseline description',
|
|
'Role definition > Scope tag IDs' => ['0', 'scope-1'],
|
|
'Permission block 1 > Allowed actions' => [
|
|
'Microsoft.Intune/deviceConfigurations/delete',
|
|
'Microsoft.Intune/deviceConfigurations/read',
|
|
],
|
|
'Permission block 1 > Denied actions' => [
|
|
'Microsoft.Intune/deviceConfigurations/wipe',
|
|
],
|
|
],
|
|
'is_built_in' => false,
|
|
'role_permission_count' => 1,
|
|
],
|
|
'current' => [
|
|
'normalized' => [
|
|
'Role definition > Display name' => 'Security Reader',
|
|
'Role definition > Description' => 'Updated description',
|
|
'Role definition > Scope tag IDs' => ['0', 'scope-1'],
|
|
'Permission block 1 > Allowed actions' => [
|
|
'Microsoft.Intune/deviceConfigurations/create',
|
|
'Microsoft.Intune/deviceConfigurations/read',
|
|
],
|
|
'Permission block 1 > Conditions' => [
|
|
'@Resource[Microsoft.Intune/deviceConfigurations] Exists',
|
|
],
|
|
],
|
|
'is_built_in' => false,
|
|
'role_permission_count' => 1,
|
|
],
|
|
], $overrides);
|
|
}
|
|
|
|
/**
|
|
* @param array<string, mixed> $base
|
|
* @param array<string, mixed> $overrides
|
|
* @return array<string, mixed>
|
|
*/
|
|
function findingViewRbacFixtureMerge(array $base, array $overrides): array
|
|
{
|
|
foreach ($overrides as $key => $value) {
|
|
if ($key === 'normalized') {
|
|
$base[$key] = $value;
|
|
|
|
continue;
|
|
}
|
|
|
|
if (
|
|
is_string($key)
|
|
&& array_key_exists($key, $base)
|
|
&& is_array($value)
|
|
&& is_array($base[$key])
|
|
&& ! array_is_list($value)
|
|
&& ! array_is_list($base[$key])
|
|
) {
|
|
$base[$key] = findingViewRbacFixtureMerge($base[$key], $value);
|
|
|
|
continue;
|
|
}
|
|
|
|
$base[$key] = $value;
|
|
}
|
|
|
|
return $base;
|
|
}
|