UNPKG

@zowe/imperative

Version:
1,033 lines 80.8 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.ProfileInfo = void 0; const fs = require("fs"); const url = require("url"); const jsonfile = require("jsonfile"); const lodash = require("lodash"); const semver = require("semver"); const IProfLoc_1 = require("./doc/IProfLoc"); const ProfileCredentials_1 = require("./ProfileCredentials"); const ProfInfoErr_1 = require("./ProfInfoErr"); // for team config functions const Config_1 = require("./Config"); const ConfigSchema_1 = require("./ConfigSchema"); // for imperative operations const utilities_1 = require("../../utilities"); const expect_1 = require("../../expect"); const rest_1 = require("../../rest"); const ConfigAutoStore_1 = require("./ConfigAutoStore"); const ConfigUtils_1 = require("./ConfigUtils"); const ConfigBuilder_1 = require("./ConfigBuilder"); const constants_1 = require("../../constants"); const censor_1 = require("../../censor"); /** * This class provides functions to retrieve profile-related information. * It can load the relevant configuration files, merge all possible * profile argument values using the Zowe order-of-precedence, and * access desired profile attributes from the Zowe configuration settings. * * Pseudocode examples: * <pre> * // Construct a new object. Use it to read the profiles from disk. * // ProfileInfo functions throw a ProfInfoErr exception for errors. * // You can catch those errors and test the errorCode for known * // values. We are only showing the try/catch on the function * // below, but it applies to any ProfileInfo function. * profInfo = new ProfileInfo("zowe"); * try { * await profInfo.readProfilesFromDisk(); * } catch(err) { * if (err instanceof ProfInfoErr) { * if (err.errcode == ProfInfoErr.CANT_GET_SCHEMA_URL) { * youTakeAnAlternateAction(); * } else { * // report the error * } * } else { * // handle other exceptions * } * } * * // Maybe you want the list of all zosmf profiles * let arrayOfProfiles = profInfo.getAllProfiles("zosmf"); * youDisplayTheListOfProfiles(arrayOfProfiles); * * // Maybe you want the default zosmf profile * let zosmfProfile = profInfo.getDefaultProfile("zosmf"); * youUseTheProfile(zosmfProfile); * * // Maybe you want the arg values for the default JCLCheck profile * let jckProfile = profInfo.getDefaultProfile("jclcheck"); * let jckMergedArgs = profInfo.mergeArgsForProfile(jckProfile); * let jckFinalArgs = youPromptForMissingArgsAndCombineWithKnownArgs( * jckMergedArgs.knownArgs, jckMergedArgs.missingArgs * ); * youRunJclCheck(jckFinalArgs); * * // Maybe no profile of type "zosmf" even exists. * let zosmfProfiles = profInfo.getAllProfiles("zosmf"); * if (zosmfProfiles.length == 0) { * // No zosmf profile exists * // Merge any required arg values for the zosmf profile type * let zosmfMergedArgs = * profInfo.mergeArgsForProfileType("zosmf"); * * // Values of secure arguments must be loaded separately. You can * // freely log the contents of zosmfMergedArgs without leaking secure * // argument values, until they are loaded with the lines below. * zosmfMergedArgs.knownArgs.forEach((arg) => { * if (arg.secure) arg.argValue = profInfo.loadSecureArg(arg); * }); * * let finalZosmfArgs = * youPromptForMissingArgsAndCombineWithKnownArgs( * zosmfMergedArgs.knownArgs, * zosmfMergedArgs.missingArgs * ); * youRunSomeZosmfCommand(finalZosmfArgs); * } * * // So you want to write to a config file? * // You must use the Config API to write to a team configuration. * // See the Config class documentation for functions to set * // and save team config arguments. * * // Let's save some zosmf arguments from the example above. * let yourZosmfArgsToWrite: IProfArgAttrs = * youSetValuesToOverwrite( * zosmfMergedArgs.knownArgs, zosmfMergedArgs.missingArgs * ); * * let configObj: Config = profInfo.getTeamConfig(); * youWriteArgValuesUsingConfigObj( * configObj, yourZosmfArgsToWrite * ); * </pre> */ class ProfileInfo { // _______________________________________________________________________ /** * Constructor for ProfileInfo class. * * @param appName * The name of the application (like "zowe" in zowe.config.json) * whose configuration you want to access. * * @param profInfoOpts * Options that will control the behavior of ProfileInfo. */ constructor(appName, profInfoOpts) { this.mLoadedConfig = null; this.mAppName = null; this.mImpLogger = null; this.mOverrideWithEnv = false; this.mHasValidSchema = false; this.mAppName = appName; // use any supplied environment override setting if (profInfoOpts === null || profInfoOpts === void 0 ? void 0 : profInfoOpts.overrideWithEnv) { this.mOverrideWithEnv = profInfoOpts.overrideWithEnv; } this.mCredentials = new ProfileCredentials_1.ProfileCredentials(this, profInfoOpts); // do enough Imperative stuff to let imperative utilities work this.mImpLogger = ConfigUtils_1.ConfigUtils.initImpUtils(this.mAppName); } /** * Checks if a JSON web token is used for authenticating the given profile * name. If so, it will decode the token to determine whether it has * expired. * * @param {string | IProfileLoaded} profile * The name of the profile or the profile object to check the JSON web * token for * @returns {boolean} * Whether the token has expired for the given profile. Returns `false` * if a token value is not set or the token type is LTPA2. */ hasTokenExpiredForProfile(profile) { const profName = typeof profile === "string" ? profile : profile.name; const profAttrs = this.getAllProfiles().find((prof) => prof.profName === profName); const knownProps = this.mergeArgsForProfile(profAttrs, { getSecureVals: true }).knownArgs; const tokenValueProp = knownProps.find((arg) => arg.argName === "tokenValue" && arg.argValue != null); // Ignore if tokenValue is not a prop if (tokenValueProp == null) { return false; } const tokenTypeProp = knownProps.find((arg) => arg.argName === "tokenType"); // Cannot decode LTPA tokens without private key if ((tokenTypeProp === null || tokenTypeProp === void 0 ? void 0 : tokenTypeProp.argValue) == "LtpaToken2") { return false; } const fullToken = tokenValueProp.argValue.toString(); return ConfigUtils_1.ConfigUtils.hasTokenExpired(fullToken); } /** * Update a given property in the config file. * @param options Set of options needed to update a given property */ updateProperty(options) { return __awaiter(this, void 0, void 0, function* () { var _a, _b, _c, _d, _e, _f, _g; this.ensureReadFromDisk(); const desiredProfile = options.profileType != null ? this.getAllProfiles(options.profileType).find(v => v.profName === options.profileName) : null; let updated = false; if (desiredProfile != null) { const mergedArgs = this.mergeArgsForProfile(desiredProfile, { getSecureVals: false }); if (options.forceUpdate) { const knownProperty = mergedArgs.knownArgs.find(v => v.argName === options.property); if (knownProperty != null) { const profPath = this.getTeamConfig().api.profiles.getProfilePathFromName(options.profileName); if (!ConfigUtils_1.ConfigUtils.jsonPathMatches(knownProperty.argLoc.jsonLoc, profPath)) { knownProperty.argLoc.jsonLoc = `${profPath}.properties.${options.property}`; } } } updated = yield this.updateKnownProperty(Object.assign(Object.assign({}, options), { mergedArgs, osLocInfo: (_a = this.getOsLocInfo(desiredProfile)) === null || _a === void 0 ? void 0 : _a[0] })); } else if (!(options.profileType == null && (options.forceUpdate || this.getTeamConfig().api.profiles.exists(options.profileName)))) { throw new ProfInfoErr_1.ProfInfoErr({ errorCode: ProfInfoErr_1.ProfInfoErr.PROF_NOT_FOUND, msg: `Failed to find profile ${options.profileName} of type ${options.profileType}` }); } if (!updated) { // Check to see if loadedConfig already contains the schema for the specified profile type if (((_d = (_c = (_b = utilities_1.ImperativeConfig.instance.loadedConfig) === null || _b === void 0 ? void 0 : _b.profiles) === null || _c === void 0 ? void 0 : _c.find(p => p.type === options.profileType)) === null || _d === void 0 ? void 0 : _d.schema) == null || ((_f = (_e = utilities_1.ImperativeConfig.instance.loadedConfig) === null || _e === void 0 ? void 0 : _e.baseProfile) === null || _f === void 0 ? void 0 : _f.schema) == null) { const loadedConfig = utilities_1.ImperativeConfig.instance.loadedConfig; if (!loadedConfig.profiles) loadedConfig.profiles = []; this.mProfileSchemaCache.forEach((value, key) => { if (key.indexOf(":base") > 0 && loadedConfig.baseProfile == null) { loadedConfig.baseProfile = { type: "base", schema: value }; } else if (key.indexOf(":base") < 0 && !loadedConfig.profiles.find(p => p.type === key.split(":")[1])) { // Add the schema corresponding to the given profile type loadedConfig.profiles.push({ type: key.split(":")[1], schema: value }); } }); utilities_1.ImperativeConfig.instance.loadedConfig = loadedConfig; } yield ConfigAutoStore_1.ConfigAutoStore._storeSessCfgProps({ config: this.mLoadedConfig, defaultBaseProfileName: (_g = this.mLoadedConfig) === null || _g === void 0 ? void 0 : _g.mProperties.defaults.base, sessCfg: { [options.property === "host" ? "hostname" : options.property]: options.value }, propsToStore: [options.property], profileName: options.profileName, profileType: options.profileType, setSecure: options.setSecure }); } }); } /** * Update a given property with the value provided. * This function only works for properties that can be found in the config files (including secure arrays). * If the property cannot be found, this function will resolve to false * @param options Set of options required to update a known property */ updateKnownProperty(options) { return __awaiter(this, void 0, void 0, function* () { var _a, _b; this.ensureReadFromDisk(); const toUpdate = options.mergedArgs.knownArgs.find(v => v.argName === options.property) || options.mergedArgs.missingArgs.find(v => v.argName === options.property); if (toUpdate == null || toUpdate.argLoc.locType === IProfLoc_1.ProfLocType.TEAM_CONFIG && !this.getTeamConfig().mProperties.autoStore) { return false; } switch (toUpdate.argLoc.locType) { case IProfLoc_1.ProfLocType.TEAM_CONFIG: { let oldLayer; const layer = this.getTeamConfig().layerActive(); const osLoc = (_a = options.osLocInfo) !== null && _a !== void 0 ? _a : (_b = this.getOsLocInfo(this.getAllProfiles().find(p => ConfigUtils_1.ConfigUtils.jsonPathMatches(toUpdate.argLoc.jsonLoc, p.profLoc.jsonLoc)))) === null || _b === void 0 ? void 0 : _b[0]; if (osLoc && (layer.user !== osLoc.user || layer.global !== osLoc.global)) { oldLayer = { user: layer.user, global: layer.global }; this.getTeamConfig().api.layers.activate(osLoc.user, osLoc.global); } const updateVaultOnly = options.setSecure && this.getTeamConfig().api.secure.secureFields().includes(toUpdate.argLoc.jsonLoc); this.getTeamConfig().set(toUpdate.argLoc.jsonLoc, options.value, { secure: options.setSecure }); if (!updateVaultOnly) { yield this.getTeamConfig().save(false); } else { yield this.getTeamConfig().api.secure.save(false); } if (oldLayer) { this.getTeamConfig().api.layers.activate(oldLayer.user, oldLayer.global); } break; } case IProfLoc_1.ProfLocType.ENV: case IProfLoc_1.ProfLocType.DEFAULT: return false; default: { let msgText = "Invalid profile location type: "; if (toUpdate.argLoc.locType == IProfLoc_1.ProfLocType.OLD_PROFILE) { msgText = "You can no longer write to V1 profiles. Location type = "; } throw new ProfInfoErr_1.ProfInfoErr({ errorCode: ProfInfoErr_1.ProfInfoErr.INVALID_PROF_LOC_TYPE, msg: msgText + toUpdate.argLoc.locType }); } } return true; }); } /** * Remove a known property from the ProfileInfo class * This method will call the updateKnownProperty method with a value set to `undefined` and serves as a helper function * to make is easier to understand when a known property is removed. * @example * The example below describes how to remove a property * ``` * // Using the removeKnownProperty method * profileInfo.removeKnownProperty({mergedArgs, property: "someProperty"}); * // Using the updateKnownProperty method * profileInfo.updateKnownProperty({mergedArgs, property: "someProperty", value: undefined, isSecure: false}); * ``` * @param options Set of options required to remove a known property * @returns Returns a boolean indicating if the property has been removed */ removeKnownProperty(options) { const updatePropertyOptions = { mergedArgs: options.mergedArgs, property: options.property, value: undefined, setSecure: false, osLocInfo: options.osLocInfo }; return this.updateKnownProperty(updatePropertyOptions); } // _______________________________________________________________________ /** * Get all of the typed profiles in the configuration. * * @param profileType * Limit selection to only profiles of the specified type. * If not supplied, the names of all typed profiles are returned. * * @returns An array of profile attribute objects. * In addition to the name, you get the profile type, * an indicator of whether the profile is the default profile * for that type, and the location of that profile. * * If no profile exists for the specified type (or if * no profiles of any kind exist), we return an empty array * ie, length is zero. */ getAllProfiles(profileType, options) { this.ensureReadFromDisk(); const profiles = []; // Do we have team config profiles? const teamConfigProfs = this.mLoadedConfig.layerMerge({ maskSecure: true, excludeGlobalLayer: options === null || options === void 0 ? void 0 : options.excludeHomeDir }).profiles; // Iterate over them for (const prof in teamConfigProfs) { // Check if the profile has a type if (teamConfigProfs[prof].type && (profileType == null || teamConfigProfs[prof].type === profileType)) { const jsonLocation = "profiles." + prof; const teamOsLocation = this.findTeamOsLocation(jsonLocation, options === null || options === void 0 ? void 0 : options.excludeHomeDir); const profAttrs = { profName: prof, profType: teamConfigProfs[prof].type, isDefaultProfile: this.isDefaultTeamProfile(prof, profileType), profLoc: { locType: IProfLoc_1.ProfLocType.TEAM_CONFIG, osLoc: teamOsLocation, jsonLoc: jsonLocation } }; profiles.push(profAttrs); } // Check for subprofiles if (teamConfigProfs[prof].profiles) { // Get the subprofiles and add to profiles list const jsonPath = "profiles." + prof; const subProfiles = this.getTeamSubProfiles(prof, jsonPath, teamConfigProfs[prof].profiles, profileType); for (const subProfile of subProfiles) { profiles.push(subProfile); } } } return profiles; } // _______________________________________________________________________ /** * Get the default profile for the specified profile type. * * @param profileType * The type of profile of interest. * * @returns The default profile. If no profile exists * for the specified type, we return null; */ getDefaultProfile(profileType) { this.ensureReadFromDisk(); const defaultProfile = { profName: null, profType: profileType, isDefaultProfile: true, profLoc: { locType: null } }; // get default profile name from the team config const configProperties = this.mLoadedConfig.mProperties; if (!Object.prototype.hasOwnProperty.call(configProperties.defaults, profileType)) { // no default exists for the requested type this.mImpLogger.warn("Found no profile of type '" + profileType + "' in Zowe client configuration."); return null; } // extract info from the underlying team config const foundProfNm = configProperties.defaults[profileType]; // for a team config, we use the last node of the jsonLoc as the name const foundJson = this.mLoadedConfig.api.profiles.getProfilePathFromName(foundProfNm); const teamOsLocation = this.findTeamOsLocation(foundJson); // assign the required poperties to defaultProfile defaultProfile.profName = foundProfNm; defaultProfile.profLoc = { locType: IProfLoc_1.ProfLocType.TEAM_CONFIG, osLoc: teamOsLocation, jsonLoc: foundJson }; return defaultProfile; } // _______________________________________________________________________ /** * Get the Config object used to manipulate the team configuration on disk. * * Our current market direction is to encourage customers to edit the * team configuration files in their favorite text editor. * * If you must ignore this recommended practice, you must use the Config * class to manipulate the team config files. This class has a more detailed * and therefore more complicated API, but it does contain functions to * write data to the team configuration files. * * You must call ProfileInfo.readProfilesFromDisk() before calling this function. * * @returns An instance of the Config class that can be used to manipulate * the team configuration on disk. */ getTeamConfig() { this.ensureReadFromDisk(); return this.mLoadedConfig; } // _______________________________________________________________________ /** * Helper function to identify if the existing config is secure or not * @returns true if the teamConfig is storing credentials securely, false otherwise */ isSecured() { var _a, _b; return (_b = (_a = this.mCredentials) === null || _a === void 0 ? void 0 : _a.isSecured) !== null && _b !== void 0 ? _b : true; } // _______________________________________________________________________ /** * Create a session from profile arguments that have been retrieved from * ProfileInfo functions. * * @param profArgs * An array of profile arguments. * * @param connOpts * Options that alter our actions. See IOptionsForAddConnProps. * The connOpts parameter need not be supplied. * Default properties may be added to any supplied connOpts. * The only option values used by this function are: * connOpts.requestToken * connOpts.defaultTokenType * * @returns A session that can be used to connect to a remote host. */ static createSession(profArgs, connOpts = {}) { // Initialize a session config with values from profile arguments const sessCfg = ProfileInfo.initSessCfg(profArgs, ["rejectUnauthorized", "basePath", "protocol"]); // Populate command arguments object with arguments to be resolved const cmdArgs = { $0: "", _: [] }; for (const { argName, argValue } of profArgs) { cmdArgs[argName] = argValue; } // resolve the choices among various session config properties rest_1.ConnectionPropsForSessCfg.resolveSessCfgProps(sessCfg, cmdArgs, connOpts); return new rest_1.Session(sessCfg); } // _______________________________________________________________________ /** * Merge all of the available values for arguments defined for the * specified profile. Values are retrieved from the following sources. * Each successive source will override the previous source. * - A default value for the argument that is defined in the profile definition. * - A value defined in the base profile. * - A value defined in the specified service profile. * - For a team configuration, both the base profile values and the * service profile values will be overridden with values from a * zowe.config.user.json file (if it exists). * - An environment variable for that argument (if environment overrides * are enabled). * * @param profile * The profile whose arguments are to be merged. * * @param mergeOpts * Options to use when merging arguments. * This parameter is not required. Defaults will be used. * * @returns An object that contains an array of known profile argument * values and an array of required profile arguments which * have no value assigned. Either of the two arrays could be * of zero length, depending on the user's configuration and * environment. * * We will return null if the profile does not exist * in the current Zowe configuration. */ mergeArgsForProfile(profile, mergeOpts = { getSecureVals: false }) { var _a, _b, _c, _d, _e, _f, _g, _h; this.ensureReadFromDisk(); expect_1.ImperativeExpect.toNotBeNullOrUndefined(profile, "Profile attributes must be defined"); const mergedArgs = { knownArgs: [], missingArgs: [] }; let configProperties; const osLocInfo = (_a = this.getOsLocInfo(profile)) === null || _a === void 0 ? void 0 : _a[0]; if (profile.profLoc.locType === IProfLoc_1.ProfLocType.TEAM_CONFIG) { configProperties = this.mLoadedConfig.mProperties; if (profile.profName != null) { // Load args from service profile if one exists const serviceProfile = this.mLoadedConfig.api.profiles.get(profile.profName, false); for (const [propName, propVal] of Object.entries(serviceProfile)) { const [argLoc, secure] = this.argTeamConfigLoc({ profileName: profile.profName, propName, osLocInfo, configProperties }); mergedArgs.knownArgs.push({ argName: utilities_1.CliUtils.getOptionFormat(propName).camelCase, dataType: this.argDataType(typeof propVal), // TODO Is using `typeof` bad for "null" values that may be int or bool? argValue: propVal, argLoc, secure, inSchema: false }); } } // if using global profile, make global base default for the operation below const osLoc = ((_b = this.getOsLocInfo(profile)) !== null && _b !== void 0 ? _b : []).find(p => p.name === profile.profName); let baseProfile = this.mLoadedConfig.api.profiles.defaultGet("base"); let realBaseProfileName; let layerProperties; if (osLoc === null || osLoc === void 0 ? void 0 : osLoc.global) { layerProperties = (_c = this.mLoadedConfig.findLayer(osLoc.user, osLoc.global)) === null || _c === void 0 ? void 0 : _c.properties; realBaseProfileName = layerProperties === null || layerProperties === void 0 ? void 0 : layerProperties.defaults.base; if (!realBaseProfileName && osLoc.user) { layerProperties = (_d = this.mLoadedConfig.findLayer(false, osLoc.global)) === null || _d === void 0 ? void 0 : _d.properties; realBaseProfileName = layerProperties === null || layerProperties === void 0 ? void 0 : layerProperties.defaults.base; } if (realBaseProfileName) baseProfile = this.mLoadedConfig.api.profiles.buildProfile(realBaseProfileName, layerProperties === null || layerProperties === void 0 ? void 0 : layerProperties.profiles); else baseProfile = null; } if (baseProfile != null) { // Load args from default base profile if one exists const baseProfileName = realBaseProfileName !== null && realBaseProfileName !== void 0 ? realBaseProfileName : configProperties.defaults.base; for (const [propName, propVal] of Object.entries(baseProfile)) { const argName = utilities_1.CliUtils.getOptionFormat(propName).camelCase; // Skip properties already loaded from service profile if (!mergedArgs.knownArgs.find((arg) => arg.argName === argName)) { const [argLoc, secure] = this.argTeamConfigLoc({ profileName: baseProfileName, propName, osLocInfo, configProperties: layerProperties !== null && layerProperties !== void 0 ? layerProperties : configProperties }); mergedArgs.knownArgs.push({ argName, dataType: this.argDataType(typeof propVal), argValue: propVal, argLoc, secure, inSchema: false }); } } } } else { throw new ProfInfoErr_1.ProfInfoErr({ errorCode: ProfInfoErr_1.ProfInfoErr.INVALID_PROF_LOC_TYPE, msg: "Invalid profile location type: " + IProfLoc_1.ProfLocType[profile.profLoc.locType] }); } // perform validation with profile schema if available const profSchema = this.loadSchema(profile); if (profSchema != null) { const missingRequired = []; for (const [propName, propInfoInSchema] of Object.entries(profSchema.properties || {})) { // Check if property in schema is missing from known args const knownArg = mergedArgs.knownArgs.find((arg) => arg.argName === propName); if (knownArg == null) { let argFound = false; if (profile.profLoc.locType === IProfLoc_1.ProfLocType.TEAM_CONFIG) { let [argLoc, foundInSecureArray] = [null, false]; try { [argLoc, foundInSecureArray] = this.argTeamConfigLoc({ profileName: profile.profName, propName, osLocInfo, configProperties }); argFound = true; } catch (_argNotFoundInServiceProfile) { if (configProperties.defaults.base != null) { try { [argLoc, foundInSecureArray] = this.argTeamConfigLoc({ profileName: configProperties.defaults.base, propName, osLocInfo, configProperties }); argFound = true; } catch (_argNotFoundInBaseProfile) { // Do nothing } } } if (argFound) { const newArg = { argName: propName, dataType: this.argDataType(propInfoInSchema.type), argValue: (_e = propInfoInSchema.optionDefinition) === null || _e === void 0 ? void 0 : _e.defaultValue, argLoc, inSchema: true, // See https://github.com/zowe/imperative/issues/739 secure: foundInSecureArray || propInfoInSchema.secure }; try { this.loadSecureArg({ argLoc, argName: propName }); mergedArgs.knownArgs.push(newArg); } catch (_secureValueNotFound) { mergedArgs.missingArgs.push(newArg); } } } if (!argFound) { mergedArgs.missingArgs.push({ argName: propName, inSchema: true, dataType: this.argDataType(propInfoInSchema.type), argValue: (_f = propInfoInSchema.optionDefinition) === null || _f === void 0 ? void 0 : _f.defaultValue, argLoc: { locType: IProfLoc_1.ProfLocType.DEFAULT }, secure: propInfoInSchema.secure }); } } else { knownArg.inSchema = true; knownArg.secure = (_g = knownArg.secure) !== null && _g !== void 0 ? _g : propInfoInSchema.secure; if (knownArg.secure) { delete knownArg.argValue; } } } // overwrite with any values found in environment this.overrideWithEnv(mergedArgs, profSchema); for (const tempArg of mergedArgs.missingArgs || []) { // Check if missing property is required if ((_h = profSchema.required) === null || _h === void 0 ? void 0 : _h.includes(tempArg.argName)) { missingRequired.push(tempArg.argName); } } if (missingRequired.length > 0) { throw new ProfInfoErr_1.ProfInfoErr({ errorCode: ProfInfoErr_1.ProfInfoErr.MISSING_REQ_PROP, msg: "Missing required properties: " + missingRequired.join(", ") }); } } else { throw new ProfInfoErr_1.ProfInfoErr({ errorCode: ProfInfoErr_1.ProfInfoErr.LOAD_SCHEMA_FAILED, msg: `Failed to load schema for profile type ${profile.profType}` }); } // did our caller request the actual values of secure arguments? if (mergeOpts.getSecureVals) { mergedArgs.knownArgs.forEach((nextArg) => { try { if (nextArg.secure) nextArg.argValue = this.loadSecureArg(nextArg); } catch (_argValueNotDefined) { nextArg.argValue = undefined; } }); } return mergedArgs; } // _______________________________________________________________________ /** * Merge all of the available values for arguments defined for the * specified profile type. See mergeArgsForProfile() for details * about the merging algorithm. * The intended use is when no profile of a specific type exists. * The consumer app can prompt for values for missing arguments * and then perform the desired operation. * * @param profileType * The type of profile of interest. * * @param mergeOpts * Options to use when merging arguments. * This parameter is not required. Defaults will be used. * * @returns The complete set of required properties; */ mergeArgsForProfileType(profileType, mergeOpts = { getSecureVals: false }) { this.ensureReadFromDisk(); return this.mergeArgsForProfile({ profName: null, profType: profileType, isDefaultProfile: false, profLoc: { locType: IProfLoc_1.ProfLocType.TEAM_CONFIG } }, mergeOpts); } /** * Retrieves the Zowe CLI home directory. In the situation Imperative has * not initialized it we use a default value. * @returns {string} - Returns the Zowe home directory * @deprecated Use ConfigUtils.getZoweDir() */ static getZoweDir() { return ConfigUtils_1.ConfigUtils.getZoweDir(); } /** * Returns an indicator that the user has no team configuration, but we * detected the existence of old-school V1 profiles. We will not work with the * V1 profiles. This function can let you tell a user that they are incorrectly * trying to use V1 profiles. * @deprecated Use non-static method instead to detect V2 profiles * @returns {boolean} `true` if a V1 profile exists, and `false` otherwise. */ static get onlyV1ProfilesExist() { return ConfigUtils_1.ConfigUtils.onlyV1ProfilesExist; } /** * Returns an indicator that the user has no team configuration, but we * detected the existence of old-school V1 profiles. We will not work with the * V1 profiles. This function can let you tell a user that they are incorrectly * trying to use V1 profiles. * * @returns True - Means there is *NO* team config *AND* we detected that a V1 profile exists. * False otherwise. */ get onlyV1ProfilesExist() { return !this.getTeamConfig().exists && ConfigUtils_1.ConfigUtils.onlyV1ProfilesExist; } // _______________________________________________________________________ /** * Convert an IProfAttrs object into an IProfileLoaded objects * This is a convenience function. IProfileLoaded was frequently passed * among functions. This conversion function allows existing code to * acquire values in the IProfAttrs structure but pass those values * around in the older IProfileLoaded structure. The IProfAttrs * properties will be copied as follows: * * IProfileLoaded.name <-- IProfAttrs.profName * IProfileLoaded.type <-- IProfAttrs.profType * IProfileLoaded.profile <-- profAttrs * * @param profAttrs * A profile attributes object. * * @param dfltProfLoadedVals * A JSON object containing additional names from IProfileLoaded for * which a value should be supplied. IProfileLoaded contains more * properties than IProfAttrs. The items in this object will be * placed into the resulting IProfileLoaded object. * We use type "any" because all of the required properties of * IProfileLoaded will not be supplied by dfltProfLoadedVals. * If dfltProfLoadedVals is not supplied, only the following minimal * set if hard-coded properties will be added to the IProfileLoaded object. * * IProfileLoaded.message <-- "" (an empty string) * IProfileLoaded.failNotFound <-- false * * @returns An IProfileLoaded object; */ static profAttrsToProfLoaded(profAttrs, dfltProfLoadedVals) { const emptyProfLoaded = {}; // used to avoid lint complaints let profLoaded = emptyProfLoaded; // set any supplied defaults if (dfltProfLoadedVals !== undefined) { profLoaded = lodash.cloneDeep(dfltProfLoadedVals); } // copy items from profAttrs profLoaded.name = profAttrs.profName; profLoaded.type = profAttrs.profType; profLoaded.profile = lodash.cloneDeep(profAttrs); // set hard-coded defaults if (!Object.prototype.hasOwnProperty.call(profLoaded, "message")) { profLoaded.message = ""; } if (!Object.prototype.hasOwnProperty.call(profLoaded, "failNotFound")) { profLoaded.failNotFound = false; } return lodash.cloneDeep(profLoaded); } // _______________________________________________________________________ /** * Read the team configuration files (if any exist). * * @param teamCfgOpts * The optional choices related to reading a team configuration. */ readProfilesFromDisk(teamCfgOpts) { return __awaiter(this, void 0, void 0, function* () { this.mLoadedConfig = yield Config_1.Config.load(this.mAppName, Object.assign({ homeDir: utilities_1.ImperativeConfig.instance.cliHome }, teamCfgOpts)); try { if (this.mCredentials.isSecured) { yield this.mCredentials.loadManager(); } } catch (error) { throw new ProfInfoErr_1.ProfInfoErr({ errorCode: ProfInfoErr_1.ProfInfoErr.LOAD_CRED_MGR_FAILED, msg: "Failed to initialize secure credential manager", causeErrors: error }); } this.mExtendersJson = ConfigUtils_1.ConfigUtils.readExtendersJson(); this.loadAllSchemas(); }); } //_________________________________________________________________________ /** * Checks whether the credential manager will load successfully. * @returns * True if it loaded successfully, or there is no credential manager * configured in Imperative settings.json */ profileManagerWillLoad() { return __awaiter(this, void 0, void 0, function* () { if (this.mCredentials.isCredentialManagerInAppSettings()) { try { yield this.mCredentials.activateCredMgrOverride(); return true; } catch (err) { this.mImpLogger.warn("Failed to initialize secure credential manager: " + err.message); return false; } } else { return true; } }); } /** * Returns whether a valid schema was found (works for v1 and v2 configs) */ get hasValidSchema() { return this.mHasValidSchema; } /** * Gather information about the paths in osLoc * @param profile Profile attributes gathered from getAllProfiles */ getOsLocInfo(profile) { var _a; this.ensureReadFromDisk(); const osLoc = (_a = profile === null || profile === void 0 ? void 0 : profile.profLoc) === null || _a === void 0 ? void 0 : _a.osLoc; if (!(osLoc === null || osLoc === void 0 ? void 0 : osLoc.length)) return undefined; if (profile.profLoc.locType === IProfLoc_1.ProfLocType.TEAM_CONFIG) { const ret = []; for (const loc of osLoc) { for (const layer of this.mLoadedConfig.mLayers) { if (layer.path === loc) { // we found the config layer matching osLoc ret.push({ name: profile.profName, path: loc, user: layer.user, global: layer.global }); } } } return ret; } return [{ name: profile.profName, path: profile.profLoc.osLoc[0], user: undefined, global: undefined }]; } /** * Load value of secure argument from the vault. * @param arg Secure argument object */ loadSecureArg(arg) { var _a; this.ensureReadFromDisk(); let argValue; switch (arg.argLoc.locType) { case IProfLoc_1.ProfLocType.TEAM_CONFIG: if (((_a = arg.argLoc.osLoc) === null || _a === void 0 ? void 0 : _a.length) > 0 && arg.argLoc.jsonLoc != null) { for (const layer of this.mLoadedConfig.mLayers) { if (layer.path === arg.argLoc.osLoc[0]) { // we found the config layer matching arg.osLoc argValue = lodash.get(layer.properties, arg.argLoc.jsonLoc); break; } } } break; default: // not stored securely if location is ENV or DEFAULT argValue = arg.argValue; } if (argValue === undefined) { throw new ProfInfoErr_1.ProfInfoErr({ errorCode: ProfInfoErr_1.ProfInfoErr.UNKNOWN_PROP_LOCATION, msg: `Failed to locate the property ${arg.argName}` }); } return argValue; } // _______________________________________________________________________ /** * Initialize a session configuration object with the arguments * from profArgs * * @param profArgs * An array of profile argument attributes. * @param argNames * An array of argument names to load from the profile. Defaults to * all arguments that have an associated ISession property. * * @returns A session containing all of the supplied profile argument * attributes that are relevant to a session. */ static initSessCfg(profArgs, argNames) { const sessCfg = {}; // the set of names of arguments in IProfArgAttrs used in ISession const profArgNames = argNames !== null && argNames !== void 0 ? argNames : [ "host", "port", "user", "password", "rejectUnauthorized", "protocol", "basePath", "tokenType", "tokenValue" ]; for (const profArgNm of profArgNames) { // map profile argument name into a sess config property name let sessCfgNm; if (profArgNm === "host") { sessCfgNm = "hostname"; } else { sessCfgNm = profArgNm; } // for each profile argument found, place its value into sessCfg const profArg = lodash.find(profArgs, { "argName": profArgNm }); if (profArg === undefined) { // we have a default for protocol if (sessCfgNm === "protocol") { sessCfg[sessCfgNm] = rest_1.SessConstants.HTTPS_PROTOCOL; } } else { sessCfg[sessCfgNm] = profArg.argValue; } } // Cache all creds that were placed into the session config. rest_1.AuthOrder.addCredsToSession(sessCfg); return sessCfg; } // _______________________________________________________________________ /** * Ensures that ProfileInfo.readProfilesFromDisk() is called before * an operation that requires that information. */ ensureReadFromDisk() { if (this.mLoadedConfig == null) { throw new ProfInfoErr_1.ProfInfoErr({ errorCode: ProfInfoErr_1.ProfInfoErr.MUST_READ_FROM_DISK, msg: "You must first call ProfileInfo.readProfilesFromDisk()." }); } } /** * Load any profile schema objects found on disk and cache them. For team * config, we check each config layer and load its schema JSON if there is * one associated. */ loadAllSchemas() { this.mProfileSchemaCache = new Map(); // Load profile schemas for all layers let lastSchema = { path: null, json: null }; for (const layer of this.getTeamConfig().mLayers) { if (layer.properties.$schema == null) continue; const schemaUri = new url.URL(layer.properties.$schema, url.pathToFileURL(layer.path)); if (schemaUri.protocol !== "file:") { throw new ProfInfoErr_1.ProfInfoErr({ errorCode: ProfInfoErr_1.ProfInfoErr.CANT_GET_SCHEMA_URL, msg: `Failed to load schema for config file ${layer.path}: web URLs are not supported by ProfileInfo API` }); } const schemaPath = url.fileURLToPath(schemaUri); if (fs.existsSync(schemaPath)) { try { let schemaJson; if (schemaPath !== lastSchema.path) { schemaJson = jsonfile.readFileSync(schemaPath); lastSchema = { path: schemaPath, json: schemaJson }; } else { schemaJson = lastSchema.json; } const loadedSchemas = ConfigSchema_1.ConfigSchema.loadSchema(schemaJson); censor_1.Censor.setProfileSchemas(loadedSchemas); for (const { type, schema } of loadedSchemas) { this.mProfileSchemaCache.set(`${layer.path}:${type}`, schema); } } catch (error) { throw new ProfInfoErr_1.ProfInfoErr({ errorCode: ProfInfoErr_1.ProfInfoErr.LOAD_SCHEMA_FAILED, msg: `Failed to load schema for config file ${layer.path}: invalid schema file`, causeErrors: error }); } } } this.mHasValidSchema = lastSchema.path != null; } /** * Reads the `extenders.json` file from the CLI home directory. * Called once in `readProfilesFromDisk` and cached to minimize I/O operations. * @internal * @deprecated Please use `ConfigUtils.readExtendersJson` instead */ static readExtendersJsonFromDisk() { return ConfigUtils_1.ConfigUtils.readExtendersJson(); } /** * Attempts to write to the `extenders.json` file in the CLI home directory. * @returns `true` if written successfully; `false` otherwise * @internal * @deprecated Please use `ConfigUtils.writeExtendersJson` instead */ static writeExtendersJson(obj) { return ConfigUtils_1.ConfigUtils.writeExtendersJson(obj); } /** * Adds a profile type to the loaded Zowe config. * The profile type must first be added to the schema using `addProfileTypeToSchema`. * * @param {string} profileType The profile