@zowe/cli
Version:
Zowe CLI is a command line interface (CLI) that provides a simple and streamlined way to interact with IBM z/OS.
499 lines • 25.3 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 });
const path = require("path");
const lodash = require("lodash");
const zosmf_for_zowe_sdk_1 = require("@zowe/zosmf-for-zowe-sdk");
const imperative_1 = require("@zowe/imperative");
const core_for_zowe_sdk_1 = require("@zowe/core-for-zowe-sdk");
/**
* This class is used by the auth command handlers as the base class for their implementation.
*/
class ApimlAutoInitHandler extends imperative_1.BaseAutoInitHandler {
constructor() {
super(...arguments);
/**
* The profile type where token type and value should be stored
*/
this.mProfileType = "base";
/**
* The description of your service to be used in CLI prompt messages
*/
this.mServiceDescription = "your API Mediation Layer";
this.NO_CHANGES_MSG = "No changes to";
this.CREATED_MSG = "Created";
this.MODIFIED_MSG = "Modified";
this.REMOVED_MSG = "Removed";
this.WARNING_MSG = "Warning:";
/**
* This structure is populated during convertApimlProfileInfoToProfileConfig
* and retrieved by the auto-init command handler to provide the data for the
* output of the auto-init command.
* @private
*/
this.mAutoInitReport = {
configFileNm: "",
changeForConfig: this.NO_CHANGES_MSG,
startingConfig: null,
endingConfig: null,
profileRpts: []
};
/**
* This is called by the {@link BaseAuthHandler#process} when it needs a
* session. Should be used to create a session to connect to the auto-init
* service.
* @param {ICommandArguments} args The command line arguments to use for building the session
* @returns {ISession} The session object built from the command line arguments.
*/
this.createSessCfgFromArgs = zosmf_for_zowe_sdk_1.ZosmfSession.createSessCfgFromArgs;
}
/**
* This is called by the "auto-init" command after it creates a session, to generate a configuration
* @param {AbstractSession} session The session object to use to connect to the configuration service
* @returns {Promise<string>} The response from the auth service containing a token
* @throws {ImperativeError}
*/
doAutoInit(session, params) {
return __awaiter(this, void 0, void 0, function* () {
var _a, _b, _c;
// Login with token authentication first, so we can support certificates
// eslint-disable-next-line no-extra-parens
if ((session.ISession.user && session.ISession.password) || (session.ISession.cert && session.ISession.certKey)) {
// If it is basic authentication, we need to set the auth type.
if (session.ISession.tokenType == null) {
session.ISession.tokenType = imperative_1.SessConstants.TOKEN_TYPE_APIML;
}
session.ISession.storeCookie = true;
session.ISession.tokenValue = yield core_for_zowe_sdk_1.Login.apimlLogin(session);
session.ISession.storeCookie = false;
// now that we requested a token, remove any request-for-token from the session
if (session.ISession._authCache) {
delete session.ISession._authCache.authTypeToRequestToken;
}
// now that we have a token, cache the token for our next REST request
imperative_1.AuthOrder.addCredsToSession(session.ISession, { "$0": "NameNotUsed", "_": [],
"authOrder": "token",
"tokenType": imperative_1.SessConstants.TOKEN_TYPE_APIML,
"tokenValue": session.ISession.tokenValue
});
}
const restErrUnauthorized = 403;
const configs = core_for_zowe_sdk_1.Services.getPluginApimlConfigs();
let profileInfos;
try {
profileInfos = yield core_for_zowe_sdk_1.Services.getServicesByConfig(session, configs);
}
catch (err) {
if (err instanceof imperative_1.RestClientError && err.mDetails && err.mDetails.httpStatus && err.mDetails.httpStatus === restErrUnauthorized) {
throw new imperative_1.ImperativeError({
msg: "HTTP(S) error status 403 received. Verify the user has access to the APIML API Services SAF resource.",
additionalDetails: err.mDetails.additionalDetails,
causeErrors: err
});
}
else {
throw err;
}
}
const profileConfig = core_for_zowe_sdk_1.Services.convertApimlProfileInfoToProfileConfig(profileInfos);
const config = imperative_1.ImperativeConfig.instance.config;
// Check to see if there is an active base profile to avoid creating a new one named "base"
let activeBaseProfile = ((_a = params.arguments) === null || _a === void 0 ? void 0 : _a[`${this.mProfileType}-profile`]) || config.properties.defaults[this.mProfileType];
let baseProfileCreated = false;
// Populate the config with base profile information
if (activeBaseProfile == null) {
// Name our base profile differently in a global config vs a project config
const globalConfig = ((_b = params.arguments) === null || _b === void 0 ? void 0 : _b.globalConfig) ? true : false;
activeBaseProfile = imperative_1.ConfigUtils.formGlobOrProjProfileNm(this.mProfileType, globalConfig);
profileConfig.profiles[activeBaseProfile] = {
type: this.mProfileType,
properties: {
host: session.ISession.hostname,
port: session.ISession.port,
rejectUnauthorized: session.ISession.rejectUnauthorized
},
secure: []
};
baseProfileCreated = true;
}
else {
const oldBaseProfile = this.getOldBaseProfileProps(config, activeBaseProfile);
lodash.set(profileConfig, config.api.profiles.getProfilePathFromName(activeBaseProfile), {
type: this.mProfileType,
properties: Object.assign(Object.assign({}, oldBaseProfile.properties), { host: session.ISession.hostname, port: session.ISession.port, rejectUnauthorized: session.ISession.rejectUnauthorized }),
secure: (_c = oldBaseProfile.secure) !== null && _c !== void 0 ? _c : []
});
}
if (session.ISession.tokenType != null && session.ISession.tokenValue != null) {
const baseProfileConfig = lodash.get(profileConfig, config.api.profiles.getProfilePathFromName(activeBaseProfile));
baseProfileConfig.properties.tokenType = session.ISession.tokenType;
baseProfileConfig.properties.tokenValue = session.ISession.tokenValue;
if (!baseProfileConfig.secure.includes("tokenValue")) {
baseProfileConfig.secure.push("tokenValue");
}
}
profileConfig.defaults[this.mProfileType] = activeBaseProfile;
this.recordProfilesFound(profileInfos);
// Report whether or not we created a base profile in this auto-init execution
this.mAutoInitReport.profileRpts.push({
profName: activeBaseProfile,
profType: this.mProfileType,
changeForProf: baseProfileCreated ? "created" : "modified",
basePath: null,
pluginNms: [],
altProfiles: [],
baseOverrides: []
});
return profileConfig;
});
}
/**
* This is called by our processAutoInit() base class function to display the set of actions
* taken by the auto-init command.
*/
displayAutoInitChanges(response) {
// all profile updates have been made. Now we can record those updates.
this.recordProfileUpdates();
if (this.mAutoInitReport.changeForConfig === this.NO_CHANGES_MSG) {
response.console.log("No changes were needed in the existing Zowe configuration file '" +
path.basename(this.mAutoInitReport.configFileNm) + "'.");
return;
}
// Report the type of config file changes
response.console.log(this.colorizeKeyword(this.mAutoInitReport.changeForConfig) +
" the Zowe configuration file '" +
path.basename(this.mAutoInitReport.configFileNm) + "'.");
// display information about each profile
for (const nextProfRpt of this.mAutoInitReport.profileRpts) {
let defOrAlt;
const isDefaultProf = this.mAutoInitReport.endingConfig.properties.defaults[nextProfRpt.profType] ===
nextProfRpt.profName;
if (isDefaultProf) {
defOrAlt = "default";
}
else {
defOrAlt = "alternate";
}
let msg;
msg = "\n" + this.colorizeKeyword(nextProfRpt.changeForProf) + " " + defOrAlt +
" profile '" + nextProfRpt.profName + "' of type '" + nextProfRpt.profType + "'";
if (nextProfRpt.profType !== "base") {
msg += " with basePath '" + nextProfRpt.basePath + "'";
}
response.console.log(msg);
// only report plugins and alternates for the default profile of each type
if (!isDefaultProf) {
continue;
}
// report plugins using this profile (except for base profiles)
let loopCount;
if (nextProfRpt.pluginNms.length > 0) {
if (nextProfRpt.profType !== this.mProfileType) {
loopCount = 1;
for (const pluginNm of nextProfRpt.pluginNms) {
if (loopCount == 1) {
msg = " Packages that use profile type '" + nextProfRpt.profType + "': ";
}
else {
msg += ", ";
}
msg += pluginNm;
loopCount++;
}
response.console.log(msg);
}
}
// display the alternate profiles
if (nextProfRpt.altProfiles.length > 0) {
loopCount = 1;
for (const altProf of nextProfRpt.altProfiles) {
if (loopCount == 1) {
msg = " Alternate profiles of type '" + nextProfRpt.profType + "': ";
}
else {
msg += ", ";
}
msg += altProf.altProfName;
loopCount++;
}
response.console.log(msg);
}
if (nextProfRpt.baseOverrides.length > 0) {
const baseProfileName = this.mAutoInitReport.endingConfig.properties.defaults.base;
msg = ` ${this.colorizeKeyword(this.WARNING_MSG)} This profile may require manual edits to work with APIML:`;
for (const baseOverride of nextProfRpt.baseOverrides) {
msg += `\n ${baseOverride.propName}: `;
if (!baseOverride.secure) {
msg += `'${baseOverride.priorityValue}' overrides '${baseOverride.baseValue}' in`;
}
else {
msg += "secure value " + (baseOverride.priorityValue != null ? "overrides" : "may override");
}
msg += ` profile '${baseProfileName}'`;
}
response.console.log(msg);
}
}
response.console.log("\nYou can edit this configuration file to change your Zowe configuration:\n " +
this.mAutoInitReport.configFileNm);
}
/**
* Colorize a change keyword for a message to be displayed.
*
* @param {string} changeKeyWd
* The keyword that we want to colorize.
*
* @returns {string} A string with the keyword in the appropriate color.
*/
colorizeKeyword(changeKeyword) {
let keywordInColor;
switch (changeKeyword) {
case this.CREATED_MSG:
case this.MODIFIED_MSG:
keywordInColor = imperative_1.TextUtils.chalk.greenBright(changeKeyword);
break;
case this.REMOVED_MSG:
keywordInColor = imperative_1.TextUtils.chalk.redBright(changeKeyword);
break;
case this.WARNING_MSG:
keywordInColor = imperative_1.TextUtils.chalk.yellowBright(changeKeyword);
break;
default:
keywordInColor = changeKeyword;
break;
}
return keywordInColor;
}
/**
* Record the set of profiles found by our interrogation of plugins and APIML.
* The information is re-arranged to enable easy reporting of the desired information.
* This function assumes that the 'services' module continues to use its algorithm
* in which the first profile of a given type is the profile that we select..
* If that changes, this function must also change.
*
* @param {IApimlProfileInfo} apimlProfInfos
* The profileInfo array returned by services.getServicesByConfig().
*/
recordProfilesFound(apimlProfInfos) {
// record our starting config
if (imperative_1.ImperativeConfig.instance.config.exists) {
this.mAutoInitReport.startingConfig = imperative_1.ImperativeConfig.instance.config.api.layers.get();
}
else {
this.mAutoInitReport.startingConfig = null;
}
// Record the profiles found by APIML for each profile type
for (const currProfInfo of apimlProfInfos) {
const newProfRpt = {
changeForProf: this.NO_CHANGES_MSG,
profName: currProfInfo.profName,
profType: currProfInfo.profType,
basePath: currProfInfo.basePaths[0],
pluginNms: [],
altProfiles: [],
baseOverrides: []
};
// add all of the plugins using this profile
for (const nextPlugin of currProfInfo.pluginConfigs) {
newProfRpt.pluginNms.push(nextPlugin.pluginName);
}
// each additional basePath for the current plugin is an alternate
for (let basePathInx = 1; basePathInx < currProfInfo.basePaths.length; basePathInx++) {
newProfRpt.altProfiles.push({
altProfName: currProfInfo.profName,
altProfType: currProfInfo.profType,
altBasePath: currProfInfo.basePaths[basePathInx]
});
}
// each of the other profiles of the same profile type is an alternate
for (const nextProfInfoOfType of apimlProfInfos) {
if (nextProfInfoOfType.profName !== currProfInfo.profName &&
nextProfInfoOfType.profType === currProfInfo.profType) {
// each basePath constitutes another alternate
for (const nextBasePath of nextProfInfoOfType.basePaths) {
newProfRpt.altProfiles.push({
altProfName: nextProfInfoOfType.profName,
altProfType: nextProfInfoOfType.profType,
altBasePath: nextBasePath
});
}
}
}
this.mAutoInitReport.profileRpts.push(newProfRpt);
}
}
/**
* Record how the profiles have been updated by auto-init.
*/
recordProfileUpdates() {
// get our current (ending) config
this.mAutoInitReport.endingConfig = imperative_1.ImperativeConfig.instance.config.api.layers.get();
// record the config file name path
this.mAutoInitReport.configFileNm = this.mAutoInitReport.endingConfig.path;
this.mAutoInitReport.changeForConfig = this.NO_CHANGES_MSG;
if (this.mAutoInitReport.startingConfig === null) {
// We started with no config file, so everything was created.
this.mAutoInitReport.changeForConfig = this.CREATED_MSG;
for (const nextProfRpt of this.mAutoInitReport.profileRpts) {
nextProfRpt.changeForProf = this.CREATED_MSG;
}
}
else {
/* We must compare starting config to ending config to determine
* if we created or updated individual profiles.
*/
const startCfgLayer = this.mAutoInitReport.startingConfig;
const endCfgLayer = this.mAutoInitReport.endingConfig;
if (!startCfgLayer.exists && endCfgLayer.exists) {
// the starting config file existed, but was in a different layer
if (this.mAutoInitReport.changeForConfig === this.NO_CHANGES_MSG) {
this.mAutoInitReport.changeForConfig = this.CREATED_MSG;
// each profile in this previously non-existent layer has been created
for (const nextProfRpt of this.mAutoInitReport.profileRpts) {
nextProfRpt.changeForProf = this.CREATED_MSG;
}
}
}
else {
/* We must compare profile-by-profile.
* Look for each profile from the ending config within the starting config
*/
for (const endProfNm of lodash.keys(endCfgLayer.properties.profiles)) {
if (lodash.has(startCfgLayer.properties.profiles, endProfNm)) {
if (lodash.isEqual(startCfgLayer.properties.profiles[endProfNm], endCfgLayer.properties.profiles[endProfNm])) {
// both starting profile and ending profile are the same
this.recordOneProfChange(endProfNm, endCfgLayer.properties.profiles[endProfNm], this.NO_CHANGES_MSG);
}
else {
this.recordOneProfChange(endProfNm, endCfgLayer.properties.profiles[endProfNm], this.MODIFIED_MSG);
}
}
else {
/* Each profile in the ending config that is not
* in the starting config has been created.
*/
this.recordOneProfChange(endProfNm, endCfgLayer.properties.profiles[endProfNm], this.CREATED_MSG);
}
}
// Look for each profile from the staring config within the ending config
for (const startProfNm of lodash.keys(startCfgLayer.properties.profiles)) {
/* We already recorded a message for profiles that exist in both
* the starting and ending configs in the loop above,
* so just record when the starting profile has been removed.
*/
if (!lodash.has(endCfgLayer.properties.profiles, startProfNm)) {
this.recordOneProfChange(startProfNm, endCfgLayer.properties.profiles[startProfNm], this.REMOVED_MSG);
}
}
this.recordProfileConflictsWithBase();
}
}
}
/**
* Record the change message for one profile with the
* this.mAutoInitReport.profileRpts array.
*
* @param {string} profNmToRecord
* The name of the profile for which we want to record a change.
*
* @param {IConfigProfile} profObj
* An IConfigProfile object which is used when a new entry must be
* created in the profileRpts array.
*
* @param {string} msgToRecord
* The message to record for the type of change to this profile.
*/
recordOneProfChange(profNmToRecord, profObj, msgToRecord) {
// when any profile has been modified, we know the config was modified
if (msgToRecord !== this.NO_CHANGES_MSG) {
this.mAutoInitReport.changeForConfig = this.MODIFIED_MSG;
}
const profRptInx = lodash.findIndex(this.mAutoInitReport.profileRpts, { profName: profNmToRecord });
if (profRptInx >= 0) {
// an entry for this profile already exists
this.mAutoInitReport.profileRpts[profRptInx].changeForProf = msgToRecord;
}
else {
// we must create a new IProfileRpt entry
const newProfRpt = {
changeForProf: msgToRecord,
profName: profNmToRecord,
profType: profObj.type,
basePath: lodash.get(profObj, "properties.basePath", "Not supplied"),
pluginNms: [],
altProfiles: [],
baseOverrides: []
};
this.mAutoInitReport.profileRpts.push(newProfRpt);
}
}
/**
* Record info about profile properties that override properties defined in
* the base profile. These properties may prevent connecting to the APIML.
*/
recordProfileConflictsWithBase() {
var _a, _b;
const configApi = imperative_1.ImperativeConfig.instance.config.api;
const configJson = this.mAutoInitReport.endingConfig.properties;
const baseProfileName = (_a = configJson.defaults) === null || _a === void 0 ? void 0 : _a.base;
if (baseProfileName == null) {
return; // default base profile is undefined
}
const baseProfile = lodash.get(configJson, configApi.profiles.getProfilePathFromName(baseProfileName));
if (baseProfile == null) {
return; // default base profile is invalid
}
for (const profileRpt of this.mAutoInitReport.profileRpts) {
if (profileRpt.changeForProf === this.MODIFIED_MSG && profileRpt.profType !== "base") {
const serviceProfile = lodash.get(configJson, configApi.profiles.getProfilePathFromName(profileRpt.profName));
for (const [name, value] of Object.entries(baseProfile.properties)) {
if (serviceProfile.properties[name] != null && serviceProfile.properties[name] !== baseProfile.properties[name]) {
profileRpt.baseOverrides.push({
propName: name,
secure: (baseProfile.secure || []).includes(name) || (serviceProfile.secure || []).includes(name),
priorityValue: serviceProfile.properties[name],
baseValue: value
});
}
}
for (const name of baseProfile.secure || []) {
if ((_b = serviceProfile.secure) === null || _b === void 0 ? void 0 : _b.includes(name)) {
profileRpt.baseOverrides.push({ propName: name, secure: true });
}
}
}
}
}
getOldBaseProfileProps(config, baseProfileName) {
const propsToRemove = ["user", "password", "certFile", "certKeyFile"];
const properties = config.api.profiles.get(baseProfileName);
for (const propName of propsToRemove) {
properties[propName] = undefined;
}
const secureProps = config.api.secure.securePropsForProfile(baseProfileName);
return {
properties,
secure: secureProps.filter((propName) => !propsToRemove.includes(propName))
};
}
}
exports.default = ApimlAutoInitHandler;
//# sourceMappingURL=ApimlAutoInitHandler.js.map