diff --git a/packages/zowe-explorer/CHANGELOG.md b/packages/zowe-explorer/CHANGELOG.md index b0dd6cf074..87054b5da6 100644 --- a/packages/zowe-explorer/CHANGELOG.md +++ b/packages/zowe-explorer/CHANGELOG.md @@ -6,6 +6,8 @@ All notable changes to the "vscode-extension-for-zowe" extension will be documen ### New features and enhancements +- Replaced multiple options for updating credentials with a single "Manage Credentials" option that prompts for preferred authentication type. [#2263](https://github.com/zowe/vscode-extension-for-zowe/issues/2263) + ### Bug fixes ## `2.10.0` diff --git a/packages/zowe-explorer/__tests__/__unit__/utils/ProfilesUtils.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/utils/ProfilesUtils.unit.test.ts index 056c52cb5b..d9d12a0a0d 100644 --- a/packages/zowe-explorer/__tests__/__unit__/utils/ProfilesUtils.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/utils/ProfilesUtils.unit.test.ts @@ -11,7 +11,7 @@ import * as fs from "fs"; import * as path from "path"; -import { Gui, IZoweTreeNode, ProfilesCache } from "@zowe/zowe-explorer-api"; +import { Gui, IZoweTreeNode, ProfilesCache, ZoweVsCodeExtension } from "@zowe/zowe-explorer-api"; import * as util from "util"; import * as globals from "../../../src/globals"; import * as profUtils from "../../../src/utils/ProfilesUtils"; @@ -21,6 +21,7 @@ import { Profiles } from "../../../src/Profiles"; import { SettingsConfig } from "../../../src/utils/SettingsConfig"; import { ZoweLogger } from "../../../src/utils/LoggerUtils"; import { ZoweExplorerExtender } from "../../../src/ZoweExplorerExtender"; +import { ZoweExplorerApiRegister } from "../../../src/ZoweExplorerApiRegister"; jest.mock("fs"); jest.mock("vscode"); @@ -359,10 +360,7 @@ describe("ProfilesUtils unit tests", () => { value: jest.fn().mockResolvedValue("emptyConfig"), configurable: true, }); - jest.spyOn(Gui, "createQuickPick").mockReturnValue({ - show: jest.fn(), - } as unknown as vscode.QuickPick); - jest.spyOn(Gui, "resolveQuickPick").mockResolvedValueOnce(undefined); + jest.spyOn(ZoweVsCodeExtension as any, "promptUserPass").mockResolvedValue([]); await profUtils.ProfilesUtils.promptCredentials(null); expect(getProfileInfoSpy).toHaveBeenCalled(); }); @@ -404,10 +402,6 @@ describe("ProfilesUtils unit tests", () => { value: jest.fn().mockResolvedValue("testConfig"), configurable: true, }); - jest.spyOn(Gui, "createQuickPick").mockReturnValue({ - show: jest.fn(), - } as unknown as vscode.QuickPick); - jest.spyOn(Gui, "resolveQuickPick").mockImplementationOnce((qp) => Promise.resolve(qp.activeItems[0])); Object.defineProperty(Gui, "showMessage", { value: jest.fn(), configurable: true, @@ -417,7 +411,37 @@ describe("ProfilesUtils unit tests", () => { expect(Gui.showMessage).toHaveBeenCalledWith("Credentials for testConfig were successfully updated"); }); - it("proceeds with SSO login if auth token option is selected", async () => { + it("prompts for credentials if basic auth option is selected in quickpick", async () => { + const mockProfileInstance = new Profiles(zowe.imperative.Logger.getAppLogger()); + const prof = { + getAllProfiles: jest.fn().mockReturnValue([]), + isSecured: jest.fn().mockReturnValue(true), + readProfilesFromDisk: jest.fn(), + }; + jest.spyOn(ProfilesCache.prototype, "getProfileInfo").mockResolvedValue(prof as unknown as zowe.imperative.ProfileInfo); + jest.spyOn(ProfilesCache.prototype, "getLoadedProfConfig").mockResolvedValue({ + profile: prof, + } as unknown as zowe.imperative.IProfileLoaded); + jest.spyOn(Profiles, "getInstance").mockReturnValue(mockProfileInstance); + jest.spyOn(ZoweExplorerApiRegister, "getInstance").mockReturnValueOnce({ + getCommonApi: jest.fn(() => ({ + getTokenTypeName: jest.fn().mockReturnValue("fakeToken"), + })), + } as any); + Object.defineProperty(vscode.window, "showInputBox", { + value: jest.fn().mockResolvedValue("testConfig"), + configurable: true, + }); + jest.spyOn(Gui, "createQuickPick").mockReturnValue({ + show: jest.fn(), + } as unknown as vscode.QuickPick); + jest.spyOn(Gui, "resolveQuickPick").mockImplementationOnce((qp) => Promise.resolve(qp.activeItems[0])); + const promptCredsSpy = jest.spyOn(mockProfileInstance, "promptCredentials").mockResolvedValueOnce([]); + await profUtils.ProfilesUtils.promptCredentials(null); + expect(promptCredsSpy).toHaveBeenCalledTimes(1); + }); + + it("proceeds with SSO login if token auth option is selected in quickpick", async () => { const mockProfileInstance = new Profiles(zowe.imperative.Logger.getAppLogger()); mockProfileInstance.getLoadedProfConfig = jest.fn().mockResolvedValue({ profile: { tokenType: "fakeToken" }, @@ -432,6 +456,11 @@ describe("ProfilesUtils unit tests", () => { profile: prof, } as unknown as zowe.imperative.IProfileLoaded); jest.spyOn(Profiles, "getInstance").mockReturnValue(mockProfileInstance); + jest.spyOn(ZoweExplorerApiRegister, "getInstance").mockReturnValueOnce({ + getCommonApi: jest.fn(() => ({ + getTokenTypeName: jest.fn().mockReturnValue("fakeToken"), + })), + } as any); Object.defineProperty(vscode.window, "showInputBox", { value: jest.fn().mockResolvedValue("testConfig"), configurable: true, @@ -445,7 +474,7 @@ describe("ProfilesUtils unit tests", () => { expect(ssoLoginSpy).toHaveBeenCalledTimes(1); }); - it("proceeds with SSO logout if log out option is selected", async () => { + it("proceeds with SSO logout if log out option is selected in quickpick", async () => { const mockProfileInstance = new Profiles(zowe.imperative.Logger.getAppLogger()); const prof = { getAllProfiles: jest.fn().mockReturnValue([]), @@ -457,6 +486,11 @@ describe("ProfilesUtils unit tests", () => { profile: prof, } as unknown as zowe.imperative.IProfileLoaded); jest.spyOn(Profiles, "getInstance").mockReturnValue(mockProfileInstance); + jest.spyOn(ZoweExplorerApiRegister, "getInstance").mockReturnValueOnce({ + getCommonApi: jest.fn(() => ({ + getTokenTypeName: jest.fn().mockReturnValue("fakeToken"), + })), + } as any); jest.spyOn(Gui, "createQuickPick").mockReturnValue({ show: jest.fn(), } as unknown as vscode.QuickPick); diff --git a/packages/zowe-explorer/i18n/sample/src/utils/ProfilesUtils.i18n.json b/packages/zowe-explorer/i18n/sample/src/utils/ProfilesUtils.i18n.json index 3f31b3363d..bccc26b39f 100644 --- a/packages/zowe-explorer/i18n/sample/src/utils/ProfilesUtils.i18n.json +++ b/packages/zowe-explorer/i18n/sample/src/utils/ProfilesUtils.i18n.json @@ -17,7 +17,7 @@ "promptCredentials.quickPick.tokenAuthLabel": "Authentication Token", "promptCredentials.quickPick.tokenAuthDescription": "Authenticate to service and store token", "promptCredentials.quickPick.logOutLabel": "Log out of Authentication Service", - "promptCredentials.quickPick.title": "Select an authentication method", + "promptCredentials.quickPick.title": "Select authentication method for {0}", "promptCredentials.updatedCredentials": "Credentials for {0} were successfully updated", "initializeZoweFolder.location": "Zowe home directory is located at {0}", "writeOverridesFile.readFile.error": "Reading imperative.json failed. Will try to create file.", diff --git a/packages/zowe-explorer/src/utils/ProfilesUtils.ts b/packages/zowe-explorer/src/utils/ProfilesUtils.ts index bd3107b74d..89331f27cd 100644 --- a/packages/zowe-explorer/src/utils/ProfilesUtils.ts +++ b/packages/zowe-explorer/src/utils/ProfilesUtils.ts @@ -23,6 +23,7 @@ import { imperative, getImperativeConfig } from "@zowe/cli"; import { ZoweExplorerExtender } from "../ZoweExplorerExtender"; import { ZoweLogger } from "./LoggerUtils"; import { SettingsConfig } from "./SettingsConfig"; +import { ZoweExplorerApiRegister } from "../ZoweExplorerApiRegister"; // Set up localization nls.config({ @@ -346,26 +347,35 @@ export class ProfilesUtils { // description: localize("promptCredentials.quickPick.certAuthDescription", "Select a PEM certificate file"), // }, }; - let currentAuthType = imperative.SessConstants.AUTH_TYPE_BASIC; - if (profile.profile?.tokenType != null) { - currentAuthType = imperative.SessConstants.AUTH_TYPE_TOKEN; - } else if (profile.profile?.certFile != null) { - // currentAuthType = imperative.SessConstants.AUTH_TYPE_CERT_PEM; + try { + ZoweExplorerApiRegister.getInstance().getCommonApi(profile).getTokenTypeName(); + } catch { + // This profile does not support token authentication + delete authTypeChoices[imperative.SessConstants.AUTH_TYPE_TOKEN]; } - authTypeChoices[currentAuthType].label = authTypeChoices[currentAuthType].label.replace("$(circle-large)", "$(record)"); const isSsoLoggedIn = profile.profile?.tokenValue != null; const quickPickOptions: vscode.QuickPickItem[] = Object.values(authTypeChoices); - if (isSsoLoggedIn) { - quickPickOptions.push(globals.SEPARATORS.BLANK, { - label: localize("promptCredentials.quickPick.logOutLabel", "Log out of Authentication Service"), - }); + let selectedItem = quickPickOptions[0]; + if (quickPickOptions.length > 1) { + let currentAuthType = imperative.SessConstants.AUTH_TYPE_BASIC; + if (profile.profile?.tokenType != null) { + currentAuthType = imperative.SessConstants.AUTH_TYPE_TOKEN; + } else if (profile.profile?.certFile != null) { + // currentAuthType = imperative.SessConstants.AUTH_TYPE_CERT_PEM; + } + authTypeChoices[currentAuthType].label = authTypeChoices[currentAuthType].label.replace("$(circle-large)", "$(record)"); + if (isSsoLoggedIn) { + quickPickOptions.push(globals.SEPARATORS.BLANK, { + label: localize("promptCredentials.quickPick.logOutLabel", "Log out of Authentication Service"), + }); + } + const qp = Gui.createQuickPick(); + qp.items = quickPickOptions; + qp.activeItems = [authTypeChoices[currentAuthType]]; + qp.placeholder = localize("promptCredentials.quickPick.title", "Select authentication method for {0}", profile.name); + qp.show(); + selectedItem = await Gui.resolveQuickPick(qp); } - const qp = Gui.createQuickPick(); - qp.items = quickPickOptions; - qp.activeItems = [authTypeChoices[currentAuthType]]; - qp.placeholder = localize("promptCredentials.quickPick.title", "Select an authentication method"); - qp.show(); - const selectedItem = await Gui.resolveQuickPick(qp); if (selectedItem === authTypeChoices[imperative.SessConstants.AUTH_TYPE_BASIC]) { const creds = await Profiles.getInstance().promptCredentials(profile, true); if (creds != null) {