>, warnings: array} */ public function normalize(?array $snapshot, string $policyType, ?string $platform = null): array { $snapshot = is_array($snapshot) ? $snapshot : []; if ($snapshot === []) { return [ 'status' => 'warning', 'settings' => [], 'warnings' => ['No snapshot available.'], ]; } $settings = []; $warnings = []; $summaryEntries = []; $this->pushEntry($summaryEntries, 'Display name', Arr::get($snapshot, 'displayName')); $this->pushEntry($summaryEntries, 'Description', Arr::get($snapshot, 'description')); $isBuiltIn = Arr::get($snapshot, 'isBuiltIn'); if (is_bool($isBuiltIn)) { $this->pushEntry($summaryEntries, 'Role source', $isBuiltIn ? 'Built-in' : 'Custom'); } $permissionBlocks = $this->normalizePermissionBlocks(Arr::get($snapshot, 'rolePermissions', [])); $this->pushEntry($summaryEntries, 'Permission blocks', count($permissionBlocks)); $scopeTagIds = $this->normalizedStringList(Arr::get($snapshot, 'roleScopeTagIds', [])); $this->pushEntry($summaryEntries, 'Scope tag IDs', $scopeTagIds); if ($summaryEntries !== []) { $settings[] = [ 'type' => 'keyValue', 'title' => 'Role definition', 'entries' => $summaryEntries, ]; } foreach ($permissionBlocks as $index => $block) { $entries = []; $this->pushEntry($entries, 'Allowed actions', $block['allowed']); $this->pushEntry($entries, 'Denied actions', $block['denied']); $this->pushEntry($entries, 'Conditions', $block['conditions']); if ($entries === []) { continue; } $settings[] = [ 'type' => 'keyValue', 'title' => sprintf('Permission block %d', $index + 1), 'entries' => $entries, ]; } if ($permissionBlocks === []) { $warnings[] = 'Role definition contains no expanded permission blocks.'; } if ($settings === []) { return [ 'status' => 'warning', 'settings' => [], 'warnings' => array_values(array_unique(array_merge($warnings, ['Role definition snapshot contains no readable fields.']))), ]; } return [ 'status' => $warnings === [] ? 'ok' : 'warning', 'settings' => $settings, 'warnings' => array_values(array_unique($warnings)), ]; } /** * @return array */ public function flattenForDiff(?array $snapshot, string $policyType, ?string $platform = null): array { return $this->defaultNormalizer->flattenNormalizedForDiff( $this->normalize($snapshot, $policyType, $platform), ); } /** * @return array, denied: array, conditions: array, fingerprint: string}> */ private function normalizePermissionBlocks(mixed $rolePermissions): array { if (! is_array($rolePermissions)) { return []; } $normalized = []; foreach ($rolePermissions as $permissionBlock) { if (! is_array($permissionBlock)) { continue; } $resourceActions = $permissionBlock['resourceActions'] ?? null; $resourceActions = is_array($resourceActions) ? $resourceActions : []; $allowed = []; $denied = []; $conditions = []; foreach ($resourceActions as $resourceAction) { if (! is_array($resourceAction)) { continue; } $allowed = array_merge($allowed, $this->normalizedStringList($resourceAction['allowedResourceActions'] ?? [])); $denied = array_merge($denied, $this->normalizedStringList($resourceAction['notAllowedResourceActions'] ?? [])); $conditions = array_merge($conditions, $this->normalizedStringList([$resourceAction['condition'] ?? null])); } $allowed = array_values(array_unique($allowed)); sort($allowed); $denied = array_values(array_unique($denied)); sort($denied); $conditions = array_values(array_unique($conditions)); sort($conditions); $block = [ 'allowed' => $allowed, 'denied' => $denied, 'conditions' => $conditions, ]; $block['fingerprint'] = hash( 'sha256', json_encode($block, JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE), ); $normalized[] = $block; } usort($normalized, fn (array $left, array $right): int => strcmp($left['fingerprint'], $right['fingerprint'])); return $normalized; } /** * @param array $entries */ private function pushEntry(array &$entries, string $key, mixed $value): void { if ($value === null) { return; } if (is_string($value) && $value === '') { return; } if (is_array($value) && $value === []) { return; } $entries[] = [ 'key' => $key, 'value' => $value, ]; } /** * @return array */ private function normalizedStringList(mixed $values): array { if (! is_array($values)) { return []; } return array_values(array_filter( array_map( static fn (mixed $value): ?string => is_string($value) && $value !== '' ? $value : null, $values, ), static fn (?string $value): bool => $value !== null, )); } }