@zowe/imperative
Version:
framework for building configurable CLIs
308 lines • 13.5 kB
JavaScript
/*
* 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.
*
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.ConfigUtils = void 0;
const fs = require("fs");
const os = require("os");
const path = require("path");
const glob = require("fast-glob");
const jsonfile = require("jsonfile");
const CredentialManagerFactory_1 = require("../../security/src/CredentialManagerFactory");
const utilities_1 = require("../../utilities");
const error_1 = require("../../error");
const LoggerManager_1 = require("../../logger/src/LoggerManager");
const LoggingConfigurer_1 = require("../../imperative/src/LoggingConfigurer");
const Logger_1 = require("../../logger/src/Logger");
const EnvironmentalVariableSettings_1 = require("../../imperative/src/env/EnvironmentalVariableSettings");
class ConfigUtils {
/**
* 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
*/
static getZoweDir() {
var _a;
const defaultHome = path.join(os.homedir(), ".zowe");
if (((_a = utilities_1.ImperativeConfig.instance.loadedConfig) === null || _a === void 0 ? void 0 : _a.defaultHome) !== defaultHome) {
utilities_1.ImperativeConfig.instance.loadedConfig = {
name: "zowe",
defaultHome,
envVariablePrefix: "ZOWE"
};
}
return utilities_1.ImperativeConfig.instance.cliHome;
}
/**
* Reads the `extenders.json` file from the CLI home directory.
* Called once in `readProfilesFromDisk` and cached to minimize I/O operations.
* @internal
* @throws If the extenders.json file cannot be created when it does not exist.
* @throws If the extenders.json file cannot be read.
*/
static readExtendersJson() {
const cliHome = utilities_1.ImperativeConfig.instance.loadedConfig != null ? utilities_1.ImperativeConfig.instance.cliHome : ConfigUtils.getZoweDir();
const extenderJsonPath = path.join(cliHome, "extenders.json");
if (!fs.existsSync(extenderJsonPath)) {
jsonfile.writeFileSync(extenderJsonPath, {
profileTypes: {}
}, { spaces: 4 });
return { profileTypes: {} };
}
else {
return jsonfile.readFileSync(extenderJsonPath);
}
}
/**
* Attempts to write to the `extenders.json` file in the CLI home directory.
* @returns `true` if written successfully; `false` otherwise
* @internal
*/
static writeExtendersJson(obj) {
try {
const extenderJsonPath = path.join(ConfigUtils.getZoweDir(), "extenders.json");
jsonfile.writeFileSync(extenderJsonPath, obj, { spaces: 4 });
}
catch (err) {
return false;
}
return true;
}
/**
* Coerces string property value to a boolean or number type.
* @param value String value
* @param type Property type defined in the schema
* @returns Boolean, number, or string
*/
static coercePropValue(value, type) {
if (type === "boolean" || type === "number") {
// For boolean or number, parse the string and throw on failure
return JSON.parse(value);
}
else if (type == null) {
// For unknown type, try to parse the string and ignore failure
try {
return JSON.parse(value);
}
catch (_a) {
return value;
}
}
else {
// For string or other type, don't do any parsing
return value.toString();
}
}
/**
* Retrieves the name of the active profile for the given type. If no such
* profile exists, returns the default name which can be used to create a new profile.
* @param profileType The type of CLI profile
* @param cmdArguments CLI arguments which may specify a profile
* @param defaultProfileName Name to fall back to if profile doesn't exist. If
* not specified, the profile type will be used.
* @returns The profile name
*/
static getActiveProfileName(profileType, cmdArguments, defaultProfileName) {
var _a;
// Look for profile name first in command line arguments, second in
// default profiles defined in config, and finally fall back to using
// the profile type as the profile name.
return (cmdArguments === null || cmdArguments === void 0 ? void 0 : cmdArguments[`${profileType}-profile`]) ||
((_a = utilities_1.ImperativeConfig.instance.config) === null || _a === void 0 ? void 0 : _a.properties.defaults[profileType]) ||
defaultProfileName || profileType;
}
/**
* Checks if partial path is equal to or nested inside full path
* @param fullPath JSON path to profile 1
* @param partialPath JSON path to profile 2
*/
static jsonPathMatches(fullPath, partialPath) {
return fullPath === partialPath || fullPath.startsWith(partialPath + ".profiles.");
}
/**
* 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.
*/
static get onlyV1ProfilesExist() {
var _a;
if ((_a = utilities_1.ImperativeConfig.instance.config) === null || _a === void 0 ? void 0 : _a.exists) {
// we have a team config
return false;
}
// look for V1 profiles in the CLI home directory
const v1ProfilePaths = glob.sync("profiles/**/*.yaml", { cwd: utilities_1.ImperativeConfig.instance.cliHome })
.filter(filename => {
// ignore meta yaml files
const { dir, name } = path.parse(filename);
return name !== path.basename(dir) + "_meta";
});
return v1ProfilePaths.length > 0;
}
/**
* Form an error message for failures to securely save a value.
* @param solution Text that our caller can supply for a solution.
* @returns ImperativeError to be thrown
*/
static secureSaveError(solution) {
let details = CredentialManagerFactory_1.CredentialManagerFactory.manager.secureErrorDetails();
if (solution != null) {
details = details != null ? details + `\n - ${solution}` : solution;
}
return new error_1.ImperativeError({
msg: "Unable to securely save credentials.",
additionalDetails: details
});
}
// _______________________________________________________________________
/**
* Perform a rudimentary initialization of some Imperative utilities.
* We must do this because VSCode apps do not typically call imperative.init.
* @internal
*/
static initImpUtils(appName) {
// create a rudimentary ImperativeConfig if it has not been initialized
if (utilities_1.ImperativeConfig.instance.loadedConfig == null) {
let homeDir = null;
const envVarPrefix = appName.toUpperCase();
const envVarNm = envVarPrefix + EnvironmentalVariableSettings_1.EnvironmentalVariableSettings.CLI_HOME_SUFFIX;
if (process.env[envVarNm] === undefined) {
// use OS home directory
homeDir = path.join(os.homedir(), "." + appName.toLowerCase());
}
else {
// use the available environment variable
homeDir = path.normalize(process.env[envVarNm]);
}
utilities_1.ImperativeConfig.instance.loadedConfig = {
name: appName,
defaultHome: homeDir,
envVariablePrefix: envVarPrefix
};
utilities_1.ImperativeConfig.instance.rootCommandName = appName;
}
// initialize logging
if (LoggerManager_1.LoggerManager.instance.isLoggerInit === false) {
const loggingConfig = LoggingConfigurer_1.LoggingConfigurer.configureLogger(utilities_1.ImperativeConfig.instance.cliHome, utilities_1.ImperativeConfig.instance.loadedConfig);
Logger_1.Logger.fromLog4jsToWinston(loggingConfig);
}
return Logger_1.Logger.getImperativeLogger();
}
// _______________________________________________________________________
/**
* Form a profile name of a given profile type to be used as a default
* profile name. The name can vary based on whether the configuration to
* contain the profile is a global config or a project config.
*
* Currently, we only form a different global/project profile name for
* a base profile. The profile name for any other profile type is currently
* set to the profile type string.
*
* @param profileType
* The profile type for which we will form a name.
*
* @param globalConfig
* Indicator that the caller knows that the profile name will be
* for a globalConfig (true) or project config (false).
* If globalConfig is not supplied, we interrogate any existing
* Config object to determine whether to form a global or project
* profile name.
*
* @returns
* A string to be used as the profile name for the specified profile type.
*/
static formGlobOrProjProfileNm(profileType, globalConfig = null) {
if (profileType !== "base") {
// everything except base profiles use profile type as the profile name
return profileType;
}
// were we told that this is for a global or project config?
if (globalConfig === true) {
return `global_${profileType}`;
}
else if (globalConfig === false) {
return `project_${profileType}`;
}
else {
// determine from existing config whether the profile is intended for a project config
const existingConfig = utilities_1.ImperativeConfig.instance.config;
for (const nextLayer of existingConfig.layers) {
// if desired profile type exists in the project layer, it wins
if (nextLayer.global === false) {
if (ConfigUtils.findProfTypeInNestedProfiles(profileType, existingConfig.layerProfiles(nextLayer))) {
return `project_${profileType}`;
}
}
}
}
// since we did not find the profile type at the project layers, return a global name
return `global_${profileType}`;
}
// _______________________________________________________________________
/**
* Find the specified profile type in the specified (or nested) profiles.
*
* @param profileType
* The profile type to search for.
*
* @param profilesObj
* The profile object in which we should search.
*
* @returns
* True if we find the profile type. False otherwise.
*/
static findProfTypeInNestedProfiles(profileType, profilesObj) {
for (const nextProfileObj of Object.values(profilesObj)) {
if ((nextProfileObj === null || nextProfileObj === void 0 ? void 0 : nextProfileObj.type) === profileType) {
return true;
}
// The specified type was not in nextProfileObj. Recursively look in its nested profiles.
if (nextProfileObj === null || nextProfileObj === void 0 ? void 0 : nextProfileObj.profiles) {
if (ConfigUtils.findProfTypeInNestedProfiles(profileType, nextProfileObj.profiles)) {
return true;
}
}
}
return false;
}
/**
* Checks if the given token has expired. Supports JSON web tokens only.
*
* @param {string} token - The JSON web token to check
* @returns {boolean}
* Whether the token has expired. Returns `false` if the token cannot
* be decoded or an expire time is not specified in the payload.
*/
static hasTokenExpired(token) {
// JWT format: [header].[payload].[signature]
const tokenParts = token.split(".");
try {
const payloadJson = JSON.parse(Buffer.from(tokenParts[1], "base64url").toString("utf8"));
if ("exp" in payloadJson) {
// The expire time is stored in seconds since UNIX epoch.
// The Date constructor expects a timestamp in milliseconds.
const msPerSec = 1000;
const expireDate = new Date(payloadJson.exp * msPerSec);
return expireDate < new Date();
}
}
catch (err) {
return false;
}
return false;
}
}
exports.ConfigUtils = ConfigUtils;
//# sourceMappingURL=ConfigUtils.js.map
;