Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow secure credentials to be enabled in Theia #2218

Merged
merged 10 commits into from
Apr 17, 2023
1 change: 1 addition & 0 deletions packages/zowe-explorer/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ All notable changes to the "vscode-extension-for-zowe" extension will be documen
- Fixed z/OSMF profiles issue with Data Sets and Jobs with special characters in the names. [#2175](https://github.com/zowe/vscode-extension-for-zowe/issues/2175)
- Fixed redundant text in error messages that included the same error details twice.
- Fixed error message when no data sets found that match pattern.
- Fixed secure credential storage not possible to enable in Theia.

## `2.7.0`

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { Profiles } from "../../src/Profiles";
import * as path from "path";
import * as fs from "fs";
import { getZoweDir, Gui } from "@zowe/zowe-explorer-api";
import * as profilesUtils from "../../src/utils/ProfilesUtils";
import * as profUtils from "../../src/utils/ProfilesUtils";
import { ZoweLogger } from "../../src/utils/LoggerUtils";
jest.mock("fs");

Expand Down Expand Up @@ -192,15 +192,6 @@ describe("ZoweExplorerExtender unit tests", () => {
}
});

it("properly handles error parsing Imperative overrides json", async () => {
const blockMocks = await createBlockMocks();
ZoweExplorerExtender.createInstance();
const errMsg = "Failed to parse JSON file imperative.json";

await ZoweExplorerExtender.showZoweConfigError(errMsg);
expect(blockMocks.mockErrorMessage).toHaveBeenCalledWith("Error: " + errMsg, undefined);
});

it("should initialize zowe", async () => {
const blockMocks = await createBlockMocks();
Object.defineProperty(vscode.workspace, "workspaceFolders", {
Expand All @@ -220,7 +211,7 @@ describe("ZoweExplorerExtender unit tests", () => {

const readProfilesFromDiskSpy = jest.fn();
const refreshProfilesQueueAddSpy = jest.spyOn((ZoweExplorerExtender as any).refreshProfilesQueue, "add");
jest.spyOn(profilesUtils, "getProfileInfo").mockReturnValue({
jest.spyOn(profUtils.ProfilesUtils, "getProfileInfo").mockReturnValue({
readProfilesFromDisk: readProfilesFromDiskSpy,
} as any);
await expect(blockMocks.instTest.initForZowe("USS", ["" as any])).resolves.not.toThrow();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import * as refreshActions from "../../../src/shared/refresh";
import * as sharedActions from "../../../src/shared/actions";
import * as sharedExtension from "../../../src/shared/init";
import { Profiles } from "../../../src/Profiles";
import * as profileUtils from "../../../src/utils/ProfilesUtils";
import * as profUtils from "../../../src/utils/ProfilesUtils";
import * as tempFolder from "../../../src/utils/TempFolder";
import * as zowe from "@zowe/cli";
import { IJestIt, ITestContext, processSubscriptions, spyOnSubscriptions } from "../../__common__/testUtils";
Expand Down Expand Up @@ -44,12 +44,12 @@ describe("Test src/shared/extension", () => {
name: "zowe.updateSecureCredentials",
mock: [
{ spy: jest.spyOn(globals, "setGlobalSecurityValue"), arg: [] },
{ spy: jest.spyOn(profileUtils, "writeOverridesFile"), arg: [] },
{ spy: jest.spyOn(profUtils.ProfilesUtils, "writeOverridesFile"), arg: [] },
],
},
{
name: "zowe.promptCredentials",
mock: [{ spy: jest.spyOn(profileUtils, "promptCredentials"), arg: [test.value] }],
mock: [{ spy: jest.spyOn(profUtils.ProfilesUtils, "promptCredentials"), arg: [test.value] }],
},
{
name: "onDidChangeConfiguration:1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@ import * as fs from "fs";
import * as path from "path";
import { Gui, ProfilesCache, ZoweVsCodeExtension } from "@zowe/zowe-explorer-api";
import * as globals from "../../../src/globals";
import * as profileUtils from "../../../src/utils/ProfilesUtils";
import * as profUtils from "../../../src/utils/ProfilesUtils";
import * as vscode from "vscode";
import * as zowe from "@zowe/cli";
import { Profiles } from "../../../src/Profiles";
import { SettingsConfig } from "../../../src/utils/SettingsConfig";
import { ZoweLogger } from "../../../src/utils/LoggerUtils";
import { ZoweExplorerExtender } from "../../../src/ZoweExplorerExtender";

jest.mock("fs");
jest.mock("vscode");
Expand Down Expand Up @@ -65,7 +66,7 @@ describe("ProfilesUtils unit tests", () => {
const errorDetails = new Error("i haz error");
const label = "test";
const moreInfo = "Task failed successfully";
await profileUtils.errorHandling(errorDetails, label, moreInfo);
await profUtils.errorHandling(errorDetails, label, moreInfo);
expect(Gui.errorMessage).toBeCalledWith(`${moreInfo} ` + errorDetails);
expect(ZoweLogger.error).toBeCalledWith(`Error: ${errorDetails.message}\n` + JSON.stringify({ errorDetails, label, moreInfo }));
});
Expand Down Expand Up @@ -94,7 +95,7 @@ describe("ProfilesUtils unit tests", () => {
openConfigFile: spyOpenConfigFile,
}),
});
await profileUtils.errorHandling(errorDetails, label, moreInfo);
await profUtils.errorHandling(errorDetails, label, moreInfo);
expect(spyOpenConfigFile).toBeCalledTimes(1);
});

Expand All @@ -106,15 +107,15 @@ describe("ProfilesUtils unit tests", () => {
});
const label = "test";
const moreInfo = "Task failed successfully";
jest.spyOn(profileUtils, "isTheia").mockReturnValue(false);
jest.spyOn(profUtils, "isTheia").mockReturnValue(false);
const showMessageSpy = jest.spyOn(Gui, "showMessage").mockResolvedValue("selection");
const ssoLoginSpy = jest.fn();
Object.defineProperty(Profiles, "getInstance", {
value: () => ({
ssoLogin: ssoLoginSpy,
}),
});
await profileUtils.errorHandling(errorDetails, label, moreInfo);
await profUtils.errorHandling(errorDetails, label, moreInfo);
expect(showMessageSpy).toBeCalledTimes(1);
expect(ssoLoginSpy).toBeCalledTimes(1);
});
Expand All @@ -133,7 +134,7 @@ describe("ProfilesUtils unit tests", () => {
configurable: true,
});
const mockReadProfilesFromDisk = jest.fn();
jest.spyOn(zowe.imperative, "ProfileInfo").mockResolvedValue({
const profInfoSpy = jest.spyOn(profUtils.ProfilesUtils, "getProfileInfo").mockReturnValue({
readProfilesFromDisk: mockReadProfilesFromDisk,
usingTeamConfig: true,
getTeamConfig: () => ({
Expand All @@ -157,8 +158,9 @@ describe("ProfilesUtils unit tests", () => {
value: jest.fn(),
configurable: true,
});
await expect(profileUtils.readConfigFromDisk()).resolves.not.toThrow();
await expect(profUtils.ProfilesUtils.readConfigFromDisk()).resolves.not.toThrow();
expect(mockReadProfilesFromDisk).toHaveBeenCalledTimes(1);
profInfoSpy.mockRestore();
});

it("should readConfigFromDisk and find with defaults", async () => {
Expand All @@ -173,7 +175,7 @@ describe("ProfilesUtils unit tests", () => {
configurable: true,
});
const mockReadProfilesFromDisk = jest.fn();
jest.spyOn(zowe.imperative, "ProfileInfo").mockResolvedValue({
const profInfoSpy = jest.spyOn(profUtils.ProfilesUtils, "getProfileInfo").mockReturnValue({
readProfilesFromDisk: mockReadProfilesFromDisk,
usingTeamConfig: true,
getTeamConfig: () => [],
Expand All @@ -182,8 +184,9 @@ describe("ProfilesUtils unit tests", () => {
value: jest.fn(),
configurable: true,
});
await expect(profileUtils.readConfigFromDisk()).resolves.not.toThrow();
await expect(profUtils.ProfilesUtils.readConfigFromDisk()).resolves.not.toThrow();
expect(mockReadProfilesFromDisk).toHaveBeenCalledTimes(1);
profInfoSpy.mockRestore();
});

it("should keep Imperative error details if readConfigFromDisk fails", async () => {
Expand All @@ -199,13 +202,14 @@ describe("ProfilesUtils unit tests", () => {
});
const impErr = new zowe.imperative.ImperativeError({ msg: "Unexpected Imperative error" });
const mockReadProfilesFromDisk = jest.fn().mockRejectedValue(impErr);
jest.spyOn(zowe.imperative, "ProfileInfo").mockResolvedValue({
const profInfoSpy = jest.spyOn(profUtils.ProfilesUtils, "getProfileInfo").mockReturnValue({
readProfilesFromDisk: mockReadProfilesFromDisk,
usingTeamConfig: true,
getTeamConfig: () => [],
} as never);
await expect(profileUtils.readConfigFromDisk()).rejects.toBe(impErr);
await expect(profUtils.ProfilesUtils.readConfigFromDisk()).rejects.toBe(impErr);
expect(mockReadProfilesFromDisk).toHaveBeenCalledTimes(1);
profInfoSpy.mockRestore();
});
});

Expand All @@ -228,7 +232,7 @@ describe("ProfilesUtils unit tests", () => {
configurable: true,
});
jest.spyOn(ZoweVsCodeExtension as any, "promptUserPass").mockResolvedValue([]);
await profileUtils.promptCredentials(null);
await profUtils.ProfilesUtils.promptCredentials(null);
expect(getProfileInfoSpy).toHaveBeenCalled();
});

Expand All @@ -249,7 +253,7 @@ describe("ProfilesUtils unit tests", () => {
configurable: true,
});
jest.spyOn(ZoweVsCodeExtension as any, "promptUserPass").mockResolvedValue([]);
await profileUtils.promptCredentials(null);
await profUtils.ProfilesUtils.promptCredentials(null);
expect(Gui.showMessage).toHaveBeenCalledWith("Operation Cancelled");
});

Expand All @@ -274,7 +278,7 @@ describe("ProfilesUtils unit tests", () => {
configurable: true,
});
jest.spyOn(Profiles.prototype, "promptCredentials").mockResolvedValue(["some_user", "some_pass", "c29tZV9iYXNlNjRfc3RyaW5n"]);
await profileUtils.promptCredentials(null);
await profUtils.ProfilesUtils.promptCredentials(null);
expect(Gui.showMessage).toHaveBeenCalledWith("Credentials for testConfig were successfully updated");
});

Expand Down Expand Up @@ -302,7 +306,7 @@ describe("ProfilesUtils unit tests", () => {
value: jest.fn(),
configurable: true,
});
await profileUtils.promptCredentials(null);
await profUtils.ProfilesUtils.promptCredentials(null);
expect(mockProfileInstance.getProfileInfo).toHaveBeenCalled();
expect(Gui.showMessage).toHaveBeenCalledWith('"Update Credentials" operation not supported when "autoStore" is false');
});
Expand All @@ -313,7 +317,7 @@ describe("ProfilesUtils unit tests", () => {
const blockMocks = createBlockMocks();
blockMocks.mockGetDirectValue.mockReturnValue(true);
blockMocks.mockExistsSync.mockReturnValue(false);
await profileUtils.initializeZoweFolder();
await profUtils.ProfilesUtils.initializeZoweFolder();
expect(globals.PROFILE_SECURITY).toBe(globals.ZOWE_CLI_SCM);
expect(blockMocks.mockMkdirSync).toHaveBeenCalledTimes(2);
expect(blockMocks.mockWriteFileSync).toHaveBeenCalledTimes(1);
Expand All @@ -323,7 +327,7 @@ describe("ProfilesUtils unit tests", () => {
const blockMocks = createBlockMocks();
blockMocks.mockGetDirectValue.mockReturnValue(false);
blockMocks.mockExistsSync.mockReturnValue(true);
await profileUtils.initializeZoweFolder();
await profUtils.ProfilesUtils.initializeZoweFolder();
expect(globals.PROFILE_SECURITY).toBe(false);
expect(blockMocks.mockMkdirSync).toHaveBeenCalledTimes(0);
expect(blockMocks.mockWriteFileSync).toHaveBeenCalledTimes(0);
Expand All @@ -336,7 +340,7 @@ describe("ProfilesUtils unit tests", () => {
const fileJson = { overrides: { CredentialManager: "@zowe/cli", testValue: true } };
const content = JSON.stringify(fileJson, null, 2);
blockMocks.mockReadFileSync.mockReturnValueOnce(JSON.stringify({ overrides: { CredentialManager: false, testValue: true } }, null, 2));
profileUtils.writeOverridesFile();
profUtils.ProfilesUtils.writeOverridesFile();
expect(blockMocks.mockOpenSync).toBeCalledWith(blockMocks.zoweDir, "r+");
expect(blockMocks.mockWriteFileSync).toBeCalledWith(blockMocks.fileHandle, content, "utf-8");
});
Expand All @@ -347,7 +351,7 @@ describe("ProfilesUtils unit tests", () => {
test: null,
};
blockMocks.mockReadFileSync.mockReturnValueOnce(JSON.stringify(fileJson, null, 2));
profileUtils.writeOverridesFile();
profUtils.ProfilesUtils.writeOverridesFile();
expect(blockMocks.mockWriteFileSync).toBeCalledTimes(0);
});

Expand All @@ -360,7 +364,7 @@ describe("ProfilesUtils unit tests", () => {
return blockMocks.fileHandle;
});
const content = JSON.stringify(blockMocks.mockFileRead, null, 2);
profileUtils.writeOverridesFile();
profUtils.ProfilesUtils.writeOverridesFile();
expect(blockMocks.mockWriteFileSync).toBeCalledWith(blockMocks.fileHandle, content, "utf-8");
expect(blockMocks.mockOpenSync).toBeCalledTimes(2);
expect(blockMocks.mockReadFileSync).toBeCalledTimes(0);
Expand All @@ -372,12 +376,54 @@ describe("ProfilesUtils unit tests", () => {
test: null,
};
blockMocks.mockReadFileSync.mockReturnValueOnce(JSON.stringify(fileJson, null, 2).slice(1));
expect(profileUtils.writeOverridesFile).toThrow();
expect(profUtils.ProfilesUtils.writeOverridesFile).toThrow();
});
});

describe("initializeZoweProfiles", () => {
it("should successfully initialize Zowe folder and read config from disk", async () => {
const initZoweFolderSpy = jest.spyOn(profUtils.ProfilesUtils, "initializeZoweFolder");
const readConfigFromDiskSpy = jest.spyOn(profUtils.ProfilesUtils, "readConfigFromDisk").mockResolvedValueOnce();
await profUtils.ProfilesUtils.initializeZoweProfiles();
expect(initZoweFolderSpy).toHaveBeenCalledTimes(1);
expect(readConfigFromDiskSpy).toHaveBeenCalledTimes(1);
expect(ZoweLogger.error).not.toHaveBeenCalled();
});

it("should handle error thrown on initialize Zowe folder", async () => {
const testError = new Error("initializeZoweFolder failed");
const initZoweFolderSpy = jest.spyOn(profUtils.ProfilesUtils, "initializeZoweFolder").mockRejectedValueOnce(testError);
const readConfigFromDiskSpy = jest.spyOn(profUtils.ProfilesUtils, "readConfigFromDisk").mockResolvedValueOnce();
await profUtils.ProfilesUtils.initializeZoweProfiles();
expect(initZoweFolderSpy).toHaveBeenCalledTimes(1);
expect(readConfigFromDiskSpy).toHaveBeenCalledTimes(1);
expect(Gui.errorMessage).toHaveBeenCalledWith(expect.stringContaining(testError.message));
});

it("should handle Imperative error thrown on read config from disk", async () => {
const testError = new zowe.imperative.ImperativeError({ msg: "readConfigFromDisk failed" });
const initZoweFolderSpy = jest.spyOn(profUtils.ProfilesUtils, "initializeZoweFolder").mockResolvedValueOnce();
const readConfigFromDiskSpy = jest.spyOn(profUtils.ProfilesUtils, "readConfigFromDisk").mockRejectedValueOnce(testError);
await profUtils.ProfilesUtils.initializeZoweProfiles();
expect(initZoweFolderSpy).toHaveBeenCalledTimes(1);
expect(readConfigFromDiskSpy).toHaveBeenCalledTimes(1);
expect(Gui.errorMessage).toHaveBeenCalledWith(expect.stringContaining(testError.message));
});

it("should handle JSON parse error thrown on read config from disk", async () => {
const testError = new Error("readConfigFromDisk failed");
const initZoweFolderSpy = jest.spyOn(profUtils.ProfilesUtils, "initializeZoweFolder").mockResolvedValueOnce();
const readConfigFromDiskSpy = jest.spyOn(profUtils.ProfilesUtils, "readConfigFromDisk").mockRejectedValueOnce(testError);
const showZoweConfigErrorSpy = jest.spyOn(ZoweExplorerExtender, "showZoweConfigError").mockReturnValueOnce();
await profUtils.ProfilesUtils.initializeZoweProfiles();
expect(initZoweFolderSpy).toHaveBeenCalledTimes(1);
expect(readConfigFromDiskSpy).toHaveBeenCalledTimes(1);
expect(showZoweConfigErrorSpy).toHaveBeenCalledWith(testError.message);
});
});

it("filterItem should get label if the filterItem icon exists", () => {
const testFilterItem = new profileUtils.FilterItem({
const testFilterItem = new profUtils.FilterItem({
icon: "test",
} as any);
expect(testFilterItem.label).toEqual("test undefined");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,35 @@ afterEach(() => {
jest.resetAllMocks();
});

describe("SettingsConfig Unit Tests - function isConfigSettingSetByUser", () => {
it("should return false if setting is undefined or empty", () => {
const falseCases = [undefined, {}];
for (const retVal of falseCases) {
jest.spyOn(vscode.workspace, "getConfiguration").mockReturnValueOnce({
inspect: jest.fn().mockReturnValue(retVal),
} as any);
expect(SettingsConfig.isConfigSettingSetByUser("zowe.setting")).toBe(false);
}
});

it("should return true if setting is defined in any scope", () => {
const trueCases = [
{ globalValue: "a" },
{ workspaceValue: "b" },
{ workspaceFolderValue: "c" },
{ globalLanguageValue: "d" },
{ workspaceLanguageValue: "e" },
{ workspaceFolderLanguageValue: "f" },
];
for (const retVal of trueCases) {
jest.spyOn(vscode.workspace, "getConfiguration").mockReturnValueOnce({
inspect: jest.fn().mockReturnValue(retVal),
} as any);
expect(SettingsConfig.isConfigSettingSetByUser("zowe.setting")).toBe(true);
}
});
});

describe("SettingsConfig Unit Tests - function promptReload", () => {
it("should trigger a reload when prompted", async () => {
const privateSettingsConfig = SettingsConfig as any;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
"errorHandling.authentication.login": "Log in to Authentication Service",
"errorHandling.checkCredentials.button": "Check Credentials",
"errorHandling.checkCredentials.cancelled": "Operation Cancelled",
"getProfile.notTreeItem": "Tree Item is not a Zowe Explorer item.",
"zowe.promptCredentials.notSupported": "\"Update Credentials\" operation not supported when \"autoStore\" is false",
"createNewConnection.option.prompt.profileName.placeholder": "Connection Name",
"createNewConnection.option.prompt.profileName": "Enter a name for the connection.",
"createNewConnection.undefined.passWord": "Operation Cancelled",
"promptCredentials.updatedCredentials": "Credentials for {0} were successfully updated",
"initializeZoweFolder.location": "Zowe home directory is located at {0}",
"writeOverridesFile.jsonParseError": "Failed to parse JSON file {0}:",
"initializeZoweFolder.error": "Failed to initialize Zowe folder: {0}",
"initializeZoweProfiles.success": "Zowe Profiles initialized successfully.",
"initializeZoweTempFolder.success": "Zowe Temp folder initialized successfully."
"initializeZoweTempFolder.success": "Zowe Temp folder initialized successfully.",
"getProfile.notTreeItem": "Tree Item is not a Zowe Explorer item."
}
Loading