UNPKG

apiconnect-cli-login-bluemix

Version:

Plugin for IBM API Connect Developer Toolkit.

286 lines (246 loc) 7.61 kB
/********************************************************* {COPYRIGHT-TOP} *** * Licensed Materials - Property of IBM * 5725-Z22, 5725-Z63, 5725-U33, 5725-Z63 * * (C) Copyright IBM Corporation 2016, 2018 * * All Rights Reserved. * US Government Users Restricted Rights - Use, duplication or disclosure * restricted by GSA ADP Schedule Contract with IBM Corp. ********************************************************** {COPYRIGHT-END} **/ // Node module: apiconnect-cli-login-bluemix var Promise = require('bluebird'); var bluemixSecurity = require('./bluemix-security'); var config = require('apiconnect-config').loadConfig(); var debug = require('debug')('apiconnect-cli-login-bluemix:bluemix-login'); var eula = require('./apiconnect-eula'); var g = require('strong-globalize')(); var jwt = require('jsonwebtoken'); module.exports = BluemixLogin; var DEV_ENV = 'stage'; var PROD_ENV = 'production'; var EULA_NAME = 'apiconnect'; var TOKEN_STORE = require('apiconnect-config').TOKEN_STORE; var DEFAULT_DOMAIN = 'apiconnect.cloud.ibm.com'; var publicCloudDomains = [ 'apiconnect.ibmcloud.com', 'apiconnect.cloud.ibm.com', 'apiconnect.test.cloud.ibm.com', 'apim.ibmcloud.com', 'cumulus.apim.ibmcloud.com', 'stratus.apim.ibmcloud.com', ]; var REGION_ENVIRONMENT_MAP = { hogan: DEV_ENV, poyle: DEV_ENV, sagami: DEV_ENV, gothics: DEV_ENV, us: PROD_ENV, eu: PROD_ENV, au: PROD_ENV, ca: PROD_ENV, ap: PROD_ENV, 'us-south': PROD_ENV, 'us-east': PROD_ENV, 'eu-gb': PROD_ENV, 'eu-de': PROD_ENV, 'au-syd': PROD_ENV, }; /** * Bluemix login class. * * @param {string} hostname * @constructor */ function BluemixLogin(hostname, options) { options = options || {}; if (!hostname) { throw new Error(g.f('hostname is undefined.')); } // Strip `apimanager.` off hostnames since we don't need that here hostname = hostname.replace(/apimanager./ig, ''); var hostnameSplit = hostname.split('.'); var region = hostnameSplit[0]; var domain = hostnameSplit.slice(1).join('.'); this.tokenKey = hostname; var isKnownRegion; if (domain.indexOf('test.cloud.ibm.com') > -1 && region in REGION_ENVIRONMENT_MAP) { isKnownRegion = true; this.tokenKey = [ DEV_ENV, domain ].join('.'); } else if (publicCloudDomains.indexOf(domain) > -1 && region in REGION_ENVIRONMENT_MAP) { var regionType = REGION_ENVIRONMENT_MAP[region] || PROD_ENV; isKnownRegion = !!REGION_ENVIRONMENT_MAP[region]; this.tokenKey = [ regionType, domain ].join('.'); } this.tokenKey = [ this.tokenKey, 'jwt' ].join('.'); if (options.loginServerUrl) { this.loginServerUrl = options.loginServerUrl; } else if (publicCloudDomains.indexOf(domain) > -1) { this.loginServerUrl = isKnownRegion ? 'https://login.service.' + hostname : 'https://login.service.us-south.' + DEFAULT_DOMAIN; } else { this.loginServerUrl = 'https://login-service-apiconnect.' + domain; } } /** * Login with username/password and return/store an encrypted jwt token. * * @param {string} username * @param {string} password * @return {Promise} returns a Promise with the encrypted jwt token. */ BluemixLogin.prototype.loginWithBasicAuth = function(username, password) { return bluemixSecurity.passwordToken(username, password, this.loginServerUrl) .then(storeToken(this.tokenKey)) .then(signEULA(this.eulaServerUrl)); }; /** * Login with a Bluemix apikey, and return/store an encrypted jwt token. * * @param {string} apikey * @return {Promise} returns a Promise with the encrypted jwt token. */ BluemixLogin.prototype.loginWithApiKey = function(apikey) { return bluemixSecurity.apiKeyForToken(apikey, this.loginServerUrl) .then(storeToken(this.tokenKey)) .then(signEULA(this.eulaServerUrl)); }; /** * Login with a one-time Bluemix SSO passcode, and return/store an encrypted jwt token. * * @param {string} passcode * @return {Promise} returns a Promise with the encrypted jwt token. */ BluemixLogin.prototype.loginWithPasscode = function(passcode) { return bluemixSecurity.passcodeForToken(passcode, this.loginServerUrl) .then(storeToken(this.tokenKey)) .then(signEULA(this.eulaServerUrl)); }; /** * Login with a temporary code, and return/store an encrypted jwt token. * * @param {string} code * @return {Promise} returns a Promise with the encrypted jwt token. */ BluemixLogin.prototype.loginWithCode = function(code) { return bluemixSecurity.codeForToken(code, this.loginServerUrl) .then(storeToken(this.tokenKey)) .then(signEULA(this.eulaServerUrl)); }; /** * Get the passcode URL. * * @return {Promise} returns a Promise with the passcode URL. */ BluemixLogin.prototype.getPasscodeUrl = function() { return bluemixSecurity.getPasscodeURL(this.loginServerUrl); }; /** * Log out of the Bluemix Security server. */ BluemixLogin.prototype.logout = function() { if (config.getOne(this.tokenKey, TOKEN_STORE)) { config.delete(this.tokenKey, TOKEN_STORE); } }; /** * Get login status. * * @return {Promise} returns a Promise with an object containing: * target url, logged in, and token expiration. */ BluemixLogin.prototype.status = function() { var token = config.getOne(this.tokenKey, TOKEN_STORE); var result = { url: this.loginServerUrl, loggedIn: false, expiration: void 0, }; if (!token) { return Promise.resolve(result); } return verifyToken(token, this.loginServerUrl) .then(function(decryptedToken) { result.loggedIn = true; result.expiration = decryptedToken.exp; return result; }) .catch(function(error) { debug(error); return result; }); }; /** * Get encrypted jwt token. * * @return {Promise} returns a Promise with the encrypted jwt token if present and valid, otherwise returns undefined. */ BluemixLogin.prototype.token = function() { var token = config.getOne(this.tokenKey, TOKEN_STORE); var that = this; if (!token) { return Promise.resolve(void 0); } return verifyToken(token, that.loginServerUrl) .then(function() { return token; }) .catch(function(error) { if (error.name === 'TokenExpiredError') { return bluemixSecurity.refresh(token, that.loginServerUrl); } return Promise.reject(error); }) .then(storeToken(that.tokenKey)) .catch(function(error) { debug(error); return void 0; }); }; /** * Get decrypted jwt token. * * @return {Promise} returns a Promise with the decrypted jwt token if present and valid, otherwise returns undefined. */ BluemixLogin.prototype.decryptedToken = function() { var that = this; return this.token() .then(function(token) { if (token) { return verifyToken(token, that.loginServerUrl); } return token; }) .catch(function(error) { debug(error); return void 0; }); }; function verifyToken(token, loginServerUrl) { return bluemixSecurity.getCert(loginServerUrl).then(function(body) { return Promise.try(function() { var options = { algorithms: [ body.alg || body.algorithm ], }; return jwt.verify(token, body.cert, options); }); }); } function storeToken(tokenKey) { function _storeToken(token) { var data = {}; data[tokenKey] = token; config.set(data, TOKEN_STORE); return token; } return _storeToken; } function signEULA(eulaServerUrl) { function _signEULA(token) { eula.signEULA(eulaServerUrl, EULA_NAME, token).catch(function(error) { debug(error); }); return token; } return _signEULA; }