apiconnect-cli-login-bluemix
Version:
Plugin for IBM API Connect Developer Toolkit.
286 lines (246 loc) • 7.61 kB
JavaScript
/********************************************************* {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;
}