@zowe/imperative
Version:
framework for building configurable CLIs
1,033 lines • 80.8 kB
JavaScript
"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