229 lines
7.5 KiB
PHP
229 lines
7.5 KiB
PHP
<?php
|
|
|
|
use App\Models\BackupSet;
|
|
use App\Models\Policy;
|
|
use App\Models\PolicyVersion;
|
|
use App\Models\Tenant;
|
|
use App\Models\User;
|
|
use App\Services\Intune\BackupService;
|
|
use App\Services\Intune\FoundationSnapshotService;
|
|
use App\Services\Intune\PolicyCaptureOrchestrator;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Mockery\MockInterface;
|
|
|
|
uses(RefreshDatabase::class);
|
|
|
|
it('reuses latest policy version for backup when it is up-to-date and satisfies capture options', function () {
|
|
$tenant = Tenant::factory()->create();
|
|
$tenant->makeCurrent();
|
|
|
|
$user = User::factory()->create();
|
|
$this->actingAs($user);
|
|
|
|
$backupSet = BackupSet::factory()->create([
|
|
'tenant_id' => $tenant->id,
|
|
'status' => 'completed',
|
|
]);
|
|
|
|
$policy = Policy::factory()->create([
|
|
'tenant_id' => $tenant->id,
|
|
'last_synced_at' => now(),
|
|
'ignored_at' => null,
|
|
]);
|
|
|
|
$existingVersion = PolicyVersion::factory()->create([
|
|
'tenant_id' => $tenant->id,
|
|
'policy_id' => $policy->id,
|
|
'captured_at' => now(),
|
|
'snapshot' => ['id' => $policy->external_id, 'name' => $policy->display_name],
|
|
'assignments' => null,
|
|
'scope_tags' => null,
|
|
]);
|
|
|
|
$this->mock(PolicyCaptureOrchestrator::class, function (MockInterface $mock) use ($existingVersion) {
|
|
$mock->shouldReceive('capture')
|
|
->once()
|
|
->andReturn([
|
|
'version' => $existingVersion,
|
|
'captured' => [
|
|
'payload' => $existingVersion->snapshot,
|
|
'assignments' => $existingVersion->assignments,
|
|
'scope_tags' => $existingVersion->scope_tags,
|
|
'metadata' => [],
|
|
],
|
|
]);
|
|
});
|
|
|
|
$service = app(BackupService::class);
|
|
|
|
$service->addPoliciesToSet(
|
|
tenant: $tenant,
|
|
backupSet: $backupSet,
|
|
policyIds: [$policy->id],
|
|
actorEmail: $user->email,
|
|
actorName: $user->name,
|
|
includeAssignments: false,
|
|
includeScopeTags: false,
|
|
includeFoundations: false,
|
|
);
|
|
|
|
expect(PolicyVersion::query()->where('policy_id', $policy->id)->count())->toBe(1);
|
|
|
|
$item = $backupSet->items()->first();
|
|
expect($item)->not->toBeNull();
|
|
expect($item->policy_version_id)->toBe($existingVersion->id);
|
|
});
|
|
|
|
it('captures a new policy version for backup when no suitable existing version is available', function () {
|
|
$tenant = Tenant::factory()->create();
|
|
$tenant->makeCurrent();
|
|
|
|
$user = User::factory()->create();
|
|
$this->actingAs($user);
|
|
|
|
$backupSet = BackupSet::factory()->create([
|
|
'tenant_id' => $tenant->id,
|
|
'status' => 'completed',
|
|
]);
|
|
|
|
$policy = Policy::factory()->create([
|
|
'tenant_id' => $tenant->id,
|
|
'last_synced_at' => now(),
|
|
'ignored_at' => null,
|
|
]);
|
|
|
|
$staleVersion = PolicyVersion::factory()->create([
|
|
'tenant_id' => $tenant->id,
|
|
'policy_id' => $policy->id,
|
|
'version_number' => 1,
|
|
'captured_at' => now()->subDays(2),
|
|
'snapshot' => ['id' => $policy->external_id, 'name' => $policy->display_name],
|
|
]);
|
|
|
|
$policy->update(['last_synced_at' => now()]);
|
|
|
|
$this->mock(PolicyCaptureOrchestrator::class, function (MockInterface $mock) use ($policy, $tenant) {
|
|
$mock->shouldReceive('capture')
|
|
->once()
|
|
->andReturnUsing(function () use ($policy, $tenant) {
|
|
$newVersion = PolicyVersion::factory()->create([
|
|
'tenant_id' => $tenant->id,
|
|
'policy_id' => $policy->id,
|
|
'version_number' => 2,
|
|
'captured_at' => now(),
|
|
'snapshot' => ['id' => $policy->external_id, 'name' => $policy->display_name, 'changed' => true],
|
|
]);
|
|
|
|
return [
|
|
'version' => $newVersion,
|
|
'captured' => [
|
|
'payload' => $newVersion->snapshot,
|
|
'assignments' => null,
|
|
'scope_tags' => null,
|
|
'metadata' => [],
|
|
],
|
|
];
|
|
});
|
|
});
|
|
|
|
$service = app(BackupService::class);
|
|
|
|
$service->addPoliciesToSet(
|
|
tenant: $tenant,
|
|
backupSet: $backupSet,
|
|
policyIds: [$policy->id],
|
|
actorEmail: $user->email,
|
|
actorName: $user->name,
|
|
includeAssignments: false,
|
|
includeScopeTags: false,
|
|
includeFoundations: false,
|
|
);
|
|
|
|
$item = $backupSet->items()->first();
|
|
expect($item)->not->toBeNull();
|
|
expect($item->policy_version_id)->not->toBe($staleVersion->id);
|
|
});
|
|
|
|
it('reuses an existing RBAC foundation version across backup sets when the snapshot is unchanged', function () {
|
|
$tenant = Tenant::factory()->create(['status' => 'active']);
|
|
ensureDefaultProviderConnection($tenant);
|
|
|
|
config()->set('tenantpilot.foundation_types', [
|
|
[
|
|
'type' => 'intuneRoleDefinition',
|
|
'label' => 'Intune Role Definition',
|
|
'category' => 'RBAC',
|
|
'platform' => 'all',
|
|
'endpoint' => 'deviceManagement/roleDefinitions',
|
|
'backup' => 'full',
|
|
'restore' => 'preview-only',
|
|
'risk' => 'high',
|
|
],
|
|
]);
|
|
|
|
$payload = [
|
|
'id' => 'role-def-1',
|
|
'displayName' => 'Policy and Profile Manager',
|
|
'description' => 'Built-in RBAC role',
|
|
'isBuiltIn' => true,
|
|
'rolePermissions' => [
|
|
[
|
|
'resourceActions' => [
|
|
[
|
|
'allowedResourceActions' => [
|
|
'Microsoft.Intune/deviceConfigurations/read',
|
|
],
|
|
],
|
|
],
|
|
],
|
|
],
|
|
];
|
|
|
|
$this->mock(FoundationSnapshotService::class, function (MockInterface $mock) use ($payload) {
|
|
$mock->shouldReceive('fetchAll')
|
|
->twice()
|
|
->withArgs(fn (Tenant $tenant, string $foundationType): bool => $foundationType === 'intuneRoleDefinition')
|
|
->andReturn([
|
|
'items' => [[
|
|
'source_id' => 'role-def-1',
|
|
'display_name' => 'Policy and Profile Manager',
|
|
'payload' => $payload,
|
|
'metadata' => [
|
|
'displayName' => 'Policy and Profile Manager',
|
|
'kind' => 'intuneRoleDefinition',
|
|
'graph' => [
|
|
'resource' => 'deviceManagement/roleDefinitions',
|
|
'apiVersion' => 'beta',
|
|
],
|
|
],
|
|
]],
|
|
'failures' => [],
|
|
]);
|
|
});
|
|
|
|
$service = app(BackupService::class);
|
|
|
|
$firstBackupSet = $service->createBackupSet(
|
|
tenant: $tenant,
|
|
policyIds: [],
|
|
name: 'RBAC Backup 1',
|
|
includeFoundations: true,
|
|
);
|
|
|
|
$secondBackupSet = $service->createBackupSet(
|
|
tenant: $tenant,
|
|
policyIds: [],
|
|
name: 'RBAC Backup 2',
|
|
includeFoundations: true,
|
|
);
|
|
|
|
$firstItem = $firstBackupSet->items()->first();
|
|
$secondItem = $secondBackupSet->items()->first();
|
|
|
|
expect($firstItem)->not->toBeNull();
|
|
expect($secondItem)->not->toBeNull();
|
|
expect($firstItem->policy_id)->toBe($secondItem->policy_id);
|
|
expect($firstItem->policy_version_id)->toBe($secondItem->policy_version_id);
|
|
expect(PolicyVersion::query()->where('tenant_id', $tenant->id)->where('policy_type', 'intuneRoleDefinition')->count())->toBe(1);
|
|
});
|