set('intune_permissions.permissions', [[ 'key' => 'DeviceManagementApps.Read.All', 'type' => 'application', 'description' => 'Read Intune apps', 'features' => ['backup'], ]]); config()->set('entra_permissions.permissions', []); } function spec394HealthyConnection(ManagedEnvironment $environment, array $attributes = []): ProviderConnection { return ProviderConnection::factory() ->platform() ->verifiedHealthy() ->create(array_replace([ 'managed_environment_id' => (int) $environment->getKey(), 'workspace_id' => (int) $environment->workspace_id, 'provider' => 'microsoft', 'is_default' => true, ], $attributes)); } function spec394PermissionEvidence(ManagedEnvironment $environment, ProviderConnection $connection, array $attributes = []): ManagedEnvironmentPermission { return ManagedEnvironmentPermission::query()->create(array_replace([ 'managed_environment_id' => (int) $environment->getKey(), 'workspace_id' => (int) $environment->workspace_id, 'permission_key' => 'DeviceManagementApps.Read.All', 'status' => 'granted', 'details' => [ 'source' => 'graph_api', 'provider_connection_id' => (int) $connection->getKey(), 'managed_environment_id' => (int) $environment->getKey(), 'workspace_id' => (int) $environment->workspace_id, 'provider' => 'microsoft', ], 'last_checked_at' => now(), ], $attributes)); } it('marks a provider ready only when granted evidence matches the current provider connection', function (): void { spec394ConfigureRequiredPermission(); [$user, $environment] = createUserWithTenant(role: 'owner'); $connection = spec394HealthyConnection($environment); spec394PermissionEvidence($environment, $connection); $result = app(ProviderReadinessResolver::class)->forConnection($connection, $user); expect($result->state)->toBe(ProviderReadinessState::Ready) ->and($result->counts['granted'])->toBe(1) ->and($result->counts['required'])->toBe(1) ->and($result->permissionRows[0]['is_effective'])->toBeTrue(); }); it('does not treat legacy unscoped permission rows as granted readiness evidence', function (): void { spec394ConfigureRequiredPermission(); [$user, $environment] = createUserWithTenant(role: 'owner'); $connection = spec394HealthyConnection($environment); spec394PermissionEvidence($environment, $connection, [ 'details' => ['source' => 'legacy-fixture'], ]); $result = app(ProviderReadinessResolver::class)->forConnection($connection, $user); expect($result->state)->toBe(ProviderReadinessState::Unknown) ->and($result->counts['granted'])->toBe(0) ->and($result->counts['unknown'])->toBe(1) ->and($result->permissionRows[0]['reason_code'])->toBe('provider_permission_evidence_scope_mismatch'); }); it('does not treat provider-connection-only permission rows as scoped readiness evidence', function (): void { spec394ConfigureRequiredPermission(); [$user, $environment] = createUserWithTenant(role: 'owner'); $connection = spec394HealthyConnection($environment); spec394PermissionEvidence($environment, $connection, [ 'details' => [ 'source' => 'partial-provider-evidence', 'provider_connection_id' => (int) $connection->getKey(), ], ]); $result = app(ProviderReadinessResolver::class)->forConnection($connection, $user); expect($result->state)->toBe(ProviderReadinessState::Unknown) ->and($result->counts['granted'])->toBe(0) ->and($result->counts['unknown'])->toBe(1) ->and($result->permissionRows[0]['reason_code'])->toBe('provider_permission_evidence_scope_mismatch'); }); it('expires matched grants when the provider verification is stale', function (): void { spec394ConfigureRequiredPermission(); [$user, $environment] = createUserWithTenant(role: 'owner'); $connection = spec394HealthyConnection($environment, [ 'last_health_check_at' => now()->subDays(31), ]); spec394PermissionEvidence($environment, $connection); $result = app(ProviderReadinessResolver::class)->forConnection($connection, $user); expect($result->state)->toBe(ProviderReadinessState::Expired) ->and($result->counts['expired'])->toBe(1) ->and($result->counts['granted'])->toBe(0); }); it('persists provider connection metadata when live permission checks refresh stored evidence', function (): void { spec394ConfigureRequiredPermission(); [, $environment] = createUserWithTenant(role: 'owner'); $connection = spec394HealthyConnection($environment); $graph = Mockery::mock(GraphClientInterface::class); $graph->shouldReceive('getServicePrincipalPermissions') ->once() ->andReturn(new GraphResponse(true, data: [ 'permissions' => ['DeviceManagementApps.Read.All'], ])); app()->instance(GraphClientInterface::class, $graph); app(ManagedEnvironmentPermissionService::class)->compare( $environment, persist: true, liveCheck: true, useConfiguredStub: false, graphOptions: ['client_id' => 'app-client-id'], providerConnection: $connection, ); $stored = ManagedEnvironmentPermission::query() ->where('managed_environment_id', (int) $environment->getKey()) ->where('permission_key', 'DeviceManagementApps.Read.All') ->first(); expect($stored)->not->toBeNull() ->and($stored->details['provider_connection_id'] ?? null)->toBe((int) $connection->getKey()) ->and($stored->details['workspace_id'] ?? null)->toBe((int) $environment->workspace_id) ->and($stored->details['managed_environment_id'] ?? null)->toBe((int) $environment->getKey()) ->and($stored->details['provider'] ?? null)->toBe('microsoft') ->and($stored->details['app_id'] ?? null)->toBe('app-client-id'); });