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

feat: Use keyring module from @zowe/secrets-for-zowe-sdk to replace use of node-keytar #2405

Merged
merged 19 commits into from
Aug 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ debug.log
npm-shrinkwrap.json
vscode-extension-for-zowe*.vsix
.vscode/settings.json
.vscode/*.env
.vscode-test
.history
.DS_Store
Expand All @@ -19,3 +20,4 @@ temp
*.tgz
.tmp
coverage/
prebuilds/
3 changes: 2 additions & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
"outFiles": ["${workspaceFolder}/packages/zowe-explorer/out/**/*.js"],
"preLaunchTask": "build dev watch",
"smartStep": true,
"skipFiles": ["<node_internals>/**"]
"skipFiles": ["<node_internals>/**"],
"envFile": "${workspaceFolder}/.vscode/.env"
},
{
// TODO: This launch-configuration should be updated to run the Theia container locally
Expand Down
6 changes: 6 additions & 0 deletions .vscode/sample_env
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Create a ".env" file with the contents of this file

# Make sure to get a valid DBus Session Address
# by unlocking the keyring on a terminal, and
# copy the contents of "echo $DBUS_SESSION_BUS_ADDRESS"
DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/0/bus
3 changes: 2 additions & 1 deletion .yarnrc
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
registry: "https://registry.npmjs.org/"
registry: "https://registry.npmjs.org/"
"@zowe:registry" "https://zowe.jfrog.io/zowe/api/npm/npm-local-release/"
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"vscode": "^1.53.2"
},
"dependencies": {
"@zowe/cli": "7.16.6",
"@zowe/cli": "7.18.0",
"vscode-nls": "4.1.2"
},
"devDependencies": {
Expand Down
2 changes: 2 additions & 0 deletions packages/zowe-explorer-api/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ All notable changes to the "zowe-explorer-api" extension will be documented in t

### New features and enhancements

- Replaced `keytar` dependency with `keyring` module from [`@zowe/secrets-for-zowe-sdk`](https://github.com/zowe/zowe-cli/tree/master/packages/secrets). [#2358](https://github.com/zowe/vscode-extension-for-zowe/issues/2358)

### Bug fixes

## `2.9.2`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ describe("ProfilesCache", () => {
const profInfo = await new ProfilesCache(fakeLogger as unknown as zowe.imperative.Logger, __dirname).getProfileInfo();
expect(readProfilesFromDiskSpy).toHaveBeenCalledTimes(1);
expect(defaultCredMgrWithKeytarSpy).toHaveBeenCalledTimes(1);
expect(defaultCredMgrWithKeytarSpy).toHaveBeenCalledWith(ProfilesCache.requireKeyring);
const teamConfig = profInfo.getTeamConfig();
expect(teamConfig.appName).toBe("zowe");
expect(teamConfig.paths).toEqual([
Expand All @@ -144,6 +145,12 @@ describe("ProfilesCache", () => {
]);
});

it("requireKeyring returns keyring module from Secrets SDK", async () => {
const keyring = ProfilesCache.requireKeyring();
expect(keyring).toBeDefined();
expect(Object.keys(keyring).length).toBe(5);
});

it("loadNamedProfile should find profiles by name and type", () => {
const profCache = new ProfilesCache(fakeLogger as unknown as zowe.imperative.Logger);
profCache.allProfiles = [lpar1Profile as zowe.imperative.IProfileLoaded, zftpProfile as zowe.imperative.IProfileLoaded];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,9 @@
import { imperative } from "@zowe/cli";
import { ProfilesCache } from "../../../src/profiles/ProfilesCache";
import { KeytarApi } from "../../../src/security/KeytarApi";
import { KeytarCredentialManager } from "../../../src/security/KeytarCredentialManager";

describe("KeytarApi", () => {
const isCredsSecuredSpy = jest.spyOn(ProfilesCache.prototype, "isSecureCredentialPluginActive");
const getSecurityModulesSpy = jest.spyOn(KeytarCredentialManager, "getSecurityModules");
const credMgrInitializeSpy = jest.spyOn(imperative.CredentialManagerFactory, "initialize");

afterEach(() => {
Expand All @@ -25,37 +23,31 @@ describe("KeytarApi", () => {

it("should initialize Imperative credential manager", async () => {
isCredsSecuredSpy.mockReturnValueOnce(true);
getSecurityModulesSpy.mockReturnValueOnce({} as NodeModule);
credMgrInitializeSpy.mockResolvedValueOnce();
await new KeytarApi(undefined as unknown as imperative.Logger).activateKeytar(false, false);
expect(isCredsSecuredSpy).toBeCalledTimes(1);
expect(getSecurityModulesSpy).toBeCalledTimes(1);
expect(credMgrInitializeSpy).toBeCalledTimes(1);
});

it("should do nothing if secure credential plugin is not active", async () => {
isCredsSecuredSpy.mockReturnValueOnce(false);
await new KeytarApi(undefined as unknown as imperative.Logger).activateKeytar(false, false);
expect(isCredsSecuredSpy).toBeCalledTimes(1);
expect(getSecurityModulesSpy).not.toBeCalled();
expect(credMgrInitializeSpy).not.toBeCalled();
});

it("should do nothing if API has already been initialized", async () => {
isCredsSecuredSpy.mockReturnValueOnce(true);
getSecurityModulesSpy.mockReturnValueOnce({} as NodeModule);
await new KeytarApi(undefined as unknown as imperative.Logger).activateKeytar(true, false);
expect(isCredsSecuredSpy).toBeCalledTimes(1);
expect(getSecurityModulesSpy).toBeCalledTimes(1);
expect(credMgrInitializeSpy).not.toBeCalled();
});

it("should do nothing if Keytar module is missing", async () => {
jest.mock("@zowe/secrets-for-zowe-sdk", () => {});
isCredsSecuredSpy.mockReturnValueOnce(true);
getSecurityModulesSpy.mockReturnValueOnce(undefined);
await new KeytarApi(undefined as unknown as imperative.Logger).activateKeytar(false, false);
expect(isCredsSecuredSpy).toBeCalledTimes(1);
expect(getSecurityModulesSpy).toBeCalledTimes(1);
expect(credMgrInitializeSpy).not.toBeCalled();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,11 @@ describe("KeytarCredentialManager", () => {
},
})
);
const keytar = KeytarCredentialManager.getSecurityModules("keytar", false);
const keytar = KeytarCredentialManager.getSecurityModules("@zowe/secrets-for-zowe-sdk", false);
expect(keytar).toBeDefined();
expect(loggerWarnSpy).not.toHaveBeenCalled();
expect(readFileSyncSpy).toHaveBeenCalledTimes(1);
expect(Object.keys(keytar as object).length).toBe(5);
expect(Object.keys((keytar as object)["keyring"]).length).toBe(5);
});

it("should handle credential-manager in Imperative settings", () => {
Expand All @@ -80,11 +80,11 @@ describe("KeytarCredentialManager", () => {
},
})
);
const keytar = KeytarCredentialManager.getSecurityModules("keytar", false);
const keytar = KeytarCredentialManager.getSecurityModules("@zowe/secrets-for-zowe-sdk", false);
expect(keytar).toBeDefined();
expect(loggerWarnSpy).not.toHaveBeenCalled();
expect(readFileSyncSpy).toHaveBeenCalledTimes(1);
expect(Object.keys(keytar as object).length).toBe(5);
expect(Object.keys((keytar as object)["keyring"]).length).toBe(5);
});

it("should handle CredentialManager in Imperative settings - Theia", () => {
Expand All @@ -97,17 +97,17 @@ describe("KeytarCredentialManager", () => {
})
);
jest.spyOn(process, "cwd").mockReturnValueOnce(__dirname + "/../../../../..");
const keytar = KeytarCredentialManager.getSecurityModules("keytar", true);
const keytar = KeytarCredentialManager.getSecurityModules("@zowe/secrets-for-zowe-sdk", true);
expect(keytar).toBeDefined();
expect(loggerWarnSpy).not.toHaveBeenCalled();
expect(readFileSyncSpy).toHaveBeenCalledTimes(1);
expect(Object.keys(keytar as object).length).toBe(5);
expect(Object.keys((keytar as object)["keyring"]).length).toBe(5);
});

it("should handle empty Imperative settings", () => {
jest.spyOn(fs, "existsSync").mockReturnValueOnce(true);
const readFileSyncSpy = jest.spyOn(fs, "readFileSync").mockReturnValue(JSON.stringify({}));
const keytar = KeytarCredentialManager.getSecurityModules("keytar", false);
const keytar = KeytarCredentialManager.getSecurityModules("@zowe/secrets-for-zowe-sdk", false);
expect(loggerWarnSpy).not.toHaveBeenCalled();
expect(readFileSyncSpy).toHaveBeenCalledTimes(1);
expect(keytar).toBeUndefined();
Expand All @@ -116,7 +116,7 @@ describe("KeytarCredentialManager", () => {
it("should handle non-existent Imperative settings", () => {
jest.spyOn(fs, "existsSync").mockReturnValueOnce(false);
const readFileSyncSpy = jest.spyOn(fs, "readFileSync");
const keytar = KeytarCredentialManager.getSecurityModules("keytar", false);
const keytar = KeytarCredentialManager.getSecurityModules("@zowe/secrets-for-zowe-sdk", false);
expect(loggerWarnSpy).not.toHaveBeenCalled();
expect(readFileSyncSpy).not.toHaveBeenCalled();
expect(keytar).toBeUndefined();
Expand All @@ -125,7 +125,7 @@ describe("KeytarCredentialManager", () => {
it("should handle error loading Imperative settings", () => {
jest.spyOn(fs, "existsSync").mockReturnValueOnce(true);
const readFileSyncSpy = jest.spyOn(fs, "readFileSync").mockReturnValueOnce("invalid json");
const keytar = KeytarCredentialManager.getSecurityModules("keytar", false);
const keytar = KeytarCredentialManager.getSecurityModules("@zowe/secrets-for-zowe-sdk", false);
expect(loggerWarnSpy).toHaveBeenCalledTimes(1);
expect(loggerWarnSpy.mock.calls[0][0].message).toContain("Unexpected token");
expect(readFileSyncSpy).toHaveBeenCalledTimes(1);
Expand Down
2 changes: 1 addition & 1 deletion packages/zowe-explorer-api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"@types/semver": "^7.5.0"
},
"dependencies": {
"@zowe/cli": "^7.16.6",
"@zowe/cli": "^7.18.0",
"semver": "^7.5.3"
},
"scripts": {
Expand Down
10 changes: 8 additions & 2 deletions packages/zowe-explorer-api/src/profiles/ProfilesCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,15 @@ export class ProfilesCache {
this.cwd = cwd != null ? getFullPath(cwd) : undefined;
}

public async getProfileInfo(envTheia = false): Promise<zowe.imperative.ProfileInfo> {
public static requireKeyring(this: void): NodeModule {
// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-var-requires
return require("@zowe/secrets-for-zowe-sdk").keyring;
}

public async getProfileInfo(_envTheia = false): Promise<zowe.imperative.ProfileInfo> {
const mProfileInfo = new zowe.imperative.ProfileInfo("zowe", {
credMgrOverride: zowe.imperative.ProfileCredentials.defaultCredMgrWithKeytar(() => getSecurityModules("keytar", envTheia)),
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
credMgrOverride: zowe.imperative.ProfileCredentials.defaultCredMgrWithKeytar(ProfilesCache.requireKeyring),
});
await mProfileInfo.readProfilesFromDisk({ homeDir: getZoweDir(), projectDir: this.cwd ?? undefined });
return mProfileInfo;
Expand Down
11 changes: 8 additions & 3 deletions packages/zowe-explorer-api/src/security/KeytarApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,19 @@ export class KeytarApi {
public constructor(protected log: imperative.Logger) {}

// v1 specific
public async activateKeytar(initialized: boolean, isTheia: boolean): Promise<void> {
public async activateKeytar(initialized: boolean, _isTheia: boolean): Promise<void> {
const log = imperative.Logger.getAppLogger();
const profiles = new ProfilesCache(log, vscode.workspace.workspaceFolders?.[0]?.uri.fsPath);
const scsActive = profiles.isSecureCredentialPluginActive();
if (scsActive) {
const keytar = KeytarCredentialManager.getSecurityModules("keytar", isTheia);
let keytar: object;
try {
keytar = (await import("@zowe/secrets-for-zowe-sdk")).keyring;
} catch (err) {
log.warn(err.toString());
}
if (!initialized && keytar) {
KeytarCredentialManager.keytar = keytar as unknown as KeytarModule;
KeytarCredentialManager.keytar = keytar as KeytarModule;
await imperative.CredentialManagerFactory.initialize({
service: globals.SETTINGS_SCS_DEFAULT,
Manager: KeytarCredentialManager,
Expand Down
1 change: 1 addition & 0 deletions packages/zowe-explorer-ftp-extension/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ All notable changes to the "zowe-explorer-ftp-extension" extension will be docum
### New features and enhancements

- Enhance throw error in zowe ftp extension. [#2143](https://github.com/zowe/vscode-extension-for-zowe/issues/2143)
- Removed `keytar` from list of external Webpack modules. Its usage has been replaced with the `keyring` module from [`@zowe/secrets-for-zowe-sdk`](https://github.com/zowe/zowe-cli/tree/master/packages/secrets). [#2358](https://github.com/zowe/vscode-extension-for-zowe/issues/2358)

### Bug fixes

Expand Down
1 change: 0 additions & 1 deletion packages/zowe-explorer-ftp-extension/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ const config = {
externals: {
// Add modules that cannot be webpack'ed, 📖 -> https://webpack.js.org/configuration/externals/
vscode: "commonjs vscode", // the vscode-module is created on-the-fly and must be excluded. Add other modules that cannot be webpack'ed, 📖 -> https://webpack.js.org/configuration/externals/
keytar: "commonjs keytar",
"spdx-exceptions": "commonjs spdx-exceptions",
"spdx-license-ids": "commonjs spdx-license-ids",
"spdx-license-ids/deprecated": "commonjs spdx-license-ids/deprecated",
Expand Down
1 change: 1 addition & 0 deletions packages/zowe-explorer/.prettierignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
out
results
scripts
1 change: 1 addition & 0 deletions packages/zowe-explorer/.vscodeignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
!out/src/nls.metadata*.json
!resources/**/*.png
!resources/**/*.svg
!prebuilds/**

!CHANGELOG.md
!LICENSE
Expand Down
2 changes: 2 additions & 0 deletions packages/zowe-explorer/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ All notable changes to the "vscode-extension-for-zowe" extension will be documen

### New features and enhancements

- Replaced `keytar` dependency with `keyring` module from [`@zowe/secrets-for-zowe-sdk`](https://github.com/zowe/zowe-cli/tree/master/packages/secrets). [#2358](https://github.com/zowe/vscode-extension-for-zowe/issues/2358)

### Bug fixes

- Fix the USS refresh icon (replacing "download" with "refresh")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -698,11 +698,13 @@ describe("ProfilesUtils unit tests", () => {

it("should retrieve the default credential manager if no custom credential manager is found", async () => {
jest.spyOn(profUtils.ProfilesUtils, "getCredentialManagerOverride").mockReturnValueOnce(undefined);
const defaultCredMgrSpy = jest.spyOn(zowe.imperative.ProfileCredentials, "defaultCredMgrWithKeytar");
jest.spyOn(vscode.extensions, "getExtension").mockReturnValueOnce(undefined);
jest.spyOn(SettingsConfig, "getDirectValue").mockReturnValueOnce(true);
const updateCredentialManagerSettingSpy = jest.spyOn(profUtils.ProfilesUtils, "updateCredentialManagerSetting").mockImplementation();
await expect(profUtils.ProfilesUtils.getProfileInfo(true)).resolves.toEqual({});
expect(updateCredentialManagerSettingSpy).toBeCalledWith(globals.ZOWE_CLI_SCM);
expect(defaultCredMgrSpy).toHaveBeenCalledWith(ProfilesCache.requireKeyring);
});
});
});
4 changes: 3 additions & 1 deletion packages/zowe-explorer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -1933,6 +1933,7 @@
"vscode": "^1.53.2"
},
"devDependencies": {
"@napi-rs/cli": "^2.16.1",
"@types/chai": "^4.2.6",
"@types/chai-as-promised": "^7.1.0",
"@types/expect": "^1.20.3",
Expand All @@ -1943,6 +1944,7 @@
"chai": "^4.1.2",
"chai-as-promised": "^7.1.1",
"chalk": "^2.4.1",
"copy-webpack-plugin": "^6.4.1",
"cross-env": "^5.2.0",
"del": "^4.1.1",
"eslint-plugin-zowe-explorer": "2.10.0-SNAPSHOT",
Expand All @@ -1958,7 +1960,6 @@
"gulp-typescript": "^5.0.1",
"jsdoc": "^3.6.3",
"jsdom": "^16.0.0",
"keytar": "^7.2.0",
"log4js": "^6.4.6",
"markdownlint-cli": "^0.33.0",
"mem": "^6.0.1",
Expand All @@ -1971,6 +1972,7 @@
"webpack-cli": "^3.3.11"
},
"dependencies": {
"@zowe/secrets-for-zowe-sdk": "7.18.1",
"@zowe/zowe-explorer-api": "2.10.0-SNAPSHOT",
"fs-extra": "8.0.1",
"isbinaryfile": "4.0.4",
Expand Down
4 changes: 4 additions & 0 deletions packages/zowe-explorer/scripts/getSecretsPrebuilds.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { copySync } from "fs-extra";
import { join, resolve } from "path";
const secretsPkgDir = resolve(require.resolve("@zowe/secrets-for-zowe-sdk"), "..", "..");
copySync(join(secretsPkgDir, "prebuilds"), resolve(__dirname, "..", "prebuilds"));
5 changes: 3 additions & 2 deletions packages/zowe-explorer/src/utils/ProfilesUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import * as globals from "../globals";
import * as path from "path";
import * as fs from "fs";
import * as util from "util";
import { getSecurityModules, IZoweTreeNode, ZoweTreeNode, getZoweDir, getFullPath, Gui } from "@zowe/zowe-explorer-api";
import { IZoweTreeNode, ZoweTreeNode, getZoweDir, getFullPath, Gui, ProfilesCache } from "@zowe/zowe-explorer-api";
import { Profiles } from "../Profiles";
import * as nls from "vscode-nls";
import { imperative, getImperativeConfig } from "@zowe/cli";
Expand Down Expand Up @@ -273,7 +273,8 @@ export class ProfilesUtils {
ZoweLogger.info(localize("ProfilesUtils.getProfileInfo.usingDefault", "No custom credential managers found, using the default instead."));
await ProfilesUtils.updateCredentialManagerSetting(globals.ZOWE_CLI_SCM);
return new imperative.ProfileInfo("zowe", {
credMgrOverride: imperative.ProfileCredentials.defaultCredMgrWithKeytar(() => getSecurityModules("keytar", envTheia)),
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
credMgrOverride: imperative.ProfileCredentials.defaultCredMgrWithKeytar(ProfilesCache.requireKeyring),
});
}

Expand Down
10 changes: 8 additions & 2 deletions packages/zowe-explorer/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ const path = require("path");
var webpack = require("webpack");
var fs = require("fs");

const CopyPlugin = require("copy-webpack-plugin");

/**@type {import('webpack').Configuration}*/
const config = {
target: "node", // vscode extensions run in a Node.js-context 📖 -> https://webpack.js.org/configuration/node/
Expand All @@ -40,7 +42,6 @@ const config = {
externals: {
// Add modules that cannot be webpack'ed, 📖 -> https://webpack.js.org/configuration/externals/
vscode: "commonjs vscode", // the vscode-module is created on-the-fly and must be excluded. Add other modules that cannot be webpack'ed, 📖 -> https://webpack.js.org/configuration/externals/
keytar: "commonjs keytar",
"spdx-exceptions": "commonjs spdx-exceptions",
"spdx-license-ids": "commonjs spdx-license-ids",
"spdx-license-ids/deprecated": "commonjs spdx-license-ids/deprecated",
Expand Down Expand Up @@ -81,7 +82,12 @@ const config = {
},
],
},
plugins: [new webpack.BannerPlugin(fs.readFileSync("../../scripts/LICENSE_HEADER", "utf-8"))],
plugins: [
new webpack.BannerPlugin(fs.readFileSync("../../scripts/LICENSE_HEADER", "utf-8")),
new CopyPlugin({
patterns: [{ from: "./node_modules/@zowe/secrets-for-zowe-sdk/prebuilds", to: "../../prebuilds/" }],
}),
],
};

module.exports = config;
Loading
Loading