Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
Expand Down Expand Up @@ -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;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,12 @@ const consoleFetchCommon = async (
options: RequestInit = {},
timeout?: number,
): Promise<Response | string> => {
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');
Expand Down
28 changes: 22 additions & 6 deletions frontend/public/co-fetch.ts
Original file line number Diff line number Diff line change
@@ -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'];
Expand Down