diff --git a/packages/zowe-explorer/CHANGELOG.md b/packages/zowe-explorer/CHANGELOG.md index 078ee39f37..0ea1ffad1e 100644 --- a/packages/zowe-explorer/CHANGELOG.md +++ b/packages/zowe-explorer/CHANGELOG.md @@ -19,6 +19,7 @@ All notable changes to the "vscode-extension-for-zowe" extension will be documen - Fixed submitting local JCL using command pallet option `Zowe Explorer: Submit JCL` by adding a check for chosen profile returned to continue the action. [#1625](https://github.com/zowe/vscode-extension-for-zowe/issues/1625) - Fixed conflict resolution being skipped if local and remote file have different contents but are the same size. [#2496](https://github.com/zowe/vscode-extension-for-zowe/issues/2496) +- Fixed issue with token based auth for unsecure profiles in Zowe Explorer [#2518](https://github.com/zowe/vscode-extension-for-zowe/issues/2518) ## `2.11.2` diff --git a/packages/zowe-explorer/__tests__/__unit__/Profiles.extended.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/Profiles.extended.unit.test.ts index fda50471b8..f1e35c53e3 100644 --- a/packages/zowe-explorer/__tests__/__unit__/Profiles.extended.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/Profiles.extended.unit.test.ts @@ -38,6 +38,7 @@ import { createIJobObject, createJobsTree } from "../../__mocks__/mockCreators/j import * as path from "path"; import { SettingsConfig } from "../../src/utils/SettingsConfig"; import { ZoweLogger } from "../../src/utils/LoggerUtils"; +import { TreeProviders } from "../../src/shared/TreeProviders"; jest.mock("child_process"); jest.mock("fs"); @@ -1645,6 +1646,14 @@ describe("Profiles Unit Tests - function ssoLogout", () => { jest.spyOn(Gui, "showMessage").mockImplementation(); }); it("should logout successfully and refresh zowe explorer", async () => { + const mockTreeProvider = { + mSessionNodes: [testNode], + flipState: jest.fn(), + refreshElement: jest.fn(), + } as any; + jest.spyOn(TreeProviders, "ds", "get").mockReturnValue(mockTreeProvider); + jest.spyOn(TreeProviders, "uss", "get").mockReturnValue(mockTreeProvider); + jest.spyOn(TreeProviders, "job", "get").mockReturnValue(mockTreeProvider); const getTokenTypeNameMock = jest.fn(); const logoutMock = jest.fn(); jest.spyOn(ZoweExplorerApiRegister.getInstance(), "getCommonApi").mockImplementation(() => ({ @@ -1803,3 +1812,67 @@ describe("Profiles Unit Tests - function getSecurePropsForProfile", () => { await expect(Profiles.getInstance().getSecurePropsForProfile(globalMocks.testProfile.name ?? "")).resolves.toEqual(["tokenValue"]); }); }); + +describe("Profiles Unit Tests - function clearFilterFromAllTrees", () => { + afterEach(() => { + jest.clearAllMocks(); + jest.resetModules(); + jest.restoreAllMocks(); + }); + + it("should fail to clear filter if no session nodes are available", async () => { + const globalMocks = await createGlobalMocks(); + const testNode = new (ZoweTreeNode as any)( + "fake", + vscode.TreeItemCollapsibleState.None, + undefined, + globalMocks.testSession, + globalMocks.testProfile + ); + + const flipStateSpy = jest.fn(); + const refreshElementSpy = jest.fn(); + + const mockTreeProvider = { + mSessionNodes: [], + flipState: flipStateSpy, + refreshElement: refreshElementSpy, + } as any; + jest.spyOn(TreeProviders, "ds", "get").mockReturnValue(mockTreeProvider); + jest.spyOn(TreeProviders, "uss", "get").mockReturnValue(mockTreeProvider); + jest.spyOn(TreeProviders, "job", "get").mockReturnValue(mockTreeProvider); + + expect(Profiles.getInstance().clearFilterFromAllTrees(testNode)); + expect(flipStateSpy).toBeCalledTimes(0); + expect(refreshElementSpy).toBeCalledTimes(0); + }); + + it("should fail to clear filters if the session node is not listed in the tree", async () => { + const globalMocks = await createGlobalMocks(); + const testNode = new (ZoweTreeNode as any)( + "fake", + vscode.TreeItemCollapsibleState.None, + undefined, + globalMocks.testSession, + globalMocks.testProfile + ); + + const flipStateSpy = jest.fn(); + const refreshElementSpy = jest.fn(); + const getProfileSpy = jest.fn(() => ({ name: "test" })); + + const mockTreeProvider = { + mSessionNodes: [{ getProfile: getProfileSpy }], + flipState: flipStateSpy, + refreshElement: refreshElementSpy, + } as any; + jest.spyOn(TreeProviders, "ds", "get").mockReturnValue(mockTreeProvider); + jest.spyOn(TreeProviders, "uss", "get").mockReturnValue(mockTreeProvider); + jest.spyOn(TreeProviders, "job", "get").mockReturnValue(mockTreeProvider); + + expect(Profiles.getInstance().clearFilterFromAllTrees(testNode)); + expect(flipStateSpy).toBeCalledTimes(0); + expect(refreshElementSpy).toBeCalledTimes(0); + expect(getProfileSpy).toBeCalledTimes(3); + }); +}); diff --git a/packages/zowe-explorer/__tests__/__unit__/abstract/TreeProvider.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/abstract/ZoweTreeProvider.unit.test.ts similarity index 100% rename from packages/zowe-explorer/__tests__/__unit__/abstract/TreeProvider.unit.test.ts rename to packages/zowe-explorer/__tests__/__unit__/abstract/ZoweTreeProvider.unit.test.ts diff --git a/packages/zowe-explorer/__tests__/__unit__/shared/TreeProvider.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/shared/TreeProvider.unit.test.ts new file mode 100644 index 0000000000..7b1f542251 --- /dev/null +++ b/packages/zowe-explorer/__tests__/__unit__/shared/TreeProvider.unit.test.ts @@ -0,0 +1,43 @@ +/** + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright Contributors to the Zowe Project. + * + */ + +import { createTreeView } from "../../../__mocks__/mockCreators/shared"; +import { TreeProviders } from "../../../src/shared/TreeProviders"; + +describe("TreeProvider Unit Tests - Function getters", () => { + it("should retrieve the ds provider", async () => { + const mockTree = createTreeView("ds"); + await TreeProviders.initializeProviders({} as any, { + ds: jest.fn(() => mockTree) as any, + uss: jest.fn(), + job: jest.fn(), + }); + expect(TreeProviders.ds).toEqual(mockTree); + }); + it("should retrieve the uss provider", async () => { + const mockTree = createTreeView("uss"); + await TreeProviders.initializeProviders({} as any, { + ds: jest.fn(), + uss: jest.fn(() => mockTree) as any, + job: jest.fn(), + }); + expect(TreeProviders.uss).toEqual(mockTree); + }); + it("should retrieve the uss provider", async () => { + const mockTree = createTreeView("job"); + await TreeProviders.initializeProviders({} as any, { + ds: jest.fn(), + uss: jest.fn(), + job: jest.fn(() => mockTree) as any, + }); + expect(TreeProviders.job).toEqual(mockTree); + }); +}); diff --git a/packages/zowe-explorer/src/Profiles.ts b/packages/zowe-explorer/src/Profiles.ts index ba164cd8d6..bad675e265 100644 --- a/packages/zowe-explorer/src/Profiles.ts +++ b/packages/zowe-explorer/src/Profiles.ts @@ -37,6 +37,7 @@ import * as globals from "./globals"; import * as nls from "vscode-nls"; import { SettingsConfig } from "./utils/SettingsConfig"; import { ZoweLogger } from "./utils/LoggerUtils"; +import { TreeProviders } from "./shared/TreeProviders"; // Set up localization nls.config({ @@ -1202,6 +1203,67 @@ export class Profiles extends ProfilesCache { } } + public clearDSFilterFromTree(node: IZoweNodeType): void { + if (!TreeProviders.ds?.mSessionNodes || !TreeProviders.ds?.mSessionNodes.length) { + return; + } + const dsNode: IZoweDatasetTreeNode = TreeProviders.ds.mSessionNodes.find( + (sessionNode: IZoweDatasetTreeNode) => sessionNode.getProfile()?.name === node.getProfile()?.name + ); + if (!dsNode) { + return; + } + dsNode.tooltip &&= node.getProfile()?.name; + dsNode.description &&= ""; + dsNode.pattern &&= ""; + TreeProviders.ds.flipState(dsNode, false); + TreeProviders.ds.refreshElement(dsNode); + } + + public clearUSSFilterFromTree(node: IZoweNodeType): void { + if (!TreeProviders.uss?.mSessionNodes || !TreeProviders.uss?.mSessionNodes.length) { + return; + } + const ussNode: IZoweUSSTreeNode = TreeProviders.uss.mSessionNodes.find( + (sessionNode: IZoweUSSTreeNode) => sessionNode.getProfile()?.name === node.getProfile()?.name + ); + if (!ussNode) { + return; + } + ussNode.tooltip &&= node.getProfile()?.name; + ussNode.description &&= ""; + ussNode.fullPath &&= ""; + TreeProviders.uss.flipState(ussNode, false); + TreeProviders.uss.refreshElement(ussNode); + } + + public clearJobFilterFromTree(node: IZoweNodeType): void { + if (!TreeProviders.job?.mSessionNodes || !TreeProviders.job?.mSessionNodes.length) { + return; + } + const jobNode: IZoweJobTreeNode = TreeProviders.job.mSessionNodes.find( + (sessionNode: IZoweJobTreeNode) => sessionNode.getProfile()?.name === node.getProfile()?.name + ); + if (!jobNode) { + return; + } + jobNode.tooltip &&= node.getProfile()?.name; + jobNode.description &&= ""; + jobNode.owner &&= ""; + jobNode.prefix &&= ""; + jobNode.status &&= ""; + jobNode.filtered &&= false; + jobNode.children &&= []; + TreeProviders.job.flipState(jobNode, false); + TreeProviders.job.refreshElement(jobNode); + } + + public clearFilterFromAllTrees(node: IZoweNodeType): void { + this.clearDSFilterFromTree(node); + this.clearUSSFilterFromTree(node); + this.clearJobFilterFromTree(node); + } + public async ssoLogout(node: IZoweNodeType): Promise { ZoweLogger.trace("Profiles.ssoLogout called."); const serviceProfile = node.getProfile(); @@ -1212,7 +1274,10 @@ export class Profiles extends ProfilesCache { ); return; } + try { + this.clearFilterFromAllTrees(node); + // this will handle extenders if (serviceProfile.type !== "zosmf" && serviceProfile.profile?.tokenType !== zowe.imperative.SessConstants.TOKEN_TYPE_APIML) { await ZoweExplorerApiRegister.getInstance() @@ -1259,13 +1324,16 @@ export class Profiles extends ProfilesCache { if (!profileName) { return []; } - if ((await this.getProfileInfo()).usingTeamConfig) { + const usingSecureCreds = !SettingsConfig.getDirectValue(globals.SETTINGS_SECURE_CREDENTIALS_ENABLED); + if ((await this.getProfileInfo()).usingTeamConfig && !usingSecureCreds) { const config = (await this.getProfileInfo()).getTeamConfig(); return config.api.secure.securePropsForProfile(profileName); } const profAttrs = await this.getProfileFromConfig(profileName); const mergedArgs = (await this.getProfileInfo()).mergeArgsForProfile(profAttrs); - return mergedArgs.knownArgs.filter((arg) => arg.secure).map((arg) => arg.argName); + return mergedArgs.knownArgs + .filter((arg) => arg.secure || arg.argName === "tokenType" || arg.argName === "tokenValue") + .map((arg) => arg.argName); } private async loginWithBaseProfile(serviceProfile: zowe.imperative.IProfileLoaded, loginTokenType: string, node?: IZoweNodeType): Promise { diff --git a/packages/zowe-explorer/src/extension.ts b/packages/zowe-explorer/src/extension.ts index e68d3f999c..d20fd9629d 100644 --- a/packages/zowe-explorer/src/extension.ts +++ b/packages/zowe-explorer/src/extension.ts @@ -19,13 +19,14 @@ import { ProfilesUtils } from "./utils/ProfilesUtils"; import { initializeSpoolProvider } from "./SpoolProvider"; import { cleanTempDir, hideTempFolder } from "./utils/TempFolder"; import { SettingsConfig } from "./utils/SettingsConfig"; -import { initDatasetProvider } from "./dataset/init"; -import { initUSSProvider } from "./uss/init"; -import { initJobsProvider } from "./job/init"; -import { IZoweProviders, registerCommonCommands, registerRefreshCommand, watchConfigProfile } from "./shared/init"; +import { registerCommonCommands, registerRefreshCommand, watchConfigProfile } from "./shared/init"; import { ZoweLogger } from "./utils/LoggerUtils"; import { ZoweSaveQueue } from "./abstract/ZoweSaveQueue"; import { PollDecorator } from "./utils/DecorationProviders"; +import { TreeProviders } from "./shared/TreeProviders"; +import { initDatasetProvider } from "./dataset/init"; +import { initUSSProvider } from "./uss/init"; +import { initJobsProvider } from "./job/init"; /** * The function that runs when the extension is loaded @@ -52,11 +53,7 @@ export async function activate(context: vscode.ExtensionContext): Promise Promise>; +export class TreeProviders { + static #ds: IZoweTree; + static #uss: IZoweTree; + static #job: IZoweTree; + + public static async initializeProviders( + context: vscode.ExtensionContext, + initializers: { ds: ProviderFunction; uss: ProviderFunction; job: ProviderFunction } + ): Promise { + TreeProviders.#ds = await initializers.ds(context); + TreeProviders.#uss = await initializers.uss(context); + TreeProviders.#job = await initializers.job(context); + return TreeProviders.providers; + } + + public static get ds(): IZoweTree { + return TreeProviders.#ds; + } + + public static get uss(): IZoweTree { + return TreeProviders.#uss; + } + + public static get job(): IZoweTree { + return TreeProviders.#job; + } + + public static get providers(): IZoweProviders { + return { + ds: TreeProviders.#ds, + uss: TreeProviders.#uss, + job: TreeProviders.#job, + }; + } +}