From 5e1b8e3db3cbdd260335b33753cea914ce5f8f33 Mon Sep 17 00:00:00 2001 From: SanthoshiBoyina1 <142206957+SanthoshiBoyina1@users.noreply.github.com> Date: Thu, 24 Oct 2024 22:03:23 +0530 Subject: [PATCH] Fix: To resolve error message upon switching the authentication methods (#3275) * To handle missing args in basicAuthClearSecureArray Signed-off-by: Santhoshi Boyina * To handle missing args in tokenAuthClearSecureArray Signed-off-by: Santhoshi Boyina * To add unit test case for missing arg's in basicAuthClearSecureArray Signed-off-by: Santhoshi Boyina * To add unit test case for missing arg's in tokenAuthClearSecureArray Signed-off-by: Santhoshi Boyina * To run pre-publish command Signed-off-by: Santhoshi Boyina * To update changelog Signed-off-by: Santhoshi Boyina * To modify tokenAuthClearSecureArray() function Signed-off-by: Santhoshi Boyina * To add additional test cases for tokenAuthClearSecureArray() Signed-off-by: Santhoshi Boyina --------- Signed-off-by: Santhoshi Boyina --- packages/zowe-explorer/CHANGELOG.md | 1 + .../configuration/Profiles.unit.test.ts | 167 ++++++++++++++++++ packages/zowe-explorer/l10n/bundle.l10n.json | 3 +- packages/zowe-explorer/l10n/poeditor.json | 3 +- .../src/configuration/Profiles.ts | 29 ++- 5 files changed, 193 insertions(+), 10 deletions(-) diff --git a/packages/zowe-explorer/CHANGELOG.md b/packages/zowe-explorer/CHANGELOG.md index edfb1d5437..cb2a470ea1 100644 --- a/packages/zowe-explorer/CHANGELOG.md +++ b/packages/zowe-explorer/CHANGELOG.md @@ -13,6 +13,7 @@ All notable changes to the "vscode-extension-for-zowe" extension will be documen - `DatasetFSProvider.stat()` will now throw a `FileNotFound` error for extenders trying to fetch an MVS resource that does not exist. [#3252](https://github.com/zowe/zowe-explorer-vscode/issues/3252) - Fixed an issue where renaming or deleting a USS file or data set did not update the opened editor. [#3260](https://github.com/zowe/zowe-explorer-vscode/issues/3260) +- Fixed issue where switching the authentication methods would cause `Cannot read properties of undefined` error. [#3142](https://github.com/zowe/zowe-explorer-vscode/issues/3142) ## `3.0.2` diff --git a/packages/zowe-explorer/__tests__/__unit__/configuration/Profiles.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/configuration/Profiles.unit.test.ts index 813d265b74..8f82c2a620 100644 --- a/packages/zowe-explorer/__tests__/__unit__/configuration/Profiles.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/configuration/Profiles.unit.test.ts @@ -2281,4 +2281,171 @@ describe("Profiles Unit Tests - function basicAuthClearSecureArray", () => { getProfileInfoMock.mockRestore(); getProfileFromConfigMock.mockRestore(); }); + + it("does not call Config.delete when user and password arg's are missing in mergeArgsForProfile", async () => { + const teamCfgMock = { + delete: jest.fn(), + save: jest.fn(), + set: jest.fn(), + }; + const profAttrsMock = { + isDefaultProfile: false, + profName: "example_profile", + profType: "zosmf", + profLoc: { + jsonLoc: undefined, + }, + }; + const mergeArgsMock = { + knownArgs: [], + }; + const getProfileInfoMock = jest.spyOn(Profiles.getInstance(), "getProfileInfo").mockResolvedValue({ + getTeamConfig: jest.fn().mockReturnValue(teamCfgMock), + mergeArgsForProfile: jest.fn().mockReturnValue(mergeArgsMock), + } as any); + const getProfileFromConfigMock = jest.spyOn(Profiles.getInstance(), "getProfileFromConfig").mockResolvedValue(profAttrsMock); + + await Profiles.getInstance().basicAuthClearSecureArray("example_profile"); + expect(teamCfgMock.delete).not.toHaveBeenCalled(); + expect(teamCfgMock.set).not.toHaveBeenCalled(); + expect(teamCfgMock.save).toHaveBeenCalled(); + getProfileInfoMock.mockRestore(); + getProfileFromConfigMock.mockRestore(); + }); +}); + +describe("Profiles Unit Tests - function tokenAuthClearSecureArray", () => { + it("calls Config APIs when profLoc.jsonLoc is valid, no loginTokenType provided", async () => { + const teamCfgMock = { + delete: jest.fn(), + save: jest.fn(), + set: jest.fn(), + }; + const profAttrsMock = { + isDefaultProfile: false, + profName: "example_profile", + profType: "zosmf", + profLoc: { + jsonLoc: "/user/path/to/zowe.config.json", + locType: imperative.ProfLocType.TEAM_CONFIG, + }, + }; + const mergeArgsMock = { + knownArgs: [ + { + argName: "tokenType", + argLoc: { + jsonLoc: "profiles.example_profile.properties.tokenType", + }, + }, + { + argName: "tokenValue", + argLoc: { + jsonLoc: "profiles.example_profile.properties.tokenValue", + }, + }, + { + argName: "tokenExpiration", + argLoc: { + jsonLoc: "profiles.example_profile.properties.tokenExpiration", + }, + }, + ], + }; + const getProfileInfoMock = jest.spyOn(Profiles.getInstance(), "getProfileInfo").mockResolvedValue({ + getTeamConfig: jest.fn().mockReturnValue(teamCfgMock), + mergeArgsForProfile: jest.fn().mockReturnValue(mergeArgsMock), + } as any); + const getProfileFromConfigMock = jest.spyOn(Profiles.getInstance(), "getProfileFromConfig").mockResolvedValue(profAttrsMock); + + await Profiles.getInstance().tokenAuthClearSecureArray("example_profile"); + expect(teamCfgMock.delete).toHaveBeenCalledWith(mergeArgsMock.knownArgs[0].argLoc.jsonLoc); + expect(teamCfgMock.delete).toHaveBeenCalledWith(mergeArgsMock.knownArgs[1].argLoc.jsonLoc); + expect(teamCfgMock.delete).toHaveBeenCalledWith(mergeArgsMock.knownArgs[2].argLoc.jsonLoc); + expect(teamCfgMock.set).toHaveBeenCalledWith(`${profAttrsMock.profLoc.jsonLoc}.secure`, ["user", "password"]); + expect(teamCfgMock.save).toHaveBeenCalled(); + getProfileInfoMock.mockRestore(); + getProfileFromConfigMock.mockRestore(); + }); + it("calls Config APIs when profLoc.jsonLoc is valid, loginTokenType provided", async () => { + const teamCfgMock = { + delete: jest.fn(), + save: jest.fn(), + set: jest.fn(), + }; + const profAttrsMock = { + isDefaultProfile: false, + profName: "example_profile", + profType: "zosmf", + profLoc: { + jsonLoc: "/user/path/to/zowe.config.json", + locType: imperative.ProfLocType.TEAM_CONFIG, + }, + }; + const mergeArgsMock = { + knownArgs: [ + { + argName: "tokenType", + argLoc: { + jsonLoc: "profiles.example_profile.properties.tokenType", + }, + }, + { + argName: "tokenValue", + argLoc: { + jsonLoc: "profiles.example_profile.properties.tokenValue", + }, + }, + { + argName: "tokenExpiration", + argLoc: { + jsonLoc: "profiles.example_profile.properties.tokenExpiration", + }, + }, + ], + }; + const getProfileInfoMock = jest.spyOn(Profiles.getInstance(), "getProfileInfo").mockResolvedValue({ + getTeamConfig: jest.fn().mockReturnValue(teamCfgMock), + mergeArgsForProfile: jest.fn().mockReturnValue(mergeArgsMock), + } as any); + const getProfileFromConfigMock = jest.spyOn(Profiles.getInstance(), "getProfileFromConfig").mockResolvedValue(profAttrsMock); + + await Profiles.getInstance().tokenAuthClearSecureArray("example_profile", "apimlAuthenticationToken"); + expect(teamCfgMock.delete).toHaveBeenCalledWith(mergeArgsMock.knownArgs[0].argLoc.jsonLoc); + expect(teamCfgMock.delete).toHaveBeenCalledWith(mergeArgsMock.knownArgs[1].argLoc.jsonLoc); + expect(teamCfgMock.delete).toHaveBeenCalledWith(mergeArgsMock.knownArgs[2].argLoc.jsonLoc); + expect(teamCfgMock.set).toHaveBeenCalledWith(`${profAttrsMock.profLoc.jsonLoc}.secure`, []); + expect(teamCfgMock.save).toHaveBeenCalled(); + getProfileInfoMock.mockRestore(); + getProfileFromConfigMock.mockRestore(); + }); + it("does not call Config.delete when tokenType, tokenValue, tokenExpiration arg's are missing in mergeArgsForProfile", async () => { + const teamCfgMock = { + delete: jest.fn(), + save: jest.fn(), + set: jest.fn(), + }; + const profAttrsMock = { + isDefaultProfile: false, + profName: "example_profile", + profType: "zosmf", + profLoc: { + jsonLoc: undefined, + }, + }; + const mergeArgsMock = { + knownArgs: [], + }; + const getProfileInfoMock = jest.spyOn(Profiles.getInstance(), "getProfileInfo").mockResolvedValue({ + getTeamConfig: jest.fn().mockReturnValue(teamCfgMock), + mergeArgsForProfile: jest.fn().mockReturnValue(mergeArgsMock), + } as any); + const getProfileFromConfigMock = jest.spyOn(Profiles.getInstance(), "getProfileFromConfig").mockResolvedValue(profAttrsMock); + await Profiles.getInstance().tokenAuthClearSecureArray("example_profile"); + expect(teamCfgMock.delete).not.toHaveBeenCalled(); + expect(teamCfgMock.set).not.toHaveBeenCalled(); + expect(teamCfgMock.save).toHaveBeenCalled(); + getProfileInfoMock.mockRestore(); + getProfileFromConfigMock.mockRestore(); + }); }); diff --git a/packages/zowe-explorer/l10n/bundle.l10n.json b/packages/zowe-explorer/l10n/bundle.l10n.json index 75ccca5c51..a58bb857eb 100644 --- a/packages/zowe-explorer/l10n/bundle.l10n.json +++ b/packages/zowe-explorer/l10n/bundle.l10n.json @@ -3,13 +3,12 @@ "Page Size:": "Page Size:", "Page": "Page", "No": "No", + "items": "items", "item": "item", - "s": "s", "selected": "selected", "Data Sets": "Data Sets", "Unix System Services (USS)": "Unix System Services (USS)", "Jobs": "Jobs", - "DataPanelContext has to be used within ": "DataPanelContext has to be used within ", "Refresh": "Refresh", "Search History": "Search History", "Favorites": "Favorites", diff --git a/packages/zowe-explorer/l10n/poeditor.json b/packages/zowe-explorer/l10n/poeditor.json index ca004c0549..71ae3ec2e0 100644 --- a/packages/zowe-explorer/l10n/poeditor.json +++ b/packages/zowe-explorer/l10n/poeditor.json @@ -414,13 +414,12 @@ "Page Size:": "", "Page": "", "No": "", + "items": "", "item": "", - "s": "", "selected": "", "Data Sets": "", "Unix System Services (USS)": "", "Jobs": "", - "DataPanelContext has to be used within ": "", "Refresh": "", "Search History": "", "Favorites": "", diff --git a/packages/zowe-explorer/src/configuration/Profiles.ts b/packages/zowe-explorer/src/configuration/Profiles.ts index de3fd1b590..56c67aedc0 100644 --- a/packages/zowe-explorer/src/configuration/Profiles.ts +++ b/packages/zowe-explorer/src/configuration/Profiles.ts @@ -834,8 +834,14 @@ export class Profiles extends ProfilesCache { if (profAttrs.profLoc.jsonLoc) { configApi.set(`${profAttrs.profLoc.jsonLoc}.secure`, loginTokenType?.startsWith("apimlAuthenticationToken") ? [] : ["tokenValue"]); } - configApi.delete(profInfo.mergeArgsForProfile(profAttrs).knownArgs.find((arg) => arg.argName === "user")?.argLoc.jsonLoc); - configApi.delete(profInfo.mergeArgsForProfile(profAttrs).knownArgs.find((arg) => arg.argName === "password")?.argLoc.jsonLoc); + const userArgJsonLoc = profInfo.mergeArgsForProfile(profAttrs).knownArgs.find((arg) => arg.argName === "user")?.argLoc.jsonLoc; + if (userArgJsonLoc) { + configApi.delete(userArgJsonLoc); + } + const passwordArgJsonLoc = profInfo.mergeArgsForProfile(profAttrs).knownArgs.find((arg) => arg.argName === "password")?.argLoc.jsonLoc; + if (passwordArgJsonLoc) { + configApi.delete(passwordArgJsonLoc); + } await configApi.save(); } @@ -848,11 +854,22 @@ export class Profiles extends ProfilesCache { // Otherwise, we want to keep `tokenValue` in the secure array of the parent profile to avoid disconnecting child profiles if (profAttrs?.profLoc.jsonLoc) { configApi.set(`${profAttrs.profLoc.jsonLoc}.secure`, usingApimlToken ? [] : ["user", "password"]); - configApi.delete(profInfo.mergeArgsForProfile(profAttrs).knownArgs.find((arg) => arg.argName === "tokenType")?.argLoc.jsonLoc); - configApi.delete(profInfo.mergeArgsForProfile(profAttrs).knownArgs.find((arg) => arg.argName === "tokenValue")?.argLoc.jsonLoc); - configApi.delete(profInfo.mergeArgsForProfile(profAttrs).knownArgs.find((arg) => arg.argName === "tokenExpiration")?.argLoc.jsonLoc); - await configApi.save(); + const tokenTypeArgJsonLoc = profInfo.mergeArgsForProfile(profAttrs).knownArgs.find((arg) => arg.argName === "tokenType")?.argLoc.jsonLoc; + if (tokenTypeArgJsonLoc) { + configApi.delete(tokenTypeArgJsonLoc); + } + const tokenValueArgJsonLoc = profInfo.mergeArgsForProfile(profAttrs).knownArgs.find((arg) => arg.argName === "tokenValue") + ?.argLoc.jsonLoc; + if (tokenValueArgJsonLoc) { + configApi.delete(tokenValueArgJsonLoc); + } + const tokenExpirationArgJsonLoc = profInfo.mergeArgsForProfile(profAttrs).knownArgs.find((arg) => arg.argName === "tokenExpiration") + ?.argLoc.jsonLoc; + if (tokenExpirationArgJsonLoc) { + configApi.delete(tokenExpirationArgJsonLoc); + } } + await configApi.save(); } public async handleSwitchAuthentication(node: Types.IZoweNodeType): Promise {