Token handling in Zowe CLI plug-ins
Introduction
{Core}
In the article titled Using tokens in the Zowe CLI, I describe how and why tokens are used in the Zowe CLI. In this article, I describe how Zowe CLI contributors can modify their Zowe CLI plug-ins to use authentication tokens.
All core Zowe commands have already been modified to work with tokens. For a Zowe CLI plug-in to gain token-handling capabilities, the plug-in development team must make the programming modifications described below.
Some profile options should no longer be required
The following options can be stored in a base profile: host, port, user, password, rejectUnauthorized, tokenType, and tokenValue.
If you want any of these options to be picked up from your base profile, any such option should no longer be “required” in your service profile definition.
For example, perhaps you only work with one mainframe host. It might be convenient to place that host name in your base profile, so that it is used with every service (thus every command). If host name is a required option in your plug-in’s service profile, then you must supply a host name when creating your plug-in’s profile. Thus, you will still have duplicate host names (in your service profile and your base profile). Further, since a plug-in’s service profile takes precedence over the base profile, you will use the host name from your plug-in’s profile instead of your common, base profile.
To allow the base profile to supply the host name, you must remove the “required” property from the host option in your plug-in’s profile definition, and remove the host name option from any existing service profiles.
The following snippet from the zosmf profile definition shows the modification which was made to ensure that host name is no longer required for the zosmf profile.
profiles:
{
type: “zosmf”,
schema: {
type: “object”,
title: “z/OSMF Profile”,
description: “z/OSMF Profile”,
properties: {
host: {
type: “string”,
optionDefinition: ZosmfSession.ZOSMF_OPTION_HOST,
},
...
},
required: ["host"],
The last line above was simply changed to:
required: [],
End users can still place a host name in your plug-in’s profile if they have a reason to override the value in the base profile. Perhaps every command group at a site connects to the APIML service, except for one plug-in that has not yet been modified to use tokens. By placing the host name and port of that plug-in’s service in the plug-in’s profile, that plug-in will use a direct connection to the specific service’s host instead of the APIML host.
Since the Imperative framework will automatically prompt for host name, port, user name, and password if they have not been supplied to a command, that makes these options particularly good candidates for the removal of the “required” property. However, you can remove the required property from any of the options which can be stored within base profiles. Once such an option is no longer required, it can be specified in a base profile and its value will be the fall-back value for all commands.
One advantage of an option being required, is that an error is generated when a required option is not specified. If you remove the required property from an option (other than the ones above which automatically prompt the user), you may need to add programming logic to your plug-in to confirm that a value has been specified for that option.
Your app can prohibit prompting
We believe that most applications will want to automatically prompt if a command is missing one of the options for which Imperative will automatically prompt. Thus, the choice to prompt is our default behavior. Alternatively, you can prohibit such prompting by supplying the “doPrompting: false” option to the “options” parameter of the ConnectionPropsForSessCfg.addPropsOrPrompt() function. We will discuss this function a bit more later.
The most likely use-case for not prompting is if your application is a GUI app and it provides a pop-up dialog to obtain the desired values from a user.
How to ensure that your plug-in uses tokens
When a plug-in command makes a REST service call, it first creates a session for the REST service. Numerous properties can be specified in a session. Some properties are unique to the particular plug-in service. To authenticate with a token, four properties are required from every plug-in command handler:
- The host name of the APIML server
- The port number if the APIML server
- A tokenType value which specifies that an APIML token is being used
- The value of the token itself
A new Imperative utility exists to help a command handler set these properties into a session.
Previously, most commands formed a session configuration object with the desired properties, and then used that configuration to create a session. The command then made a REST call with that session. The actions to create a session were something like the following.
const sessCfg: ISession = {
hostname: commandParameters.arguments.host,
port: commandParameters.arguments.port
user: commandParameters.arguments.user,
password: commandParameters.arguments.password,
rejectUnauthorized: commandParameters.arguments.rejectUnauthorized,
basePath: commandParameters.arguments.basePath
};
mySession = new Session(sessCfg);
A command will now create a session configuration with no connection information, call the new ConnectionPropsForSessCfg.addPropsOrPrompt() utility function to add the connection properties to the session, and then create the session.
const sessCfg: ISession = {
rejectUnauthorized: commandParameters.arguments.rejectUnauthorized,
basePath: commandParameters.arguments.basePath
};const connectableSessCfg = await
ConnectionPropsForSessCfg.addPropsOrPrompt<ISession>(
sessCfg, commandParameters.arguments);mySession = new Session(connectableSessCfg);
The addPropsOrPrompt function will obtain host, port, user, password, token, and tokenType from the Zowe order of precedence and adds them as appropriate to the session configuration.
- It adds host and port to the session configuration. If either is missing, it will prompt for them.
- If a user name is supplied, then it adds user name and password to the configuration. It will prompt for password if password is missing. In this case, a token is not used for authentication.
- If neither user name or token value are supplied, it will prompt for user name (and password if not supplied), and add the user and password to the configuration. Again, a token is not used for authentication.
- If no user name is supplied, but a token is supplied, it will add the token value to the configuration. Thus to use tokens, the end user must not supply a user name to the command (including any existing profile value).
- If a tokenType is supplied, it is added to the session configuration. If no tokenType is supplied, it implies that a bearer token should be used. The tokenType used by all core Zowe commands is apimlAuthenticationToken.
The addPropsOrPrompt function adds the correct combination of connection properties to a session to enable the Zowe REST client API to appropriately use basic authentication, cookie token authentication, or bearer token authentication in its request.
Plug-ins that make these programming modifications will not only obtain automatic token-handling, but will also obtain automatic prompting for the four options listed previously.
Release compatibility of token modifications
Any plug-in that does not re-tool for token handling, will continue to work as before. It simply will not be able to perform token authentication.
The token handling modifications are backward-compatible and non-breaking. Thus, plug-ins that are modified to handle authentication tokens will be compatible with both the @latest and @zowe-v1-lts NPM tags of Zowe CLI and Imperative.
Learn more about Zowe at this site
More Zowe blogs here