diff --git a/CHANGELOG.md b/CHANGELOG.md index 2cf8ac82..4ac7c97c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,9 @@ All notable changes to the Zlux Server Framework package will be documented in this file. This repo is part of the app-server Zowe Component, and the change logs here may appear on Zowe.org in that section. +## 3.1.0 +- Fix: Server would not work when API Gateway token name was customized (#573) + ## 3.0.0 - Enhancement: Add ability for server to dynamically load plugin web content based on `entryPoint` specification in the `pluginDefinition.json` diff --git a/plugins/sso-auth/lib/apimlHandler.js b/plugins/sso-auth/lib/apimlHandler.js index 18da936b..9977f324 100644 --- a/plugins/sso-auth/lib/apimlHandler.js +++ b/plugins/sso-auth/lib/apimlHandler.js @@ -21,7 +21,6 @@ const zluxUtil = require('../../../lib/util'); */ const DEFAULT_EXPIRATION_MS = 29700000; const TOKEN_NAME = 'apimlAuthenticationToken'; -const TOKEN_LENGTH = TOKEN_NAME.length; function readUtf8FilesToArray(fileArray) { var contentArray = []; @@ -57,6 +56,11 @@ function readUtf8FilesToArray(fileArray) { class ApimlHandler { constructor(pluginDef, pluginConf, componentConf, context, zoweConf) { this.logger = context.logger; + if (zoweConf.zowe.cookieIdentifier && (zoweConf.components.gateway?.apiml?.security?.auth?.uniqueCookie === true)) { + this.tokenName = `${TOKEN_NAME}.${zoweConf.zowe.cookieIdentifier}`; + } else { + this.tokenName = TOKEN_NAME; + } this.apimlConf = componentConf.node.mediationLayer.server; this.gatewayUrl = `https://${this.apimlConf.gatewayHostname}:${this.apimlConf.gatewayPort}`; this.isHttps = !zluxUtil.isClientAttls(zoweConf); @@ -72,7 +76,7 @@ class ApimlHandler { logout(request, sessionState) { return new Promise((resolve, reject) => { - if (!(request.cookies && request.cookies[TOKEN_NAME])) { + if (!(request.cookies && request.cookies[this.tokenName])) { return resolve({success: true}); } const gatewayUrl = this.gatewayUrl; @@ -83,7 +87,7 @@ class ApimlHandler { path: '/apicatalog/api/v1/auth/logout', method: 'POST', headers: { - 'apimlAuthenticationToken': request.cookies[TOKEN_NAME] + 'apimlAuthenticationToken': request.cookies[this.tokenName] }, agent: this.httpsAgent } @@ -94,7 +98,7 @@ class ApimlHandler { res.on('end', () => { let apimlCookie; if (res.statusCode >= 200 && res.statusCode < 300) { - resolve({ success: true, cookies: [{name:TOKEN_NAME, + resolve({ success: true, cookies: [{name:this.tokenName, value:'non-token', options: {httpOnly: true, secure: true, @@ -153,7 +157,7 @@ class ApimlHandler { resolve({success: false}); // return the object directly }); }); - } else if (request.cookies && request.cookies[TOKEN_NAME]) { + } else if (request.cookies && request.cookies[this.tokenName]) { return this.authenticateViaCookie(request, sessionState); } else { return Promise.resolve({success: false}); @@ -162,8 +166,8 @@ class ApimlHandler { authenticateViaCookie(request, sessionState) { return new Promise((resolve, reject)=> { - this.logger.debug(`Authenticate with cookie`,TOKEN_NAME); - this.queryToken(request.cookies[TOKEN_NAME]).then(data=> { + this.logger.debug(`Authenticate with cookie`,this.tokenName); + this.queryToken(request.cookies[this.tokenName]).then(data=> { let expiration; const expirationDate = new Date(data.expiration); const creationDate = new Date(data.creation); @@ -177,7 +181,7 @@ class ApimlHandler { this.doLogin(request, sessionState, true).then(result=> resolve(result)) .catch(e => reject(e)); } else { - this.setState(request.cookies[TOKEN_NAME], + this.setState(request.cookies[this.tokenName], data.userId, sessionState); resolve({success: true, username: sessionState.username, expms: expiration}); } @@ -230,7 +234,7 @@ class ApimlHandler { return new Promise((resolve, reject) => { const options = this.makeOptions('/gateway/api/v1/auth/query', 'GET', - TOKEN_NAME+'='+token); + this.tokenName+'='+token); let data = []; this.logger.debug(`Sending query token request to path=${options.path}`); @@ -283,9 +287,9 @@ class ApimlHandler { if (typeof res.headers['set-cookie'] === 'object') { for (const cookie of res.headers['set-cookie']) { const content = cookie.split(';')[0]; - let index = content.indexOf(TOKEN_NAME); + let index = content.indexOf(this.tokenName); if (index >= 0) { - token = content.substring(index+1+TOKEN_LENGTH); + token = content.substring(index+1+this.tokenName.length); } } } @@ -305,7 +309,7 @@ class ApimlHandler { if (expiration > 0) { this.setState(token, data.userId, sessionState); resolve({ success: true, username: sessionState.username, expms: expiration, - cookies: [{name:TOKEN_NAME, value:token, options: {httpOnly: true, secure: true}}]}); + cookies: [{name:this.tokenName, value:token, options: {httpOnly: true, secure: true}}]}); } else { resolve({ success: false, reason: 'Unknown'}); } @@ -371,7 +375,7 @@ class ApimlHandler { authorized(request, sessionState) { if (sessionState.authenticated) { request.username = sessionState.username; - request.ssoToken = request.cookies[TOKEN_NAME]; + request.ssoToken = request.cookies[this.tokenName]; return Promise.resolve({ authenticated: true, authorized: true }); } else { return Promise.resolve({ authenticated: false, authorized: false }); @@ -382,7 +386,7 @@ class ApimlHandler { if (!sessionState.apimlToken) { return; } -// req2Options.headers[TOKEN_NAME] = sessionState.apimlToken; +// req2Options.headers[this.tokenName] = sessionState.apimlToken; if (this.usingSso) { req2Options.headers['Authorization'] = 'Bearer '+sessionState.apimlToken; } @@ -390,7 +394,7 @@ class ApimlHandler { restoreSessionState(request, sessionState) { return new Promise((resolve, _reject) => { - const token = request.cookies[TOKEN_NAME]; + const token = request.cookies[this.tokenName]; if (!token) { sessionState.authenticated = false; resolve({success: false});