Skip to content

Commit

Permalink
iam: wip use API-KEY for interim iam support
Browse files Browse the repository at this point in the history
work in progress

Fixes: #147
  • Loading branch information
srl295 committed Apr 16, 2019
1 parent a197285 commit 2dfa686
Show file tree
Hide file tree
Showing 7 changed files with 234 additions and 24 deletions.
46 changes: 33 additions & 13 deletions lib/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
var utils = require('./utils.js');
var SwaggerClient = require('swagger-client');
var GpHmac = require('./gp-hmac');
var GpIAM = require('./gp-iam');
var cfEnvUtil = require('./cfenv-credsbylabel');
const TranslationRequest = require('./tr.js');
const DocumentTranslationRequest = require('./doctr.js');
Expand Down Expand Up @@ -54,11 +55,22 @@ class Client {
}
this._options = options;
if (!this._options.credentials) {
throw new Error("g11n-pipeline: missing 'credentials' " + Object.keys(Consts.exampleCredentials));
throw new Error("g11n-pipeline: missing 'credentials'"); // don't give specific contents
// could be CF or IAM.
}
var missingField = utils.isMissingField(this._options.credentials, Object.keys(Consts.exampleCredentials));
if (missingField.length !== 0) {
throw new Error("g11n-pipeline: missing credentials fields: \"" + missingField.join(' ') + "\" - expected: " + Consts.exampleCredentialsString);
if(this._options.credentials.apikey) {
const missingField = utils.isMissingField(this._options.credentials, Object.keys(Consts.exampleIamCredentials));
if (missingField.length !== 0) {
throw new Error("g11n-pipeline: missing IAM credentials fields: \"" + missingField.join(' ') +
"\" - expected: " + Consts.exampleIamCredentialsString);
}
} else {
// expect 'GP auth' fields
const missingField = utils.isMissingField(this._options.credentials, Object.keys(Consts.exampleCredentials));
if (missingField.length !== 0) {
throw new Error("g11n-pipeline: missing GP credentials fields: \"" +
missingField.join(' ') + "\" - expected: " + Consts.exampleCredentialsString + ' but got ' + JSON.stringify(this._options.credentials));
}
}

// instanceId optional
Expand Down Expand Up @@ -106,16 +118,24 @@ class Client {
const schemaUrl = this._schemaUrl = this._options.credentials.url + '/swagger.json';
// if (debugREST) /*istanbul ignore next*/ console.log('.. fetching ' + schemaUrl);

const gphmac = new GpHmac("gp-hmac", this._options.credentials.userId, this._options.credentials.password);
if(this._options.basicAuth) {
// ignore basicAuth.
// throw Error('basicAuth is not supported'); // TODO: support this- maybe?
if (this._options.credentials.apikey) {
// IAM
const gpiam = new GpIAM(this._options.credentials);
const clientPromise = new SwaggerClient({
url: schemaUrl,
requestInterceptor: (req) => gpiam.apply(req)
});
return clientPromise;
} else {
// assume GP credentials
const gphmac = new GpHmac("gp-hmac", this._options.credentials.userId, this._options.credentials.password);
const clientPromise = new SwaggerClient({
url: schemaUrl,
requestInterceptor: (req) => gphmac.apply(req) // TODO: change if we are using Basic
});
return clientPromise;
}
const clientPromise = new SwaggerClient({
url: schemaUrl,
requestInterceptor: (req) => gphmac.apply(req) // TODO: change if we are using Basic
});
return clientPromise;
// Not supported: this._options.basicAuth
}


Expand Down
18 changes: 18 additions & 0 deletions lib/consts.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,30 @@ exports.exampleCredentials = {
password: "secretpassword",
instanceId: "your Instance ID"
};

/**
* Example IAM credentials such as for documentation.
* @property exampleUamCredentials
*/
exports.exampleIamCredentials = {
url: "Globalization Pipeline URL",
apikey: "your IAM apikey", // if we see apikey, we assume this is IAM
iam_endpoint: "your IAM endpoint URL",
instanceId: "your Instance ID"
};

/**
* Example credentials string
* @property exampleCredentialsString
*/
exports.exampleCredentialsString = "credentials: " + JSON.stringify(exports.exampleCredentials);

/**
* Example IAM credentials string
* @property exampleIamCredentialsString
*/
exports.exampleIamCredentialsString = "credentials: " + JSON.stringify(exports.exampleIamCredentials);

/**
* Current version
*/
Expand Down
123 changes: 123 additions & 0 deletions lib/gp-iam.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/*
* Copyright IBM Corp. 2015
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/* eslint no-console: "off" */

// const crypto = require('crypto');
// const url = require('url');
/**
* @author Steven R. Loomis
* @ignore
*/

const GpIAM = function GpIAM(credentials) {
this.credentials = credentials;
if(!this.credentials || !this.credentials.apikey || !this.credentials.iam_endpoint) {
throw new Error('GpIAM: params need to be "apikey, iam_endpoint"');
}
};

GpIAM.prototype.name = null;
GpIAM.prototype.user = undefined;
GpIAM.prototype.secret = undefined;

GpIAM.prototype.API_KEY = "API-KEY";
// GpIAM.prototype.AUTH_SCHEME = "GP-HMAC";
GpIAM.prototype.SEP = "\n";
GpIAM.prototype.ENC = "ascii"; // ISO-8859-1 not supported!
GpIAM.prototype.HMAC_SHA1_ALGORITHM = 'sha1'; // "HmacSHA1";
GpIAM.prototype.forceDate = null;
GpIAM.prototype.forceDateString = null;

GpIAM.prototype.VERBOSE = process.env.GP_VERBOSE || false;

// function rfc1123date(d) {
// return d.toUTCString();
// }

/**
* ( from GpIAM.java )
*
* Generate GaaS HMAC credentials used for HTTP Authorization header.
* Gaas HMAC uses HMAC SHA1 algorithm signing a message composed by:
*
* (HTTP method): (in UPPERCASE)
* (Target URL):
* (RFC1123 date):
* (Request Body)
*
* If the request body is empty, it is simply omitted,
* the 'message' then ends with the separator ":"
*
* The format for HTTP Authorization header is:
*
* "Authorization: GaaS-HMAC (user ID):(HMAC above)"
*
* For example, with user "MyUser" and secret "MySecret",
* the URL "http://example.com/gaas",
* the method "https",
* the date "Mon, 30 Jun 2014 00:00:00 -0000",
* the body "param=value",
* the following text to be signed will be generated:
*
* GET:http://example.com/gaas:Mon, 30 Jun 2014 00:00:00 -0000:param=value
*
* And the resulting headers are:
* Authorization: GaaS-HMAC MyUser:Y4qPpmKpyYhdAKA7p3U/y4nNDvY=
* Date: Mon, 30 Jun 2014 00:00:00 -0000
*
* The Date: header is required for HMAC.
*/
GpIAM.prototype.apply = function(obj) {
if(this.VERBOSE) console.dir(obj, {color: true, depth: null});
if(obj.url.indexOf("/swagger.json") !== -1) return obj; // skip for swagger.json

// const href = url.parse(obj.url).href;
// if(href !== obj.url) {
// if(this.VERBOSE) console.log('hmac: Warn: normalized', obj.url, '->', href);
// // For example, if there is a quote (') in the URL, it will turn into %27
// // AFTER this interceptor is called (by Fetch).
// obj.url = href;
// }

// var dateString = (this.forceDateString ||
// rfc1123date(this.forceDate || new Date()));
// var bodyString = "";
// if(obj.body) {
// if((typeof obj.body) === "string") {
// bodyString = obj.body;
// } else {
// bodyString = JSON.stringify(obj.body); // === what swagger does
// }
// }
// var hmacText = obj.method.toUpperCase() + this.SEP +
// obj.url + this.SEP +
// dateString + this.SEP +
// bodyString;
// if(this.VERBOSE) console.log('hmacText = <<' + hmacText + '>>');

// var hmacHash = crypto.createHmac(this.HMAC_SHA1_ALGORITHM, this.secretBuffer )
// .update(hmacText, 'utf8')
// .digest('base64');
// if(this.VERBOSE) console.log('hmacHash = ' + hmacHash );
var hmacHeader = this.API_KEY + ' ' + this.credentials.apikey;
if(this.VERBOSE) console.log('hmacHeader = ' + hmacHeader);
obj.headers.Authorization = hmacHeader;
// obj.headers["GP-Date"] = dateString;
return obj;
};

module.exports = GpIAM;
8 changes: 6 additions & 2 deletions lib/gpcli.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ class Cli {
instanceId: 'i',
user: 'u',
password: 'p',
apikey: 'a',
iam_endpoint: 'A',
jsonCreds: 'j',
bundle: 'b',
outputFormat: 'F',
Expand Down Expand Up @@ -138,12 +140,14 @@ class Cli {
if(credentials.credentials) return credentials.credentials;
return credentials;
} else {
const {GP_URL, GP_INSTANCE_ID, GP_USER_ID, GP_PASSWORD} = process.env;
const {GP_URL, GP_INSTANCE_ID, GP_USER_ID, GP_PASSWORD, GP_IAM_API_KEY, GP_IAM_ENDPOINT} = process.env;
const credentials = {
url: this.argv.serviceUrl || GP_URL,
userId: this.argv.user || GP_USER_ID,
password: this.argv.password || GP_PASSWORD,
instanceId: this.argv.instanceId || GP_INSTANCE_ID
instanceId: this.argv.instanceId || GP_INSTANCE_ID,
apikey: this.argv.apikey || GP_IAM_API_KEY,
iam_endpoint: this.argv.iam_endpoint || GP_IAM_ENDPOINT
};
// TODO: validate
return credentials;
Expand Down
7 changes: 5 additions & 2 deletions lib/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,15 @@ const utils = require('./utils.js');
/**
* Construct a g11n-pipeline client.
* params.credentials is required unless params.appEnv is supplied.
* Required either: (userId & password) or (apikey & iam_endpoint)
* @param {Object} params - configuration params
* @param {Object} params.appEnv - pass the result of cfEnv.getAppEnv(). Ignored if params.credentials is supplied.
* @param {Object.<string,string>} params.credentials - Bound credentials as from the CF service broker (overrides appEnv)
* @param {string} params.credentials.url - service URL. (should end in '/translate')
* @param {string} params.credentials.userId - service API key.
* @param {string} params.credentials.password - service API key.
* @param {string} params.credentials.userId - GP auth userid.
* @param {string} params.credentials.password - GP auth password.
* @param {string} params.credentials.apikey - IAM apikey
* @param {string} params.credentials.iam_endpoint - IAM endpoint
* @param {string} params.credentials.instanceId - instance ID
* @returns {Client}
* @function getClient
Expand Down
Loading

0 comments on commit 2dfa686

Please sign in to comment.