UNPKG

@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
"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