Skip to content

Commit

Permalink
Merge pull request #2382 from zowe/attach-proxy-options
Browse files Browse the repository at this point in the history
Attach authentication header to proxy agent
  • Loading branch information
zFernand0 authored Dec 17, 2024
2 parents a0b45a0 + 92aa324 commit fff3f23
Show file tree
Hide file tree
Showing 5 changed files with 181 additions and 62 deletions.
4 changes: 4 additions & 0 deletions packages/imperative/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

All notable changes to the Imperative package will be documented in this file.

## Recent Changes

- BugFix: Modified location of Proxy-Authorization header to be located in the agent instead of the request. [#2389](https://github.com/zowe/zowe-cli/issues/2389)

## `8.8.3`

- BugFix: Modified 8.8.2 bugfix to correct web help alias. [#2361](https://github.com/zowe/zowe-cli/pull/2361)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1502,22 +1502,6 @@ describe("AbstractRestClient tests", () => {
const result = privateRestClient.buildOptions(resource, request, reqHeaders);
expect(Object.keys(result)).toContain('agent');
});

it('Should use session proxy options over env vars for proxy agent', () => {
restSession.ISession.proxy = { proxy_authorization: 'proxy_auth_string'};
const resource = '/resource';
const request = '';
const reqHeaders: any[] = [];
const url = new URL('https://www.zowe.com');
const proxyAgent = new HttpsProxyAgent(url, { rejectUnauthorized: true });
getSystemProxyUrlSpy.mockReturnValue(url);
getProxyAgentSpy.mockReturnValue(proxyAgent);
setCertPemAuthSpy.mockReturnValue(true);
const headerSpy = jest.spyOn(privateRestClient, "appendHeaders");
const result = privateRestClient.buildOptions(resource, request, reqHeaders);
expect(Object.keys(result)).toContain('agent');
expect(headerSpy).toHaveBeenCalledWith([{'Proxy-Authorization': restSession.ISession.proxy.proxy_authorization}]);
});
});
});
});
137 changes: 117 additions & 20 deletions packages/imperative/src/rest/__tests__/client/ProxySettings.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,17 @@ describe("Proxy tests", () => {
const session = {
hostname: "fake.com",
port: 443,
rejectUnauthorized: false
rejectUnauthorized: false,
} as ISession;
const privateProxy = ProxySettings as any;
const httpUrl = "http://www.zowe.com";
const httpsUrl = "https://www.zowe.com";
const noProxyList = "www.zowe.com, fake.com,ibm.com,broadcom.com ";
const passedUrl = "passedurl.com";
let getProxySettingsSpy: jest.SpyInstance;
let getProxyAuthSettingSpy: jest.SpyInstance;
let checkUrlSpy: jest.SpyInstance;
let matchesNoProxySettingsSpy: jest.SpyInstance;

describe("recognise passed proxy values in session", () => {
const noProxySpy = jest.spyOn(privateProxy, "matchesNoProxySettings");
Expand All @@ -40,7 +42,7 @@ describe("Proxy tests", () => {
checkUrlSpy = jest.spyOn(privateProxy, "checkUrl");
const expected = {
proxyUrl: passedUrl,
protocol: HTTPS_PROTOCOL
protocol: HTTPS_PROTOCOL,
};

beforeEach(() => {
Expand All @@ -55,7 +57,9 @@ describe("Proxy tests", () => {
expect(httpEnvVarSpy).not.toHaveBeenCalled();
expect(httpsEnvVarSpy).not.toHaveBeenCalled();
checkUrlSpy.mockReturnValueOnce(passedUrl);
expect(JSON.stringify(ProxySettings["getProxySettings"](session))).toEqual(JSON.stringify(expected));
expect(
JSON.stringify(ProxySettings["getProxySettings"](session))
).toEqual(JSON.stringify(expected));
noProxySpy.mockClear();
checkUrlSpy.mockClear();
});
Expand All @@ -67,34 +71,89 @@ describe("Proxy tests", () => {
expect(httpEnvVarSpy).not.toHaveBeenCalled();
expect(httpsEnvVarSpy).not.toHaveBeenCalled();
checkUrlSpy.mockReturnValueOnce(passedUrl);
expect(JSON.stringify(ProxySettings["getProxySettings"](session))).toEqual(JSON.stringify(expected));
expect(
JSON.stringify(ProxySettings["getProxySettings"](session))
).toEqual(JSON.stringify(expected));
noProxySpy.mockClear();
checkUrlSpy.mockClear();
});
});

describe("getProxyAgent", () => {
const headers = {
"Proxy-Authorization": "Basic ==ThisIsATest123",
};

beforeEach(() => {
jest.clearAllMocks();
jest.restoreAllMocks();
jest.resetModules();
jest.resetAllMocks();
getProxySettingsSpy = jest.spyOn(privateProxy, "getProxySettings");
getProxyAuthSettingSpy = jest.spyOn(
privateProxy,
"getProxyAuthHeader"
);
});

it("Should retrieve the HTTP proxy agent", () => {
const expected = new HttpProxyAgent(httpUrl);
const expected = new HttpProxyAgent(httpUrl, { headers });
getProxySettingsSpy.mockReturnValue({
proxyUrl: httpUrl,
protocol: HTTP_PROTOCOL
protocol: HTTP_PROTOCOL,
});
expect(JSON.stringify(ProxySettings.getProxyAgent(session))).toEqual(JSON.stringify(expected));
getProxyAuthSettingSpy.mockReturnValue(headers);
expect(
JSON.stringify(ProxySettings.getProxyAgent(session))
).toEqual(JSON.stringify(expected));
});

it("Should retrieve the HTTPS proxy agent", () => {
const expected = new HttpsProxyAgent(httpsUrl, { rejectUnauthorized: false });
const expected = new HttpsProxyAgent(httpsUrl, {
rejectUnauthorized: false,
});
getProxySettingsSpy.mockReturnValue({
proxyUrl: httpsUrl,
protocol: HTTPS_PROTOCOL
protocol: HTTPS_PROTOCOL,
});
expect(JSON.stringify(ProxySettings.getProxyAgent(session))).toEqual(JSON.stringify(expected));
expect(
JSON.stringify(ProxySettings.getProxyAgent(session))
).toEqual(JSON.stringify(expected));
});

it("Should return undefined when a protocol is not defined in the session", () => {
const noProtocolSession = { ...session };
noProtocolSession.protocol = undefined;
expect(ProxySettings.getProxyAgent(session)).toEqual(undefined);
});
});

describe("getProxyAuthHeader", () => {
beforeEach(() => {
jest.clearAllMocks();
jest.restoreAllMocks();
jest.resetModules();
jest.resetAllMocks();
});

it("Should retrieve the auth header from the proxy settings", () => {
const proxyAuthSetting = "Basic ==ThisIsATest123";
expect(
ProxySettings["getProxyAuthHeader"]({
authSetting: proxyAuthSetting,
proxyUrl: new URL("https://www.google.com/"),
protocol: HTTPS_PROTOCOL,
})
).toEqual({ "Proxy-Authorization": proxyAuthSetting });
});

it("Should return undefined if the proxy auth setting is not in the proxy settings", () => {
expect(
ProxySettings["getProxyAuthHeader"]({
proxyUrl: new URL("https://www.google.com/"),
protocol: HTTPS_PROTOCOL,
})
).toEqual(undefined);
});
});

Expand All @@ -107,7 +166,7 @@ describe("Proxy tests", () => {
it("Should retrieve the system proxy URL", () => {
getProxySettingsSpy.mockReturnValue({
proxyUrl: httpsUrl,
protocol: HTTPS_PROTOCOL
protocol: HTTPS_PROTOCOL,
});
expect(ProxySettings.getSystemProxyUrl(session)).toEqual(httpsUrl);
});
Expand All @@ -116,16 +175,36 @@ describe("Proxy tests", () => {
describe("getProxySettings", () => {
beforeEach(() => {
jest.clearAllMocks();
jest.restoreAllMocks();
jest.resetModules();
jest.resetAllMocks();
checkUrlSpy = jest.spyOn(privateProxy, "checkUrl");
matchesNoProxySettingsSpy = jest.spyOn(
privateProxy,
"matchesNoProxySettings"
);
});

it("Should return proxy settings from session", () => {
const expected = {
proxyUrl: httpsUrl,
protocol: HTTPS_PROTOCOL
protocol: HTTPS_PROTOCOL,
authSetting: "Basic ==ThisIsATest123",
};
checkUrlSpy.mockReturnValue(httpsUrl);
expect(ProxySettings["getProxySettings"](session)).toEqual(expected);
session.proxy = {
proxy_authorization: "Basic ==ThisIsATest123",
};
expect(ProxySettings["getProxySettings"](session)).toEqual(
expected
);
});

it("Should return undefined proxy url matchesNoProxySettings", () => {
matchesNoProxySettingsSpy.mockReturnValue(true);
expect(ProxySettings["getProxySettings"](session)).toEqual(
undefined
);
});
});

Expand All @@ -143,28 +222,46 @@ describe("Proxy tests", () => {
});

describe("matchesNoProxySettings", () => {
it("Should match session hostname with no_proxy", () => {
beforeEach(() => {
jest.clearAllMocks();
jest.restoreAllMocks();
jest.resetModules();
jest.resetAllMocks();
});

it("Should match session hostname with no_proxy", () => {
const expected = true;
process.env["NO_PROXY"] = noProxyList;
expect(ProxySettings["matchesNoProxySettings"](session)).toEqual(expected);
expect(ProxySettings["matchesNoProxySettings"](session)).toEqual(
expected
);
process.env["NO_PROXY"] = undefined;
});
it("Should return true for match with no_proxy passed with session proxy", () => {
session.proxy = { http_proxy: passedUrl, no_proxy: ["fake.com"] };
session.protocol = HTTP_PROTOCOL;
expect(ProxySettings["matchesNoProxySettings"](session)).toEqual(true);
expect(ProxySettings["matchesNoProxySettings"](session)).toEqual(
true
);
});
it("Should not match session hostname with no_proxy", () => {
it("Should not match session hostname with no_proxy", () => {
const expected = false;
process.env["NO_PROXY"] = noProxyList;
session.hostname = "microsoft.com";
expect(ProxySettings["matchesNoProxySettings"](session)).toEqual(expected);
expect(ProxySettings["matchesNoProxySettings"](session)).toEqual(
expected
);
process.env["NO_PROXY"] = undefined;
});
it("Should return false for match with no_proxy passed with session proxy", () => {
session.proxy = { http_proxy: passedUrl, no_proxy: ["false.com", "blah.com"] };
session.proxy = {
http_proxy: passedUrl,
no_proxy: ["false.com", "blah.com"],
};
session.protocol = HTTP_PROTOCOL;
expect(ProxySettings["matchesNoProxySettings"](session)).toEqual(false);
expect(ProxySettings["matchesNoProxySettings"](session)).toEqual(
false
);
});
});
});
3 changes: 0 additions & 3 deletions packages/imperative/src/rest/src/client/AbstractRestClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -476,9 +476,6 @@ export abstract class AbstractRestClient {
this.mLogger.info(`Proxy setting "${proxyUrl.href}" will not be used as hostname was found listed under "no_proxy" setting.`);
} else {
this.mLogger.info(`Using the following proxy setting for the request: ${proxyUrl.href}`);
if (this.session.ISession.proxy?.proxy_authorization) {
reqHeaders.push({ 'Proxy-Authorization': this.session.ISession.proxy.proxy_authorization});
}
options.agent = ProxySettings.getProxyAgent(this.session.ISession);
}
}
Expand Down
Loading

0 comments on commit fff3f23

Please sign in to comment.