diff --git a/frontend/packages/console-dynamic-plugin-sdk/src/utils/fetch/console-fetch-utils.ts b/frontend/packages/console-dynamic-plugin-sdk/src/utils/fetch/console-fetch-utils.ts index dfafa0e8346..15c43df6635 100644 --- a/frontend/packages/console-dynamic-plugin-sdk/src/utils/fetch/console-fetch-utils.ts +++ b/frontend/packages/console-dynamic-plugin-sdk/src/utils/fetch/console-fetch-utils.ts @@ -2,7 +2,7 @@ import { getImpersonate } from '../../app/core/reducers'; import storeHandler from '../../app/storeHandler'; type ConsoleRequestHeaders = { - 'Impersonate-Group'?: string; + 'Impersonate-Group'?: string | string[]; 'Impersonate-User'?: string; 'X-CSRFToken'?: string; }; @@ -35,12 +35,23 @@ export const getConsoleRequestHeaders = (): ConsoleRequestHeaders => { }; // Set impersonation headers - const { kind, name } = getImpersonate(state) || {}; - if ((kind === 'User' || kind === 'Group') && name) { - // Even if we are impersonating a group, we still need to set Impersonate-User to something or k8s will complain - headers['Impersonate-User'] = name; - if (kind === 'Group') { + const impersonateData = getImpersonate(state); + if (impersonateData) { + const { kind, name, groups } = impersonateData; + + if (kind === 'User' && name) { + // Simple user impersonation + headers['Impersonate-User'] = name; + } else if (kind === 'Group' && name) { + // Single group impersonation (backward compatibility) + // Even if we are impersonating a group, we still need to set Impersonate-User to something or k8s will complain + headers['Impersonate-User'] = name; headers['Impersonate-Group'] = name; + } else if (kind === 'UserWithGroups' && name && groups && groups.length > 0) { + // User with multiple groups impersonation + headers['Impersonate-User'] = name; + // Set multiple Impersonate-Group headers - one for each group + headers['Impersonate-Group'] = groups; } } diff --git a/frontend/packages/console-dynamic-plugin-sdk/src/utils/fetch/console-fetch.ts b/frontend/packages/console-dynamic-plugin-sdk/src/utils/fetch/console-fetch.ts index 507915a5bf1..9b18b1d9951 100644 --- a/frontend/packages/console-dynamic-plugin-sdk/src/utils/fetch/console-fetch.ts +++ b/frontend/packages/console-dynamic-plugin-sdk/src/utils/fetch/console-fetch.ts @@ -50,9 +50,12 @@ const consoleFetchCommon = async ( options: RequestInit = {}, timeout?: number, ): Promise => { - const headers = getConsoleRequestHeaders(); - // Pass headers last to let callers to override Accept. - const allOptions = _.defaultsDeep({ method }, options, { headers }); + const consoleHeaders = getConsoleRequestHeaders(); + + // Merge headers properly - console headers first, then let options override + const mergedHeaders = { ...consoleHeaders, ...options.headers }; + const allOptions = _.defaultsDeep({ method, headers: mergedHeaders }, options); + const response = await consoleFetch(url, allOptions, timeout); const dataPromise = parseData(response); const warning = response.headers.get('Warning'); diff --git a/frontend/public/co-fetch.ts b/frontend/public/co-fetch.ts index 3007699dd89..55f177243f0 100644 --- a/frontend/public/co-fetch.ts +++ b/frontend/public/co-fetch.ts @@ -1,16 +1,32 @@ import * as _ from 'lodash'; import { HttpError, RetryError } from '@console/dynamic-plugin-sdk/src/utils/error/http-error'; import { authSvc } from './module/auth'; -import { getCSRFToken } from '@console/dynamic-plugin-sdk/src/utils/fetch/console-fetch-utils'; +import { getConsoleRequestHeaders } from '@console/dynamic-plugin-sdk/src/utils/fetch/console-fetch-utils'; export const applyConsoleHeaders = (url, options) => { - const token = getCSRFToken(); - if (options.headers) { - options.headers['X-CSRFToken'] = token; - } else { - options.headers = { 'X-CSRFToken': token }; + const consoleHeaders = getConsoleRequestHeaders(); + + if (!options.headers) { + options.headers = {}; } + // Apply console headers, handling array values for multiple headers + Object.entries(consoleHeaders || {}).forEach(([key, value]) => { + if (Array.isArray(value)) { + // For multiple Impersonate-Group headers, we need special handling + // because fetch() API combines them into a single comma-separated header + // which doesn't work for Kubernetes impersonation + if (key === 'Impersonate-Group') { + // Send as a special header that the backend will split + options.headers['X-Console-Impersonate-Groups'] = value.join(','); + } else { + options.headers[key] = value; + } + } else if (value) { + options.headers[key] = value; + } + }); + // X-CSRFToken is used only for non-GET requests targeting bridge if (options.method === 'GET' || url.indexOf('://') >= 0) { delete options.headers['X-CSRFToken'];