From 8e6874d4e18919a5806c5dc057a60b3295bfce21 Mon Sep 17 00:00:00 2001 From: Gene Johnston Date: Wed, 9 Oct 2024 12:34:19 -0400 Subject: [PATCH 01/25] Add design document. Signed-off-by: Gene Johnston --- docs/Design_for_selecting_auth_type.md | 199 +++++++++++++++++++++++++ 1 file changed, 199 insertions(+) create mode 100644 docs/Design_for_selecting_auth_type.md diff --git a/docs/Design_for_selecting_auth_type.md b/docs/Design_for_selecting_auth_type.md new file mode 100644 index 0000000000..4ee6158433 --- /dev/null +++ b/docs/Design_for_selecting_auth_type.md @@ -0,0 +1,199 @@ +# Order of authentication in Zowe Clients + +This document identifies a design to enable users to specify the order in which credentials are selected for authentication when multiple credentials are specified by the user. + +Users may not intentionally specify multiple credentials for the same operation. However, because configuration properties can be inherited from a base profile or within nested profiles, it is possible that multiple credentials may be available when a Zowe client attempts to make a connection to a mainframe service. + +## Use cases + +A user may use the same user and password to authenticate to most of their services. It makes sense to place that user & password in a base profile so that they are automatically available to every service. This reduces redundancy and reduces maintenance efforts. If one plugin requires a token for authentication, the user would store a token value within that plugin's profile. For that plugin, both the user & password and a token will be available when Zowe CLI attempts to make a connection to that service. For this example, the token is the right choice for this service. For historical reasons, Zowe always selects a user & password over a token when both are available. The use of user & password does not give the desired results. + +When a site gradually deploys API-ML, they will very likely encounter another (but opposite) authentication issue. Sites login to APIML to obtain a token, which is then used to authenticate all future requests to services through API-ML. The API-ML token is typically stored in a base profile so that connections to all services are done through API-ML with its token. When a new service is brought on-line at a site, it is common for that service to not be immediately integrated with APIML. For at least a period of time, the site makes a direct connection to that service. The site adds user/password properties to that service's profile to authenticate that direct connection. Once again, both user & password and a token are available when Zowe attempts to connect to the service for that profile. In this case, the user & password are the right choice for this service. This is the opposite choice from the previous example. + +As these examples demonstrate, Zowe cannot simply change which authentication type should be given preference. It varies based on what a site is trying to do. That order might also change from one part of the customer's configuration to another. The preferred order in which credentials are chosen is further complicated when we consider that certificates may also be used by a site for some of its services. + +## General approach for a solution + +In this section we identify the key features that a solution would have to provide. + +- The order in which different types of authentication are used should be controlled by the user in the Zowe client configuration. + +- The user must be able to change that authentication order for different parts of the Zowe client configuration. + +- Zowe client logic must be enhanced to select the right authentication type for a profile being used to make a connection. + +- Zowe client extenders (CLI plugins and ZE extensions) should **not** be able to alter the order of authentication types. The user should control that choice. + +## Detailed requirements + +- If a user does not specify the order of authentication, Zowe should use the historical order of authentication so that we do not introduce a breaking change. + +- Zowe has an order of precedence for obtaining property values (config file, environment variable, command line). While a command line option will override the same property stored in a config file, it should not alter the order of authentication. + + - For example, if a user specifies a token value on the command line, it will override a token value stored in a config file. However, + + - If the user has specified that a certificate should be used before a token, and a certificate is also available in the configuration, the certificate should be used because the user configured that certificates are used before tokens. + + - The token should not be used just because it was supplied on the command line. + +- The authentication order only identifies the order in which Zowe chooses the **one** authentication method that will be used. If that first authentication method fails, Zowe will not make a second attempt to authenticate with any of the later authentication methods. + +- Once an auth type is selected, our logic should ensure that only that one auth type is placed into a session object. Thus, logic in down-stream handlers will not alter the order of auth selection, simply by testing for the auth types within the session in a different order than the order that the user specified. + + - If we continue to allow multiple authTypes to be placed into a session, we will have to re-work various functions to test for authType in the desired order. We will also have to provide the object containing that well-defined order to each such function. This will increase the amount of code being changed, and thus increase the probability of mistakes. + +- Zowe clients do not currently support AUTH_TYPE_CERT_PFX, so we cannot add it to a set of available authTypes. If AUTH_TYPE_CERT_PFX is implemented, it should be placed immediately after AUTH_TYPE_CERT_PEM in the default order. + +- Propose that AUTH_TYPE_NONE gets a place in our list of possible authentication types + +- ## Historical behavior + +The objective of this feature is to enable users to define the order in which an authentication type is selected. However, when a user does not specify any such order, the default order should reflect past behavior. + +- The AbstractRestClient currently enforces an order of: + + - AUTH_TYPE_TOKEN + + - AUTH_TYPE_BASIC + + - AUTH_TYPE_BEARER + + - AUTH_TYPE_CERT_PEM + +- All Zowe functions down-stream from AbstractRestClient explicitly override the authentication order from AbstractRestClient to be: + + - AUTH_TYPE_BASIC + + - AUTH_TYPE_BEARER + + - AUTH_TYPE_TOKEN + + - AUTH_TYPE_CERT_PEM + + - Confirm this order + + - Describe how AUTH_TYPE_NONE gets set + +These selections of authentication should be maintained as the default selections for their respective classes to avoid introducing a breaking change. + +## Configuration enhancement + +- authTypeOrder config property +- Levels in config where authType order can reside + +## Functions that are candidates for modification + +The set of candidates for modification consist of all functions that contain the string ***"AUTH_TYPE_"***. This section contains an assessment of whether each such function affects the authentication order. Based on this analysis, those functions that warrant modification are identified in the "Proposed software modifications" section of this document. + +- cli\src\config\auto-init\ApimlAutoInitHandler + + - doAutoInit - This function logins into APIML with the session object if either user & password or cert are in the session object. doAutoInit does not make a selection of order. It lets Login.apimlLogin() make that decision. If only the selected authType is placed into a session, + - **Modify doAutoInit ? No** + +- core\src\rest\ZosmfRestClient.ts + + - processError - This function just alters error message text based on auth types found in the session. If only the selected authType is placed into a session, + - **Modify processError ? No** + +- imperative\src\config\src\ConfigAutoStore.ts + + - _fetchTokenForSessCfg - Since this function is used to explicitly retrieve a token value to be auto-stored into a session config, its use of AUTH_TYPE_TOKEN does not affect the auth order. So, + - **Modify _fetchTokenForSessCfg ? No** + +- imperative\src\imperative\src\config\cmd\import\import.handler.ts + + - buildSession - This function is used to import a config from a URL. That URL is an arbitrary location at a customer site where a config file is kept. It is not the target of a REST request to a mainframe service. By design, the only auth type that it will use is user & password. Supporting more authentication types in the 'import' command is beyond the scoope of this authentication-order feature. Therefore, + - **Modify buildSession ? No** + +- imperative\src\rest\src\client\AbstractRestClient.ts + + This class is the only class to use the recently created ISession.authTypeOrder property, which is an arry of authentication types supplied in the order in which they should be selected. + + - buildOptions - This function tests for the authType based on the order in which they occur in ISession.authTypeOrder. Therefore, + + - **Modify buildOptions ? No** + + - constructor - This function hard-codes the order of authentication types into the ISession.authTypeOrder array. We must create a means to provide the customer-defined order to this function. This function should only use its hard-coded order (as the default order) if a customers does not specify an order. + + - That default order should be: + + - AUTH_TYPE_TOKEN + + - AUTH_TYPE_BASIC + + - AUTH_TYPE_BEARER + + - AUTH_TYPE_CERT_PEM + + - AUTH_TYPE_CERT_PFX + + - AUTH_TYPE_NONE + + - Confirm this order + + - **Modify constructor? yes** + + - Each of the following functions reference AUTH_TYPE_ to a place an identified type into the ISession.type property. Since buildOptions calls just one of the following functions based on being the first available authType in the ISession.authTypeOrder array, none of these functions need to change. + + - **Modify setBearerAuth ? No** + + - **Modify setCertPemAuth ? No** + + - **Modify setPasswordAuth ? No** + + - **Modify setTokenAuth ? No** + +- imperative\src\rest\src\session\AbstractSession.ts + + - buildSession - This function accepts a session. It already confirms that a valid authType has already been set in the session. It then populates some session properties based on the authType that it finds. Other new logic, which ensures that only the selected authType is placed into a session, should have no adverse affect on this function. So, + + - **Modify buildSession ? No** + + - DEFAULT_TYPE - This simply a constant definition set to AUTH_TYPE_NONE. It is not used in any CLI or ZE code outside of this AbstractSession class. Because it is a public property, it cannot be removed without risk of breaking change. If AUTH_TYPE_NONE is added to the ISession.authTypeOrder array, DEFAULT_TYPE should be deprecated, so + + - **Modify DEFAULT_TYPE ? Yes** + +- imperative\src\rest\src\session\ConnectionPropsForSessCfg.ts + + - addPropsOrPrompt + + - resolveSessCfgProps + + - setTypeForTokenRequest + +- imperative\src\rest\src\session\SessConstants.ts + + - Constants and type definitions + +- imperative\src\rest\src\session\Session.ts + + - createFromUrl + +- imperative\src\rest\src\session\doc\IOptionsForAddConnProps.ts + + - supportedAuthTypes + +- imperative\src\rest\src\session\doc\ISession.ts + + - type + + - authTypeOrder + + - Could be the focus of our refactoring, but currently it is only used in + AbstractRestClient.constructor & AbstractRestClient.buildOptions + +- packages\zosuss\src\SshBaseHandler.ts + + - process + +## New software logic to add + +- Describe new functions that must be written to get the authTypeOrder + +- API functions allow programs to specify the auth options that they want. How can this be refactored in a non-breaking way? + +## Proposed deprecations + +Remove this section? + +imperative\src\rest\src\session\AbstractSession.ts: DEFAULT_TYPE \ No newline at end of file From 07197300804495ca6094edd637e853b4cc375da3 Mon Sep 17 00:00:00 2001 From: Gene Johnston Date: Tue, 15 Oct 2024 13:20:54 -0400 Subject: [PATCH 02/25] Committing local updates before merge from master Signed-off-by: Gene Johnston --- docs/Design_for_selecting_auth_type.md | 179 +++++++++++++++++-------- 1 file changed, 122 insertions(+), 57 deletions(-) diff --git a/docs/Design_for_selecting_auth_type.md b/docs/Design_for_selecting_auth_type.md index 4ee6158433..ef66c9e3d3 100644 --- a/docs/Design_for_selecting_auth_type.md +++ b/docs/Design_for_selecting_auth_type.md @@ -40,11 +40,15 @@ In this section we identify the key features that a solution would have to provi - Once an auth type is selected, our logic should ensure that only that one auth type is placed into a session object. Thus, logic in down-stream handlers will not alter the order of auth selection, simply by testing for the auth types within the session in a different order than the order that the user specified. - - If we continue to allow multiple authTypes to be placed into a session, we will have to re-work various functions to test for authType in the desired order. We will also have to provide the object containing that well-defined order to each such function. This will increase the amount of code being changed, and thus increase the probability of mistakes. + - If we continue to allow multiple auth types to be placed into a session, we will have to re-work various functions to test for auth type in the desired order. We will also have to provide the object containing that well-defined order to each such function. This will increase the amount of code being changed, and thus increase the probability of mistakes. -- Zowe clients do not currently support AUTH_TYPE_CERT_PFX, so we cannot add it to a set of available authTypes. If AUTH_TYPE_CERT_PFX is implemented, it should be placed immediately after AUTH_TYPE_CERT_PEM in the default order. +- Zowe clients do not currently support AUTH_TYPE_CERT_PFX, so we cannot add it to a set of available auth types. If AUTH_TYPE_CERT_PFX is implemented, it should be placed immediately after AUTH_TYPE_CERT_PEM in the default order. -- Propose that AUTH_TYPE_NONE gets a place in our list of possible authentication types +- Customers should be able to specify AUTH_TYPE_NONE in their preferred order of auth types. While it is not advisable to have no authentication, if a customer has a service that requires no authentication, the customer should be able to specify AUTH_TYPE_NONE at the top of their list of auth types applicable to that particular profile. + +- A customer should not have to specify every possible auth type in their ordered list of auth types. If a site only uses password and tokens, the customer should be able to specify only those two auth types in their list. + +- A customer-specified list of auth types must contain at least one of our supported auth types. - ## Historical behavior @@ -60,7 +64,7 @@ The objective of this feature is to enable users to define the order in which an - AUTH_TYPE_CERT_PEM -- All Zowe functions down-stream from AbstractRestClient explicitly override the authentication order from AbstractRestClient to be: +- Zowe classes other than AbstractRestClient (like AbstractSession) currently override the authentication order from AbstractRestClient into: - AUTH_TYPE_BASIC @@ -69,71 +73,54 @@ The objective of this feature is to enable users to define the order in which an - AUTH_TYPE_TOKEN - AUTH_TYPE_CERT_PEM - - - Confirm this order - - - Describe how AUTH_TYPE_NONE gets set These selections of authentication should be maintained as the default selections for their respective classes to avoid introducing a breaking change. ## Configuration enhancement - authTypeOrder config property -- Levels in config where authType order can reside +- Levels in config where auth type order can reside + - Base + - Nested parent + - Individual profile -## Functions that are candidates for modification +## Determination of functions to be modified The set of candidates for modification consist of all functions that contain the string ***"AUTH_TYPE_"***. This section contains an assessment of whether each such function affects the authentication order. Based on this analysis, those functions that warrant modification are identified in the "Proposed software modifications" section of this document. - cli\src\config\auto-init\ApimlAutoInitHandler - - doAutoInit - This function logins into APIML with the session object if either user & password or cert are in the session object. doAutoInit does not make a selection of order. It lets Login.apimlLogin() make that decision. If only the selected authType is placed into a session, + - doAutoInit - This function logins into APIML with the session object if either user & password or cert are in the session object. doAutoInit does not make a selection of order. It lets Login.apimlLogin() make that decision. If only the selected auth type is placed into a session, then no need to change. - **Modify doAutoInit ? No** - core\src\rest\ZosmfRestClient.ts - - processError - This function just alters error message text based on auth types found in the session. If only the selected authType is placed into a session, + - processError - This function just alters error message text based on auth types found in the session. If only the selected auth type is placed into a session, then no need to change. - **Modify processError ? No** - imperative\src\config\src\ConfigAutoStore.ts - - _fetchTokenForSessCfg - Since this function is used to explicitly retrieve a token value to be auto-stored into a session config, its use of AUTH_TYPE_TOKEN does not affect the auth order. So, + - _fetchTokenForSessCfg - Since this function is used to explicitly retrieve a token value to be auto-stored into a session config, its use of AUTH_TYPE_TOKEN does not affect the auth order. So, no need to change. - **Modify _fetchTokenForSessCfg ? No** - imperative\src\imperative\src\config\cmd\import\import.handler.ts - - buildSession - This function is used to import a config from a URL. That URL is an arbitrary location at a customer site where a config file is kept. It is not the target of a REST request to a mainframe service. By design, the only auth type that it will use is user & password. Supporting more authentication types in the 'import' command is beyond the scoope of this authentication-order feature. Therefore, + - buildSession - This function is used to import a config from a URL. That URL is an arbitrary location at a customer site where a config file is kept. It is not the target of a REST request to a mainframe service. By design, the only auth type that it will use is user & password. Supporting more authentication types in the 'import' command is beyond the scope of this authentication-order feature. Therefore, no need to change. - **Modify buildSession ? No** - imperative\src\rest\src\client\AbstractRestClient.ts - This class is the only class to use the recently created ISession.authTypeOrder property, which is an arry of authentication types supplied in the order in which they should be selected. + This class is the only class to use the recently created ISession.authTypeOrder property, which is an array of authentication types supplied in the order in which they should be selected. - - buildOptions - This function tests for the authType based on the order in which they occur in ISession.authTypeOrder. Therefore, + - buildOptions - This function tests for the auth type based on the order in which they occur in ISession.authTypeOrder. Therefore, no need to change. - **Modify buildOptions ? No** - - constructor - This function hard-codes the order of authentication types into the ISession.authTypeOrder array. We must create a means to provide the customer-defined order to this function. This function should only use its hard-coded order (as the default order) if a customers does not specify an order. - - - That default order should be: - - - AUTH_TYPE_TOKEN - - - AUTH_TYPE_BASIC - - - AUTH_TYPE_BEARER - - - AUTH_TYPE_CERT_PEM - - - AUTH_TYPE_CERT_PFX - - - AUTH_TYPE_NONE - - - Confirm this order + - constructor - This function currently hard-codes an order of authentication types into the ISession.authTypeOrder array. We must create a means to record the customer-defined order in this function. This function should call a common function (tentatively named recordAuthTypeOrderFromConfig) to do this work. recordAuthTypeOrderFromConfig should be able to set the old hard-coded order (as the default order) only if a customer does not specify an order. - **Modify constructor? yes** - - Each of the following functions reference AUTH_TYPE_ to a place an identified type into the ISession.type property. Since buildOptions calls just one of the following functions based on being the first available authType in the ISession.authTypeOrder array, none of these functions need to change. + - Each of the following functions reference AUTH_TYPE_XXX to a place an identified type into the ISession.type property. Since buildOptions calls just one of the following functions based on being the first available auth type in the ISession.authTypeOrder array, none of these functions need to change. - **Modify setBearerAuth ? No** @@ -145,55 +132,133 @@ The set of candidates for modification consist of all functions that contain the - imperative\src\rest\src\session\AbstractSession.ts - - buildSession - This function accepts a session. It already confirms that a valid authType has already been set in the session. It then populates some session properties based on the authType that it finds. Other new logic, which ensures that only the selected authType is placed into a session, should have no adverse affect on this function. So, + - buildSession - This private function is called by the constructor, which accepts an Isession object. A caller could populate multiple auth types (and related properties) into that supplied session. Session.buildSession() will have to scrub all but the highest priority available auth type from the session. We should create a common utility function (tentatively named selectTopAuthType) to do the scrubbing. selectTopAuthType can be also be called from ConnectionPropsForSessCfg.resolveSessCfgProps function as described below. - - **Modify buildSession ? No** + - **Modify buildSession ? Yes** - - DEFAULT_TYPE - This simply a constant definition set to AUTH_TYPE_NONE. It is not used in any CLI or ZE code outside of this AbstractSession class. Because it is a public property, it cannot be removed without risk of breaking change. If AUTH_TYPE_NONE is added to the ISession.authTypeOrder array, DEFAULT_TYPE should be deprecated, so + - DEFAULT_TYPE - This simply a constant definition set to AUTH_TYPE_NONE. It is not used in any CLI or ZE code outside of this AbstractSession class. Because it is a public property, it cannot be removed without risk of breaking change. If AUTH_TYPE_NONE is added to the ISession.authTypeOrder array, DEFAULT_TYPE should be deprecated. - **Modify DEFAULT_TYPE ? Yes** - imperative\src\rest\src\session\ConnectionPropsForSessCfg.ts - - addPropsOrPrompt + - addPropsOrPrompt - This function only uses AUTH_TYPE_XXX to determine whether a token and a cert are irrelevant based on the existence of AUTH_TYPE_TOKEN. Those items should not need to change. However, addPropsOrPrompt accepts three parameters, each of which can have property overrides of auth type. After calling resolveSessCfgProps, addPropsOrPrompt continues to modify the session properties with values from its parameters. Thus addPropsOrPrompt must be refactored to work properly with a refactored resolveSessCfgProps. + + - **Modify addPropsOrPrompt ? Yes** - - resolveSessCfgProps + - resolveSessCfgProps - Many functions call this function before creating a new session. This function could scrub all but the selected auth type and related properties from the session object. However, callers could call 'new Session()' without first calling resolveSessCfgProps(). Thus, Session.buildSession() will have to perform the same scrubbing of auth types from the session. Both Session.buildSession and resolveSessCfgProps should call selectTopAuthType() to do the scrubbing. + + - **Modify resolveSessCfgProps ? Yes** - - setTypeForTokenRequest + - setTypeForTokenRequest - This function handles setting auth type to AUTH_TYPE_TOKEN to get a token back from user & password. This does not appear to require any change, but it should be revisited after resolveSessCfgProps is refactored. + + - **Modify setTypeForTokenRequest ? Maybe** - imperative\src\rest\src\session\SessConstants.ts - - Constants and type definitions + - Constants and type definitions of AUTH_TYPE_XXX are what they need to be. + - **Modify constants ? No** - imperative\src\rest\src\session\Session.ts - - createFromUrl + - createFromUrl - This function is only called from ImportHandler.buildSession when to enable importing a config from a URL. As with ImportHandler.buildSession, the use of AUTH_TYPE_BASIC when user & password exist is appropriate and should not need to change. + - **Modify createFromUrl ? No** - imperative\src\rest\src\session\doc\IOptionsForAddConnProps.ts - - supportedAuthTypes + - supportedAuthTypes - Our set of supported auth types will not change as part of this feature. + - **Modify supportedAuthTypes ? No** - imperative\src\rest\src\session\doc\ISession.ts - - type - - - authTypeOrder + - authTypeOrder - This property could be the focus of our refactoring, but currently it is hard-coded and only used in AbstractRestClient.constructor & AbstractRestClient.buildOptions. This property should be used to hold the customer-defined order of authentication types. There is no reason to change this property. Other code will repopulate authTypeOrder's set of values based on customer input. It is possible that an ISession may not be available at the time we want to store the customer-defined order of authentication types. Another object may need to store that order, which is later transferred into this ISession property. - - Could be the focus of our refactoring, but currently it is only used in - AbstractRestClient.constructor & AbstractRestClient.buildOptions + - **Modify authTypeOrder ? No** - packages\zosuss\src\SshBaseHandler.ts - - process + - process - Since our SSH connection can only use basic authentication, this function's use of AUTH_TYPE_BASIC is appropriate and does not need to change. + - **Modify process ? No** ## New software logic to add -- Describe new functions that must be written to get the authTypeOrder - -- API functions allow programs to specify the auth options that they want. How can this be refactored in a non-breaking way? - -## Proposed deprecations +This section describes new functions that must be added to achieve the desired functionality. -Remove this section? - -imperative\src\rest\src\session\AbstractSession.ts: DEFAULT_TYPE \ No newline at end of file +- Utility function to record the order of authentication types. + + > --- + > + > @internal + > + > public UnknownClass.recordAuthTypeOrderFromConfig(tokenIsTopDefault: boolean = false): string[ ] { + > + > - This function should obtain the customer-defined authentication order from the Zowe client config file. + > + > - It must confirm that the customer specified valid values. If not, it should record the error, and fall-back to the appropriate default order. + > + > - It should place those auth types into a string array that is returned. + > + > - Maybe instead of returning that array, the array is set into come commonly accessible object (like ImperativeConfig)? + > + > - If no order has been configured into zowe.config.json, it should create a default order to be backward compatible. + > + > - If tokenIsTopDefault is true, it should place token at the top of the order, resulting in this order: + > + > - AUTH_TYPE_TOKEN + > + > - AUTH_TYPE_BASIC + > + > - AUTH_TYPE_BEARER + > + > - AUTH_TYPE_CERT_PEM + > + > - AUTH_TYPE_CERT_PFX + > + > - AUTH_TYPE_NONE + > + > - If tokenIsTopDefault is false, it should place basic at the top of the order, resulting in this order: + > + > - AUTH_TYPE_BASIC + > + > - AUTH_TYPE_TOKEN + > + > -  AUTH_TYPE_BEARER + > + > - AUTH_TYPE_CERT_PEM + > + > - AUTH_TYPE_CERT_PFX + > + > - AUTH_TYPE_NONE + > + > } + +- Utility function to get the array of authentication types. + + > --- + > + > public UnknownClass.getAuthTypeOrder(): string[ ] { + > + > - This function should return the recorded array of authentication types. They will be in the order of top preference first. + > + > - Maybe this function lives in ImperativeConfig?. + > + > } + +- Utility function to select the top available authentication type in a session. + + > --- + > + > @internal + > + > public UnknownClass.selectTopAuthType(iSessObj: ISession): void { + > + > - This function should use the new getAuthTypeOrder function to get the ordered array of auth types. + > - It should confirm if the auth types (in the preferred order) and their associated properties are available in the iSessObj. + > - Once the first auth type is found, the properties related to all other auth types should be removed from iSessObj. + > - Maybe this function lives in AbstractSession?. + > + > } + + + +# \ No newline at end of file From 4f85c56050ef25223feb7e9cf32d7d999cfbf0a0 Mon Sep 17 00:00:00 2001 From: Gene Johnston Date: Tue, 12 Nov 2024 14:52:26 -0500 Subject: [PATCH 03/25] Add Doc Impact Signed-off-by: Gene Johnston --- docs/Design_for_selecting_auth_type.md | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/docs/Design_for_selecting_auth_type.md b/docs/Design_for_selecting_auth_type.md index ef66c9e3d3..9292693215 100644 --- a/docs/Design_for_selecting_auth_type.md +++ b/docs/Design_for_selecting_auth_type.md @@ -83,6 +83,17 @@ These selections of authentication should be maintained as the default selection - Base - Nested parent - Individual profile + - As a sibling of the secure array. + +## Documentation Impact + +- We must describe the purpose and function of the new authOrder property. + +- We must describe where users can place the authOrder property. + +- We must describe the default order of authentication, when no authOrder property is supplied. + +- We must notify extenders to guide their customers to supply an appropriate authOrder property if their extension needs a non-default order. ## Determination of functions to be modified @@ -180,7 +191,7 @@ The set of candidates for modification consist of all functions that contain the - process - Since our SSH connection can only use basic authentication, this function's use of AUTH_TYPE_BASIC is appropriate and does not need to change. - **Modify process ? No** -## New software logic to add +## New software logic that must be added This section describes new functions that must be added to achieve the desired functionality. @@ -259,6 +270,4 @@ This section describes new functions that must be added to achieve the desired f > > } - - # \ No newline at end of file From f953ab837564e5a600af1919d0bb5df67748e642 Mon Sep 17 00:00:00 2001 From: Gene Johnston Date: Tue, 12 Nov 2024 18:04:54 -0500 Subject: [PATCH 04/25] Complete section "configuration enhancement" Signed-off-by: Gene Johnston --- docs/Design_for_selecting_auth_type.md | 118 +++++++++++++++++-------- 1 file changed, 81 insertions(+), 37 deletions(-) diff --git a/docs/Design_for_selecting_auth_type.md b/docs/Design_for_selecting_auth_type.md index 9292693215..b828393dc6 100644 --- a/docs/Design_for_selecting_auth_type.md +++ b/docs/Design_for_selecting_auth_type.md @@ -20,7 +20,7 @@ In this section we identify the key features that a solution would have to provi - The user must be able to change that authentication order for different parts of the Zowe client configuration. -- Zowe client logic must be enhanced to select the right authentication type for a profile being used to make a connection. +- Zowe client logic must be enhanced to select the authentication type for a profile used to make a connection based on a user-specified preferred order. - Zowe client extenders (CLI plugins and ZE extensions) should **not** be able to alter the order of authentication types. The user should control that choice. @@ -32,25 +32,25 @@ In this section we identify the key features that a solution would have to provi - For example, if a user specifies a token value on the command line, it will override a token value stored in a config file. However, - - If the user has specified that a certificate should be used before a token, and a certificate is also available in the configuration, the certificate should be used because the user configured that certificates are used before tokens. + - If the user has specified that a certificate should be used before a token, and a certificate is also available in the configuration, the certificate should be used because the user configured that certificates should be used before tokens. - The token should not be used just because it was supplied on the command line. - The authentication order only identifies the order in which Zowe chooses the **one** authentication method that will be used. If that first authentication method fails, Zowe will not make a second attempt to authenticate with any of the later authentication methods. -- Once an auth type is selected, our logic should ensure that only that one auth type is placed into a session object. Thus, logic in down-stream handlers will not alter the order of auth selection, simply by testing for the auth types within the session in a different order than the order that the user specified. +- Once an authentication is selected, our logic should ensure that only that one type of authentication is placed into a session object. Thus, logic in down-stream handlers will not alter the order of authentication selection, simply by testing for the authentications within the session in a different order than the order that the user specified. - - If we continue to allow multiple auth types to be placed into a session, we will have to re-work various functions to test for auth type in the desired order. We will also have to provide the object containing that well-defined order to each such function. This will increase the amount of code being changed, and thus increase the probability of mistakes. + - If we were to continue to allow multiple authentications to be placed into a session, we will have to re-work various functions to test for authentications in the desired order. We will also have to provide the object containing that well-defined order to each such function. This will increase the amount of code being changed, and thus increase the probability of mistakes. -- Zowe clients do not currently support AUTH_TYPE_CERT_PFX, so we cannot add it to a set of available auth types. If AUTH_TYPE_CERT_PFX is implemented, it should be placed immediately after AUTH_TYPE_CERT_PEM in the default order. +- Zowe clients do not currently support AUTH_TYPE_CERT_PFX, so we cannot add it to a set of available authentications. If AUTH_TYPE_CERT_PFX is implemented, it should be placed immediately after AUTH_TYPE_CERT_PEM in the default order. -- Customers should be able to specify AUTH_TYPE_NONE in their preferred order of auth types. While it is not advisable to have no authentication, if a customer has a service that requires no authentication, the customer should be able to specify AUTH_TYPE_NONE at the top of their list of auth types applicable to that particular profile. +- Customers should be able to specify AUTH_TYPE_NONE in their preferred order of authentications. While it is not advisable to have no authentication, if a customer has a service that requires no authentication, the customer should be able to specify AUTH_TYPE_NONE at the top of their list of authentications applicable to that particular profile. -- A customer should not have to specify every possible auth type in their ordered list of auth types. If a site only uses password and tokens, the customer should be able to specify only those two auth types in their list. +- A customer should not have to specify every possible authentication in their ordered list of authentications. If a site only uses password and tokens, the customer should be able to specify only those two authentications in their list. -- A customer-specified list of auth types must contain at least one of our supported auth types. +- A customer-specified list of authentications must contain at least one of our supported authentications. -- ## Historical behavior +## Historical behavior The objective of this feature is to enable users to define the order in which an authentication type is selected. However, when a user does not specify any such order, the default order should reflect past behavior. @@ -78,12 +78,56 @@ These selections of authentication should be maintained as the default selection ## Configuration enhancement -- authTypeOrder config property -- Levels in config where auth type order can reside - - Base - - Nested parent - - Individual profile - - As a sibling of the secure array. +A new profile property named **authOrder** should be created to enable users to specify their order of precedence for the authentication to be used when making a REST connection. The authOrder property should be treated like one of our key connection properties (like host and port). Thus authOrder would have the following characteristics: + +- It must be specified within a "properties" object. That "properties" object could reside in: + + - A base profile + + - A parent profile of a nested configuration. + + - Any profile of type **zosmf**. + + - Any profile specific to a plugin (or VSCode extension) that supports a REST connection. For example an **endevor** profile could contain an **authOrder** property, but an **endevor-location** profile would not. + +- Our existing inheritance of connection properties should also apply to the inheritance of the authOrder property. + +- We should be able to use our logic for where and how the **rejectUnauthorized** property is handled as a model for how we handle the **authOrder** property. + +For example, the user could specify their desired authOrder like this: + +``` +"properties": { +    "host": ... , +    "port": ... , +    "rejectUnauthorized": ... , +    "authOrder": [ "basic", "token", "cert-pem"] +} +``` + +The programmatic definition of authOrder would be: + +``` +authOrder[SessConstants.AUTH_TYPE_CHOICES]    +``` + +The current set of AUTH_TYPE_CHOICES are: + +- AUTH_TYPE_BASIC = "basic" + +- AUTH_TYPE_BEARER = "bearer" + +- AUTH_TYPE_TOKEN = "token" + +- AUTH_TYPE_CERT_PEM = "cert-pem" + +- AUTH_TYPE_NONE = "none" + +We should add a new AUTH_TYPE_CHOICE of: + +- AUTH_TYPE_SSH_KEY = "ssh-key" + +That addition would enable customers to also specify the authentication order of precedence for an SSH connection using an authOrder property. The only permissible values for an ssh connection would be "basic" and "ssh-key". Our ssh-handling logic will have to be modified to enforce that restriction and to honor the order. Conversely, our zosmf-handling logic would have to be modified to reject "ssh-key" in authOrder (or at least ignore it). If we choose not to implement authOrder for ssh at this time, we should at least create a design and implementation that can tolerate the addition of "ssh-key" at a later date. ## Documentation Impact @@ -97,16 +141,16 @@ These selections of authentication should be maintained as the default selection ## Determination of functions to be modified -The set of candidates for modification consist of all functions that contain the string ***"AUTH_TYPE_"***. This section contains an assessment of whether each such function affects the authentication order. Based on this analysis, those functions that warrant modification are identified in the "Proposed software modifications" section of this document. +The set of candidates for modification consist of all functions that contain the string ***"AUTH_TYPE_"***. This section contains an assessment of whether each such function affects the authentication order and must be modified. - cli\src\config\auto-init\ApimlAutoInitHandler - - doAutoInit - This function logins into APIML with the session object if either user & password or cert are in the session object. doAutoInit does not make a selection of order. It lets Login.apimlLogin() make that decision. If only the selected auth type is placed into a session, then no need to change. + - doAutoInit - This function logins into APIML with the session object if either user & password or cert are in the session object. doAutoInit does not make a selection of order. It lets Login.apimlLogin() make that decision. If only the selected authentication is placed into a session, then no need to change. - **Modify doAutoInit ? No** - core\src\rest\ZosmfRestClient.ts - - processError - This function just alters error message text based on auth types found in the session. If only the selected auth type is placed into a session, then no need to change. + - processError - This function just alters error message text based on authentications found in the session. If only the selected authentication is placed into a session, then no need to change. - **Modify processError ? No** - imperative\src\config\src\ConfigAutoStore.ts @@ -116,22 +160,22 @@ The set of candidates for modification consist of all functions that contain the - imperative\src\imperative\src\config\cmd\import\import.handler.ts - - buildSession - This function is used to import a config from a URL. That URL is an arbitrary location at a customer site where a config file is kept. It is not the target of a REST request to a mainframe service. By design, the only auth type that it will use is user & password. Supporting more authentication types in the 'import' command is beyond the scope of this authentication-order feature. Therefore, no need to change. + - buildSession - This function is used to import a config from a URL. That URL is an arbitrary location at a customer site where a config file is kept. It is not the target of a REST request to a mainframe service. By design, the only authentication that it will use is user & password. Supporting more authentication types in the 'import' command is beyond the scope of this authentication-order feature. Therefore, no need to change. - **Modify buildSession ? No** - imperative\src\rest\src\client\AbstractRestClient.ts This class is the only class to use the recently created ISession.authTypeOrder property, which is an array of authentication types supplied in the order in which they should be selected. - - buildOptions - This function tests for the auth type based on the order in which they occur in ISession.authTypeOrder. Therefore, no need to change. + - buildOptions - This function tests for the authentication based on the order in which they occur in ISession.authTypeOrder. Therefore, no need to change. - **Modify buildOptions ? No** - - constructor - This function currently hard-codes an order of authentication types into the ISession.authTypeOrder array. We must create a means to record the customer-defined order in this function. This function should call a common function (tentatively named recordAuthTypeOrderFromConfig) to do this work. recordAuthTypeOrderFromConfig should be able to set the old hard-coded order (as the default order) only if a customer does not specify an order. + - constructor - This function currently hard-codes an order of authentication types into the ISession.authTypeOrder array. We must create a means to record the customer-defined order in this function. This function should call a common function (tentatively named recordAuthOrderFromConfig) to do this work. recordAuthOrderFromConfig should be able to set the old hard-coded order (as the default order) only if a customer does not specify an order. - **Modify constructor? yes** - - Each of the following functions reference AUTH_TYPE_XXX to a place an identified type into the ISession.type property. Since buildOptions calls just one of the following functions based on being the first available auth type in the ISession.authTypeOrder array, none of these functions need to change. + - Each of the following functions reference AUTH_TYPE_XXX to a place an identified type into the ISession.type property. Since buildOptions calls just one of the following functions based on being the first available authentication in the ISession.authTypeOrder array, none of these functions need to change. - **Modify setBearerAuth ? No** @@ -143,7 +187,7 @@ The set of candidates for modification consist of all functions that contain the - imperative\src\rest\src\session\AbstractSession.ts - - buildSession - This private function is called by the constructor, which accepts an Isession object. A caller could populate multiple auth types (and related properties) into that supplied session. Session.buildSession() will have to scrub all but the highest priority available auth type from the session. We should create a common utility function (tentatively named selectTopAuthType) to do the scrubbing. selectTopAuthType can be also be called from ConnectionPropsForSessCfg.resolveSessCfgProps function as described below. + - buildSession - This private function is called by the constructor, which accepts an Isession object. A caller could populate multiple authentications (and related properties) into that supplied session. Session.buildSession() will have to scrub all but the highest priority available authentication from the session. We should create a common utility function (tentatively named selectTopAuthType) to do the scrubbing. selectTopAuthType can be also be called from ConnectionPropsForSessCfg.resolveSessCfgProps function as described below. - **Modify buildSession ? Yes** @@ -153,15 +197,15 @@ The set of candidates for modification consist of all functions that contain the - imperative\src\rest\src\session\ConnectionPropsForSessCfg.ts - - addPropsOrPrompt - This function only uses AUTH_TYPE_XXX to determine whether a token and a cert are irrelevant based on the existence of AUTH_TYPE_TOKEN. Those items should not need to change. However, addPropsOrPrompt accepts three parameters, each of which can have property overrides of auth type. After calling resolveSessCfgProps, addPropsOrPrompt continues to modify the session properties with values from its parameters. Thus addPropsOrPrompt must be refactored to work properly with a refactored resolveSessCfgProps. + - addPropsOrPrompt - This function only uses AUTH_TYPE_XXX to determine whether a token and a cert are irrelevant based on the existence of AUTH_TYPE_TOKEN. Those items should not need to change. However, addPropsOrPrompt accepts three parameters, each of which can have property overrides of authentication. After calling resolveSessCfgProps, addPropsOrPrompt continues to modify the session properties with values from its parameters. Thus addPropsOrPrompt must be refactored to work properly with a refactored resolveSessCfgProps. - **Modify addPropsOrPrompt ? Yes** - - resolveSessCfgProps - Many functions call this function before creating a new session. This function could scrub all but the selected auth type and related properties from the session object. However, callers could call 'new Session()' without first calling resolveSessCfgProps(). Thus, Session.buildSession() will have to perform the same scrubbing of auth types from the session. Both Session.buildSession and resolveSessCfgProps should call selectTopAuthType() to do the scrubbing. + - resolveSessCfgProps - Many functions call this function before creating a new session. This function could scrub all but the selected authentication and related properties from the session object. However, callers could call 'new Session()' without first calling resolveSessCfgProps(). Thus, Session.buildSession() will have to perform the same scrubbing of authentications from the session. Both Session.buildSession and resolveSessCfgProps should call selectTopAuthType() to do the scrubbing. - **Modify resolveSessCfgProps ? Yes** - - setTypeForTokenRequest - This function handles setting auth type to AUTH_TYPE_TOKEN to get a token back from user & password. This does not appear to require any change, but it should be revisited after resolveSessCfgProps is refactored. + - setTypeForTokenRequest - This function handles setting authentication to AUTH_TYPE_TOKEN to get a token back from user & password. This does not appear to require any change, but it should be revisited after resolveSessCfgProps is refactored. - **Modify setTypeForTokenRequest ? Maybe** @@ -177,21 +221,21 @@ The set of candidates for modification consist of all functions that contain the - imperative\src\rest\src\session\doc\IOptionsForAddConnProps.ts - - supportedAuthTypes - Our set of supported auth types will not change as part of this feature. + - supportedAuthTypes - Our set of supported authentications will not change as part of this feature. - **Modify supportedAuthTypes ? No** - imperative\src\rest\src\session\doc\ISession.ts - - authTypeOrder - This property could be the focus of our refactoring, but currently it is hard-coded and only used in AbstractRestClient.constructor & AbstractRestClient.buildOptions. This property should be used to hold the customer-defined order of authentication types. There is no reason to change this property. Other code will repopulate authTypeOrder's set of values based on customer input. It is possible that an ISession may not be available at the time we want to store the customer-defined order of authentication types. Another object may need to store that order, which is later transferred into this ISession property. + - authTypeOrder - This property could be the focus of our refactoring, but currently it is hard-coded and only used in AbstractRestClient.constructor & AbstractRestClient.buildOptions. This property should be used to hold the customer-defined order of authentication types. There is no reason to change this property. Other code will repopulate authTypeOrder's set of values based on customer input. It is possible that an ISession may not be available at the time we want to store the customer-defined order of authentication types. Another object may need to store that order, which is later transferred into this ISession property. The only reason that we may want to change this item is to rename it from authtypeOrder to authOrder to reflect the same, simpler object name that a user will use to specify the authentication order. We should only change the name of authTypeOrder if it is only used internally by Zowe (and thus would be a non-breaking change). - - **Modify authTypeOrder ? No** + - **Modify authTypeOrder ? Maybe** - packages\zosuss\src\SshBaseHandler.ts - process - Since our SSH connection can only use basic authentication, this function's use of AUTH_TYPE_BASIC is appropriate and does not need to change. - **Modify process ? No** -## New software logic that must be added +## New functions that must be added This section describes new functions that must be added to achieve the desired functionality. @@ -201,13 +245,13 @@ This section describes new functions that must be added to achieve the desired f > > @internal > - > public UnknownClass.recordAuthTypeOrderFromConfig(tokenIsTopDefault: boolean = false): string[ ] { + > public UnknownClass.recordAuthOrderFromConfig(tokenIsTopDefault: boolean = false): string[ ] { > - > - This function should obtain the customer-defined authentication order from the Zowe client config file. + > - as siblingThis function should obtain the customer-defined authentication order from the Zowe client config file. > > - It must confirm that the customer specified valid values. If not, it should record the error, and fall-back to the appropriate default order. > - > - It should place those auth types into a string array that is returned. + > - It should place those authentications into a string array that is returned. > > - Maybe instead of returning that array, the array is set into come commonly accessible object (like ImperativeConfig)? > @@ -247,7 +291,7 @@ This section describes new functions that must be added to achieve the desired f > --- > - > public UnknownClass.getAuthTypeOrder(): string[ ] { + > public UnknownClass.getAuthOrder(): string[ ] { > > - This function should return the recorded array of authentication types. They will be in the order of top preference first. > @@ -263,9 +307,9 @@ This section describes new functions that must be added to achieve the desired f > > public UnknownClass.selectTopAuthType(iSessObj: ISession): void { > - > - This function should use the new getAuthTypeOrder function to get the ordered array of auth types. - > - It should confirm if the auth types (in the preferred order) and their associated properties are available in the iSessObj. - > - Once the first auth type is found, the properties related to all other auth types should be removed from iSessObj. + > - This function should use the new getAuthOrder function to get the ordered array of authentications. + > - It should confirm if the authentications (in the preferred order) and their associated properties are available in the iSessObj. + > - Once the first authentication is found, the properties related to all other authentications should be removed from iSessObj. > - Maybe this function lives in AbstractSession?. > > } From d6417f4efeb18abdf7d96ce34dd9faf739614028 Mon Sep 17 00:00:00 2001 From: Gene Johnston Date: Wed, 13 Nov 2024 18:09:03 -0500 Subject: [PATCH 05/25] Add ssh functions to list of functions to be modified Signed-off-by: Gene Johnston --- docs/Design_for_selecting_auth_type.md | 29 +++++++++++++++----------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/docs/Design_for_selecting_auth_type.md b/docs/Design_for_selecting_auth_type.md index b828393dc6..fbf2134e2f 100644 --- a/docs/Design_for_selecting_auth_type.md +++ b/docs/Design_for_selecting_auth_type.md @@ -93,8 +93,10 @@ A new profile property named **authOrder** should be created to enable users to - Our existing inheritance of connection properties should also apply to the inheritance of the authOrder property. - We should be able to use our logic for where and how the **rejectUnauthorized** property is handled as a model for how we handle the **authOrder** property. + + - One difference from **rejectUnauthorized** is that for **authOrder** we should accept its value from a zowe.config.json file, but not from a command line argument or environment variable. Because **authOrder** is an array, it will be difficult and error-prone for users to correctly specify **authOrder** on a command line or in an environment variable. -For example, the user could specify their desired authOrder like this: +As an example, the user could specify their desired authOrder like this: ``` "properties": { @@ -141,7 +143,7 @@ That addition would enable customers to also specify the authentication order of ## Determination of functions to be modified -The set of candidates for modification consist of all functions that contain the string ***"AUTH_TYPE_"***. This section contains an assessment of whether each such function affects the authentication order and must be modified. +The set of candidates for modification consist of all functions that contain the string ***"AUTH_TYPE_"***. This section contains an assessment of whether each such function affects the authentication order and must be modified. - cli\src\config\auto-init\ApimlAutoInitHandler @@ -171,7 +173,7 @@ The set of candidates for modification consist of all functions that contain the - **Modify buildOptions ? No** - - constructor - This function currently hard-codes an order of authentication types into the ISession.authTypeOrder array. We must create a means to record the customer-defined order in this function. This function should call a common function (tentatively named recordAuthOrderFromConfig) to do this work. recordAuthOrderFromConfig should be able to set the old hard-coded order (as the default order) only if a customer does not specify an order. + - constructor - This function currently hard-codes an order of authentication types into the ISession.authTypeOrder array. We must create a means to record the customer-defined order in this function. This function should call a common function (tentatively named recordAuthOrderFromConfig) to do this work. recordAuthOrderFromConfig should be able to set the old hard-coded order (as the default order) only if a customer does not specify an order. - **Modify constructor? yes** @@ -187,7 +189,7 @@ The set of candidates for modification consist of all functions that contain the - imperative\src\rest\src\session\AbstractSession.ts - - buildSession - This private function is called by the constructor, which accepts an Isession object. A caller could populate multiple authentications (and related properties) into that supplied session. Session.buildSession() will have to scrub all but the highest priority available authentication from the session. We should create a common utility function (tentatively named selectTopAuthType) to do the scrubbing. selectTopAuthType can be also be called from ConnectionPropsForSessCfg.resolveSessCfgProps function as described below. + - buildSession - This private function is called by the constructor, which accepts an Isession object. A caller could populate multiple authentications (and related properties) into that supplied session. Session.buildSession() will have to scrub all but the highest priority available authentication from the session. We should create a common utility function (tentatively named selectPreferredAuth) to do the scrubbing. selectPreferredAuth can be also be called from ConnectionPropsForSessCfg.resolveSessCfgProps function as described below. - **Modify buildSession ? Yes** @@ -201,13 +203,13 @@ The set of candidates for modification consist of all functions that contain the - **Modify addPropsOrPrompt ? Yes** - - resolveSessCfgProps - Many functions call this function before creating a new session. This function could scrub all but the selected authentication and related properties from the session object. However, callers could call 'new Session()' without first calling resolveSessCfgProps(). Thus, Session.buildSession() will have to perform the same scrubbing of authentications from the session. Both Session.buildSession and resolveSessCfgProps should call selectTopAuthType() to do the scrubbing. + - resolveSessCfgProps - Many functions call this function before creating a new session. This function could scrub all but the selected authentication and related properties from the session object. However, callers could call 'new Session()' without first calling resolveSessCfgProps(). Thus, Session.buildSession() will have to perform the same scrubbing of authentications from the session. Both Session.buildSession and resolveSessCfgProps should call selectPreferredAuth() to do the scrubbing. - **Modify resolveSessCfgProps ? Yes** - setTypeForTokenRequest - This function handles setting authentication to AUTH_TYPE_TOKEN to get a token back from user & password. This does not appear to require any change, but it should be revisited after resolveSessCfgProps is refactored. - - **Modify setTypeForTokenRequest ? Maybe** + - **Modify setTypeForTokenRequest ? Maybe** - imperative\src\rest\src\session\SessConstants.ts @@ -228,12 +230,17 @@ The set of candidates for modification consist of all functions that contain the - authTypeOrder - This property could be the focus of our refactoring, but currently it is hard-coded and only used in AbstractRestClient.constructor & AbstractRestClient.buildOptions. This property should be used to hold the customer-defined order of authentication types. There is no reason to change this property. Other code will repopulate authTypeOrder's set of values based on customer input. It is possible that an ISession may not be available at the time we want to store the customer-defined order of authentication types. Another object may need to store that order, which is later transferred into this ISession property. The only reason that we may want to change this item is to rename it from authtypeOrder to authOrder to reflect the same, simpler object name that a user will use to specify the authentication order. We should only change the name of authTypeOrder if it is only used internally by Zowe (and thus would be a non-breaking change). - - **Modify authTypeOrder ? Maybe** + - **Modify authTypeOrder ? Maybe** - packages\zosuss\src\SshBaseHandler.ts - - process - Since our SSH connection can only use basic authentication, this function's use of AUTH_TYPE_BASIC is appropriate and does not need to change. - - **Modify process ? No** + - process - This function explicitly sets a property named **supportedAuthTypes** to AUTH_TYPE_BASIC. It is unclear why there is no option for in this logic for the use of an ssh-key. + - **Modify process ? Maybe** + + packages\zosuss\src\Shell.ts + + - connect - This function explicitly checks for an ssh key (first) or a password (second) in a hard-coded fashion. If we want the user's authOrder to apply to ssh connections, this function must use the proposed selectPreferredAuth() utility function to to make the right authentication choice. + - **Modify connect ? Yes** ## New functions that must be added @@ -303,9 +310,7 @@ This section describes new functions that must be added to achieve the desired f > --- > - > @internal - > - > public UnknownClass.selectTopAuthType(iSessObj: ISession): void { + > public UnknownClass.selectPreferredAuth(iSessObj: ISession): void { > > - This function should use the new getAuthOrder function to get the ordered array of authentications. > - It should confirm if the authentications (in the preferred order) and their associated properties are available in the iSessObj. From f29132d0de639aca89d04380a3140c3d44c1c0c7 Mon Sep 17 00:00:00 2001 From: Gene Johnston Date: Tue, 19 Nov 2024 17:03:37 -0500 Subject: [PATCH 06/25] Clean up before draft PR Signed-off-by: Gene Johnston --- docs/Design_for_selecting_auth_type.md | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/docs/Design_for_selecting_auth_type.md b/docs/Design_for_selecting_auth_type.md index fbf2134e2f..81a265dbd8 100644 --- a/docs/Design_for_selecting_auth_type.md +++ b/docs/Design_for_selecting_auth_type.md @@ -1,6 +1,6 @@ # Order of authentication in Zowe Clients -This document identifies a design to enable users to specify the order in which credentials are selected for authentication when multiple credentials are specified by the user. +This document proposes a design to enable users to specify the order in which credentials are selected for authentication when multiple credentials are specified by the user. Users may not intentionally specify multiple credentials for the same operation. However, because configuration properties can be inherited from a base profile or within nested profiles, it is possible that multiple credentials may be available when a Zowe client attempts to make a connection to a mainframe service. @@ -103,7 +103,7 @@ As an example, the user could specify their desired authOrder like this:     "host": ... ,     "port": ... ,     "rejectUnauthorized": ... , -    "authOrder": [ "basic", "token", "cert-pem"] +    "authOrder": [ "basic", "token", "cert-pem" ] } ``` @@ -218,7 +218,7 @@ The set of candidates for modification consist of all functions that contain the - imperative\src\rest\src\session\Session.ts - - createFromUrl - This function is only called from ImportHandler.buildSession when to enable importing a config from a URL. As with ImportHandler.buildSession, the use of AUTH_TYPE_BASIC when user & password exist is appropriate and should not need to change. + - createFromUrl - This function is only called from ImportHandler.buildSession to enable importing a config from a URL. As with ImportHandler.buildSession, the use of AUTH_TYPE_BASIC when user & password exist is appropriate and should not need to change. - **Modify createFromUrl ? No** - imperative\src\rest\src\session\doc\IOptionsForAddConnProps.ts @@ -246,21 +246,23 @@ The set of candidates for modification consist of all functions that contain the This section describes new functions that must be added to achieve the desired functionality. +This section is a work in progress. The ideas are still pipe dreams and not confirmed by analysis of our code. + - Utility function to record the order of authentication types. > --- > > @internal > - > public UnknownClass.recordAuthOrderFromConfig(tokenIsTopDefault: boolean = false): string[ ] { + > public ClassNotDecidedYet.recordAuthOrderFromConfig(tokenIsTopDefault: boolean = false): string[ ] { > - > - as siblingThis function should obtain the customer-defined authentication order from the Zowe client config file. + > - This function should obtain the customer-defined authentication order from the Zowe client config file. > > - It must confirm that the customer specified valid values. If not, it should record the error, and fall-back to the appropriate default order. > > - It should place those authentications into a string array that is returned. > - > - Maybe instead of returning that array, the array is set into come commonly accessible object (like ImperativeConfig)? + > - Maybe instead of returning that array, the array is set into come commonly accessible object (like the Config class)? > > - If no order has been configured into zowe.config.json, it should create a default order to be backward compatible. > @@ -298,11 +300,11 @@ This section describes new functions that must be added to achieve the desired f > --- > - > public UnknownClass.getAuthOrder(): string[ ] { + > public ClassNotDecidedYet.getAuthOrder(): string[ ] { > > - This function should return the recorded array of authentication types. They will be in the order of top preference first. > - > - Maybe this function lives in ImperativeConfig?. + > - Maybe this function lives in Config?. > > } @@ -310,7 +312,7 @@ This section describes new functions that must be added to achieve the desired f > --- > - > public UnknownClass.selectPreferredAuth(iSessObj: ISession): void { + > public ClassNotDecidedYet.selectPreferredAuth(iSessObj: ISession): void { > > - This function should use the new getAuthOrder function to get the ordered array of authentications. > - It should confirm if the authentications (in the preferred order) and their associated properties are available in the iSessObj. From aef982c5f928f463f8b079b870f8abd3d0c88518 Mon Sep 17 00:00:00 2001 From: Gene Johnston Date: Mon, 25 Nov 2024 13:57:47 -0500 Subject: [PATCH 07/25] Add section of functions using rejectUnauthorized Signed-off-by: Gene Johnston --- docs/Design_for_selecting_auth_type.md | 76 +++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/docs/Design_for_selecting_auth_type.md b/docs/Design_for_selecting_auth_type.md index 81a265dbd8..e9249f69b8 100644 --- a/docs/Design_for_selecting_auth_type.md +++ b/docs/Design_for_selecting_auth_type.md @@ -141,7 +141,7 @@ That addition would enable customers to also specify the authentication order of - We must notify extenders to guide their customers to supply an appropriate authOrder property if their extension needs a non-default order. -## Determination of functions to be modified +## Functions that reference AUTH_TYPE may need modification The set of candidates for modification consist of all functions that contain the string ***"AUTH_TYPE_"***. This section contains an assessment of whether each such function affects the authentication order and must be modified. @@ -242,6 +242,80 @@ The set of candidates for modification consist of all functions that contain the - connect - This function explicitly checks for an ssh key (first) or a password (second) in a hard-coded fashion. If we want the user's authOrder to apply to ssh connections, this function must use the proposed selectPreferredAuth() utility function to to make the right authentication choice. - **Modify connect ? Yes** +## Functions that reference rejectUnauthorized may need modification + +If we treat **authOrder** like other connection properties, those functions that process **rejectUnauthorized** may also need to process **authOrder**. This section contains an assessment of whether each such function must be modified. + +- packages\cli\src\config\auto-init\ApimlAutoInitHandler.ts + + - doAutoInit - This function uses a connection to APIML. However, it is passed an AbstractSession object. An authOrder property should already be placed into the AbstractSession by a calling function. + - **Modify doAutoInit ? No** + +- packages\cli\src\imperative.ts + + - This class provides definitions used to create the zowe command tree and other CLI operating properties. It includes command-line options for connection properties (like host, port, user, and password). Unless we chose to add authOrder as a command line option (contrary to this proposal) this class should not require modification. + - **Modify imperative.ts? No** + +- packages\core\src\constants\Core.constants.ts + + - BaseProfile - This object contains a schema definition for a base profile. An authOrder property must be added to this object. + - **Modify BaseProfile ? Yes** + +- packages\imperative\src\config\src\ProfileInfo.ts + + - createSession - This function creates a session with key connection properties. An authOrder property must be added to those properties. + - **Modify createSession ? Yes** + +- packages\imperative\src\imperative\src\config\cmd\import\import.handler.ts + + - buildSession - This class imports a config file from a URL. To connect to the URL, this function specifically uses only user & password authentication. It does not access the URL through z/OSMF or APIML. Providing an ability to connect with other credentials for this purpose is beyond the scope of this proposal, and so will not be done as part of this design. + - **Modify buildSession ? No** + +- packages\imperative\src\rest\src\client\AbstractRestClient.ts + + - buildOptions - The constructor for the AbstractRestClient requires an AbstractSession object. That AbstractSession should have already been populated with connection properties. Multiple authentication types should be scrubbed from the AbstractSession by earlier code. Therefore, no change should be required in buildOptions. + - **Modify buildOptions ? No** + +- packages\imperative\src\rest\src\client\ProxySettings.ts + + - getProxyAgent - This function requires an ISession object. The ISession.authTypeOrder should have already been populated. Therefore, no change should be required in getProxyAgent + - **Modify getProxyAgent ? No** + +- packages\imperative\src\rest\src\client\doc\IHTTPSOptions.ts + + - IHTTPSOptions - This interface is only used by AbstractRestClient. Credentials are never extracted from nor added to this an instance of this interface. Therefore there is no reason to add authOrder to this interface. + - **Modify IHTTPSOptions ? No** + +- packages\imperative\src\rest\src\session\AbstractSession.ts + + - buildSession - This function was analyzed in the previous section of this document. + - **Modify buildSession ? Yes** + +- packages\imperative\src\rest\src\session\doc\ISession.ts + + - authTypeOrder - This property was analyzed in the previous section of this document. + - **Modify authTypeOrder ? Maybe** + +- packages\zosjobs\src\GetJobs.ts + + - getJob - This function displays rejectUnauthorized in a diagnostic message. No need to process authOrder here. + - **Modify getJob ? No** + +- packages\zosmf\src\ZosmfSession.ts + + - This Class contains option definitions for connection properties that can be defined in profiles. A definition of the authOrder connection property with a name like ZOSMF_OPTION_AUTH_ORDER should be added. If we choose to not allow authOrder to be specified on the command line (as recommended by this proposal), we may have to provide a definition someplace else. The **autoSave** property could be similar example. + - **Modify ZosmfSession.ts ? Yes** + +- packages\zosmf\src\constants\Zosmf.messages.ts + + - This class provides message text used to display error details. There is no clear reason to add authOrder to this class. + - **Modify Zosmf.messages.ts ? No** + +- packages\zosmf\src\constants\Zosmf.profile.ts + + - ZosmfProfile - This class provides the property definitions for a zosmf profile type. We must add a definition for the authOrder property. + - **Modify Zosmf.profile.ts ? Yes** + ## New functions that must be added This section describes new functions that must be added to achieve the desired functionality. From 9ee18b97061ec05607e444fa006dfd172850fb79 Mon Sep 17 00:00:00 2001 From: Gene Johnston Date: Wed, 4 Dec 2024 18:31:41 -0500 Subject: [PATCH 08/25] Add AuthOrder class and update design doc Signed-off-by: Gene Johnston --- docs/Design_for_selecting_auth_type.md | 235 ++++++------- .../src/rest/src/session/AuthOrder.ts | 326 ++++++++++++++++++ 2 files changed, 438 insertions(+), 123 deletions(-) create mode 100644 packages/imperative/src/rest/src/session/AuthOrder.ts diff --git a/docs/Design_for_selecting_auth_type.md b/docs/Design_for_selecting_auth_type.md index e9249f69b8..88c7ed944b 100644 --- a/docs/Design_for_selecting_auth_type.md +++ b/docs/Design_for_selecting_auth_type.md @@ -8,7 +8,7 @@ Users may not intentionally specify multiple credentials for the same operation. A user may use the same user and password to authenticate to most of their services. It makes sense to place that user & password in a base profile so that they are automatically available to every service. This reduces redundancy and reduces maintenance efforts. If one plugin requires a token for authentication, the user would store a token value within that plugin's profile. For that plugin, both the user & password and a token will be available when Zowe CLI attempts to make a connection to that service. For this example, the token is the right choice for this service. For historical reasons, Zowe always selects a user & password over a token when both are available. The use of user & password does not give the desired results. -When a site gradually deploys API-ML, they will very likely encounter another (but opposite) authentication issue. Sites login to APIML to obtain a token, which is then used to authenticate all future requests to services through API-ML. The API-ML token is typically stored in a base profile so that connections to all services are done through API-ML with its token. When a new service is brought on-line at a site, it is common for that service to not be immediately integrated with APIML. For at least a period of time, the site makes a direct connection to that service. The site adds user/password properties to that service's profile to authenticate that direct connection. Once again, both user & password and a token are available when Zowe attempts to connect to the service for that profile. In this case, the user & password are the right choice for this service. This is the opposite choice from the previous example. +In another example, sites gradually deploy API-ML and can easily encounter another (but opposite) authentication issue. Sites login to APIML to obtain a token, which is then used to authenticate all future requests to services through API-ML. The API-ML token is typically stored in a base profile so that connections to all services are done through API-ML with its token. When a new service is brought on-line at a site, it is common for that service to not be immediately integrated with APIML. For at least a period of time, the site makes a direct connection to that service. The site adds user/password properties to that service's profile to authenticate that direct connection. Once again, both user & password and a token are available when Zowe attempts to connect to the service for that profile. In this case, the user & password are the right choice for this service. However, this is the opposite choice from the previous example. As these examples demonstrate, Zowe cannot simply change which authentication type should be given preference. It varies based on what a site is trying to do. That order might also change from one part of the customer's configuration to another. The preferred order in which credentials are chosen is further complicated when we consider that certificates may also be used by a site for some of its services. @@ -20,7 +20,7 @@ In this section we identify the key features that a solution would have to provi - The user must be able to change that authentication order for different parts of the Zowe client configuration. -- Zowe client logic must be enhanced to select the authentication type for a profile used to make a connection based on a user-specified preferred order. +- Zowe client logic must be enhanced to select the authentication type for a profile based on a user-specified preferred order. - Zowe client extenders (CLI plugins and ZE extensions) should **not** be able to alter the order of authentication types. The user should control that choice. @@ -30,19 +30,19 @@ In this section we identify the key features that a solution would have to provi - Zowe has an order of precedence for obtaining property values (config file, environment variable, command line). While a command line option will override the same property stored in a config file, it should not alter the order of authentication. - - For example, if a user specifies a token value on the command line, it will override a token value stored in a config file. However, + - For example, assume that a user has specified that a certificate should be used before a token. Assume that a certificate is supplied in a config file and a token is supplied on the command line. - - If the user has specified that a certificate should be used before a token, and a certificate is also available in the configuration, the certificate should be used because the user configured that certificates should be used before tokens. + - The certificate should be used because the user configured that certificates should be used before tokens. - The token should not be used just because it was supplied on the command line. - The authentication order only identifies the order in which Zowe chooses the **one** authentication method that will be used. If that first authentication method fails, Zowe will not make a second attempt to authenticate with any of the later authentication methods. -- Once an authentication is selected, our logic should ensure that only that one type of authentication is placed into a session object. Thus, logic in down-stream handlers will not alter the order of authentication selection, simply by testing for the authentications within the session in a different order than the order that the user specified. +- Once an authentication is selected, our logic should ensure that only that one type of authentication is placed into a session object. Thus, logic in down-stream Zowe code will not alter the order of authentication selection, simply by testing for the authentications from the session in a different order than the order that the user specified. - - If we were to continue to allow multiple authentications to be placed into a session, we will have to re-work various functions to test for authentications in the desired order. We will also have to provide the object containing that well-defined order to each such function. This will increase the amount of code being changed, and thus increase the probability of mistakes. + - If we were to continue to allow multiple authentications to be placed into a session, we will have to re-work various functions to test for authentications in the desired order. We will also have to provide the object containing that well-defined order to each such function. This will increase the amount of code being changed, and thus increase the time to market and increase the probability of mistakes. -- Zowe clients do not currently support AUTH_TYPE_CERT_PFX, so we cannot add it to a set of available authentications. If AUTH_TYPE_CERT_PFX is implemented, it should be placed immediately after AUTH_TYPE_CERT_PEM in the default order. +- Zowe clients do not currently support AUTH_TYPE_CERT_PFX, so we cannot add it to a set of available authentications at this time. If AUTH_TYPE_CERT_PFX is implemented, it should be placed immediately after AUTH_TYPE_CERT_PEM in the default order. - Customers should be able to specify AUTH_TYPE_NONE in their preferred order of authentications. While it is not advisable to have no authentication, if a customer has a service that requires no authentication, the customer should be able to specify AUTH_TYPE_NONE at the top of their list of authentications applicable to that particular profile. @@ -50,6 +50,8 @@ In this section we identify the key features that a solution would have to provi - A customer-specified list of authentications must contain at least one of our supported authentications. +- The `--show-input-only` option should show the order of authentication as part of its displayed connection properties. + ## Historical behavior The objective of this feature is to enable users to define the order in which an authentication type is selected. However, when a user does not specify any such order, the default order should reflect past behavior. @@ -57,21 +59,15 @@ The objective of this feature is to enable users to define the order in which an - The AbstractRestClient currently enforces an order of: - AUTH_TYPE_TOKEN - - AUTH_TYPE_BASIC - - AUTH_TYPE_BEARER - - AUTH_TYPE_CERT_PEM - Zowe classes other than AbstractRestClient (like AbstractSession) currently override the authentication order from AbstractRestClient into: - AUTH_TYPE_BASIC - - - AUTH_TYPE_BEARER - - AUTH_TYPE_TOKEN - + - AUTH_TYPE_BEARER - AUTH_TYPE_CERT_PEM These selections of authentication should be maintained as the default selections for their respective classes to avoid introducing a breaking change. @@ -86,17 +82,25 @@ A new profile property named **authOrder** should be created to enable users to - A parent profile of a nested configuration. - - Any profile of type **zosmf**. + - Any service profile. - Any profile specific to a plugin (or VSCode extension) that supports a REST connection. For example an **endevor** profile could contain an **authOrder** property, but an **endevor-location** profile would not. - Our existing inheritance of connection properties should also apply to the inheritance of the authOrder property. - We should be able to use our logic for where and how the **rejectUnauthorized** property is handled as a model for how we handle the **authOrder** property. + +- A new property can be added to the **properties** of any profile. Since we are proposing that **authOrder** be handled by imperative functions, no CLI plugin or ZE extension will need modification for the **authOrder** property to be correctly handled. + +- Such a new item within the properties object will not currently be included in the zowe.schema.json file. An IntelliSense-like editor will not display **authOrder** as an option when a user starts to add a new property in a profile. + + - We do not want to require plugins/extensions to add **authOrder** to their profiles. Requesting new work from plugins/extensions in the middle of a release's lifecycle can be a burden for those contributors. - - One difference from **rejectUnauthorized** is that for **authOrder** we should accept its value from a zowe.config.json file, but not from a command line argument or environment variable. Because **authOrder** is an array, it will be difficult and error-prone for users to correctly specify **authOrder** on a command line or in an environment variable. + - When the CLI is installed, the schema is not updated, so the CLI cannot currently take on that burden. It is possible that a CLI post-install step could be performed to update the schema. This is not considered to be a trivial task. + + - It should be noted if **authOrder** is absent from the schema, users will lose a nice convenience, but the user's config will not be broken in any functional way. -As an example, the user could specify their desired authOrder like this: +To represent a series of values, the **authOrder** property should be an array. The following example shows how a user could specify their desired authOrder. ``` "properties": { @@ -110,7 +114,7 @@ As an example, the user could specify their desired authOrder like this: The programmatic definition of authOrder would be: ``` -authOrder[SessConstants.AUTH_TYPE_CHOICES]    +authOrder: SessConstants.AUTH_TYPE_CHOICES[] ``` The current set of AUTH_TYPE_CHOICES are: @@ -141,18 +145,82 @@ That addition would enable customers to also specify the authentication order of - We must notify extenders to guide their customers to supply an appropriate authOrder property if their extension needs a non-default order. +## A new class must be created to support authOrder + +A new class located in + +    [zowe-cli/packages/imperative/src/rest/src/session/AuthOrder.ts](https://github.com/zowe/zowe-cli/blob/master/packages/imperative/src/rest/src/session/AuthOrder.ts) + +should be implemented to detect the authentication order supplied by a user, and place the correct set of credentials into a session. The class currently exists as a workable proof of concept. With no confirmation of the many edge cases, it does appear to accomplished the basic needs. It can be finalized once it is fully integrated with existing Zowe logic. + +This class is declared **@internal** which will limit its use to the imperative package. More access can be provided during implementation if we identify additional places in Zowe code that need to use functions from this class. + +Functions in the AuthOrder class include: + +- setTopDefaultAuth - This function will set the top authentication choice (either basic or token) when a default authOrder must be created because the user did not supply an auth order. Through the use of this function we can maintain backward compatibility and not introduce a breaking change. + + When authOrder has not been configured into zowe.config.json, we create a default order to be backward compatible. + + - If an argument of AUTH_TYPE_TOKEN is supplied, it should place token at the top of the order, resulting in this order: + + - AUTH_TYPE_TOKEN + + - AUTH_TYPE_BASIC + + - AUTH_TYPE_BEARER + + - AUTH_TYPE_CERT_PEM + + - AUTH_TYPE_NONE + + - If AUTH_TYPE_BASIC is supplied (or this function is not called), it should place basic at the top of the order, resulting in this order: + + - AUTH_TYPE_BASIC + + - AUTH_TYPE_TOKEN + + -  AUTH_TYPE_BEARER + + - AUTH_TYPE_CERT_PEM + + - AUTH_TYPE_NONE + + - It should be called from AbstractRestClient.constructor to replace its hard-coded array of credential types. + + - It might be called from other existing locations as needed. + +- putTopAuthInSession - This function will find the highest auth type (according to the authOrder) which exists in either the supplied session config or command line arguments, place the credentials associated with that auth type into the supplied session config, and remove the credentials for all other auth types from the supplied session config. + + - It might be called from ConnectionPropsForSessCfg.addPropsOrPrompt. + + - By temporarily modifying a local copy of addPropsOrPrompt to call putTopAuthInSession, I confirmed that the resulting session object contained only those credentials obtained from the command arguments associated with highest authentication type (as determined from the authOrder). + + - It might be called from ConnectionPropsForSessCfg.resolveSessCfgProps. + + - It might be called from ProfileInfo.createSession. + + - While apps like ZE do not take command line arguments, they do form an ICommandArguments object. The createSesion function already has such an object, so it can be passed to putTopAuthInSession. + + - Any other location in ZE-related code which needs to call putTopAuthInSession will need to form an ICommandArguments object. + + - It might be called from other existing locations as needed. + +- cacheAuthOrder - This function stores the authOrder property from the supplied command arguments for later use. In the prototype, it is only called from putTopAuthInSession. However, cacheAuthOrder is available should we find that we need to to call it from other locations (like at the end of the CommandProcessor.prepare function). + +- removeExtraCredsFromSess - This function removes all credential properties from the supplied session except for the top available authentication type. In the prototype, it is only called from putTopAuthInSession. However, removeExtraCredsFromSess is available should we find that we must scrub unneeded credentials from a session in other location in existing Zowe code. + ## Functions that reference AUTH_TYPE may need modification The set of candidates for modification consist of all functions that contain the string ***"AUTH_TYPE_"***. This section contains an assessment of whether each such function affects the authentication order and must be modified. - cli\src\config\auto-init\ApimlAutoInitHandler - - doAutoInit - This function logins into APIML with the session object if either user & password or cert are in the session object. doAutoInit does not make a selection of order. It lets Login.apimlLogin() make that decision. If only the selected authentication is placed into a session, then no need to change. + - doAutoInit - This function logins into APIML with the session object if either user & password or cert are in the session object. doAutoInit does not make a selection of order. However, it is passed an AbstractSession object. The session credentials should already have been placed into the AbstractSession by a previously called function - **Modify doAutoInit ? No** - core\src\rest\ZosmfRestClient.ts - - processError - This function just alters error message text based on authentications found in the session. If only the selected authentication is placed into a session, then no need to change. + - processError - This function just alters error message text based on authentications found in the session. If only the top authentication type resides in a session, then no need to change. - **Modify processError ? No** - imperative\src\config\src\ConfigAutoStore.ts @@ -173,11 +241,11 @@ The set of candidates for modification consist of all functions that contain the - **Modify buildOptions ? No** - - constructor - This function currently hard-codes an order of authentication types into the ISession.authTypeOrder array. We must create a means to record the customer-defined order in this function. This function should call a common function (tentatively named recordAuthOrderFromConfig) to do this work. recordAuthOrderFromConfig should be able to set the old hard-coded order (as the default order) only if a customer does not specify an order. + - constructor - This function currently hard-codes an order of authentication types into the ISession.authTypeOrder array contained the supplied session. After AuthOrder is implemented, the ISession.authTypeOrder array should already be set in the session which is passed as an argument to this constructor. We should remove the logic that hard-codes the authTypeOrder into the session. - **Modify constructor? yes** - - Each of the following functions reference AUTH_TYPE_XXX to a place an identified type into the ISession.type property. Since buildOptions calls just one of the following functions based on being the first available authentication in the ISession.authTypeOrder array, none of these functions need to change. + - Each of the following functions reference AUTH_TYPE_XXX to a place an identified type into the ISession.type property of the session. Since buildOptions calls just one of the following functions based on being the first available authentication in the ISession.authTypeOrder array, none of these functions need to change. Note that these functions may no longer be necessary since AuthOrder.putTopAuthInSession should have already set the ISession.type property. - **Modify setBearerAuth ? No** @@ -189,23 +257,23 @@ The set of candidates for modification consist of all functions that contain the - imperative\src\rest\src\session\AbstractSession.ts - - buildSession - This private function is called by the constructor, which accepts an Isession object. A caller could populate multiple authentications (and related properties) into that supplied session. Session.buildSession() will have to scrub all but the highest priority available authentication from the session. We should create a common utility function (tentatively named selectPreferredAuth) to do the scrubbing. selectPreferredAuth can be also be called from ConnectionPropsForSessCfg.resolveSessCfgProps function as described below. + - buildSession - This private function is called by the constructor, which accepts an Isession object. A caller could populate multiple authentications (and related properties) into that supplied session. Ideally the caller of the Session.constructor will have already called ConnectionPropsForSessCfg.addPropsOrPrompt() which will have already removed all but the highest priority available authentication from the session. We should confirm that this always true. If not, buildSession may have to call AuthOrder.putTopAuthInSession. We should also check whether assignments to session properties by buildSession will override any of the properties set by AuthOrder.putTopAuthInSession. - - **Modify buildSession ? Yes** + - **Modify buildSession ? Maybe** - - DEFAULT_TYPE - This simply a constant definition set to AUTH_TYPE_NONE. It is not used in any CLI or ZE code outside of this AbstractSession class. Because it is a public property, it cannot be removed without risk of breaking change. If AUTH_TYPE_NONE is added to the ISession.authTypeOrder array, DEFAULT_TYPE should be deprecated. + - DEFAULT_TYPE - This is simply a constant definition set to AUTH_TYPE_NONE. It is not used in any CLI or ZE code outside of this AbstractSession class. Because it is a public property, it cannot be removed without risk of breaking change. If AUTH_TYPE_NONE is added to the ISession.authTypeOrder array, DEFAULT_TYPE should be deprecated. - **Modify DEFAULT_TYPE ? Yes** - imperative\src\rest\src\session\ConnectionPropsForSessCfg.ts - - addPropsOrPrompt - This function only uses AUTH_TYPE_XXX to determine whether a token and a cert are irrelevant based on the existence of AUTH_TYPE_TOKEN. Those items should not need to change. However, addPropsOrPrompt accepts three parameters, each of which can have property overrides of authentication. After calling resolveSessCfgProps, addPropsOrPrompt continues to modify the session properties with values from its parameters. Thus addPropsOrPrompt must be refactored to work properly with a refactored resolveSessCfgProps. + - addPropsOrPrompt - Near the end of this function, a call to AuthOrder.putTopAuthInSession will ensure that the right authentication resides in the session. - **Modify addPropsOrPrompt ? Yes** - - resolveSessCfgProps - Many functions call this function before creating a new session. This function could scrub all but the selected authentication and related properties from the session object. However, callers could call 'new Session()' without first calling resolveSessCfgProps(). Thus, Session.buildSession() will have to perform the same scrubbing of authentications from the session. Both Session.buildSession and resolveSessCfgProps should call selectPreferredAuth() to do the scrubbing. + - resolveSessCfgProps - Several functions call this function before creating a new session. Either this function must call AuthOrder.putTopAuthInSession, or each caller of resolveSessCfgProps must first call putTopAuthInSession. - - **Modify resolveSessCfgProps ? Yes** + - **Modify resolveSessCfgProps ? Maybe** - setTypeForTokenRequest - This function handles setting authentication to AUTH_TYPE_TOKEN to get a token back from user & password. This does not appear to require any change, but it should be revisited after resolveSessCfgProps is refactored. @@ -228,18 +296,18 @@ The set of candidates for modification consist of all functions that contain the - imperative\src\rest\src\session\doc\ISession.ts - - authTypeOrder - This property could be the focus of our refactoring, but currently it is hard-coded and only used in AbstractRestClient.constructor & AbstractRestClient.buildOptions. This property should be used to hold the customer-defined order of authentication types. There is no reason to change this property. Other code will repopulate authTypeOrder's set of values based on customer input. It is possible that an ISession may not be available at the time we want to store the customer-defined order of authentication types. Another object may need to store that order, which is later transferred into this ISession property. The only reason that we may want to change this item is to rename it from authtypeOrder to authOrder to reflect the same, simpler object name that a user will use to specify the authentication order. We should only change the name of authTypeOrder if it is only used internally by Zowe (and thus would be a non-breaking change). + - authTypeOrder - This property is used to hold the order of authentication types. It is currently populated by hard-coded login and is only used in AbstractRestClient.constructor & AbstractRestClient.buildOptions. There is no reason to change this property. AuthOrder.putTopAuthInSession should populate authTypeOrder's set of values based on customer input. The only change for authTypeOrder will be to remove the comment that states that this property is hard-coded. - - **Modify authTypeOrder ? Maybe** + - **Modify authTypeOrder ? Yes** - packages\zosuss\src\SshBaseHandler.ts - - process - This function explicitly sets a property named **supportedAuthTypes** to AUTH_TYPE_BASIC. It is unclear why there is no option for in this logic for the use of an ssh-key. + - process - This function explicitly sets a property named **supportedAuthTypes** to AUTH_TYPE_BASIC. It is unclear why there is no option in this logic to use an ssh-key. - **Modify process ? Maybe** packages\zosuss\src\Shell.ts - - connect - This function explicitly checks for an ssh key (first) or a password (second) in a hard-coded fashion. If we want the user's authOrder to apply to ssh connections, this function must use the proposed selectPreferredAuth() utility function to to make the right authentication choice. + - connect - This function explicitly checks for an ssh key (first) or a password (second) in a hard-coded fashion. If we want the user's authOrder to apply to ssh connections, this function must call the proposed AuthOrder.putTopAuthInSession to to make the right authentication choice. - **Modify connect ? Yes** ## Functions that reference rejectUnauthorized may need modification @@ -248,13 +316,13 @@ If we treat **authOrder** like other connection properties, those functions that - packages\cli\src\config\auto-init\ApimlAutoInitHandler.ts - - doAutoInit - This function uses a connection to APIML. However, it is passed an AbstractSession object. An authOrder property should already be placed into the AbstractSession by a calling function. - - **Modify doAutoInit ? No** + - doAutoInit - This function was analyzed in the previous section of this document. + - **Modify doAutoInit ? No** - packages\cli\src\imperative.ts - - This class provides definitions used to create the zowe command tree and other CLI operating properties. It includes command-line options for connection properties (like host, port, user, and password). Unless we chose to add authOrder as a command line option (contrary to this proposal) this class should not require modification. - - **Modify imperative.ts? No** + - This class provides definitions used to create the Zowe command tree and other CLI operating properties. It includes command-line options for connection properties (like host, port, user, and password). We must add **authOrder** as a new command line option into this class. + - **Modify imperative.ts? Yes** - packages\core\src\constants\Core.constants.ts @@ -263,17 +331,17 @@ If we treat **authOrder** like other connection properties, those functions that - packages\imperative\src\config\src\ProfileInfo.ts - - createSession - This function creates a session with key connection properties. An authOrder property must be added to those properties. + - createSession - This function creates a session with key connection properties. Depending on the modification choices made for ConnectionPropsForSessCfg.resolveSessCfgProps, this function may have to call AuthOrder.putTopAuthInSession directly. - **Modify createSession ? Yes** - packages\imperative\src\imperative\src\config\cmd\import\import.handler.ts - - buildSession - This class imports a config file from a URL. To connect to the URL, this function specifically uses only user & password authentication. It does not access the URL through z/OSMF or APIML. Providing an ability to connect with other credentials for this purpose is beyond the scope of this proposal, and so will not be done as part of this design. + - buildSession - This function was analyzed in the previous section of this document. - **Modify buildSession ? No** - packages\imperative\src\rest\src\client\AbstractRestClient.ts - - buildOptions - The constructor for the AbstractRestClient requires an AbstractSession object. That AbstractSession should have already been populated with connection properties. Multiple authentication types should be scrubbed from the AbstractSession by earlier code. Therefore, no change should be required in buildOptions. + - buildOptions - This function was analyzed in the previous section of this document. - **Modify buildOptions ? No** - packages\imperative\src\rest\src\client\ProxySettings.ts @@ -289,21 +357,21 @@ If we treat **authOrder** like other connection properties, those functions that - packages\imperative\src\rest\src\session\AbstractSession.ts - buildSession - This function was analyzed in the previous section of this document. - - **Modify buildSession ? Yes** + - **Modify buildSession ? Maybe** - packages\imperative\src\rest\src\session\doc\ISession.ts - authTypeOrder - This property was analyzed in the previous section of this document. - - **Modify authTypeOrder ? Maybe** + - **Modify authTypeOrder ? No** - packages\zosjobs\src\GetJobs.ts - - getJob - This function displays rejectUnauthorized in a diagnostic message. No need to process authOrder here. + - getJob - This function displays rejectUnauthorized in a diagnostic message. No need to process authOrder here. - **Modify getJob ? No** - packages\zosmf\src\ZosmfSession.ts - - This Class contains option definitions for connection properties that can be defined in profiles. A definition of the authOrder connection property with a name like ZOSMF_OPTION_AUTH_ORDER should be added. If we choose to not allow authOrder to be specified on the command line (as recommended by this proposal), we may have to provide a definition someplace else. The **autoSave** property could be similar example. + - This Class contains option definitions for connection properties that can be defined in profiles. A definition of the authOrder connection property with a name like ZOSMF_OPTION_AUTH_ORDER should be added. - **Modify ZosmfSession.ts ? Yes** - packages\zosmf\src\constants\Zosmf.messages.ts @@ -316,83 +384,4 @@ If we treat **authOrder** like other connection properties, those functions that - ZosmfProfile - This class provides the property definitions for a zosmf profile type. We must add a definition for the authOrder property. - **Modify Zosmf.profile.ts ? Yes** -## New functions that must be added - -This section describes new functions that must be added to achieve the desired functionality. - -This section is a work in progress. The ideas are still pipe dreams and not confirmed by analysis of our code. - -- Utility function to record the order of authentication types. - - > --- - > - > @internal - > - > public ClassNotDecidedYet.recordAuthOrderFromConfig(tokenIsTopDefault: boolean = false): string[ ] { - > - > - This function should obtain the customer-defined authentication order from the Zowe client config file. - > - > - It must confirm that the customer specified valid values. If not, it should record the error, and fall-back to the appropriate default order. - > - > - It should place those authentications into a string array that is returned. - > - > - Maybe instead of returning that array, the array is set into come commonly accessible object (like the Config class)? - > - > - If no order has been configured into zowe.config.json, it should create a default order to be backward compatible. - > - > - If tokenIsTopDefault is true, it should place token at the top of the order, resulting in this order: - > - > - AUTH_TYPE_TOKEN - > - > - AUTH_TYPE_BASIC - > - > - AUTH_TYPE_BEARER - > - > - AUTH_TYPE_CERT_PEM - > - > - AUTH_TYPE_CERT_PFX - > - > - AUTH_TYPE_NONE - > - > - If tokenIsTopDefault is false, it should place basic at the top of the order, resulting in this order: - > - > - AUTH_TYPE_BASIC - > - > - AUTH_TYPE_TOKEN - > - > -  AUTH_TYPE_BEARER - > - > - AUTH_TYPE_CERT_PEM - > - > - AUTH_TYPE_CERT_PFX - > - > - AUTH_TYPE_NONE - > - > } - -- Utility function to get the array of authentication types. - - > --- - > - > public ClassNotDecidedYet.getAuthOrder(): string[ ] { - > - > - This function should return the recorded array of authentication types. They will be in the order of top preference first. - > - > - Maybe this function lives in Config?. - > - > } - -- Utility function to select the top available authentication type in a session. - - > --- - > - > public ClassNotDecidedYet.selectPreferredAuth(iSessObj: ISession): void { - > - > - This function should use the new getAuthOrder function to get the ordered array of authentications. - > - It should confirm if the authentications (in the preferred order) and their associated properties are available in the iSessObj. - > - Once the first authentication is found, the properties related to all other authentications should be removed from iSessObj. - > - Maybe this function lives in AbstractSession?. - > - > } - -# \ No newline at end of file +## \ No newline at end of file diff --git a/packages/imperative/src/rest/src/session/AuthOrder.ts b/packages/imperative/src/rest/src/session/AuthOrder.ts new file mode 100644 index 0000000000..3052c86e72 --- /dev/null +++ b/packages/imperative/src/rest/src/session/AuthOrder.ts @@ -0,0 +1,326 @@ +/* +* 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. +* +*/ + +import { ICommandArguments } from "../../../cmd"; +import { ImperativeError } from "../../../error"; +import { ISession } from "./doc/ISession"; +import { Logger } from "../../../logger"; +import * as SessConstants from "./SessConstants"; + +/** + * @internal - Cannot be used outside of the imperative package + * + * The purpose of this class is to detect an authentication order property + * supplied by a user in a profile, command line, or environment variable. + * That authOrder is then used to place the correct set of credentials into + * a session for authentication. + */ +export class AuthOrder { + + /** + * When a user does not supply an auth order, Zowe clients will use a + * hard-coded default order. This property records whether AUTH_TYPE_BASIC + * or AUTH_TYPE_TOKEN is the top auth choice in the order. + */ + private static m_topDefaultAuth: string = SessConstants.AUTH_TYPE_BASIC; + + /** + * This array of authentication types specifies the order of preferred + * authentication. It contains the user-specified order, or a default order + * if the user does not specify an order. m_authOrder[0] is the highest + * preferred authentication. + */ + private static m_authOrder: SessConstants.AUTH_TYPE_CHOICES[] = null; + + // *********************************************************************** + /** + * Set the top auth type when a default authOrder is used. Previously, + * two different hard-coded orders were present in the Zowe clients. + * Both hard-coded orders are now provided by this class. Zowe code, + * that still sets a specific order for backward compatibility, now + * calls this function to ensure that that the original behavior remains + * the same and avoids a breaking change when a user has not specified + * an authOrder property. + * + * @param topDefaultAuth - Input. + * The top authentication type that will be used when forming a + * default authOrder. + */ + public static setTopDefaultAuth( + topDefaultAuth: typeof SessConstants.AUTH_TYPE_BASIC | typeof SessConstants.AUTH_TYPE_TOKEN + ): void { + AuthOrder.m_topDefaultAuth = topDefaultAuth; + } + + // *********************************************************************** + /** + * Cache the authOrder property from the supplied cmdArgs. If no authOrder exists + * in cmdArgs, a default authOrder is created and cached. + * + * @param cmdArgs - Input. + * The set of arguments that the calling function is using. + */ + private static cacheAuthOrder(cmdArgs: ICommandArguments): void { + // have we already cached the authOrder? + if (AuthOrder.m_authOrder !== null) { + // start over with an empty order. + AuthOrder.m_authOrder = null; + } + + if (cmdArgs.authOrder) { + // validate each user-supplied type of authentication + for (const nextUserAuth of cmdArgs.authOrder) { + switch (nextUserAuth) { + case SessConstants.AUTH_TYPE_BASIC: + case SessConstants.AUTH_TYPE_TOKEN: + case SessConstants.AUTH_TYPE_BEARER: + case SessConstants.AUTH_TYPE_CERT_PEM: + case SessConstants.AUTH_TYPE_NONE: + if (AuthOrder.m_authOrder === null) { + AuthOrder.m_authOrder = []; + } + AuthOrder.m_authOrder.push(nextUserAuth); + break; + default: + Logger.getImperativeLogger().error( + `The authentication = '${nextUserAuth}' is not valid and will be ignored.` + ); + /* todo: Remove diagnostic print statements + console.log("____ cacheAuthOrder: nextUserAuth = '" + nextUserAuth + "' is not valid and will be ignored."); + // end Remove todo: */ + } + } + } + + // the user supplied an authOrder + if (AuthOrder.m_authOrder !== null) { + /* todo: Remove diagnostic print statements + console.log("____ cacheAuthOrder: cached authOrder = " + AuthOrder.m_authOrder); + // end Remove todo: */ + return; + } + + // No authOrder was supplied by the user. Create a default order. + AuthOrder.m_authOrder = []; + if (AuthOrder.m_topDefaultAuth === SessConstants.AUTH_TYPE_BASIC) { + // we want user & password auth as the top choice + AuthOrder.m_authOrder.push(SessConstants.AUTH_TYPE_BASIC); + AuthOrder.m_authOrder.push(SessConstants.AUTH_TYPE_TOKEN); + } else { + // we want token auth as the top choice + AuthOrder.m_authOrder.push(SessConstants.AUTH_TYPE_TOKEN); + AuthOrder.m_authOrder.push(SessConstants.AUTH_TYPE_BASIC); + } + // add remaining auth types. We do not include 'none' in our defaults. + AuthOrder.m_authOrder.push(SessConstants.AUTH_TYPE_BEARER); + AuthOrder.m_authOrder.push(SessConstants.AUTH_TYPE_CERT_PEM); + /* todo: Remove diagnostic print statements + console.log("____ cacheAuthOrder: cached authOrder = " + AuthOrder.m_authOrder); + // end Remove todo: */ + } + + // *********************************************************************** + /** + * Find the highest auth type (according to the authOrder) which exists + * in either the supplied session config or command line arguments. + * Then place the credentials associated with that auth type into the + * supplied session config. Credentials for all other auth types are + * removed from the session config. + * + * @param sessCfg - Modified. + * Authentication properties are added to and removed from this + * session configuration, which can already have properties in + * this object when passed to this function. + * + * @param cmdArgs - Input. + * The set of arguments with which the calling function is operating. + * For CLI, the cmdArgs come from the command line, profile, or + * environment. Other apps can place relevant arguments into this + * object to be processed by this function. + */ + public static putTopAuthInSession( + sessCfg: SessCfgType, + cmdArgs: ICommandArguments + ): void { + let sessTypeToUse: SessConstants.AUTH_TYPE_CHOICES = null; + + // cache the correct authOrder to use + AuthOrder.cacheAuthOrder(cmdArgs); + + /* todo: Remove diagnostic print statements + sessCfg.tokenType = "apimlAuthenticationToken"; + sessCfg.tokenValue = "SomeTokenValue"; + sessCfg.cert = "./certFile.txt"; + sessCfg.certKey = "./certKeyFile.txt"; + sessCfg.authTypeOrder = []; + sessCfg.authTypeOrder.push("bogusAuthType1"); + sessCfg.authTypeOrder.push("bogusAuthType2"); + sessCfg.authTypeOrder.push("bogusAuthType3"); + sessCfg.authTypeOrder.push("bogusAuthType4"); + console.log("____ putTopAuthInSession:\ncmdArgs = " + JSON.stringify(cmdArgs, null, 2)); + console.log("____ putTopAuthInSession:\nsessCfg before processing = " + JSON.stringify(sessCfg, null, 2)); + // end Remove todo: */ + + // Detect the first auth type (from our auth order) provided in the session config or in command args. + // Ensure that the auth properties are placed in the session config. + // Record the detected auth type for use as the session type. + let errMsg: string; + for (const nextAuth of AuthOrder.m_authOrder) { + switch (nextAuth) { + case SessConstants.AUTH_TYPE_BASIC: + // todo: do we have to check for sessCfg.base64EncodedAuth ? + if (cmdArgs.user?.length > 0) { + sessCfg.user = cmdArgs.user; + } + if (cmdArgs.password?.length > 0) { + sessCfg.password = cmdArgs.password; + } + if (sessCfg.user?.length > 0 && sessCfg.password?.length > 0) { + // TODO: Confirm in ConnectionPropsForSessCfg that requestToken will work ok with sessTypeToUse + sessTypeToUse = SessConstants.AUTH_TYPE_BASIC; + } + break; + case SessConstants.AUTH_TYPE_TOKEN: + if (cmdArgs.tokenType?.length > 0) { + sessCfg.tokenType = cmdArgs.tokenType; + } + if (cmdArgs.tokenValue?.length > 0) { + sessCfg.tokenValue = cmdArgs.tokenValue; + } + if (sessCfg.tokenType?.length > 0 && sessCfg.tokenValue?.length > 0) { + sessTypeToUse = SessConstants.AUTH_TYPE_TOKEN; + } + break; + case SessConstants.AUTH_TYPE_BEARER: + if (cmdArgs.tokenType?.length > 0) { + sessCfg.tokenType = cmdArgs.tokenType; + } + if (cmdArgs.tokenValue?.length > 0) { + sessCfg.tokenValue = cmdArgs.tokenValue; + } + // a tokenValue with no tokenType implies a bearer token + if (!(sessCfg.tokenType?.length > 0) && sessCfg.tokenValue?.length > 0) { + sessTypeToUse = SessConstants.AUTH_TYPE_BEARER; + } + break; + case SessConstants.AUTH_TYPE_CERT_PEM: + if (cmdArgs.certFile?.length > 0) { + sessCfg.cert = cmdArgs.certFile; + } + if (cmdArgs.certKeyFile?.length > 0) { + sessCfg.certKey = cmdArgs.certKeyFile; + } + if (sessCfg.cert?.length > 0 && sessCfg.certKey?.length > 0) { + sessTypeToUse = SessConstants.AUTH_TYPE_CERT_PEM; + } + break; + case SessConstants.AUTH_TYPE_NONE: + sessTypeToUse = SessConstants.AUTH_TYPE_NONE; + break; + default: + // authOrder was validated. A wrong value now is our programming error. + errMsg = `authOrder contains an invalid authentication = ${nextAuth}.`; + Logger.getImperativeLogger().error(errMsg); + throw new ImperativeError({ msg: errMsg }); + } + if (sessTypeToUse !== null) { + // stop looking for auth types after we find the first one + break; + } + } + + // When no creds are in the session and AUTH_TYPE_NONE is not in the user's authOrder, + // remove the session type from the session. Otherwise set the type that we found. + if (sessTypeToUse === null) { + delete sessCfg.type; + } else { + sessCfg.type = sessTypeToUse; + } + + // remove all extra auth creds from the session + AuthOrder.removeExtraCredsFromSess(sessCfg); + + // copy our authOrder into the session object + sessCfg.authTypeOrder = [...AuthOrder.m_authOrder]; + + // todo: Should we throw error if no creds are in the session, or let other functions throw the error? + + /* todo: Remove diagnostic print statements + console.log("____ putTopAuthInSession:\nsessCfg after processing = " + JSON.stringify(sessCfg, null, 2)); + // end Remove todo: */ + } + + // *********************************************************************** + /** + * Remove all credential properties from the supplied session except for the + * creds related to the session type specified within the sessCfg argument. + * + * @param sessCfg - Modified. + * Authentication credentials are removed from this session configuration. + */ + private static removeExtraCredsFromSess( + sessCfg: SessCfgType + ): void { + if (AuthOrder.cacheAuthOrder == null) { + const errMsg = "AuthOrder.cacheAuthOrder must be called before AuthOrder.removeExtraCredsFromSess"; + Logger.getImperativeLogger().error(errMsg); + throw new ImperativeError({ msg: errMsg }); + } + if (!sessCfg?.type) { + const errMsg = "Session type must exist in the supplied session."; + Logger.getImperativeLogger().error(errMsg); + throw new ImperativeError({ msg: errMsg }); + } + + // initially set all creds to be removed from the session. Later we delete the desired creds from this set + const credsToRemove = new Set(["user", "password", "base64EncodedAuth", "tokenType", "tokenValue", "cert", "certKey"]); + + // Delete the selected creds from the set of creds that will be removed from our session config. + let errMsg: string; + switch (sessCfg.type) { + case SessConstants.AUTH_TYPE_BASIC: + credsToRemove.delete("user"); + credsToRemove.delete("password"); + credsToRemove.delete("base64EncodedAuth"); + break; + case SessConstants.AUTH_TYPE_TOKEN: + credsToRemove.delete("tokenType"); + credsToRemove.delete("tokenValue"); + break; + case SessConstants.AUTH_TYPE_BEARER: + credsToRemove.delete("tokenValue"); + break; + case SessConstants.AUTH_TYPE_CERT_PEM: + credsToRemove.delete("cert"); + credsToRemove.delete("certKey"); + break; + case SessConstants.AUTH_TYPE_NONE: + break; + default: + // authOrder was validated. A wrong value now is our programming error. + errMsg = `Session an invalid type = ${sessCfg.type}.`; + Logger.getImperativeLogger().error(errMsg); + throw new ImperativeError({ msg: errMsg }); + } + + // remove all auth creds from the session, except the creds for the auth type that we chose to keep + const credIter = credsToRemove.values(); + let nextCredToRemove = credIter.next(); + while (!nextCredToRemove.done) { + /* todo: Remove diagnostic print statements + console.log("____ removeExtraCredsFromSess: deleting = 'sessCfg." + nextCredToRemove.value + "' from the session."); + // end Remove todo: */ + + delete (sessCfg as any)[nextCredToRemove.value]; + nextCredToRemove = credIter.next(); + } + } +} \ No newline at end of file From 94c52d3e2fa14751c34522b3f74c46b6cf88347a Mon Sep 17 00:00:00 2001 From: jace-roell Date: Mon, 9 Dec 2024 11:04:53 -0500 Subject: [PATCH 09/25] initial commit Signed-off-by: jace-roell --- file.ts | 17 +++++++++++++++++ .../imperative/src/config/src/ProfileInfo.ts | 4 ++++ 2 files changed, 21 insertions(+) create mode 100644 file.ts diff --git a/file.ts b/file.ts new file mode 100644 index 0000000000..f490ca01a2 --- /dev/null +++ b/file.ts @@ -0,0 +1,17 @@ +import { ProfileInfo } from "@zowe/imperative"; +import { GetJobs } from "@zowe/zos-jobs-for-zowe-sdk"; +(async () => { + // Load connection info from default z/OSMF profile + const profInfo = new ProfileInfo("zowe"); + await profInfo.readProfilesFromDisk(); + const zosmfProfAttrs = profInfo.getDefaultProfile("zosmf"); + const zosmfMergedArgs = profInfo.mergeArgsForProfile(zosmfProfAttrs!, { getSecureVals: true }); + const session = ProfileInfo.createSession(zosmfMergedArgs.knownArgs); + const owner: string | undefined = session.ISession.user; + // This may take awhile... + const response = await GetJobs.getJobsByOwner(session, owner!); + console.log(response); +})().catch((err) => { + console.error(err); + process.exit(1); +}); \ No newline at end of file diff --git a/packages/imperative/src/config/src/ProfileInfo.ts b/packages/imperative/src/config/src/ProfileInfo.ts index 27119a92b9..dd0c771161 100644 --- a/packages/imperative/src/config/src/ProfileInfo.ts +++ b/packages/imperative/src/config/src/ProfileInfo.ts @@ -597,6 +597,10 @@ export class ProfileInfo { if (osLoc?.global) { layerProperties = this.mLoadedConfig.findLayer(osLoc.user, osLoc.global)?.properties; realBaseProfileName = layerProperties?.defaults.base; + if (!realBaseProfileName && osLoc.user) { + layerProperties = this.mLoadedConfig.findLayer(false, osLoc.global)?.properties; + realBaseProfileName = layerProperties?.defaults.base; + } if (realBaseProfileName) baseProfile = this.mLoadedConfig.api.profiles.buildProfile(realBaseProfileName, layerProperties?.profiles); else baseProfile = null; } From ad13b8b9f3260414135ff56dd638bcbe0f563069 Mon Sep 17 00:00:00 2001 From: jace-roell Date: Tue, 10 Dec 2024 14:09:03 -0500 Subject: [PATCH 10/25] changelog Signed-off-by: jace-roell --- packages/imperative/CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/imperative/CHANGELOG.md b/packages/imperative/CHANGELOG.md index ded21b7c84..6a407cb842 100644 --- a/packages/imperative/CHANGELOG.md +++ b/packages/imperative/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to the Imperative package will be documented in this file. +## Recent Changes + +- BugFix: Resolved issue where team config base profiles were being overwritten if a user config did not have a base profile. + ## `8.8.3` - BugFix: Modified 8.8.2 bugfix to correct web help alias. [#2361](https://github.com/zowe/zowe-cli/pull/2361) From 1314ffa52cc5fd56061c6a8f4093bdc2c7f86bdd Mon Sep 17 00:00:00 2001 From: jace-roell Date: Tue, 10 Dec 2024 14:13:19 -0500 Subject: [PATCH 11/25] removed test file and changelog fix Signed-off-by: jace-roell --- file.ts | 17 ----------------- packages/imperative/CHANGELOG.md | 2 +- 2 files changed, 1 insertion(+), 18 deletions(-) delete mode 100644 file.ts diff --git a/file.ts b/file.ts deleted file mode 100644 index f490ca01a2..0000000000 --- a/file.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { ProfileInfo } from "@zowe/imperative"; -import { GetJobs } from "@zowe/zos-jobs-for-zowe-sdk"; -(async () => { - // Load connection info from default z/OSMF profile - const profInfo = new ProfileInfo("zowe"); - await profInfo.readProfilesFromDisk(); - const zosmfProfAttrs = profInfo.getDefaultProfile("zosmf"); - const zosmfMergedArgs = profInfo.mergeArgsForProfile(zosmfProfAttrs!, { getSecureVals: true }); - const session = ProfileInfo.createSession(zosmfMergedArgs.knownArgs); - const owner: string | undefined = session.ISession.user; - // This may take awhile... - const response = await GetJobs.getJobsByOwner(session, owner!); - console.log(response); -})().catch((err) => { - console.error(err); - process.exit(1); -}); \ No newline at end of file diff --git a/packages/imperative/CHANGELOG.md b/packages/imperative/CHANGELOG.md index 6a407cb842..52045f1d05 100644 --- a/packages/imperative/CHANGELOG.md +++ b/packages/imperative/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to the Imperative package will be documented in this file. ## Recent Changes -- BugFix: Resolved issue where team config base profiles were being overwritten if a user config did not have a base profile. +- BugFix: Resolved issue where team config base profiles were being overwritten if a user config did not have a base profile. [#2383](https://github.com/zowe/zowe-cli/pull/2383) ## `8.8.3` From ba9e5dca58bada99509a5d82e6c2b63d38d7bea5 Mon Sep 17 00:00:00 2001 From: jace-roell Date: Wed, 11 Dec 2024 14:19:43 -0500 Subject: [PATCH 12/25] unit test Signed-off-by: jace-roell --- .../ProfileInfo.TeamConfig.unit.test.ts | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/packages/imperative/src/config/__tests__/ProfileInfo.TeamConfig.unit.test.ts b/packages/imperative/src/config/__tests__/ProfileInfo.TeamConfig.unit.test.ts index d4f2aeaf6f..4f0d2b9cac 100644 --- a/packages/imperative/src/config/__tests__/ProfileInfo.TeamConfig.unit.test.ts +++ b/packages/imperative/src/config/__tests__/ProfileInfo.TeamConfig.unit.test.ts @@ -38,6 +38,7 @@ const testEnvPrefix = testAppNm.toUpperCase(); const profileTypes = ["zosmf", "tso", "base", "dummy"]; const testDir = path.join(__dirname, "__resources__"); const teamProjDir = path.join(testDir, testAppNm + "_team_config_proj"); +const teamProjBaseTest = path.join(testDir, testAppNm + "_team_config_proj_base"); function createNewProfInfo(newDir: string, opts?: IProfOpts): ProfileInfo { // create a new ProfileInfo in the desired directory @@ -956,6 +957,37 @@ describe("TeamConfig ProfileInfo tests", () => { expect(caughtError).toBeDefined(); expect(caughtError.message).toContain("Profile attributes must be defined"); }); + it("should fall back to layerProperties when realBaseProfileName is undefined", async () => { + const profInfo = createNewProfInfo(teamProjDir); + await profInfo.readProfilesFromDisk(); + + // Simulate the condition where the active layer has no `defaults.base` but global and user layers exist + const layerActive = profInfo.getTeamConfig().layerActive(); + delete layerActive.properties.defaults.base; + + const globalLayer = profInfo.getTeamConfig().findLayer(false, true); + globalLayer.properties.defaults.base = "globalBaseProfile"; + + const userLayer = profInfo.getTeamConfig().findLayer(true, true); + userLayer.properties.defaults.base = ""; + + const profAttrs = profInfo.getDefaultProfile("zosmf") as IProfAttrs; + + // Merge args to trigger the logic + const mergedArgs = profInfo.mergeArgsForProfile(profAttrs); + + // Expected args should include those from the global base profile + const expectedArgs = [ + {argName: 'host', dataType: 'string', argValue: 'LPAR1.your.domain.net', secure: false}, + {argName: 'port', dataType: 'number', argValue: 1234, secure: false}, + {argName: 'responseFormatHeader', dataType: 'boolean', argValue: true, secure: false} + ]; + + expect(mergedArgs.knownArgs.length).toBeGreaterThanOrEqual(expectedArgs.length); + for (const [idx, arg] of expectedArgs.entries()) { + expect(mergedArgs.knownArgs[idx]).toMatchObject(arg); + } + }); }); describe("mergeArgsForProfileType", () => { From cd8057014b12d72c94cebd3262be1b1f21b871d5 Mon Sep 17 00:00:00 2001 From: jace-roell Date: Thu, 12 Dec 2024 07:30:48 -0500 Subject: [PATCH 13/25] unused var Signed-off-by: jace-roell --- .../src/config/__tests__/ProfileInfo.TeamConfig.unit.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/imperative/src/config/__tests__/ProfileInfo.TeamConfig.unit.test.ts b/packages/imperative/src/config/__tests__/ProfileInfo.TeamConfig.unit.test.ts index 4f0d2b9cac..f6c599ee2e 100644 --- a/packages/imperative/src/config/__tests__/ProfileInfo.TeamConfig.unit.test.ts +++ b/packages/imperative/src/config/__tests__/ProfileInfo.TeamConfig.unit.test.ts @@ -38,7 +38,6 @@ const testEnvPrefix = testAppNm.toUpperCase(); const profileTypes = ["zosmf", "tso", "base", "dummy"]; const testDir = path.join(__dirname, "__resources__"); const teamProjDir = path.join(testDir, testAppNm + "_team_config_proj"); -const teamProjBaseTest = path.join(testDir, testAppNm + "_team_config_proj_base"); function createNewProfInfo(newDir: string, opts?: IProfOpts): ProfileInfo { // create a new ProfileInfo in the desired directory From a24a5511b8928e9ba265ab5b67bca162dc4e671e Mon Sep 17 00:00:00 2001 From: jace-roell Date: Thu, 12 Dec 2024 08:59:20 -0500 Subject: [PATCH 14/25] coverage and proper mocking Signed-off-by: jace-roell --- .../config/__tests__/ProfileInfo.TeamConfig.unit.test.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/imperative/src/config/__tests__/ProfileInfo.TeamConfig.unit.test.ts b/packages/imperative/src/config/__tests__/ProfileInfo.TeamConfig.unit.test.ts index f6c599ee2e..b28ccd720f 100644 --- a/packages/imperative/src/config/__tests__/ProfileInfo.TeamConfig.unit.test.ts +++ b/packages/imperative/src/config/__tests__/ProfileInfo.TeamConfig.unit.test.ts @@ -32,6 +32,7 @@ import { ConfigProfiles } from "../src/api"; import { IExtendersJsonOpts } from "../src/doc/IExtenderOpts"; import { ConfigSchema } from "../src/ConfigSchema"; import { Logger } from "../../logger/src/Logger"; +import { IProfLocOsLoc } from "@zowe/imperative"; const testAppNm = "ProfInfoApp"; const testEnvPrefix = testAppNm.toUpperCase(); @@ -960,6 +961,14 @@ describe("TeamConfig ProfileInfo tests", () => { const profInfo = createNewProfInfo(teamProjDir); await profInfo.readProfilesFromDisk(); + const mockResp: IProfLocOsLoc[] = [{ + global: true, + name: "LPAR1", + path: "/mocked/path/xyz", + user: true + }]; + jest.spyOn(ProfileInfo.prototype, "getOsLocInfo").mockReturnValue(mockResp); + // Simulate the condition where the active layer has no `defaults.base` but global and user layers exist const layerActive = profInfo.getTeamConfig().layerActive(); delete layerActive.properties.defaults.base; From 4c7779452e8fbf8d41a83b9b4f95a427161e4ce7 Mon Sep 17 00:00:00 2001 From: jace-roell Date: Thu, 12 Dec 2024 09:29:11 -0500 Subject: [PATCH 15/25] consolidated code Signed-off-by: jace-roell --- .../config/__tests__/ProfileInfo.TeamConfig.unit.test.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/imperative/src/config/__tests__/ProfileInfo.TeamConfig.unit.test.ts b/packages/imperative/src/config/__tests__/ProfileInfo.TeamConfig.unit.test.ts index b28ccd720f..1ed8dd659d 100644 --- a/packages/imperative/src/config/__tests__/ProfileInfo.TeamConfig.unit.test.ts +++ b/packages/imperative/src/config/__tests__/ProfileInfo.TeamConfig.unit.test.ts @@ -32,7 +32,6 @@ import { ConfigProfiles } from "../src/api"; import { IExtendersJsonOpts } from "../src/doc/IExtenderOpts"; import { ConfigSchema } from "../src/ConfigSchema"; import { Logger } from "../../logger/src/Logger"; -import { IProfLocOsLoc } from "@zowe/imperative"; const testAppNm = "ProfInfoApp"; const testEnvPrefix = testAppNm.toUpperCase(); @@ -961,13 +960,12 @@ describe("TeamConfig ProfileInfo tests", () => { const profInfo = createNewProfInfo(teamProjDir); await profInfo.readProfilesFromDisk(); - const mockResp: IProfLocOsLoc[] = [{ + jest.spyOn(ProfileInfo.prototype, "getOsLocInfo").mockReturnValue([{ global: true, name: "LPAR1", path: "/mocked/path/xyz", user: true - }]; - jest.spyOn(ProfileInfo.prototype, "getOsLocInfo").mockReturnValue(mockResp); + }]); // Simulate the condition where the active layer has no `defaults.base` but global and user layers exist const layerActive = profInfo.getTeamConfig().layerActive(); From bcad1ab9c37411536dd3edb423ea8781d9154016 Mon Sep 17 00:00:00 2001 From: Gene Johnston Date: Fri, 13 Dec 2024 12:08:39 -0500 Subject: [PATCH 16/25] Move AuthOrder prototype into 'prototypes' directory Signed-off-by: Gene Johnston --- prototypes/Readme.md | 7 + prototypes/packages/.gitkeep | 0 .../src/zosfiles/list/ds/DataSet.handler.ts | 47 ++ .../cli/src/zosfiles/list/ds/Readme.md | 7 + .../src/rest/src/session/AuthOrder.ts | 316 ++++++++++ .../src/session/ConnectionPropsForSessCfg.ts | 569 ++++++++++++++++++ .../imperative/src/rest/src/session/Readme.md | 7 + 7 files changed, 953 insertions(+) create mode 100644 prototypes/Readme.md create mode 100644 prototypes/packages/.gitkeep create mode 100644 prototypes/packages/cli/src/zosfiles/list/ds/DataSet.handler.ts create mode 100644 prototypes/packages/cli/src/zosfiles/list/ds/Readme.md create mode 100644 prototypes/packages/imperative/src/rest/src/session/AuthOrder.ts create mode 100644 prototypes/packages/imperative/src/rest/src/session/ConnectionPropsForSessCfg.ts create mode 100644 prototypes/packages/imperative/src/rest/src/session/Readme.md diff --git a/prototypes/Readme.md b/prototypes/Readme.md new file mode 100644 index 0000000000..a8121f6ec7 --- /dev/null +++ b/prototypes/Readme.md @@ -0,0 +1,7 @@ +The purpose of this **prototypes** folder is to provide a home for prototype or proof-of-concept code. As an example, new, experimental code that implements a compelling feature may be good model for the future permanent implementation of that feature. + +During experiments, you might place a new class into a particular package to confirm that an idea is feasible. However, that code may not be complete enough to be built and packaged into the product. Lint rules might fail on this new code. You may not have any tests yet, so code coverage verification may fail. + +You do not want this new code to cause Zowe build pipelines to fail, but you do not want to lose the useful work that has been completed. Placing your source file(s) under the **prototypes** directory is a way to keep your valuable experiment and not break the Zowe build process. + +Place your source file into a path under the **prototypes** directory that mirrors the real directory path . Personally, I like to create a hard link from the file in my real file path to an identical file path under the **prototypes** directory. That way I can import, compile, and debug in the real directory until I complete experiments. Before committing, I can just delete the file in the real directory path and the remaining hard-linked file in the **prototypes** directory path will have all of the latest changes. diff --git a/prototypes/packages/.gitkeep b/prototypes/packages/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prototypes/packages/cli/src/zosfiles/list/ds/DataSet.handler.ts b/prototypes/packages/cli/src/zosfiles/list/ds/DataSet.handler.ts new file mode 100644 index 0000000000..e918eb5e8a --- /dev/null +++ b/prototypes/packages/cli/src/zosfiles/list/ds/DataSet.handler.ts @@ -0,0 +1,47 @@ +/* +* 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. +* +*/ + +import { AbstractSession, IHandlerParameters, TextUtils } from "@zowe/imperative"; +import { IZosFilesResponse, List } from "@zowe/zos-files-for-zowe-sdk"; +import { ZosFilesBaseHandler } from "../../ZosFilesBase.handler"; + +/** + * Handler to list a data sets + * @export + */ +export default class DataSetHandler extends ZosFilesBaseHandler { + public async processWithSession(commandParameters: IHandlerParameters, session: AbstractSession): Promise { + /* todo: Remove diagnostic print statements + */ + return { + commandResponse: "____ DataSetHandler: Pretend that we ran the list command.", + success: true + }; + // end Remove todo: */ + + const response = await List.dataSet(session, commandParameters.arguments.dataSetName, { + volume: commandParameters.arguments.volumeSerial, + attributes: commandParameters.arguments.attributes, + maxLength: commandParameters.arguments.maxLength, + responseTimeout: commandParameters.arguments.responseTimeout, + start: commandParameters.arguments.start + }); + + if (commandParameters.arguments.attributes && response.apiResponse.items.length > 0) { + commandParameters.response.console.log(TextUtils.prettyJson(response.apiResponse.items)); + } else { + const dsnameList = response.apiResponse.items.map((mem: any) => mem.dsname); + commandParameters.response.console.log(dsnameList.join("\n")); + } + + return response; + } +} diff --git a/prototypes/packages/cli/src/zosfiles/list/ds/Readme.md b/prototypes/packages/cli/src/zosfiles/list/ds/Readme.md new file mode 100644 index 0000000000..5a1554004d --- /dev/null +++ b/prototypes/packages/cli/src/zosfiles/list/ds/Readme.md @@ -0,0 +1,7 @@ +Files used to create a prototype of AuthOrder are: + +prototypes\packages\imperative\src\rest\src\session\AuthOrder.ts + +prototypes\packages\imperative\src\rest\src\session\ConnectionPropsForSessCfg.ts + +prototypes\packages\cli\src\zosfiles\list\ds\DataSet.handler.ts diff --git a/prototypes/packages/imperative/src/rest/src/session/AuthOrder.ts b/prototypes/packages/imperative/src/rest/src/session/AuthOrder.ts new file mode 100644 index 0000000000..dade7829d5 --- /dev/null +++ b/prototypes/packages/imperative/src/rest/src/session/AuthOrder.ts @@ -0,0 +1,316 @@ +/* +* 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. +* +*/ + +import { ICommandArguments } from "../../../cmd"; +import { ImperativeError } from "../../../error"; +import { ISession } from "./doc/ISession"; +import { Logger } from "../../../logger"; +import * as SessConstants from "./SessConstants"; + +/** + * @internal - Cannot be used outside of the imperative package + * + * The purpose of this class is to detect an authentication order property + * supplied by a user in a profile, command line, or environment variable. + * That authOrder is then used to place the correct set of credentials into + * a session for authentication. + */ +export class AuthOrder { + + /** + * When a user does not supply an auth order, Zowe clients will use a + * hard-coded default order. This property records whether AUTH_TYPE_BASIC + * or AUTH_TYPE_TOKEN is the top auth choice in the order. + */ + private static m_topDefaultAuth: string = SessConstants.AUTH_TYPE_BASIC; + + /** + * This array of authentication types specifies the order of preferred + * authentication. It contains the user-specified order, or a default order + * if the user does not specify an order. m_authOrder[0] is the highest + * preferred authentication. + */ + private static m_authOrder: SessConstants.AUTH_TYPE_CHOICES[] = null; + + // *********************************************************************** + /** + * Set the top auth type when a default authOrder is used. Previously, + * two different hard-coded orders were present in the Zowe clients. + * Both hard-coded orders are now provided by this class. Zowe code, + * that still sets a specific order for backward compatibility, now + * calls this function to ensure that that the original behavior remains + * the same and avoids a breaking change when a user has not specified + * an authOrder property. + * + * @param topDefaultAuth - Input. + * The top authentication type that will be used when forming a + * default authOrder. + */ + public static setTopDefaultAuth( + topDefaultAuth: typeof SessConstants.AUTH_TYPE_BASIC | typeof SessConstants.AUTH_TYPE_TOKEN + ): void { + AuthOrder.m_topDefaultAuth = topDefaultAuth; + } + + // *********************************************************************** + /** + * Cache the authOrder property from the supplied cmdArgs. If no authOrder exists + * in cmdArgs, a default authOrder is created and cached. + * + * @param cmdArgs - Input. + * The set of arguments that the calling function is using. + */ + private static cacheAuthOrder(cmdArgs: ICommandArguments): void { + // have we already cached the authOrder? + if (AuthOrder.m_authOrder !== null) { + // start over with an empty order. + AuthOrder.m_authOrder = null; + } + + if (cmdArgs.authOrder) { + // validate each user-supplied type of authentication + for (const nextUserAuth of cmdArgs.authOrder) { + switch (nextUserAuth) { + case SessConstants.AUTH_TYPE_BASIC: + case SessConstants.AUTH_TYPE_TOKEN: + case SessConstants.AUTH_TYPE_BEARER: + case SessConstants.AUTH_TYPE_CERT_PEM: + case SessConstants.AUTH_TYPE_NONE: + if (AuthOrder.m_authOrder === null) { + AuthOrder.m_authOrder = []; + } + AuthOrder.m_authOrder.push(nextUserAuth); + break; + default: + Logger.getImperativeLogger().error( + `The authentication = '${nextUserAuth}' is not valid and will be ignored.` + ); + // todo: Remove diagnostic print statements + console.log("____ cacheAuthOrder: nextUserAuth = '" + nextUserAuth + "' is not valid and will be ignored."); + // todo: end Remove + } + } + } + + // the user supplied an authOrder + if (AuthOrder.m_authOrder !== null) { + return; + } + + // No authOrder was supplied by the user. Create a default order. + AuthOrder.m_authOrder = []; + if (AuthOrder.m_topDefaultAuth === SessConstants.AUTH_TYPE_BASIC) { + // we want user & password auth as the top choice + AuthOrder.m_authOrder.push(SessConstants.AUTH_TYPE_BASIC); + AuthOrder.m_authOrder.push(SessConstants.AUTH_TYPE_TOKEN); + } else { + // we want token auth as the top choice + AuthOrder.m_authOrder.push(SessConstants.AUTH_TYPE_TOKEN); + AuthOrder.m_authOrder.push(SessConstants.AUTH_TYPE_BASIC); + } + // add remaining auth types. We do not include 'none' in our defaults. + AuthOrder.m_authOrder.push(SessConstants.AUTH_TYPE_BEARER); + AuthOrder.m_authOrder.push(SessConstants.AUTH_TYPE_CERT_PEM); + } + + // *********************************************************************** + /** + * Find the highest auth type (according to the authOrder) which exists + * in either the supplied session config or command line arguments. + * Then place the credentials associated with that auth type into the + * supplied session config. Credentials for all other auth types are + * removed from the session config. + * + * @param sessCfg - Modified. + * Authentication properties are added to and removed from this + * session configuration, which can already have properties in + * this object when passed to this function. + * + * @param cmdArgs - Input. + * The set of arguments with which the calling function is operating. + * For CLI, the cmdArgs come from the command line, profile, or + * environment. Other apps can place relevant arguments into this + * object to be processed by this function. + */ + public static putTopAuthInSession( + sessCfg: SessCfgType, + cmdArgs: ICommandArguments + ): void { + let sessTypeToUse: SessConstants.AUTH_TYPE_CHOICES = null; + + // todo: Remove diagnostic print statements + console.log("____ putTopAuthInSession: cmdArgs = " + JSON.stringify(cmdArgs, null, 2)); + sessCfg.tokenType = "apimlAuthenticationToken"; + sessCfg.tokenValue = "SomeTokenValue"; + sessCfg.cert = "./certFile.txt"; + sessCfg.certKey = "./certKeyFile.txt"; + sessCfg.authTypeOrder = []; + sessCfg.authTypeOrder.push("bogusAuthType1"); + sessCfg.authTypeOrder.push("bogusAuthType2"); + sessCfg.authTypeOrder.push("bogusAuthType3"); + sessCfg.authTypeOrder.push("bogusAuthType4"); + console.log("____ putTopAuthInSession: sessCfg before processing = " + JSON.stringify(sessCfg, null, 2)); + // todo: end Remove + + // cache the correct authOrder to use + AuthOrder.cacheAuthOrder(cmdArgs); + + // Detect the first auth type (from our auth order) provided in the session config or in command args. + // Ensure that the auth properties are placed in the session config. + // Record the detected auth type for use as the session type. + let errMsg: string; + for (const nextAuth of AuthOrder.m_authOrder) { + switch (nextAuth) { + case SessConstants.AUTH_TYPE_BASIC: + // todo: do we have to check for sessCfg.base64EncodedAuth ? + if (cmdArgs.user?.length > 0) { + sessCfg.user = cmdArgs.user; + } + if (cmdArgs.password?.length > 0) { + sessCfg.password = cmdArgs.password; + } + if (sessCfg.user?.length > 0 && sessCfg.password?.length > 0) { + // TODO: Confirm in ConnectionPropsForSessCfg that requestToken will work ok with sessTypeToUse + sessTypeToUse = SessConstants.AUTH_TYPE_BASIC; + } + break; + case SessConstants.AUTH_TYPE_TOKEN: + if (cmdArgs.tokenType?.length > 0) { + sessCfg.tokenType = cmdArgs.tokenType; + } + if (cmdArgs.tokenValue?.length > 0) { + sessCfg.tokenValue = cmdArgs.tokenValue; + } + if (sessCfg.tokenType?.length > 0 && sessCfg.tokenValue?.length > 0) { + sessTypeToUse = SessConstants.AUTH_TYPE_TOKEN; + } + break; + case SessConstants.AUTH_TYPE_BEARER: + if (cmdArgs.tokenType?.length > 0) { + sessCfg.tokenType = cmdArgs.tokenType; + } + if (cmdArgs.tokenValue?.length > 0) { + sessCfg.tokenValue = cmdArgs.tokenValue; + } + // a tokenValue with no tokenType implies a bearer token + if (!(sessCfg.tokenType?.length > 0) && sessCfg.tokenValue?.length > 0) { + sessTypeToUse = SessConstants.AUTH_TYPE_BEARER; + } + break; + case SessConstants.AUTH_TYPE_CERT_PEM: + if (cmdArgs.certFile?.length > 0) { + sessCfg.cert = cmdArgs.certFile; + } + if (cmdArgs.certKeyFile?.length > 0) { + sessCfg.certKey = cmdArgs.certKeyFile; + } + if (sessCfg.cert?.length > 0 && sessCfg.certKey?.length > 0) { + sessTypeToUse = SessConstants.AUTH_TYPE_CERT_PEM; + } + break; + case SessConstants.AUTH_TYPE_NONE: + sessTypeToUse = SessConstants.AUTH_TYPE_NONE; + break; + default: + // authOrder was validated. A wrong value now is our programming error. + errMsg = `authOrder contains an invalid authentication = ${nextAuth}.`; + Logger.getImperativeLogger().error(errMsg); + throw new ImperativeError({ msg: errMsg }); + } + if (sessTypeToUse !== null) { + // stop looking for auth types after we find the first one + break; + } + } + + // When no creds are in the session and AUTH_TYPE_NONE is not in the user's authOrder, + // remove the session type from the session. Otherwise set the type that we found. + if (sessTypeToUse === null) { + delete sessCfg.type; + } else { + sessCfg.type = sessTypeToUse; + } + + // remove all extra auth creds from the session + AuthOrder.removeExtraCredsFromSess(sessCfg); + + // copy our authOrder into the session object + sessCfg.authTypeOrder = [...AuthOrder.m_authOrder]; + + // todo: Should we throw error if no creds are in the session, or let other functions throw the error? + + // todo: Remove diagnostic print statements + console.log("____ putTopAuthInSession:\nsessCfg after processing = " + JSON.stringify(sessCfg, null, 2)); + // todo: end Remove + } + + // *********************************************************************** + /** + * Remove all credential properties from the supplied session except for the + * creds related to the session type specified within the sessCfg argument. + * + * @param sessCfg - Modified. + * Authentication credentials are removed from this session configuration. + */ + private static removeExtraCredsFromSess( + sessCfg: SessCfgType + ): void { + if (AuthOrder.cacheAuthOrder == null) { + const errMsg = "AuthOrder.cacheAuthOrder must be called before AuthOrder.removeExtraCredsFromSess"; + Logger.getImperativeLogger().error(errMsg); + throw new ImperativeError({ msg: errMsg }); + } + if (!sessCfg?.type) { + const errMsg = "Session type must exist in the supplied session."; + Logger.getImperativeLogger().error(errMsg); + throw new ImperativeError({ msg: errMsg }); + } + + // initially set all creds to be removed from the session. Later we delete the desired creds from this set + const credsToRemove = new Set(["user", "password", "base64EncodedAuth", "tokenType", "tokenValue", "cert", "certKey"]); + + // Delete the selected creds from the set of creds that will be removed from our session config. + let errMsg: string; + switch (sessCfg.type) { + case SessConstants.AUTH_TYPE_BASIC: + credsToRemove.delete("user"); + credsToRemove.delete("password"); + credsToRemove.delete("base64EncodedAuth"); + break; + case SessConstants.AUTH_TYPE_TOKEN: + credsToRemove.delete("tokenType"); + credsToRemove.delete("tokenValue"); + break; + case SessConstants.AUTH_TYPE_BEARER: + credsToRemove.delete("tokenValue"); + break; + case SessConstants.AUTH_TYPE_CERT_PEM: + credsToRemove.delete("cert"); + credsToRemove.delete("certKey"); + break; + case SessConstants.AUTH_TYPE_NONE: + break; + default: + // authOrder was validated. A wrong value now is our programming error. + errMsg = `Session an invalid type = ${sessCfg.type}.`; + Logger.getImperativeLogger().error(errMsg); + throw new ImperativeError({ msg: errMsg }); + } + + // remove all auth creds from the session, except the creds for the auth type that we chose to keep + const credIter = credsToRemove.values(); + let nextCredToRemove = credIter.next(); + while (!nextCredToRemove.done) { + delete (sessCfg as any)[nextCredToRemove.value]; + nextCredToRemove = credIter.next(); + } + } +} \ No newline at end of file diff --git a/prototypes/packages/imperative/src/rest/src/session/ConnectionPropsForSessCfg.ts b/prototypes/packages/imperative/src/rest/src/session/ConnectionPropsForSessCfg.ts new file mode 100644 index 0000000000..76ea99871b --- /dev/null +++ b/prototypes/packages/imperative/src/rest/src/session/ConnectionPropsForSessCfg.ts @@ -0,0 +1,569 @@ +/* +* 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. +* +*/ + +import { CliUtils, ImperativeConfig, TextUtils } from "../../../utilities"; +import { ICommandArguments, IHandlerParameters } from "../../../cmd"; +import { ImperativeError } from "../../../error"; +import { IOptionsForAddConnProps } from "./doc/IOptionsForAddConnProps"; +import { Logger } from "../../../logger"; +import * as SessConstants from "./SessConstants"; +import { IPromptOptions } from "../../../cmd/src/doc/response/api/handler/IPromptOptions"; +import { ISession } from "./doc/ISession"; +import { IProfileProperty } from "../../../profiles"; +import { ConfigAutoStore } from "../../../config/src/ConfigAutoStore"; +import { ConfigUtils } from "../../../config/src/ConfigUtils"; +import { AuthOrder } from "./AuthOrder"; + +/** + * Extend options for IPromptOptions for internal wrapper method + * @interface IHandlePromptOptions + * @extends {IPromptOptions} + */ +interface IHandlePromptOptions extends IPromptOptions { + + /** + * Adds IHandlerParameters to IPromptOptions + * @type {IHandlerParameters} + * @memberof IHandlePromptOptions + */ + parms?: IHandlerParameters; +} + +/** + * This class adds connection information to an existing session configuration + * object for making REST API calls with the Imperative RestClient. + */ +export class ConnectionPropsForSessCfg { + + // *********************************************************************** + /** + * Create a REST session configuration object starting with the supplied + * initialSessCfg and retrieving connection properties from the command + * line arguments (or environment, or profile). If required connection + * properties are missing we interactively prompt the user for the values. + * for any of the following properties: + * host + * port + * user name + * password + * + * Any prompt will timeout after 30 seconds so that this function can + * be run from an automated script, and will not indefinitely hang that + * script. + * + * In addition to properties that we prompt for, we will also add the following + * properties to the session configuration if they are available. + * type + * tokenType + * tokenValue + * + * @param initialSessCfg + * An initial session configuration (like ISession, or other + * specially defined configuration) that contains your desired + * session configuration properties. + * + * @param cmdArgs + * The arguments specified by the user on the command line + * (or in environment, or in profile). The contents of the + * supplied cmdArgs will be modified. + * + * @param connOpts + * Options that alter our connection actions. See IOptionsForAddConnProps. + * The connOpts parameter need not be supplied. + * + * @example + * // Within the process() function of a command handler, + * // do steps similar to the following: + * const sessCfg: ISession = { + * rejectUnauthorized: commandParameters.arguments.rejectUnauthorized, + * basePath: commandParameters.arguments.basePath + * }; + * const connectableSessCfg = await ConnectionPropsForSessCfg.addPropsOrPrompt( + * sessCfg, commandParameters.arguments + * ); + * mySession = new Session(connectableSessCfg); + * + * @returns A session configuration object with connection information + * added to the initialSessCfg. Its intended use is for our + * caller to create a session for a REST Client. + */ + public static async addPropsOrPrompt( + initialSessCfg: SessCfgType, + cmdArgs: ICommandArguments, + connOpts: IOptionsForAddConnProps = {} + ): Promise { + const impLogger = Logger.getImperativeLogger(); + + /* Create copies of our initialSessCfg and connOpts so that + * we can modify them without changing the caller's copy. + */ + const sessCfgToUse = { ...initialSessCfg }; + const connOptsToUse = { ...connOpts }; + + // resolve all values between sessCfg and cmdArgs using option choices + ConnectionPropsForSessCfg.resolveSessCfgProps( + sessCfgToUse, cmdArgs, connOptsToUse + ); + + // This function will provide all the needed properties in one array + let promptForValues: (keyof SessCfgType & string)[] = []; + const doNotPromptForValues: (keyof SessCfgType & string)[] = []; + + /* Add the override properties to the session object. + */ + if (connOpts.propertyOverrides?.length > 0) { + for (const override of connOpts.propertyOverrides) { + const argName = override.argumentName ?? override.propertyName; + // If the override is found on the session or command arguments, start setting things and do not prompt for overridden properties + if ((sessCfgToUse as any)[override.propertyName] != null || cmdArgs[argName] != null) { + // Set the session config to use the command line argument if it exists. + if (cmdArgs[argName] != null) { (sessCfgToUse as any)[override.propertyName] = cmdArgs[argName]; } + for (const prop of override.propertiesOverridden) { + // Make sure we do not prompt for the overridden property. + if (!doNotPromptForValues.includes(prop)) { doNotPromptForValues.push(prop); } + if (prop in sessCfgToUse) { (sessCfgToUse as any)[prop] = undefined; } + } + } + } + } + + // Set default values on propsToPromptFor + if(connOpts.propsToPromptFor?.length > 0) { + connOpts.propsToPromptFor.forEach(obj => { + if(obj.secure == null) obj.secure = true; + if(obj.secure) this.secureSessCfgProps.add(obj.name.toString()); + promptForValues.push(obj.name as keyof ISession); + this.promptTextForValues[obj.name.toString()] = obj.description; + }); + } + // check what properties are needed to be prompted + if (ConnectionPropsForSessCfg.propHasValue(sessCfgToUse.hostname) === false && !doNotPromptForValues.includes("hostname")) { + promptForValues.push("hostname"); + } + + if ((ConnectionPropsForSessCfg.propHasValue(sessCfgToUse.port) === false || sessCfgToUse.port === 0) && + !doNotPromptForValues.includes("port")) { + promptForValues.push("port"); + } + + const isTokenIrrelevant = ConnectionPropsForSessCfg.propHasValue(sessCfgToUse.tokenValue) === false || + connOpts.supportedAuthTypes && !connOpts.supportedAuthTypes.includes(SessConstants.AUTH_TYPE_TOKEN); + const isCertIrrelevant = ConnectionPropsForSessCfg.propHasValue(sessCfgToUse.cert) === false || + connOpts.supportedAuthTypes && !connOpts.supportedAuthTypes.includes(SessConstants.AUTH_TYPE_CERT_PEM); + if (isTokenIrrelevant && isCertIrrelevant) { + if (ConnectionPropsForSessCfg.propHasValue(sessCfgToUse.user) === false && !doNotPromptForValues.includes("user")) { + promptForValues.push("user"); + } + + if (ConnectionPropsForSessCfg.propHasValue(sessCfgToUse.password) === false && !doNotPromptForValues.includes("password")) { + promptForValues.push("password"); + } + } + + this.loadSecureSessCfgProps(connOptsToUse.parms, promptForValues); + + if (connOptsToUse.getValuesBack == null && connOptsToUse.doPrompting) { + connOptsToUse.getValuesBack = this.getValuesBack(connOptsToUse); + } + + if (connOptsToUse.getValuesBack != null) { + // put all the needed properties in an array and call the external function + const answers = await connOptsToUse.getValuesBack(promptForValues); + + if(connOpts.propsToPromptFor?.length > 0) + { + connOpts.propsToPromptFor.forEach(obj => { + if(obj.isGivenValueValid != null) + { + if(!obj.isGivenValueValid(answers[obj.name])) promptForValues = promptForValues.filter(item => obj.name !== item); + } + }); + } + // validate what values are given back and move it to sessCfgToUse + for (const value of promptForValues) { + if (ConnectionPropsForSessCfg.propHasValue(answers[value])) { + (sessCfgToUse as any)[value] = answers[value]; + } + } + + // + if (connOptsToUse.autoStore !== false && connOptsToUse.parms != null) { + await ConfigAutoStore.storeSessCfgProps(connOptsToUse.parms, sessCfgToUse, promptForValues); + } + } + + AuthOrder.putTopAuthInSession(sessCfgToUse, cmdArgs); + + impLogger.debug("Session config after any prompting for missing values:"); + ConnectionPropsForSessCfg.logSessCfg(sessCfgToUse); + return sessCfgToUse; + } + + // *********************************************************************** + /** + * Resolve the overlapping or mutually exclusive properties that can + * occur. Ensure that the resulting session configuration object contains + * only the applicable properties. The contents of the supplied sessCfg, + * cmdArgs, and connOpts will be modified. + * + * @param sessCfg + * An initial session configuration that contains your desired + * session configuration properties. + * + * @param cmdArgs + * The arguments specified by the user on the command line + * (or in environment, or in profile) + * + * @param connOpts + * Options that alter our actions. See IOptionsForAddConnProps. + * The connOpts parameter need not be supplied. + * The only option values used by this function are: + * connOpts.requestToken + * connOpts.defaultTokenType + * + * @example + * let sessCfg = YouCollectAllProfilePropertiesRelatedToSession(); + * let cmdArgs = YouSetPropertiesRequiredInCmdArgs(); + * ConnectionPropsForSessCfg.resolveSessCfgProps(sessCfg, cmdArgs); + * sessionToUse = new Session(sessCfg); + */ + public static resolveSessCfgProps( + sessCfg: SessCfgType, + cmdArgs: ICommandArguments = { $0: "", _: [] }, + connOpts: IOptionsForAddConnProps = {} + ) { + const impLogger = Logger.getImperativeLogger(); + + // use defaults if caller has not specified these properties. + if (!Object.prototype.hasOwnProperty.call(connOpts, "requestToken")) { + connOpts.requestToken = false; + } + if (!Object.prototype.hasOwnProperty.call(connOpts, "doPrompting")) { + connOpts.doPrompting = true; + } + if (!Object.prototype.hasOwnProperty.call(connOpts, "defaultTokenType")) { + connOpts.defaultTokenType = SessConstants.TOKEN_TYPE_JWT; + } + + /* Override properties from our caller's sessCfg + * with any values from the command line. + */ + if (ConnectionPropsForSessCfg.propHasValue(cmdArgs.host)) { + sessCfg.hostname = cmdArgs.host; + } + if (ConnectionPropsForSessCfg.propHasValue(cmdArgs.port)) { + sessCfg.port = cmdArgs.port; + } + if (ConnectionPropsForSessCfg.propHasValue(cmdArgs.user)) { + sessCfg.user = cmdArgs.user; + } + if (ConnectionPropsForSessCfg.propHasValue(cmdArgs.password)) { + sessCfg.password = cmdArgs.password; + } + + if (connOpts.requestToken) { + // deleting any tokenValue, ensures that basic creds are used to authenticate and get token + delete sessCfg.tokenValue; + } else if (ConnectionPropsForSessCfg.propHasValue(sessCfg.user) === false && + ConnectionPropsForSessCfg.propHasValue(sessCfg.password) === false && + ConnectionPropsForSessCfg.propHasValue(cmdArgs.tokenValue)) { + // set tokenValue if token is in args, and user and password are NOT supplied. + sessCfg.tokenValue = cmdArgs.tokenValue; + } + + // we use a cert when none of user, password, or token are supplied + if (ConnectionPropsForSessCfg.propHasValue(sessCfg.user) === false && + ConnectionPropsForSessCfg.propHasValue(sessCfg.password) === false && + ConnectionPropsForSessCfg.propHasValue(sessCfg.tokenValue) === false && + ConnectionPropsForSessCfg.propHasValue(cmdArgs.certFile)) { + if (ConnectionPropsForSessCfg.propHasValue(cmdArgs.certKeyFile)) { + sessCfg.cert = cmdArgs.certFile; + sessCfg.certKey = cmdArgs.certKeyFile; + } + // else if (ConnectionPropsForSessCfg.propHasValue(cmdArgs.certFilePassphrase)) { + // sessCfg.cert = cmdArgs.certFile; + // sessCfg.passphrase = cmdArgs.certFilePassphrase; + // } + } + + const isTokenUsed = ConnectionPropsForSessCfg.propHasValue(sessCfg.tokenValue) && + (connOpts.supportedAuthTypes == null || connOpts.supportedAuthTypes.includes(SessConstants.AUTH_TYPE_TOKEN)); + const isCertUsed = ConnectionPropsForSessCfg.propHasValue(sessCfg.cert) && + (connOpts.supportedAuthTypes == null || connOpts.supportedAuthTypes.includes(SessConstants.AUTH_TYPE_CERT_PEM)); + if (isTokenUsed) { + // when tokenValue is set at this point, we are definitely using the token. + impLogger.debug("Using token authentication"); + + // override any token type in sessCfg with cmdArgs value + if (ConnectionPropsForSessCfg.propHasValue(cmdArgs.tokenType)) { + sessCfg.tokenType = cmdArgs.tokenType; + } + + // set the auth type based on token type + if (ConnectionPropsForSessCfg.propHasValue(sessCfg.tokenType)) { + sessCfg.type = SessConstants.AUTH_TYPE_TOKEN; + } else { + // When no tokenType supplied, user wants bearer + sessCfg.type = SessConstants.AUTH_TYPE_BEARER; + } + } else if (isCertUsed) { + // when cert property is set at this point, we will use the certificate + if (ConnectionPropsForSessCfg.propHasValue(sessCfg.certKey)) { + impLogger.debug("Using PEM Certificate authentication"); + sessCfg.type = SessConstants.AUTH_TYPE_CERT_PEM; + } + // else if (ConnectionPropsForSessCfg.propHasValue(sessCfg.passphrase)) { + // impLogger.debug("Using PFX Certificate authentication"); + // sessCfg.type = SessConstants.AUTH_TYPE_CERT_PFX; + // } + } else { + // we are using basic auth + impLogger.debug("Using basic authentication"); + sessCfg.type = SessConstants.AUTH_TYPE_BASIC; + } + ConnectionPropsForSessCfg.setTypeForTokenRequest(sessCfg, connOpts, cmdArgs.tokenType); + ConnectionPropsForSessCfg.logSessCfg(sessCfg); + } + + // *********************************************************************** + /** + * Confirm whether the given session has a credentials. + * + * @param sessToTest + * the session to be confirmed. + * + * @returns true is the session has credentials. false otherwise. + */ + public static sessHasCreds(sessToTest: ISession) { + if (sessToTest == null) { + return false; + } + const hasToken = sessToTest.tokenType != null && sessToTest.tokenValue != null; + const hasCert = sessToTest.certKey != null && sessToTest.cert; + const hasBasicAuth = sessToTest.base64EncodedAuth != null; + const hasCreds = sessToTest.user != null && sessToTest.password; + return hasToken || hasCert || hasBasicAuth || hasCreds; + } + + /** + * List of properties on `sessCfg` object that should be kept secret and + * may not appear in Imperative log files. + * + * NOTE(Kelosky): redundant from LoggerUtils.SECURE_PROMPT_OPTIONS - leaving + * for future date to consolidate + */ + private static secureSessCfgProps: Set = new Set(["user", "password", "tokenValue", "passphrase"]); + + /** + * List of prompt messages that is used when the CLI prompts for session + * config values. + */ + private static readonly promptTextForValues: { [key: string]: string } = { + hostname: "Enter the host name of", + port: "Enter the port number of", + user: "Enter the user name for", + password: "Enter the password for" + }; + + /** + * Prompts the user to input session config values in a CLI environment. + * This is the default implementation of the `getValuesBack` callback when + * `connOpts.doPrompting` is true. + * @param connOpts Options for adding connection properties + * @returns Name-value pairs of connection properties + */ + private static getValuesBack(connOpts: IOptionsForAddConnProps): + (properties: string[]) => Promise<{ [key: string]: any }> { + return async (promptForValues: string[]) => { + /* The check for console.log in the following 'if' statement is only needed for tests + * which do not create a mock for the connOpts.parms.response.console.log property. + * In the real world, that property always exists for this CLI-only path of logic. + */ + if (promptForValues.length > 0 && connOpts.parms?.response.console.log) { + // We need to prompt for some values. Determine why we need to prompt. + let reasonForPrompts: string = ""; + if (ImperativeConfig.instance.config?.exists) { + reasonForPrompts += "Some required connection properties have not been specified " + + "in your Zowe client configuration. "; + } else if (ConfigUtils.onlyV1ProfilesExist) { + reasonForPrompts += "Only V1 profiles exist. V1 profiles are no longer supported. " + + "You should convert your V1 profiles to a newer Zowe client configuration. "; + } else { + reasonForPrompts += "No Zowe client configuration exists. "; + } + + reasonForPrompts += "Therefore, you will be asked for the connection properties " + + "that are required to complete your command.\n"; + connOpts.parms.response.console.log(TextUtils.wordWrap( + TextUtils.chalk.yellowBright(reasonForPrompts)) + ); + } + + const answers: { [key: string]: any } = {}; + const profileSchema = this.loadSchemaForSessCfgProps(connOpts.parms, promptForValues); + const serviceDescription = connOpts.serviceDescription || "your service"; + + for (const value of promptForValues) { + let answer; + while (answer === undefined) { + const hideText = profileSchema[value]?.secure || this.secureSessCfgProps.has(value); + const valuePrompt = this.promptTextForValues[value] ?? `Enter your ${value} for`; + let promptText = `${valuePrompt} ${serviceDescription}`; + if (hideText) { + promptText += " (will be hidden)"; + } + answer = await this.clientPrompt(`${promptText}: `, { hideText, parms: connOpts.parms }); + if (answer === null) { + throw new ImperativeError({ msg: `Timed out waiting for ${value}.` }); + } + } + if (profileSchema[value]?.type === "number") { + answer = Number(answer); + if (isNaN(answer)) { + throw new ImperativeError({ msg: `Specified ${value} was not a number.` }); + } + } + answers[value] = answer; + } + + return answers; + }; + } + + /** + * Handle prompting for clients. If in a CLI environment, use the IHandlerParameters.response + * object prompt method. + * @private + * @static + * @param {string} promptText + * @param {IHandlePromptOptions} opts + * @returns {Promise} + * @memberof ConnectionPropsForSessCfg + */ + private static async clientPrompt(promptText: string, opts: IHandlePromptOptions): Promise { + if (opts.parms) { + return opts.parms.response.console.prompt(promptText, opts); + } else { + return CliUtils.readPrompt(promptText, opts); + } + } + + // *********************************************************************** + /** + * Determine if we want to request a token. + * Set the session's type and tokenType accordingly. + * + * @param sessCfg + * The session configuration to be updated. + * + * @param options + * Options that alter our actions. See IOptionsForAddConnProps. + * + * @param tokenType + * The type of token that we expect to receive. + */ + private static setTypeForTokenRequest( + sessCfg: SessCfgType, + options: IOptionsForAddConnProps, + tokenType: SessConstants.TOKEN_TYPE_CHOICES + ) { + const impLogger = Logger.getImperativeLogger(); + if (options.requestToken) { + impLogger.debug("Requesting a token"); + if (sessCfg.type === SessConstants.AUTH_TYPE_BASIC) { + // Set our type to token to get a token from user and pass + sessCfg.type = SessConstants.AUTH_TYPE_TOKEN; + } + sessCfg.tokenType = tokenType || sessCfg.tokenType || options.defaultTokenType; + } + } + + // *********************************************************************** + /** + * Log the session configuration that resulted from the addition of + * credentials. Hide the password. + * + * @param sessCfg + * The session configuration to be logged. + */ + private static logSessCfg(sessCfg: any) { + const impLogger = Logger.getImperativeLogger(); + + // create copy of sessCfg and obscure secure fields for displaying in the log + const sanitizedSessCfg = JSON.parse(JSON.stringify(sessCfg)); + for (const secureProp of ConnectionPropsForSessCfg.secureSessCfgProps) { + if (sanitizedSessCfg[secureProp] != null) { + sanitizedSessCfg[secureProp] = `${secureProp}_is_hidden`; + } + } + impLogger.debug("Creating a session config with these properties:\n" + + JSON.stringify(sanitizedSessCfg, null, 2) + ); + } + + // *********************************************************************** + /** + * Confirm whether the specified property has a value. + * + * @param propToTest + * the property key to be confirmed. + * + * @returns true is the property exists and has a value. false otherwise. + */ + private static propHasValue(propToTest: any) { + return propToTest != null && propToTest !== ""; + } + + /** + * Load base profile property schema for connection properties. + * @param params CLI handler parameters object + * @param promptForValues List of ISessCfg properties to prompt for + * @returns Key-value pairs of ISessCfg property name and profile property schema + */ + private static loadSchemaForSessCfgProps(params: IHandlerParameters | undefined, promptForValues: string[]): { [key: string]: IProfileProperty } { + if (params == null || ImperativeConfig.instance.loadedConfig?.baseProfile == null) { + return {}; + } + + const schemas: { [key: string]: IProfileProperty } = {}; + for (const propName of promptForValues) { + const profilePropName = propName === "hostname" ? "host" : propName; + schemas[propName] = ImperativeConfig.instance.loadedConfig.baseProfile.schema.properties[profilePropName]; + } + return schemas; + } + + /** + * Load list of secure property names defined in team config. + * @param params CLI handler parameters object + * @param promptForValues List of ISessCfg properties to prompt for + */ + private static loadSecureSessCfgProps(params: IHandlerParameters | undefined, promptForValues: string[]): void { + if (params == null || !ImperativeConfig.instance.config?.exists) { + return; + } + + // Find profile that includes all the properties being prompted for + const profileProps = promptForValues.map(propName => propName === "hostname" ? "host" : propName); + const profileData = ConfigAutoStore.findActiveProfile(params, profileProps); + if (profileData == null) { + return; + } + + // Load secure property names that are defined for active profiles + const config = ImperativeConfig.instance.config; + const baseProfileName = ConfigUtils.getActiveProfileName("base", params.arguments); + for (const secureProp of [...config.api.secure.securePropsForProfile(profileData[1]), + ...config.api.secure.securePropsForProfile(baseProfileName)]) { + this.secureSessCfgProps.add(secureProp === "host" ? "hostname" : secureProp); + } + } +} \ No newline at end of file diff --git a/prototypes/packages/imperative/src/rest/src/session/Readme.md b/prototypes/packages/imperative/src/rest/src/session/Readme.md new file mode 100644 index 0000000000..5a1554004d --- /dev/null +++ b/prototypes/packages/imperative/src/rest/src/session/Readme.md @@ -0,0 +1,7 @@ +Files used to create a prototype of AuthOrder are: + +prototypes\packages\imperative\src\rest\src\session\AuthOrder.ts + +prototypes\packages\imperative\src\rest\src\session\ConnectionPropsForSessCfg.ts + +prototypes\packages\cli\src\zosfiles\list\ds\DataSet.handler.ts From 91d425e513b01ae43597a6571bdbd1b8871e9f64 Mon Sep 17 00:00:00 2001 From: Gene Johnston Date: Fri, 13 Dec 2024 12:16:01 -0500 Subject: [PATCH 17/25] No env var, cmd-line option, or schema. AuthOrder reserved word. Signed-off-by: Gene Johnston --- docs/Design_for_selecting_auth_type.md | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/docs/Design_for_selecting_auth_type.md b/docs/Design_for_selecting_auth_type.md index 88c7ed944b..9466b16a2d 100644 --- a/docs/Design_for_selecting_auth_type.md +++ b/docs/Design_for_selecting_auth_type.md @@ -32,10 +32,22 @@ In this section we identify the key features that a solution would have to provi - For example, assume that a user has specified that a certificate should be used before a token. Assume that a certificate is supplied in a config file and a token is supplied on the command line. - - The certificate should be used because the user configured that certificates should be used before tokens. + - The certificate should be used because the user-configured authentication order specifies that certificates should be used before tokens. - The token should not be used just because it was supplied on the command line. +- Zowe should ignore any authentication order property that is specified in a environment variable (unlike most other connection properties). + + - A single environment variable property would override **every** authentication order property specified in **every** profile within the user's zowe.config.json file. The most likely customer use of this property will be to specify a different authentication order for different profiles. A single environment variable would likely defeat the primary purpose of enabling a user to specify a different authentication order for different profiles. + +- The authentication order **could** be specified on the command line. However, in the initial implementation of this feature, Zowe CLI will **NOT** implement a command line option for the authentication order. + + - Conceptually, specifying an authentication order as a command line option is a reasonable idea. However, implementing that behavior could involve modifications to dozens of CLI command handlers, which could increase our implementation effort and time-to-market significantly. + + - It is not clear whether a command line option for authentication order will be particularly valuable to customers. The authentication order would typically be static for a given profile. It is unlikely to change from one CLI command to another. + + - Therefore, we will **NOT** implement a command-line option for authentication order until customer demand makes such work a priority. + - The authentication order only identifies the order in which Zowe chooses the **one** authentication method that will be used. If that first authentication method fails, Zowe will not make a second attempt to authenticate with any of the later authentication methods. - Once an authentication is selected, our logic should ensure that only that one type of authentication is placed into a session object. Thus, logic in down-stream Zowe code will not alter the order of authentication selection, simply by testing for the authentications from the session in a different order than the order that the user specified. @@ -48,7 +60,7 @@ In this section we identify the key features that a solution would have to provi - A customer should not have to specify every possible authentication in their ordered list of authentications. If a site only uses password and tokens, the customer should be able to specify only those two authentications in their list. -- A customer-specified list of authentications must contain at least one of our supported authentications. +- If a customer-specified list of authentications contains none of our supported authentications, a default order will be used. - The `--show-input-only` option should show the order of authentication as part of its displayed connection properties. @@ -84,7 +96,7 @@ A new profile property named **authOrder** should be created to enable users to - Any service profile. - - Any profile specific to a plugin (or VSCode extension) that supports a REST connection. For example an **endevor** profile could contain an **authOrder** property, but an **endevor-location** profile would not. + - Any profile specific to a plugin (or VSCode extension) that supports a REST connction. For example an **endevor** profile could contain an **authOrder** property, but an **endevor-location** profile would not. - Our existing inheritance of connection properties should also apply to the inheritance of the authOrder property. @@ -94,11 +106,9 @@ A new profile property named **authOrder** should be created to enable users to - Such a new item within the properties object will not currently be included in the zowe.schema.json file. An IntelliSense-like editor will not display **authOrder** as an option when a user starts to add a new property in a profile. - - We do not want to require plugins/extensions to add **authOrder** to their profiles. Requesting new work from plugins/extensions in the middle of a release's lifecycle can be a burden for those contributors. + - We do not want to require plugins/extensions to add **authOrder** to their profiles. Asking extenders to add AuthOrder to their set of options (and thus the schema) in the middle of a release's lifecycle can be a burden for those contributors. - - When the CLI is installed, the schema is not updated, so the CLI cannot currently take on that burden. It is possible that a CLI post-install step could be performed to update the schema. This is not considered to be a trivial task. - - - It should be noted if **authOrder** is absent from the schema, users will lose a nice convenience, but the user's config will not be broken in any functional way. + - As a result, we feel that losing IntelliSense is an acceptable compromise to avoid adding work to every extender. Users will lose a nice convenience, but the user's config will not be limited in any way at runtime. To represent a series of values, the **authOrder** property should be an array. The following example shows how a user could specify their desired authOrder. @@ -143,6 +153,8 @@ That addition would enable customers to also specify the authentication order of - We must describe the default order of authentication, when no authOrder property is supplied. +- We must document that the new authOrder property name is a Zowe reserved word that should **NOT** be used by any extender. + - We must notify extenders to guide their customers to supply an appropriate authOrder property if their extension needs a non-default order. ## A new class must be created to support authOrder From 60058ae7e15d16edb7a851890b52335684406157 Mon Sep 17 00:00:00 2001 From: Gene Johnston Date: Fri, 13 Dec 2024 12:24:31 -0500 Subject: [PATCH 18/25] Remove AuthOrder.ts from our real packages directory Signed-off-by: Gene Johnston --- .../src/rest/src/session/AuthOrder.ts | 326 ------------------ 1 file changed, 326 deletions(-) delete mode 100644 packages/imperative/src/rest/src/session/AuthOrder.ts diff --git a/packages/imperative/src/rest/src/session/AuthOrder.ts b/packages/imperative/src/rest/src/session/AuthOrder.ts deleted file mode 100644 index 3052c86e72..0000000000 --- a/packages/imperative/src/rest/src/session/AuthOrder.ts +++ /dev/null @@ -1,326 +0,0 @@ -/* -* 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. -* -*/ - -import { ICommandArguments } from "../../../cmd"; -import { ImperativeError } from "../../../error"; -import { ISession } from "./doc/ISession"; -import { Logger } from "../../../logger"; -import * as SessConstants from "./SessConstants"; - -/** - * @internal - Cannot be used outside of the imperative package - * - * The purpose of this class is to detect an authentication order property - * supplied by a user in a profile, command line, or environment variable. - * That authOrder is then used to place the correct set of credentials into - * a session for authentication. - */ -export class AuthOrder { - - /** - * When a user does not supply an auth order, Zowe clients will use a - * hard-coded default order. This property records whether AUTH_TYPE_BASIC - * or AUTH_TYPE_TOKEN is the top auth choice in the order. - */ - private static m_topDefaultAuth: string = SessConstants.AUTH_TYPE_BASIC; - - /** - * This array of authentication types specifies the order of preferred - * authentication. It contains the user-specified order, or a default order - * if the user does not specify an order. m_authOrder[0] is the highest - * preferred authentication. - */ - private static m_authOrder: SessConstants.AUTH_TYPE_CHOICES[] = null; - - // *********************************************************************** - /** - * Set the top auth type when a default authOrder is used. Previously, - * two different hard-coded orders were present in the Zowe clients. - * Both hard-coded orders are now provided by this class. Zowe code, - * that still sets a specific order for backward compatibility, now - * calls this function to ensure that that the original behavior remains - * the same and avoids a breaking change when a user has not specified - * an authOrder property. - * - * @param topDefaultAuth - Input. - * The top authentication type that will be used when forming a - * default authOrder. - */ - public static setTopDefaultAuth( - topDefaultAuth: typeof SessConstants.AUTH_TYPE_BASIC | typeof SessConstants.AUTH_TYPE_TOKEN - ): void { - AuthOrder.m_topDefaultAuth = topDefaultAuth; - } - - // *********************************************************************** - /** - * Cache the authOrder property from the supplied cmdArgs. If no authOrder exists - * in cmdArgs, a default authOrder is created and cached. - * - * @param cmdArgs - Input. - * The set of arguments that the calling function is using. - */ - private static cacheAuthOrder(cmdArgs: ICommandArguments): void { - // have we already cached the authOrder? - if (AuthOrder.m_authOrder !== null) { - // start over with an empty order. - AuthOrder.m_authOrder = null; - } - - if (cmdArgs.authOrder) { - // validate each user-supplied type of authentication - for (const nextUserAuth of cmdArgs.authOrder) { - switch (nextUserAuth) { - case SessConstants.AUTH_TYPE_BASIC: - case SessConstants.AUTH_TYPE_TOKEN: - case SessConstants.AUTH_TYPE_BEARER: - case SessConstants.AUTH_TYPE_CERT_PEM: - case SessConstants.AUTH_TYPE_NONE: - if (AuthOrder.m_authOrder === null) { - AuthOrder.m_authOrder = []; - } - AuthOrder.m_authOrder.push(nextUserAuth); - break; - default: - Logger.getImperativeLogger().error( - `The authentication = '${nextUserAuth}' is not valid and will be ignored.` - ); - /* todo: Remove diagnostic print statements - console.log("____ cacheAuthOrder: nextUserAuth = '" + nextUserAuth + "' is not valid and will be ignored."); - // end Remove todo: */ - } - } - } - - // the user supplied an authOrder - if (AuthOrder.m_authOrder !== null) { - /* todo: Remove diagnostic print statements - console.log("____ cacheAuthOrder: cached authOrder = " + AuthOrder.m_authOrder); - // end Remove todo: */ - return; - } - - // No authOrder was supplied by the user. Create a default order. - AuthOrder.m_authOrder = []; - if (AuthOrder.m_topDefaultAuth === SessConstants.AUTH_TYPE_BASIC) { - // we want user & password auth as the top choice - AuthOrder.m_authOrder.push(SessConstants.AUTH_TYPE_BASIC); - AuthOrder.m_authOrder.push(SessConstants.AUTH_TYPE_TOKEN); - } else { - // we want token auth as the top choice - AuthOrder.m_authOrder.push(SessConstants.AUTH_TYPE_TOKEN); - AuthOrder.m_authOrder.push(SessConstants.AUTH_TYPE_BASIC); - } - // add remaining auth types. We do not include 'none' in our defaults. - AuthOrder.m_authOrder.push(SessConstants.AUTH_TYPE_BEARER); - AuthOrder.m_authOrder.push(SessConstants.AUTH_TYPE_CERT_PEM); - /* todo: Remove diagnostic print statements - console.log("____ cacheAuthOrder: cached authOrder = " + AuthOrder.m_authOrder); - // end Remove todo: */ - } - - // *********************************************************************** - /** - * Find the highest auth type (according to the authOrder) which exists - * in either the supplied session config or command line arguments. - * Then place the credentials associated with that auth type into the - * supplied session config. Credentials for all other auth types are - * removed from the session config. - * - * @param sessCfg - Modified. - * Authentication properties are added to and removed from this - * session configuration, which can already have properties in - * this object when passed to this function. - * - * @param cmdArgs - Input. - * The set of arguments with which the calling function is operating. - * For CLI, the cmdArgs come from the command line, profile, or - * environment. Other apps can place relevant arguments into this - * object to be processed by this function. - */ - public static putTopAuthInSession( - sessCfg: SessCfgType, - cmdArgs: ICommandArguments - ): void { - let sessTypeToUse: SessConstants.AUTH_TYPE_CHOICES = null; - - // cache the correct authOrder to use - AuthOrder.cacheAuthOrder(cmdArgs); - - /* todo: Remove diagnostic print statements - sessCfg.tokenType = "apimlAuthenticationToken"; - sessCfg.tokenValue = "SomeTokenValue"; - sessCfg.cert = "./certFile.txt"; - sessCfg.certKey = "./certKeyFile.txt"; - sessCfg.authTypeOrder = []; - sessCfg.authTypeOrder.push("bogusAuthType1"); - sessCfg.authTypeOrder.push("bogusAuthType2"); - sessCfg.authTypeOrder.push("bogusAuthType3"); - sessCfg.authTypeOrder.push("bogusAuthType4"); - console.log("____ putTopAuthInSession:\ncmdArgs = " + JSON.stringify(cmdArgs, null, 2)); - console.log("____ putTopAuthInSession:\nsessCfg before processing = " + JSON.stringify(sessCfg, null, 2)); - // end Remove todo: */ - - // Detect the first auth type (from our auth order) provided in the session config or in command args. - // Ensure that the auth properties are placed in the session config. - // Record the detected auth type for use as the session type. - let errMsg: string; - for (const nextAuth of AuthOrder.m_authOrder) { - switch (nextAuth) { - case SessConstants.AUTH_TYPE_BASIC: - // todo: do we have to check for sessCfg.base64EncodedAuth ? - if (cmdArgs.user?.length > 0) { - sessCfg.user = cmdArgs.user; - } - if (cmdArgs.password?.length > 0) { - sessCfg.password = cmdArgs.password; - } - if (sessCfg.user?.length > 0 && sessCfg.password?.length > 0) { - // TODO: Confirm in ConnectionPropsForSessCfg that requestToken will work ok with sessTypeToUse - sessTypeToUse = SessConstants.AUTH_TYPE_BASIC; - } - break; - case SessConstants.AUTH_TYPE_TOKEN: - if (cmdArgs.tokenType?.length > 0) { - sessCfg.tokenType = cmdArgs.tokenType; - } - if (cmdArgs.tokenValue?.length > 0) { - sessCfg.tokenValue = cmdArgs.tokenValue; - } - if (sessCfg.tokenType?.length > 0 && sessCfg.tokenValue?.length > 0) { - sessTypeToUse = SessConstants.AUTH_TYPE_TOKEN; - } - break; - case SessConstants.AUTH_TYPE_BEARER: - if (cmdArgs.tokenType?.length > 0) { - sessCfg.tokenType = cmdArgs.tokenType; - } - if (cmdArgs.tokenValue?.length > 0) { - sessCfg.tokenValue = cmdArgs.tokenValue; - } - // a tokenValue with no tokenType implies a bearer token - if (!(sessCfg.tokenType?.length > 0) && sessCfg.tokenValue?.length > 0) { - sessTypeToUse = SessConstants.AUTH_TYPE_BEARER; - } - break; - case SessConstants.AUTH_TYPE_CERT_PEM: - if (cmdArgs.certFile?.length > 0) { - sessCfg.cert = cmdArgs.certFile; - } - if (cmdArgs.certKeyFile?.length > 0) { - sessCfg.certKey = cmdArgs.certKeyFile; - } - if (sessCfg.cert?.length > 0 && sessCfg.certKey?.length > 0) { - sessTypeToUse = SessConstants.AUTH_TYPE_CERT_PEM; - } - break; - case SessConstants.AUTH_TYPE_NONE: - sessTypeToUse = SessConstants.AUTH_TYPE_NONE; - break; - default: - // authOrder was validated. A wrong value now is our programming error. - errMsg = `authOrder contains an invalid authentication = ${nextAuth}.`; - Logger.getImperativeLogger().error(errMsg); - throw new ImperativeError({ msg: errMsg }); - } - if (sessTypeToUse !== null) { - // stop looking for auth types after we find the first one - break; - } - } - - // When no creds are in the session and AUTH_TYPE_NONE is not in the user's authOrder, - // remove the session type from the session. Otherwise set the type that we found. - if (sessTypeToUse === null) { - delete sessCfg.type; - } else { - sessCfg.type = sessTypeToUse; - } - - // remove all extra auth creds from the session - AuthOrder.removeExtraCredsFromSess(sessCfg); - - // copy our authOrder into the session object - sessCfg.authTypeOrder = [...AuthOrder.m_authOrder]; - - // todo: Should we throw error if no creds are in the session, or let other functions throw the error? - - /* todo: Remove diagnostic print statements - console.log("____ putTopAuthInSession:\nsessCfg after processing = " + JSON.stringify(sessCfg, null, 2)); - // end Remove todo: */ - } - - // *********************************************************************** - /** - * Remove all credential properties from the supplied session except for the - * creds related to the session type specified within the sessCfg argument. - * - * @param sessCfg - Modified. - * Authentication credentials are removed from this session configuration. - */ - private static removeExtraCredsFromSess( - sessCfg: SessCfgType - ): void { - if (AuthOrder.cacheAuthOrder == null) { - const errMsg = "AuthOrder.cacheAuthOrder must be called before AuthOrder.removeExtraCredsFromSess"; - Logger.getImperativeLogger().error(errMsg); - throw new ImperativeError({ msg: errMsg }); - } - if (!sessCfg?.type) { - const errMsg = "Session type must exist in the supplied session."; - Logger.getImperativeLogger().error(errMsg); - throw new ImperativeError({ msg: errMsg }); - } - - // initially set all creds to be removed from the session. Later we delete the desired creds from this set - const credsToRemove = new Set(["user", "password", "base64EncodedAuth", "tokenType", "tokenValue", "cert", "certKey"]); - - // Delete the selected creds from the set of creds that will be removed from our session config. - let errMsg: string; - switch (sessCfg.type) { - case SessConstants.AUTH_TYPE_BASIC: - credsToRemove.delete("user"); - credsToRemove.delete("password"); - credsToRemove.delete("base64EncodedAuth"); - break; - case SessConstants.AUTH_TYPE_TOKEN: - credsToRemove.delete("tokenType"); - credsToRemove.delete("tokenValue"); - break; - case SessConstants.AUTH_TYPE_BEARER: - credsToRemove.delete("tokenValue"); - break; - case SessConstants.AUTH_TYPE_CERT_PEM: - credsToRemove.delete("cert"); - credsToRemove.delete("certKey"); - break; - case SessConstants.AUTH_TYPE_NONE: - break; - default: - // authOrder was validated. A wrong value now is our programming error. - errMsg = `Session an invalid type = ${sessCfg.type}.`; - Logger.getImperativeLogger().error(errMsg); - throw new ImperativeError({ msg: errMsg }); - } - - // remove all auth creds from the session, except the creds for the auth type that we chose to keep - const credIter = credsToRemove.values(); - let nextCredToRemove = credIter.next(); - while (!nextCredToRemove.done) { - /* todo: Remove diagnostic print statements - console.log("____ removeExtraCredsFromSess: deleting = 'sessCfg." + nextCredToRemove.value + "' from the session."); - // end Remove todo: */ - - delete (sessCfg as any)[nextCredToRemove.value]; - nextCredToRemove = credIter.next(); - } - } -} \ No newline at end of file From 1ae21230fabf8580cd842f263f2b8146ac776f2c Mon Sep 17 00:00:00 2001 From: jace-roell Date: Mon, 16 Dec 2024 15:55:06 -0500 Subject: [PATCH 19/25] changelog Signed-off-by: jace-roell --- packages/imperative/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/imperative/CHANGELOG.md b/packages/imperative/CHANGELOG.md index 52045f1d05..8531bdc5ca 100644 --- a/packages/imperative/CHANGELOG.md +++ b/packages/imperative/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to the Imperative package will be documented in this file. ## Recent Changes -- BugFix: Resolved issue where team config base profiles were being overwritten if a user config did not have a base profile. [#2383](https://github.com/zowe/zowe-cli/pull/2383) +- BugFix: Resolved an issue where base profiles in a team configuration file were overwritten when a user configuration file did not include a base profile. [#2383](https://github.com/zowe/zowe-cli/pull/2383) ## `8.8.3` From c9c4ab8bff4b18fc3924f1ba07fe860aaaf1bf53 Mon Sep 17 00:00:00 2001 From: Gene Johnston Date: Tue, 17 Dec 2024 10:09:00 -0500 Subject: [PATCH 20/25] Generalize hard-link advice. Signed-off-by: Gene Johnston --- prototypes/Readme.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/prototypes/Readme.md b/prototypes/Readme.md index a8121f6ec7..763191fe3d 100644 --- a/prototypes/Readme.md +++ b/prototypes/Readme.md @@ -4,4 +4,8 @@ During experiments, you might place a new class into a particular package to con You do not want this new code to cause Zowe build pipelines to fail, but you do not want to lose the useful work that has been completed. Placing your source file(s) under the **prototypes** directory is a way to keep your valuable experiment and not break the Zowe build process. -Place your source file into a path under the **prototypes** directory that mirrors the real directory path . Personally, I like to create a hard link from the file in my real file path to an identical file path under the **prototypes** directory. That way I can import, compile, and debug in the real directory until I complete experiments. Before committing, I can just delete the file in the real directory path and the remaining hard-linked file in the **prototypes** directory path will have all of the latest changes. +Place your source file into a path under the **prototypes** directory that mirrors the real directory path. + +**Tip:** Create a hard link from the file in the real file path to an identical file path under the **prototypes** directory. That way imports, compiles, and debugging will work in the real directory during experiments, while all changes are automatically recorded in the **prototypes** path. + +**Note:** Remember to delete a new file or revert an existing file in the real directory path. The remaining hard-linked file(s) in the **prototypes** directory path will still have all of the latest changes. From 5b85f223c3ed69efd26983b722a3b2eedd5c28e7 Mon Sep 17 00:00:00 2001 From: jace-roell Date: Tue, 17 Dec 2024 15:54:03 -0500 Subject: [PATCH 21/25] user config osLoc fix and unit test Signed-off-by: jace-roell --- .../config/__tests__/ProfileInfo.TeamConfig.unit.test.ts | 9 ++++++++- packages/imperative/src/config/src/ProfileInfo.ts | 4 ---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/imperative/src/config/__tests__/ProfileInfo.TeamConfig.unit.test.ts b/packages/imperative/src/config/__tests__/ProfileInfo.TeamConfig.unit.test.ts index 1ed8dd659d..8732ef1399 100644 --- a/packages/imperative/src/config/__tests__/ProfileInfo.TeamConfig.unit.test.ts +++ b/packages/imperative/src/config/__tests__/ProfileInfo.TeamConfig.unit.test.ts @@ -677,7 +677,14 @@ describe("TeamConfig ProfileInfo tests", () => { expect(arg.argValue).toEqual(expectedArgs[idx].argValue); expect(arg.argLoc.locType).toBe(ProfLocType.TEAM_CONFIG); expect(arg.argLoc.jsonLoc).toMatch(/^profiles\.(base_glob|LPAR2_home)\.properties\./); - expect(arg.argLoc.osLoc[0]).toEqual(path.normalize(path.join(teamHomeProjDir, `${testAppNm}.config.json`))); + expect([ + path.normalize( + path.join(teamHomeProjDir, `${testAppNm}.config.json`) + ), + path.normalize( + path.join(teamProjDir, `${testAppNm}.config.json`) + ), + ]).toContain(arg.argLoc.osLoc[0]); } }); diff --git a/packages/imperative/src/config/src/ProfileInfo.ts b/packages/imperative/src/config/src/ProfileInfo.ts index dd0c771161..e65a079e28 100644 --- a/packages/imperative/src/config/src/ProfileInfo.ts +++ b/packages/imperative/src/config/src/ProfileInfo.ts @@ -1654,16 +1654,12 @@ export class ProfileInfo { }; let filePath: string; - if (_isPropInLayer(opts.configProperties) && opts.osLocInfo) { - filePath = opts.osLocInfo.path; - } else { for (const layer of this.mLoadedConfig.mLayers) { // Find the first layer that includes the JSON path if (_isPropInLayer(layer.properties)) { filePath = layer.path; break; } - } } return [{ From e2f465cdf5cd6cf8122257cb6273e65ee0cb1464 Mon Sep 17 00:00:00 2001 From: jace-roell Date: Wed, 18 Dec 2024 09:57:24 -0500 Subject: [PATCH 22/25] linting warnings Signed-off-by: jace-roell --- file.ts | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 file.ts diff --git a/file.ts b/file.ts new file mode 100644 index 0000000000..70ab34a41b --- /dev/null +++ b/file.ts @@ -0,0 +1,8 @@ +const { ProfileInfo } = require("@zowe/imperative"); +(async () => { + const profInfo = new ProfileInfo("zowe"); + await profInfo.readProfilesFromDisk(); + const upd = { profileName: "lpar1.test", profileType: "zosmf" }; + await profInfo.updateProperty({ ...upd, property: "user", value: "abc", setSecure: false }); + await profInfo.updateProperty({ ...upd, property: "password", value: "aa", setSecure: false }); +})(); \ No newline at end of file From 95a3fdd83a4ed39a97c9f426fcdba505218b136c Mon Sep 17 00:00:00 2001 From: jace-roell Date: Wed, 18 Dec 2024 09:59:06 -0500 Subject: [PATCH 23/25] rm dummy file and fix linting Signed-off-by: jace-roell --- file.ts | 8 -------- packages/imperative/src/config/src/ProfileInfo.ts | 12 ++++++------ 2 files changed, 6 insertions(+), 14 deletions(-) delete mode 100644 file.ts diff --git a/file.ts b/file.ts deleted file mode 100644 index 70ab34a41b..0000000000 --- a/file.ts +++ /dev/null @@ -1,8 +0,0 @@ -const { ProfileInfo } = require("@zowe/imperative"); -(async () => { - const profInfo = new ProfileInfo("zowe"); - await profInfo.readProfilesFromDisk(); - const upd = { profileName: "lpar1.test", profileType: "zosmf" }; - await profInfo.updateProperty({ ...upd, property: "user", value: "abc", setSecure: false }); - await profInfo.updateProperty({ ...upd, property: "password", value: "aa", setSecure: false }); -})(); \ No newline at end of file diff --git a/packages/imperative/src/config/src/ProfileInfo.ts b/packages/imperative/src/config/src/ProfileInfo.ts index e65a079e28..978a34cd9b 100644 --- a/packages/imperative/src/config/src/ProfileInfo.ts +++ b/packages/imperative/src/config/src/ProfileInfo.ts @@ -1654,12 +1654,12 @@ export class ProfileInfo { }; let filePath: string; - for (const layer of this.mLoadedConfig.mLayers) { - // Find the first layer that includes the JSON path - if (_isPropInLayer(layer.properties)) { - filePath = layer.path; - break; - } + for (const layer of this.mLoadedConfig.mLayers) { + // Find the first layer that includes the JSON path + if (_isPropInLayer(layer.properties)) { + filePath = layer.path; + break; + } } return [{ From 70213e1d9f4ca00b639cb75078dc7a18306dc83b Mon Sep 17 00:00:00 2001 From: zowe-robot Date: Wed, 18 Dec 2024 16:07:32 +0000 Subject: [PATCH 24/25] Bump version to 8.10.1 [ci skip] Signed-off-by: zowe-robot --- .../__packages__/cli-test-utils/package.json | 4 +- lerna.json | 2 +- npm-shrinkwrap.json | 116 +++++++++--------- packages/cli/package.json | 26 ++-- packages/core/package.json | 6 +- packages/imperative/CHANGELOG.md | 2 +- packages/imperative/package.json | 2 +- packages/provisioning/package.json | 8 +- packages/workflows/package.json | 10 +- packages/zosconsole/package.json | 8 +- packages/zosfiles/package.json | 10 +- packages/zosjobs/package.json | 10 +- packages/zoslogs/package.json | 8 +- packages/zosmf/package.json | 8 +- packages/zostso/package.json | 10 +- packages/zosuss/package.json | 6 +- 16 files changed, 118 insertions(+), 118 deletions(-) diff --git a/__tests__/__packages__/cli-test-utils/package.json b/__tests__/__packages__/cli-test-utils/package.json index e9f85b05d8..41c25d94fe 100644 --- a/__tests__/__packages__/cli-test-utils/package.json +++ b/__tests__/__packages__/cli-test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@zowe/cli-test-utils", - "version": "8.10.0", + "version": "8.10.1", "description": "Test utilities package for Zowe CLI plug-ins", "author": "Zowe", "license": "EPL-2.0", @@ -43,7 +43,7 @@ "devDependencies": { "@types/js-yaml": "^4.0.9", "@types/uuid": "^10.0.0", - "@zowe/imperative": "8.10.0" + "@zowe/imperative": "8.10.1" }, "peerDependencies": { "@zowe/imperative": "^8.0.0" diff --git a/lerna.json b/lerna.json index 20d40cb3b5..1df9859d41 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "8.10.0", + "version": "8.10.1", "command": { "publish": { "ignoreChanges": [ diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 486f42b2ad..62f8db5971 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -52,7 +52,7 @@ }, "__tests__/__packages__/cli-test-utils": { "name": "@zowe/cli-test-utils", - "version": "8.10.0", + "version": "8.10.1", "license": "EPL-2.0", "dependencies": { "find-up": "^5.0.0", @@ -63,7 +63,7 @@ "devDependencies": { "@types/js-yaml": "^4.0.9", "@types/uuid": "^10.0.0", - "@zowe/imperative": "8.10.0" + "@zowe/imperative": "8.10.1" }, "peerDependencies": { "@zowe/imperative": "^8.0.0" @@ -16269,21 +16269,21 @@ }, "packages/cli": { "name": "@zowe/cli", - "version": "8.10.0", + "version": "8.10.1", "hasInstallScript": true, "license": "EPL-2.0", "dependencies": { - "@zowe/core-for-zowe-sdk": "8.10.0", - "@zowe/imperative": "8.10.0", - "@zowe/provisioning-for-zowe-sdk": "8.10.0", - "@zowe/zos-console-for-zowe-sdk": "8.10.0", - "@zowe/zos-files-for-zowe-sdk": "8.10.0", - "@zowe/zos-jobs-for-zowe-sdk": "8.10.0", - "@zowe/zos-logs-for-zowe-sdk": "8.10.0", - "@zowe/zos-tso-for-zowe-sdk": "8.10.0", - "@zowe/zos-uss-for-zowe-sdk": "8.10.0", - "@zowe/zos-workflows-for-zowe-sdk": "8.10.0", - "@zowe/zosmf-for-zowe-sdk": "8.10.0", + "@zowe/core-for-zowe-sdk": "8.10.1", + "@zowe/imperative": "8.10.1", + "@zowe/provisioning-for-zowe-sdk": "8.10.1", + "@zowe/zos-console-for-zowe-sdk": "8.10.1", + "@zowe/zos-files-for-zowe-sdk": "8.10.1", + "@zowe/zos-jobs-for-zowe-sdk": "8.10.1", + "@zowe/zos-logs-for-zowe-sdk": "8.10.1", + "@zowe/zos-tso-for-zowe-sdk": "8.10.1", + "@zowe/zos-uss-for-zowe-sdk": "8.10.1", + "@zowe/zos-workflows-for-zowe-sdk": "8.10.1", + "@zowe/zosmf-for-zowe-sdk": "8.10.1", "find-process": "1.4.7", "lodash": "4.17.21", "minimatch": "9.0.5", @@ -16296,7 +16296,7 @@ "@types/diff": "^5.0.9", "@types/lodash": "^4.17.6", "@types/tar": "^6.1.11", - "@zowe/cli-test-utils": "8.10.0", + "@zowe/cli-test-utils": "8.10.1", "comment-json": "^4.2.3", "strip-ansi": "^6.0.1", "which": "^4.0.0" @@ -16352,15 +16352,15 @@ }, "packages/core": { "name": "@zowe/core-for-zowe-sdk", - "version": "8.10.0", + "version": "8.10.1", "license": "EPL-2.0", "dependencies": { "comment-json": "~4.2.3", "string-width": "^4.2.3" }, "devDependencies": { - "@zowe/cli-test-utils": "8.10.0", - "@zowe/imperative": "8.10.0" + "@zowe/cli-test-utils": "8.10.1", + "@zowe/imperative": "8.10.1" }, "engines": { "node": ">=18.12.0" @@ -16371,7 +16371,7 @@ }, "packages/imperative": { "name": "@zowe/imperative", - "version": "8.10.0", + "version": "8.10.1", "license": "EPL-2.0", "dependencies": { "@types/yargs": "^17.0.32", @@ -16565,16 +16565,16 @@ }, "packages/provisioning": { "name": "@zowe/provisioning-for-zowe-sdk", - "version": "8.10.0", + "version": "8.10.1", "license": "EPL-2.0", "dependencies": { "js-yaml": "^4.1.0" }, "devDependencies": { "@types/js-yaml": "^4.0.9", - "@zowe/cli-test-utils": "8.10.0", - "@zowe/core-for-zowe-sdk": "8.10.0", - "@zowe/imperative": "8.10.0" + "@zowe/cli-test-utils": "8.10.1", + "@zowe/core-for-zowe-sdk": "8.10.1", + "@zowe/imperative": "8.10.1" }, "engines": { "node": ">=18.12.0" @@ -16599,15 +16599,15 @@ }, "packages/workflows": { "name": "@zowe/zos-workflows-for-zowe-sdk", - "version": "8.10.0", + "version": "8.10.1", "license": "EPL-2.0", "dependencies": { - "@zowe/zos-files-for-zowe-sdk": "8.10.0" + "@zowe/zos-files-for-zowe-sdk": "8.10.1" }, "devDependencies": { - "@zowe/cli-test-utils": "8.10.0", - "@zowe/core-for-zowe-sdk": "8.10.0", - "@zowe/imperative": "8.10.0" + "@zowe/cli-test-utils": "8.10.1", + "@zowe/core-for-zowe-sdk": "8.10.1", + "@zowe/imperative": "8.10.1" }, "engines": { "node": ">=18.12.0" @@ -16619,12 +16619,12 @@ }, "packages/zosconsole": { "name": "@zowe/zos-console-for-zowe-sdk", - "version": "8.10.0", + "version": "8.10.1", "license": "EPL-2.0", "devDependencies": { - "@zowe/cli-test-utils": "8.10.0", - "@zowe/core-for-zowe-sdk": "8.10.0", - "@zowe/imperative": "8.10.0" + "@zowe/cli-test-utils": "8.10.1", + "@zowe/core-for-zowe-sdk": "8.10.1", + "@zowe/imperative": "8.10.1" }, "engines": { "node": ">=18.12.0" @@ -16636,17 +16636,17 @@ }, "packages/zosfiles": { "name": "@zowe/zos-files-for-zowe-sdk", - "version": "8.10.0", + "version": "8.10.1", "license": "EPL-2.0", "dependencies": { "lodash": "^4.17.21", "minimatch": "^9.0.5" }, "devDependencies": { - "@zowe/cli-test-utils": "8.10.0", - "@zowe/core-for-zowe-sdk": "8.10.0", - "@zowe/imperative": "8.10.0", - "@zowe/zos-uss-for-zowe-sdk": "8.10.0" + "@zowe/cli-test-utils": "8.10.1", + "@zowe/core-for-zowe-sdk": "8.10.1", + "@zowe/imperative": "8.10.1", + "@zowe/zos-uss-for-zowe-sdk": "8.10.1" }, "engines": { "node": ">=18.12.0" @@ -16678,15 +16678,15 @@ }, "packages/zosjobs": { "name": "@zowe/zos-jobs-for-zowe-sdk", - "version": "8.10.0", + "version": "8.10.1", "license": "EPL-2.0", "dependencies": { - "@zowe/zos-files-for-zowe-sdk": "8.10.0" + "@zowe/zos-files-for-zowe-sdk": "8.10.1" }, "devDependencies": { - "@zowe/cli-test-utils": "8.10.0", - "@zowe/core-for-zowe-sdk": "8.10.0", - "@zowe/imperative": "8.10.0" + "@zowe/cli-test-utils": "8.10.1", + "@zowe/core-for-zowe-sdk": "8.10.1", + "@zowe/imperative": "8.10.1" }, "engines": { "node": ">=18.12.0" @@ -16698,12 +16698,12 @@ }, "packages/zoslogs": { "name": "@zowe/zos-logs-for-zowe-sdk", - "version": "8.10.0", + "version": "8.10.1", "license": "EPL-2.0", "devDependencies": { - "@zowe/cli-test-utils": "8.10.0", - "@zowe/core-for-zowe-sdk": "8.10.0", - "@zowe/imperative": "8.10.0" + "@zowe/cli-test-utils": "8.10.1", + "@zowe/core-for-zowe-sdk": "8.10.1", + "@zowe/imperative": "8.10.1" }, "engines": { "node": ">=18.12.0" @@ -16715,12 +16715,12 @@ }, "packages/zosmf": { "name": "@zowe/zosmf-for-zowe-sdk", - "version": "8.10.0", + "version": "8.10.1", "license": "EPL-2.0", "devDependencies": { - "@zowe/cli-test-utils": "8.10.0", - "@zowe/core-for-zowe-sdk": "8.10.0", - "@zowe/imperative": "8.10.0" + "@zowe/cli-test-utils": "8.10.1", + "@zowe/core-for-zowe-sdk": "8.10.1", + "@zowe/imperative": "8.10.1" }, "engines": { "node": ">=18.12.0" @@ -16732,15 +16732,15 @@ }, "packages/zostso": { "name": "@zowe/zos-tso-for-zowe-sdk", - "version": "8.10.0", + "version": "8.10.1", "license": "EPL-2.0", "dependencies": { - "@zowe/zosmf-for-zowe-sdk": "8.10.0" + "@zowe/zosmf-for-zowe-sdk": "8.10.1" }, "devDependencies": { - "@zowe/cli-test-utils": "8.10.0", - "@zowe/core-for-zowe-sdk": "8.10.0", - "@zowe/imperative": "8.10.0" + "@zowe/cli-test-utils": "8.10.1", + "@zowe/core-for-zowe-sdk": "8.10.1", + "@zowe/imperative": "8.10.1" }, "engines": { "node": ">=18.12.0" @@ -16752,15 +16752,15 @@ }, "packages/zosuss": { "name": "@zowe/zos-uss-for-zowe-sdk", - "version": "8.10.0", + "version": "8.10.1", "license": "EPL-2.0", "dependencies": { "ssh2": "^1.15.0" }, "devDependencies": { "@types/ssh2": "^1.11.19", - "@zowe/cli-test-utils": "8.10.0", - "@zowe/imperative": "8.10.0" + "@zowe/cli-test-utils": "8.10.1", + "@zowe/imperative": "8.10.1" }, "engines": { "node": ">=18.12.0" diff --git a/packages/cli/package.json b/packages/cli/package.json index 6f4a7fec69..cf52e04915 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@zowe/cli", - "version": "8.10.0", + "version": "8.10.1", "zoweVersion": "v3.0.0", "description": "Zowe CLI is a command line interface (CLI) that provides a simple and streamlined way to interact with IBM z/OS.", "author": "Zowe", @@ -58,17 +58,17 @@ "preshrinkwrap": "node ../../scripts/rewriteShrinkwrap.js" }, "dependencies": { - "@zowe/core-for-zowe-sdk": "8.10.0", - "@zowe/imperative": "8.10.0", - "@zowe/provisioning-for-zowe-sdk": "8.10.0", - "@zowe/zos-console-for-zowe-sdk": "8.10.0", - "@zowe/zos-files-for-zowe-sdk": "8.10.0", - "@zowe/zos-jobs-for-zowe-sdk": "8.10.0", - "@zowe/zos-logs-for-zowe-sdk": "8.10.0", - "@zowe/zos-tso-for-zowe-sdk": "8.10.0", - "@zowe/zos-uss-for-zowe-sdk": "8.10.0", - "@zowe/zos-workflows-for-zowe-sdk": "8.10.0", - "@zowe/zosmf-for-zowe-sdk": "8.10.0", + "@zowe/core-for-zowe-sdk": "8.10.1", + "@zowe/imperative": "8.10.1", + "@zowe/provisioning-for-zowe-sdk": "8.10.1", + "@zowe/zos-console-for-zowe-sdk": "8.10.1", + "@zowe/zos-files-for-zowe-sdk": "8.10.1", + "@zowe/zos-jobs-for-zowe-sdk": "8.10.1", + "@zowe/zos-logs-for-zowe-sdk": "8.10.1", + "@zowe/zos-tso-for-zowe-sdk": "8.10.1", + "@zowe/zos-uss-for-zowe-sdk": "8.10.1", + "@zowe/zos-workflows-for-zowe-sdk": "8.10.1", + "@zowe/zosmf-for-zowe-sdk": "8.10.1", "find-process": "1.4.7", "lodash": "4.17.21", "minimatch": "9.0.5", @@ -78,7 +78,7 @@ "@types/diff": "^5.0.9", "@types/lodash": "^4.17.6", "@types/tar": "^6.1.11", - "@zowe/cli-test-utils": "8.10.0", + "@zowe/cli-test-utils": "8.10.1", "comment-json": "^4.2.3", "strip-ansi": "^6.0.1", "which": "^4.0.0" diff --git a/packages/core/package.json b/packages/core/package.json index bd4db2f727..91a8be1c76 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@zowe/core-for-zowe-sdk", - "version": "8.10.0", + "version": "8.10.1", "description": "Core libraries shared by Zowe SDK packages", "author": "Zowe", "license": "EPL-2.0", @@ -49,8 +49,8 @@ "string-width": "^4.2.3" }, "devDependencies": { - "@zowe/cli-test-utils": "8.10.0", - "@zowe/imperative": "8.10.0" + "@zowe/cli-test-utils": "8.10.1", + "@zowe/imperative": "8.10.1" }, "peerDependencies": { "@zowe/imperative": "^8.0.0" diff --git a/packages/imperative/CHANGELOG.md b/packages/imperative/CHANGELOG.md index 31d5b27ac3..0ba57c37bf 100644 --- a/packages/imperative/CHANGELOG.md +++ b/packages/imperative/CHANGELOG.md @@ -3,7 +3,7 @@ All notable changes to the Imperative package will be documented in this file. -## Recent Changes +## `8.10.1` - BugFix: Resolved an issue where base profiles in a team configuration file were overwritten when a user configuration file did not include a base profile. [#2383](https://github.com/zowe/zowe-cli/pull/2383) diff --git a/packages/imperative/package.json b/packages/imperative/package.json index addb75bd41..1f79f56d67 100644 --- a/packages/imperative/package.json +++ b/packages/imperative/package.json @@ -1,6 +1,6 @@ { "name": "@zowe/imperative", - "version": "8.10.0", + "version": "8.10.1", "description": "framework for building configurable CLIs", "author": "Zowe", "license": "EPL-2.0", diff --git a/packages/provisioning/package.json b/packages/provisioning/package.json index 2453327ebf..d73cca706d 100644 --- a/packages/provisioning/package.json +++ b/packages/provisioning/package.json @@ -1,6 +1,6 @@ { "name": "@zowe/provisioning-for-zowe-sdk", - "version": "8.10.0", + "version": "8.10.1", "description": "Zowe SDK to interact with the z/OS provisioning APIs", "author": "Zowe", "license": "EPL-2.0", @@ -49,9 +49,9 @@ }, "devDependencies": { "@types/js-yaml": "^4.0.9", - "@zowe/cli-test-utils": "8.10.0", - "@zowe/core-for-zowe-sdk": "8.10.0", - "@zowe/imperative": "8.10.0" + "@zowe/cli-test-utils": "8.10.1", + "@zowe/core-for-zowe-sdk": "8.10.1", + "@zowe/imperative": "8.10.1" }, "peerDependencies": { "@zowe/core-for-zowe-sdk": "^8.0.0", diff --git a/packages/workflows/package.json b/packages/workflows/package.json index 696c7bf8b8..83e62ab6ef 100644 --- a/packages/workflows/package.json +++ b/packages/workflows/package.json @@ -1,6 +1,6 @@ { "name": "@zowe/zos-workflows-for-zowe-sdk", - "version": "8.10.0", + "version": "8.10.1", "description": "Zowe SDK to interact with the z/OS workflows APIs", "author": "Zowe", "license": "EPL-2.0", @@ -45,12 +45,12 @@ "prepack": "node ../../scripts/prepareLicenses.js" }, "dependencies": { - "@zowe/zos-files-for-zowe-sdk": "8.10.0" + "@zowe/zos-files-for-zowe-sdk": "8.10.1" }, "devDependencies": { - "@zowe/cli-test-utils": "8.10.0", - "@zowe/core-for-zowe-sdk": "8.10.0", - "@zowe/imperative": "8.10.0" + "@zowe/cli-test-utils": "8.10.1", + "@zowe/core-for-zowe-sdk": "8.10.1", + "@zowe/imperative": "8.10.1" }, "peerDependencies": { "@zowe/core-for-zowe-sdk": "^8.0.0", diff --git a/packages/zosconsole/package.json b/packages/zosconsole/package.json index 1f0e7d4dad..a8bb679910 100644 --- a/packages/zosconsole/package.json +++ b/packages/zosconsole/package.json @@ -1,6 +1,6 @@ { "name": "@zowe/zos-console-for-zowe-sdk", - "version": "8.10.0", + "version": "8.10.1", "description": "Zowe SDK to interact with the z/OS console", "author": "Zowe", "license": "EPL-2.0", @@ -45,9 +45,9 @@ "prepack": "node ../../scripts/prepareLicenses.js" }, "devDependencies": { - "@zowe/cli-test-utils": "8.10.0", - "@zowe/core-for-zowe-sdk": "8.10.0", - "@zowe/imperative": "8.10.0" + "@zowe/cli-test-utils": "8.10.1", + "@zowe/core-for-zowe-sdk": "8.10.1", + "@zowe/imperative": "8.10.1" }, "peerDependencies": { "@zowe/core-for-zowe-sdk": "^8.0.0", diff --git a/packages/zosfiles/package.json b/packages/zosfiles/package.json index 27302db5f8..a3d961cdec 100644 --- a/packages/zosfiles/package.json +++ b/packages/zosfiles/package.json @@ -1,6 +1,6 @@ { "name": "@zowe/zos-files-for-zowe-sdk", - "version": "8.10.0", + "version": "8.10.1", "description": "Zowe SDK to interact with files and data sets on z/OS", "author": "Zowe", "license": "EPL-2.0", @@ -50,10 +50,10 @@ "minimatch": "^9.0.5" }, "devDependencies": { - "@zowe/cli-test-utils": "8.10.0", - "@zowe/core-for-zowe-sdk": "8.10.0", - "@zowe/imperative": "8.10.0", - "@zowe/zos-uss-for-zowe-sdk": "8.10.0" + "@zowe/cli-test-utils": "8.10.1", + "@zowe/core-for-zowe-sdk": "8.10.1", + "@zowe/imperative": "8.10.1", + "@zowe/zos-uss-for-zowe-sdk": "8.10.1" }, "peerDependencies": { "@zowe/core-for-zowe-sdk": "^8.0.0", diff --git a/packages/zosjobs/package.json b/packages/zosjobs/package.json index d403fdb683..cdd98b4ab8 100644 --- a/packages/zosjobs/package.json +++ b/packages/zosjobs/package.json @@ -1,6 +1,6 @@ { "name": "@zowe/zos-jobs-for-zowe-sdk", - "version": "8.10.0", + "version": "8.10.1", "description": "Zowe SDK to interact with jobs on z/OS", "author": "Zowe", "license": "EPL-2.0", @@ -46,12 +46,12 @@ "prepack": "node ../../scripts/prepareLicenses.js" }, "dependencies": { - "@zowe/zos-files-for-zowe-sdk": "8.10.0" + "@zowe/zos-files-for-zowe-sdk": "8.10.1" }, "devDependencies": { - "@zowe/cli-test-utils": "8.10.0", - "@zowe/core-for-zowe-sdk": "8.10.0", - "@zowe/imperative": "8.10.0" + "@zowe/cli-test-utils": "8.10.1", + "@zowe/core-for-zowe-sdk": "8.10.1", + "@zowe/imperative": "8.10.1" }, "peerDependencies": { "@zowe/core-for-zowe-sdk": "^8.0.0", diff --git a/packages/zoslogs/package.json b/packages/zoslogs/package.json index 4d5cce70e9..df452cd7ca 100644 --- a/packages/zoslogs/package.json +++ b/packages/zoslogs/package.json @@ -1,6 +1,6 @@ { "name": "@zowe/zos-logs-for-zowe-sdk", - "version": "8.10.0", + "version": "8.10.1", "description": "Zowe SDK to interact with the z/OS logs", "author": "Zowe", "license": "EPL-2.0", @@ -45,9 +45,9 @@ "prepack": "node ../../scripts/prepareLicenses.js" }, "devDependencies": { - "@zowe/cli-test-utils": "8.10.0", - "@zowe/core-for-zowe-sdk": "8.10.0", - "@zowe/imperative": "8.10.0" + "@zowe/cli-test-utils": "8.10.1", + "@zowe/core-for-zowe-sdk": "8.10.1", + "@zowe/imperative": "8.10.1" }, "peerDependencies": { "@zowe/core-for-zowe-sdk": "^8.0.0", diff --git a/packages/zosmf/package.json b/packages/zosmf/package.json index fa09136aa6..e25ea44139 100644 --- a/packages/zosmf/package.json +++ b/packages/zosmf/package.json @@ -1,6 +1,6 @@ { "name": "@zowe/zosmf-for-zowe-sdk", - "version": "8.10.0", + "version": "8.10.1", "description": "Zowe SDK to interact with the z/OS Management Facility", "author": "Zowe", "license": "EPL-2.0", @@ -44,9 +44,9 @@ "prepack": "node ../../scripts/prepareLicenses.js" }, "devDependencies": { - "@zowe/cli-test-utils": "8.10.0", - "@zowe/core-for-zowe-sdk": "8.10.0", - "@zowe/imperative": "8.10.0" + "@zowe/cli-test-utils": "8.10.1", + "@zowe/core-for-zowe-sdk": "8.10.1", + "@zowe/imperative": "8.10.1" }, "peerDependencies": { "@zowe/core-for-zowe-sdk": "^8.0.0", diff --git a/packages/zostso/package.json b/packages/zostso/package.json index 41a72be750..e9bb16f105 100644 --- a/packages/zostso/package.json +++ b/packages/zostso/package.json @@ -1,6 +1,6 @@ { "name": "@zowe/zos-tso-for-zowe-sdk", - "version": "8.10.0", + "version": "8.10.1", "description": "Zowe SDK to interact with TSO on z/OS", "author": "Zowe", "license": "EPL-2.0", @@ -45,12 +45,12 @@ "prepack": "node ../../scripts/prepareLicenses.js" }, "dependencies": { - "@zowe/zosmf-for-zowe-sdk": "8.10.0" + "@zowe/zosmf-for-zowe-sdk": "8.10.1" }, "devDependencies": { - "@zowe/cli-test-utils": "8.10.0", - "@zowe/core-for-zowe-sdk": "8.10.0", - "@zowe/imperative": "8.10.0" + "@zowe/cli-test-utils": "8.10.1", + "@zowe/core-for-zowe-sdk": "8.10.1", + "@zowe/imperative": "8.10.1" }, "peerDependencies": { "@zowe/core-for-zowe-sdk": "^8.0.0", diff --git a/packages/zosuss/package.json b/packages/zosuss/package.json index 60e7ffbd0a..2b5821dfe7 100644 --- a/packages/zosuss/package.json +++ b/packages/zosuss/package.json @@ -1,6 +1,6 @@ { "name": "@zowe/zos-uss-for-zowe-sdk", - "version": "8.10.0", + "version": "8.10.1", "description": "Zowe SDK to interact with USS on z/OS", "author": "Zowe", "license": "EPL-2.0", @@ -49,8 +49,8 @@ }, "devDependencies": { "@types/ssh2": "^1.11.19", - "@zowe/cli-test-utils": "8.10.0", - "@zowe/imperative": "8.10.0" + "@zowe/cli-test-utils": "8.10.1", + "@zowe/imperative": "8.10.1" }, "peerDependencies": { "@zowe/imperative": "^8.0.0" From d108662e4b0c62e488b81bb2e156cc9b9c6eb0ab Mon Sep 17 00:00:00 2001 From: Gene Johnston Date: Wed, 18 Dec 2024 13:21:35 -0500 Subject: [PATCH 25/25] Add new 'Edge Case' section Signed-off-by: Gene Johnston --- docs/Design_for_selecting_auth_type.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/docs/Design_for_selecting_auth_type.md b/docs/Design_for_selecting_auth_type.md index 9466b16a2d..c7f2c9761a 100644 --- a/docs/Design_for_selecting_auth_type.md +++ b/docs/Design_for_selecting_auth_type.md @@ -157,9 +157,21 @@ That addition would enable customers to also specify the authentication order of - We must notify extenders to guide their customers to supply an appropriate authOrder property if their extension needs a non-default order. +## Edge cases that must be confirmed during implementation + +Every edge case was not included in the protype used to confirm the validity of this design document. The following edge cases must be confirmed and appropriate logic must be written during the implementation of this design. + +- As an alternative to user & password, a property named base64EncodedAuth can be used in a session. The new APIs must handle this alternative. + +- To login to APIML, a user supplies a user & password (or cert) and receives a token. The new APIs must determine the correct authentication type for such a session which kind of morphs its authentication type during the transaction. + +- Zowe Explorer currently inherits the same hard-coded order as the CLI from the client SDKs. The new authentication order APIs must be integrated into the APIs used by Zowe Explorer. We must confirm whether any additional logic changes must be made within Zowe Explorer itself. + +- TSO commands can create a sequence of actions, which use a user & password, receive a token from TSO, and then use that token for additional actions. This change in authentication type during the life of a single transaction may be self contained within the transaction and may not not influenced by either the existing hard-coded authentication order or the new user-controlled authentication order. This behavior must be confirmed and modifications made if necessary. + ## A new class must be created to support authOrder -A new class located in +A new class located in:     [zowe-cli/packages/imperative/src/rest/src/session/AuthOrder.ts](https://github.com/zowe/zowe-cli/blob/master/packages/imperative/src/rest/src/session/AuthOrder.ts)