Skip to content

Commit 554ca7c

Browse files
committed
Enhance impersonation header handling in console-fetch-utils and co-fetch. Support multiple 'Impersonate-Group' values and ensure proper merging of headers in fetch requests.
1 parent d62c016 commit 554ca7c

File tree

3 files changed

+45
-15
lines changed

3 files changed

+45
-15
lines changed

frontend/packages/console-dynamic-plugin-sdk/src/utils/fetch/console-fetch-utils.ts

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { getImpersonate } from '../../app/core/reducers';
22
import storeHandler from '../../app/storeHandler';
33

44
type ConsoleRequestHeaders = {
5-
'Impersonate-Group'?: string;
5+
'Impersonate-Group'?: string | string[];
66
'Impersonate-User'?: string;
77
'X-CSRFToken'?: string;
88
};
@@ -35,12 +35,23 @@ export const getConsoleRequestHeaders = (): ConsoleRequestHeaders => {
3535
};
3636

3737
// Set impersonation headers
38-
const { kind, name } = getImpersonate(state) || {};
39-
if ((kind === 'User' || kind === 'Group') && name) {
40-
// Even if we are impersonating a group, we still need to set Impersonate-User to something or k8s will complain
41-
headers['Impersonate-User'] = name;
42-
if (kind === 'Group') {
38+
const impersonateData = getImpersonate(state);
39+
if (impersonateData) {
40+
const { kind, name, groups } = impersonateData;
41+
42+
if (kind === 'User' && name) {
43+
// Simple user impersonation
44+
headers['Impersonate-User'] = name;
45+
} else if (kind === 'Group' && name) {
46+
// Single group impersonation (backward compatibility)
47+
// Even if we are impersonating a group, we still need to set Impersonate-User to something or k8s will complain
48+
headers['Impersonate-User'] = name;
4349
headers['Impersonate-Group'] = name;
50+
} else if (kind === 'UserWithGroups' && name && groups && groups.length > 0) {
51+
// User with multiple groups impersonation
52+
headers['Impersonate-User'] = name;
53+
// Set multiple Impersonate-Group headers - one for each group
54+
headers['Impersonate-Group'] = groups;
4455
}
4556
}
4657

frontend/packages/console-dynamic-plugin-sdk/src/utils/fetch/console-fetch.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,12 @@ const consoleFetchCommon = async (
5050
options: RequestInit = {},
5151
timeout?: number,
5252
): Promise<Response | string> => {
53-
const headers = getConsoleRequestHeaders();
54-
// Pass headers last to let callers to override Accept.
55-
const allOptions = _.defaultsDeep({ method }, options, { headers });
53+
const consoleHeaders = getConsoleRequestHeaders();
54+
55+
// Merge headers properly - console headers first, then let options override
56+
const mergedHeaders = { ...consoleHeaders, ...options.headers };
57+
const allOptions = _.defaultsDeep({ method, headers: mergedHeaders }, options);
58+
5659
const response = await consoleFetch(url, allOptions, timeout);
5760
const dataPromise = parseData(response);
5861
const warning = response.headers.get('Warning');

frontend/public/co-fetch.ts

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,32 @@
11
import * as _ from 'lodash';
22
import { HttpError, RetryError } from '@console/dynamic-plugin-sdk/src/utils/error/http-error';
33
import { authSvc } from './module/auth';
4-
import { getCSRFToken } from '@console/dynamic-plugin-sdk/src/utils/fetch/console-fetch-utils';
4+
import { getConsoleRequestHeaders } from '@console/dynamic-plugin-sdk/src/utils/fetch/console-fetch-utils';
55

66
export const applyConsoleHeaders = (url, options) => {
7-
const token = getCSRFToken();
8-
if (options.headers) {
9-
options.headers['X-CSRFToken'] = token;
10-
} else {
11-
options.headers = { 'X-CSRFToken': token };
7+
const consoleHeaders = getConsoleRequestHeaders();
8+
9+
if (!options.headers) {
10+
options.headers = {};
1211
}
1312

13+
// Apply console headers, handling array values for multiple headers
14+
Object.entries(consoleHeaders || {}).forEach(([key, value]) => {
15+
if (Array.isArray(value)) {
16+
// For multiple Impersonate-Group headers, we need special handling
17+
// because fetch() API combines them into a single comma-separated header
18+
// which doesn't work for Kubernetes impersonation
19+
if (key === 'Impersonate-Group') {
20+
// Send as a special header that the backend will split
21+
options.headers['X-Console-Impersonate-Groups'] = value.join(',');
22+
} else {
23+
options.headers[key] = value;
24+
}
25+
} else if (value) {
26+
options.headers[key] = value;
27+
}
28+
});
29+
1430
// X-CSRFToken is used only for non-GET requests targeting bridge
1531
if (options.method === 'GET' || url.indexOf('://') >= 0) {
1632
delete options.headers['X-CSRFToken'];

0 commit comments

Comments
 (0)