Skip to content

Commit

Permalink
iam: Phase I: use API-KEY for interim iam support
Browse files Browse the repository at this point in the history
- iam_endpoint and apikey required
- new CLI options and variables
- test updates
- docs

Fixes: #147
  • Loading branch information
srl295 committed Apr 17, 2019
1 parent a197285 commit 4cf5259
Show file tree
Hide file tree
Showing 9 changed files with 234 additions and 32 deletions.
40 changes: 37 additions & 3 deletions API.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,15 @@ Usage: <code>var credentials = require(&#39;cfEnv&#39;)
<dt><a href="#exampleCredentials">exampleCredentials</a></dt>
<dd><p>Example credentials such as for documentation.</p>
</dd>
<dt><a href="#exampleIamCredentials">exampleIamCredentials</a></dt>
<dd><p>Example IAM credentials such as for documentation.</p>
</dd>
<dt><a href="#exampleCredentialsString">exampleCredentialsString</a></dt>
<dd><p>Example credentials string</p>
</dd>
<dt><a href="#exampleIamCredentialsString">exampleIamCredentialsString</a></dt>
<dd><p>Example IAM credentials string</p>
</dd>
<dt><a href="#version">version</a></dt>
<dd><p>Current version</p>
</dd>
Expand All @@ -112,7 +118,8 @@ Usage: <code>var credentials = require(&#39;cfEnv&#39;)
<dl>
<dt><a href="#getClient">getClient(params)</a> ⇒ <code><a href="#Client">Client</a></code></dt>
<dd><p>Construct a g11n-pipeline client.
params.credentials is required unless params.appEnv is supplied.</p>
params.credentials is required unless params.appEnv is supplied.
Required either: (userId &amp; password) or (apikey &amp; iam_endpoint)</p>
</dd>
<dt><a href="#readJson">readJson(filename)</a> ⇒ <code>Promise.&lt;Object&gt;</code></dt>
<dd><p>Read a file, return promise to parsed obj</p>
Expand Down Expand Up @@ -1352,6 +1359,18 @@ Example credentials such as for documentation.
| --- |
| exampleCredentials |

<a name="exampleIamCredentials"></a>

## exampleIamCredentials
Example IAM credentials such as for documentation.

**Kind**: global variable
**Properties**

| Name |
| --- |
| exampleUamCredentials |

<a name="exampleCredentialsString"></a>

## exampleCredentialsString
Expand All @@ -1364,6 +1383,18 @@ Example credentials string
| --- |
| exampleCredentialsString |

<a name="exampleIamCredentialsString"></a>

## exampleIamCredentialsString
Example IAM credentials string

**Kind**: global variable
**Properties**

| Name |
| --- |
| exampleIamCredentialsString |

<a name="version"></a>

## version
Expand Down Expand Up @@ -1417,6 +1448,7 @@ Possible translation domains. These provide hints as to the type of translation
## getClient(params) ⇒ [<code>Client</code>](#Client)
Construct a g11n-pipeline client.
params.credentials is required unless params.appEnv is supplied.
Required either: (userId & password) or (apikey & iam_endpoint)

**Kind**: global function

Expand All @@ -1426,8 +1458,10 @@ params.credentials is required unless params.appEnv is supplied.
| params.appEnv | <code>Object</code> | pass the result of cfEnv.getAppEnv(). Ignored if params.credentials is supplied. |
| params.credentials | <code>Object.&lt;string, string&gt;</code> | Bound credentials as from the CF service broker (overrides appEnv) |
| params.credentials.url | <code>string</code> | service URL. (should end in '/translate') |
| params.credentials.userId | <code>string</code> | service API key. |
| params.credentials.password | <code>string</code> | service API key. |
| params.credentials.userId | <code>string</code> | GP auth userid. |
| params.credentials.password | <code>string</code> | GP auth password. |
| params.credentials.apikey | <code>string</code> | IAM apikey |
| params.credentials.iam_endpoint | <code>string</code> | IAM endpoint |
| params.credentials.instanceId | <code>string</code> | instance ID |

<a name="readJson"></a>
Expand Down
39 changes: 34 additions & 5 deletions CLI.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,26 @@ Options have a short or a long form. Therefore, the following are all equivalent
Credentials
--

See also the [`getClient()` API docs](./API.md#getClient)
Credentials may be passed in one of the following ways:

1. Via the `-j/--jsonCreds` option, which takes a path to a JSON file with credentials
2. Via the four individual options `--serviceUrl`, `--instanceId`, `--user`, and `--password`
2a. For GP Auth: the four individual options `--serviceUrl`, `--instanceId`, `--user`, and `--password`
2b. for IAM auth via `--iam_endpoint` and `--apikey`
3. By the following environment variables:

* __GP_URL__: Service URL (e.g. https://gp-rest.ng.bluemix.net/translate/rest)
* __GP_INSTANCE_ID__: Service instance ID (e.g. d3f537cd617f34c86ac6b270f3065e73)
* __GP_USER_ID__: User ID (e.g. e92a1282a0e4f97bec93aa9f56fdb838)
* __GP_PASSWORD__: User password (e.g. zg5SlD+ftXYRIZDblLgEA/ILkkCNqE1y)

- _if using GP Authentication_:

* __GP_USER_ID__: User ID (e.g. e92a1282a0e4f97bec93aa9f56fdb838)
* __GP_PASSWORD__: User password (e.g. zg5SlD+ftXYRIZDblLgEA/ILkkCNqE1y)

- _if using IAM Authentication_:

* __GP_IAM_API_KEY__: IAM API Key
* __GP_IAM_ENDPOINT__: IAM endpoint (e.g. https://iam.cloud.ibm.com)

Common Options
--
Expand All @@ -54,7 +63,7 @@ Common Options

See [Credentials](#Credentials), above.

This option specifies a credentials file containing the [GP credentials](https://github.com/IBM-Cloud/gp-common/blob/master/README.md#4-credentials). This is a JSON file with either of the following formats:
This option specifies a credentials file containing the [GP credentials](https://github.com/IBM-Cloud/gp-common/blob/master/README.md#4-credentials). This is a JSON file with any of the following formats:

```json
{"url":"",
Expand All @@ -70,11 +79,31 @@ Common Options
"userId":"",
"password":""}}
```

```json
{"url":"",
"instanceId":"",
"iam_endpoint":"",
"apikey":""}
```

```json
{"credentials":
{"url":"",
"instanceId":"",
"iam_endpoint":"",
"apikey":""}}
```

- `--serviceUrl`, `--instanceId`, `--user`, `--password`

See [Credentials](#Credentials), above.
These options specify the four credential parameters individually.
These options specify the four credential parameters individually for GP Auth.

- `--iam_endpoint`, `--apikey`

See [Credentials](#Credentials), above.
These options specify the two credential parameters individually for IAM Auth.

- `-F json` | `--outputFormat=json`

Expand Down
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
52 changes: 52 additions & 0 deletions lib/gp-iam.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Copyright IBM Corp. 2015-2019
*
* 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" */

/**
* Manage use of IAM API Keys and Tokens
* Docs: https://cloud.ibm.com/docs/iam?topic=iam-iamtoken_from_apikey&locale=en#iamtoken_from_apikey
*
* @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.API_KEY = "API-KEY"; // GP SPECIFIC header

GpIAM.prototype.VERBOSE = process.env.GP_VERBOSE || false;
GpIAM.prototype.GP_USE_APIKEY = process.env.GP_USE_APIKEY || true; // if false: use token manager

/**
* Generate HTTP Authorization header.
*/
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 authHeader = this.API_KEY + ' ' + this.credentials.apikey;
if(this.VERBOSE) console.log('hmacHeader = ' + authHeader);
obj.headers.Authorization = authHeader;
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 4cf5259

Please sign in to comment.