UNPKG

@zowe/imperative

Version:
319 lines 14.4 kB
"use strict"; /* * 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. * */ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ConfigSecure = void 0; const fs = require("fs"); const JSONC = require("comment-json"); const lodash = require("lodash"); const ConfigApi_1 = require("./ConfigApi"); const ConfigConstants_1 = require("../ConfigConstants"); const security_1 = require("../../../security"); const ConfigUtils_1 = require("../ConfigUtils"); const EventConstants_1 = require("../../../events/src/EventConstants"); const EventOperator_1 = require("../../../events/src/EventOperator"); /** * API Class for manipulating config layers. */ class ConfigSecure extends ConfigApi_1.ConfigApi { // _______________________________________________________________________ /** * Load the secure application properties from secure storage using the * specified vault interface. The vault interface is placed into our * Config object. The secure values are placed into our Config layers. * * @param vault Interface for loading and saving to secure storage. */ load(vault) { return __awaiter(this, void 0, void 0, function* () { if (vault != null) { this.mConfig.mVault = vault; } if (this.mConfig.mVault == null) return; // load the secure fields try { const s = yield this.mConfig.mVault.load(ConfigConstants_1.ConfigConstants.SECURE_ACCT); if (s == null) return; // Typecasting because of this issue: https://github.com/kaelzhang/node-comment-json/issues/42 this.mConfig.mSecure = JSONC.parse(s); } catch (error) { this.mLoadFailed = true; throw error; } this.mLoadFailed = false; // populate each layers properties this.mConfig.mLayers.forEach(this.loadCached.bind(this)); }); } // _______________________________________________________________________ /** * Copy secure config properties from cached secure properties into the * specified layer. To load secure config properties directly from the * vault, use the asynchronous method `load` instead. * * @internal * @param opts The user and global flags that specify one of the four * config files (aka layers). */ loadCached(opts) { if (this.mConfig.mVault == null) return; const layer = opts ? this.mConfig.findLayer(opts.user, opts.global) : this.mConfig.layerActive(); // Find the matching layer for (const [filePath, secureProps] of Object.entries(this.mConfig.mSecure)) { if (filePath === layer.path) { // Only set those indicated by the config for (const p of this.secureFields(layer)) { // Extract and set secure properties for (const [sPath, sValue] of Object.entries(secureProps)) { if (sPath === p) { const segments = sPath.split("."); let obj = layer.properties; for (let x = 0; x < segments.length; x++) { const segment = segments[x]; if (x === segments.length - 1) { obj[segment] = sValue; break; } obj = obj[segment]; if (obj == null) break; } } } } } } } // _______________________________________________________________________ /** * Save the secure application properties into secure storage using * the vault interface from our config object. * * @param allLayers Specify true to save all config layers instead of only the active one */ save(allLayers) { return __awaiter(this, void 0, void 0, function* () { if (this.mConfig.mVault == null) return; const beforeLen = Object.keys(this.mConfig.mSecure).length; // Build the entries for each layer for (const { user, global } of this.mConfig.mLayers) { if (allLayers || user === this.mConfig.mActive.user && global === this.mConfig.mActive.global) { this.cacheAndPrune({ user, global }); } } // Save the entries if needed if (Object.keys(this.mConfig.mSecure).length > 0 || beforeLen > 0) { yield this.directSave(); } }); } // _______________________________________________________________________ /** * Save secure properties to the vault without rebuilding the JSON object. * @internal */ directSave() { return __awaiter(this, void 0, void 0, function* () { yield this.mConfig.mVault.save(ConfigConstants_1.ConfigConstants.SECURE_ACCT, JSONC.stringify(this.mConfig.mSecure)); EventOperator_1.EventOperator.getZoweProcessor().emitZoweEvent(EventConstants_1.ZoweUserEvents.ON_VAULT_CHANGED); }); } // _______________________________________________________________________ /** * Copy secure config properties from the specified layer into cached * secure properties. To save secure config properties directly to the * vault, use the asynchronous method `save` instead. * * Warning: Do not pass an `IConfigLayer` object into this method unless * you want its properties to be edited. * * @internal * @param opts The user and global flags that specify one of the four * config files (aka layers). * @param opts.properties `IConfig` object cloned from the specified layer. * If specified, secure properties will be removed. */ cacheAndPrune(opts) { var _a; const layer = opts ? this.mConfig.findLayer(opts.user, opts.global) : this.mConfig.layerActive(); // Create all the secure property entries const sp = {}; for (const path of this.secureFields(layer)) { const segments = path.split("."); let obj = (_a = opts === null || opts === void 0 ? void 0 : opts.properties) !== null && _a !== void 0 ? _a : layer.properties; for (let x = 0; x < segments.length; x++) { const segment = segments[x]; const value = obj[segment]; if (value == null) break; if (x === segments.length - 1) { sp[path] = value; if ((opts === null || opts === void 0 ? void 0 : opts.properties) != null) { delete obj[segment]; } break; } obj = obj[segment]; } } if (this.mConfig.mVault != null) { // Clear the entry and rebuild it delete this.mConfig.mSecure[layer.path]; // Create the entry to set the secure properties if (Object.keys(sp).length > 0) { this.mConfig.mSecure[layer.path] = sp; } } } // _______________________________________________________________________ /** * List full paths of all secure properties found in a team config file. * * @param opts Specify `true` to get secure fields for all layers. Specify `false` to get secure fields for the active layer * @param opts.user The user flag that specifies the user-related config files (aka layers). * @param opts.global The global flag that specifies the project-related config files (aka layers). * @default opts = false * @returns Array of secure property paths * (e.g., "profiles.lpar1.properties.password") */ secureFields(opts = false) { if (opts === true) { return this.findSecure(this.mConfig.mProperties.profiles, "profiles"); } if (opts === false) { return this.findSecure(this.mConfig.layerActive().properties.profiles, "profiles"); } return this.findSecure(this.mConfig.findLayer(opts.user, opts.global).properties.profiles, "profiles"); } // _______________________________________________________________________ /** * List names of secure properties for a profile. They may be defined at * the profile's level, or at a higher level if the config is nested. * @param profileName Profile name to search for * @returns Array of secure property names */ securePropsForProfile(profileName) { const profilePath = this.mConfig.api.profiles.getProfilePathFromName(profileName); const secureProps = new Set(); for (const propPath of this.findSecure(this.mConfig.mProperties.profiles, "profiles")) { const pathSegments = propPath.split("."); // profiles.XXX.properties.YYY // eslint-disable-next-line @typescript-eslint/no-magic-numbers const propProfilePath = pathSegments.slice(0, -2).join("."); if (ConfigUtils_1.ConfigUtils.jsonPathMatches(profilePath, propProfilePath)) { secureProps.add(pathSegments.pop()); } } return [...secureProps]; } /** * Recursively find secure property paths inside a team config * "profiles" object. * @internal * @param profiles The "profiles" object that is present at the top level * of team config files, and may also be present at lower * levels. * @param path The JSON path to the "profiles" object * @returns Array of secure property paths */ findSecure(profiles, path) { const secureProps = []; for (const profName of Object.keys(profiles)) { for (const propName of profiles[profName].secure || []) { secureProps.push(`${path}.${profName}.properties.${propName}`); } if (profiles[profName].profiles != null) { secureProps.push(...this.findSecure(profiles[profName].profiles, `${path}.${profName}.profiles`)); } } return secureProps; } /** * Retrieve secure properties for a given layer path * * @param layerPath Path of the layer to get secure properties for * @returns the secure properties for the given layer, or null if not found */ securePropsForLayer(layerPath) { const secureLayer = Object.keys(this.mConfig.mSecure).find(osLocation => osLocation === layerPath); return secureLayer ? this.mConfig.mSecure[secureLayer] : null; } /** * Retrieve info that can be used to store a profile property securely. * * For example, to securely store "profiles.lpar1.properties.password", the * name "password" would be stored in "profiles.lpar1.secure". * * @internal * @param propertyPath The full path of the profile property * @param findUp Specify true to search up in the config file for higher level secure arrays * @returns Object with the following attributes: * - `path` The JSON path of the secure array * - `prop` The name of the property */ secureInfoForProp(propertyPath, findUp) { var _a; if (!propertyPath.includes(".properties.")) { return; } const pathSegments = propertyPath.split("."); // profiles.XXX.properties.YYY const secureProp = pathSegments.pop(); let securePath = propertyPath.replace(/\.properties.+/, ".secure"); if (findUp) { const layer = this.mConfig.layerActive(); while (layer.exists && pathSegments.length > 2) { pathSegments.pop(); const testSecurePath = pathSegments.join(".") + ".secure"; if ((_a = lodash.get(layer.properties, testSecurePath)) === null || _a === void 0 ? void 0 : _a.includes(secureProp)) { securePath = testSecurePath; break; } } } return { path: securePath, prop: secureProp }; } /** * Delete secure properties stored for team config files that do not exist. * @returns Array of file paths for which properties were deleted */ rmUnusedProps() { const prunedFiles = []; for (const filename of Object.keys(this.mConfig.mSecure)) { if (!fs.existsSync(filename)) { delete this.mConfig.mSecure[filename]; prunedFiles.push(filename); } } return prunedFiles; } /** * Return true if the secure load method was called and threw an error, or * it was never called because the CredentialManager failed to initialize. */ get loadFailed() { return this.mLoadFailed != null ? this.mLoadFailed : !security_1.CredentialManagerFactory.initialized; } } exports.ConfigSecure = ConfigSecure; //# sourceMappingURL=ConfigSecure.js.map