}> */ public function getRequiredPermissions(): array { return config('intune_permissions.permissions', []); } /** * @return array|null,last_checked_at:?\Illuminate\Support\Carbon}> */ public function getGrantedPermissions(Tenant $tenant): array { return TenantPermission::query() ->where('tenant_id', $tenant->id) ->get() ->keyBy('permission_key') ->map(fn (TenantPermission $permission) => [ 'status' => $permission->status, 'details' => $permission->details, 'last_checked_at' => $permission->last_checked_at, ]) ->all(); } /** * @param array|null}|string>|null $grantedStatuses * @param bool $persist Persist comparison results to tenant_permissions * @param bool $liveCheck If true, fetch actual permissions from Graph API * @return array{overall_status:string,permissions:array,status:string,details:array|null}>} */ public function compare(Tenant $tenant, ?array $grantedStatuses = null, bool $persist = true, bool $liveCheck = false): array { $required = $this->getRequiredPermissions(); // If liveCheck is requested, fetch actual permissions from Graph if ($liveCheck && $grantedStatuses === null) { $grantedStatuses = $this->fetchLivePermissions($tenant); } $granted = $this->normalizeGrantedStatuses( $grantedStatuses ?? array_replace_recursive($this->configuredGrantedStatuses(), $this->getGrantedPermissions($tenant)) ); $results = []; $hasMissing = false; $hasErrors = false; $checkedAt = now(); foreach ($required as $permission) { $key = $permission['key']; $status = $granted[$key]['status'] ?? 'missing'; $details = $granted[$key]['details'] ?? null; if ($persist) { TenantPermission::updateOrCreate( [ 'tenant_id' => $tenant->id, 'permission_key' => $key, ], [ 'status' => $status, 'details' => $details, 'last_checked_at' => $checkedAt, ] ); } $results[] = [ 'key' => $key, 'type' => $permission['type'] ?? 'application', 'description' => $permission['description'] ?? null, 'features' => $permission['features'] ?? [], 'status' => $status, 'details' => $details, ]; $hasMissing = $hasMissing || $status === 'missing'; $hasErrors = $hasErrors || $status === 'error'; } $overall = match (true) { $hasErrors => 'error', $hasMissing => 'missing', default => 'ok', }; return [ 'overall_status' => $overall, 'permissions' => $results, ]; } /** * @param array|null}|string> $granted * @return array|null}> */ private function normalizeGrantedStatuses(array $granted): array { $normalized = []; foreach ($granted as $key => $value) { if (is_string($value)) { $normalized[$key] = ['status' => $value, 'details' => null]; continue; } $normalized[$key] = [ 'status' => $value['status'] ?? 'missing', 'details' => $value['details'] ?? null, ]; } return $normalized; } /** * @return array|null}> */ public function configuredGrantedStatuses(): array { $configured = $this->configuredGrantedKeys(); $normalized = []; foreach ($configured as $key) { $normalized[$key] = [ 'status' => 'ok', 'details' => ['source' => 'configured'], ]; } return $normalized; } /** * @return array */ private function configuredGrantedKeys(): array { $env = env('INTUNE_GRANTED_PERMISSIONS'); if (is_string($env) && filled($env)) { return collect(explode(',', $env)) ->map(fn (string $key) => trim($key)) ->filter() ->values() ->all(); } return config('intune_permissions.granted_stub', []); } /** * Fetch actual granted permissions from Graph API. * * @return array|null}> */ private function fetchLivePermissions(Tenant $tenant): array { try { $response = $this->graphClient->getServicePrincipalPermissions( $tenant->graphOptions() ); if (! $response->success) { return []; } $grantedPermissions = $response->data['permissions'] ?? []; $normalized = []; foreach ($grantedPermissions as $permission) { $normalized[$permission] = [ 'status' => 'ok', 'details' => ['source' => 'graph_api', 'checked_at' => now()->toIso8601String()], ]; } return $normalized; } catch (\Throwable $e) { // Log error but don't fail - fall back to config \Log::warning('Failed to fetch live permissions from Graph', [ 'tenant_id' => $tenant->id, 'error' => $e->getMessage(), ]); return []; } } }