You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@openwhisk.apache.org by GitBox <gi...@apache.org> on 2018/08/14 16:41:53 UTC

[GitHub] csantanapr closed pull request #129: Iam enablement

csantanapr closed pull request #129: Iam enablement
URL: https://github.com/apache/incubator-openwhisk-client-js/pull/129
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/README.md b/README.md
index 74df04e..c790392 100644
--- a/README.md
+++ b/README.md
@@ -64,6 +64,41 @@ var ow = openwhisk(options);
 ow.actions.invoke('sample').then(result => console.log(result))
 ```
 
+### Using 3rd party Authentication Handler
+You can specify an authentication handler in `options.authHandler` this is a an object that provides a function `getAuthHeader` that returns a Promise or String to be used in the `Authorization` header for every http request.
+```javascript
+const authHandler = {
+  getAuthHeader: ()=>{
+    return Promise.resolve('Basic user:password')
+  }
+}
+var openwhisk = require('openwhisk');
+var options = {
+                 apihost: 'openwhisk.ng.bluemix.net',
+                 authHandler: authHandler
+              }
+var ow = openwhisk(options)
+ow.actions.invoke('sample').then(result => console.log(result))
+``` 
+
+### Example Using a Authentication Handler with IBM IAM
+For example IBM Functions namespaces that required IAM authentcation use the following:
+```javascript
+const IAM_API_KEY = 'secretkey' //required if environment variable __OW_IAM_NAMESPACE_API_KEY not set
+const IAM_API_URL = 'https://iam.bluemix.net/identity/token' //optional or set environment variable __OW_IAM_API_HOST
+var openwhisk = require('openwhisk')
+var authHandler = require('@ibm-functions/iam-token-manager')
+var options = {
+                 apihost: 'openwhisk.ng.bluemix.net',
+                 authHandler: new authHandler({
+                   iamApiKey: IAM_API_KEY,
+                   iamUrl: IAM_API_URL
+                 })
+              }
+var ow = openwhisk(options)
+ow.actions.invoke('sample').then(result => console.log(result))
+```
+
 ### constructor options
 
 _Client constructor supports the following mandatory parameters:_
diff --git a/lib/client.js b/lib/client.js
index 48723e3..834226f 100644
--- a/lib/client.js
+++ b/lib/client.js
@@ -66,6 +66,7 @@ class Client {
    * @param {boolean} [options.ignore_certs]
    * @param {string} [options.apigw_token]
    * @param {string} [options.apigw_space_guid]
+   * @param {Function} [options.authHandler]
    */
   constructor (options) {
     this.options = this.parseOptions(options || {})
@@ -91,13 +92,13 @@ class Client {
       apigwSpaceGuid = apiKey.split(':')[0]
     }
 
-    if (!apiKey) {
-      throw new Error(`${messages.INVALID_OPTIONS_ERROR} Missing api_key parameter.`)
+    if (!apiKey && !options.authHandler) {
+      throw new Error(`${messages.INVALID_OPTIONS_ERROR} Missing api_key parameter or token plugin.`)
     } else if (!api) {
       throw new Error(`${messages.INVALID_OPTIONS_ERROR} Missing either api or apihost parameters.`)
     }
 
-    return {apiKey: apiKey, api, ignoreCerts: ignoreCerts, namespace: options.namespace, apigwToken: apigwToken, apigwSpaceGuid: apigwSpaceGuid}
+    return {apiKey: apiKey, api, ignoreCerts: ignoreCerts, namespace: options.namespace, apigwToken: apigwToken, apigwSpaceGuid: apigwSpaceGuid, authHandler: options.authHandler}
   }
 
   urlFromApihost (apihost) {
@@ -113,21 +114,23 @@ class Client {
   }
 
   request (method, path, options) {
-    const req = this.params(method, path, options)
-    return rp(req).catch(err => this.handleErrors(err))
+    const params = this.params(method, path, options)
+    return params.then(req => rp(req)).catch(err => this.handleErrors(err))
   }
 
   params (method, path, options) {
-    return Object.assign({
-      json: true,
-      method: method,
-      url: this.pathUrl(path),
-      rejectUnauthorized: !this.options.ignoreCerts,
-      headers: {
-        'User-Agent': (options && options['User-Agent']) || 'openwhisk-client-js',
-        Authorization: this.authHeader()
-      }
-    }, options)
+    return this.authHeader().then(header => {
+      return Object.assign({
+        json: true,
+        method: method,
+        url: this.pathUrl(path),
+        rejectUnauthorized: !this.options.ignoreCerts,
+        headers: {
+          'User-Agent': (options && options['User-Agent']) || 'openwhisk-client-js',
+          Authorization: header
+        }
+      }, options)
+    })
   }
 
   pathUrl (urlPath) {
@@ -143,10 +146,13 @@ class Client {
   }
 
   authHeader () {
-    const apiKeyBase64 = Buffer.from(this.options.apiKey).toString('base64')
-    return `Basic ${apiKeyBase64}`
+    if (this.options.authHandler) {
+      return this.options.authHandler.getAuthHeader()
+    } else {
+      const apiKeyBase64 = Buffer.from(this.options.apiKey).toString('base64')
+      return Promise.resolve(`Basic ${apiKeyBase64}`)
+    }
   }
-
   handleErrors (reason) {
     let message = `Unknown Error From API: ${reason.message}`
     if (reason.hasOwnProperty('statusCode')) {
diff --git a/test/unit/client.test.js b/test/unit/client.test.js
index dc97643..6b9ec35 100644
--- a/test/unit/client.test.js
+++ b/test/unit/client.test.js
@@ -108,12 +108,12 @@ test('should handle multiple api parameter formats', t => {
   t.is(client.urlFromApihost('http://my_host:80'), 'http://my_host:80/api/v1/')
 })
 
-test('should return default request parameters without options', t => {
+test('should return default request parameters without options', async t => {
   const client = new Client({api_key: 'username:password', apihost: 'blah'})
   const METHOD = 'get'
   const PATH = 'some/path/to/resource'
 
-  const params = client.params(METHOD, PATH)
+  const params = await client.params(METHOD, PATH)
   t.is(params.url, 'https://blah/api/v1/some/path/to/resource')
   t.is(params.method, METHOD)
   t.true(params.json)
@@ -121,13 +121,13 @@ test('should return default request parameters without options', t => {
   t.true(params.headers.hasOwnProperty('Authorization'))
 })
 
-test('should return request parameters with merged options', t => {
+test('should return request parameters with merged options', async t => {
   const client = new Client({api_key: 'username:password', apihost: 'blah'})
   const METHOD = 'get'
   const PATH = 'some/path/to/resource'
   const OPTIONS = {b: {bar: 'foo'}, a: {foo: 'bar'}}
 
-  const params = client.params(METHOD, PATH, OPTIONS)
+  const params = await client.params(METHOD, PATH, OPTIONS)
   t.is(params.url, 'https://blah/api/v1/some/path/to/resource')
   t.is(params.method, METHOD)
   t.true(params.json)
@@ -137,20 +137,30 @@ test('should return request parameters with merged options', t => {
   t.deepEqual(params.b, {bar: 'foo'})
 })
 
-test('should return request parameters with explicit api option', t => {
+test('should return request parameters with explicit api option', async t => {
   const client = new Client({api_key: 'username:password', api: 'https://api.com/api/v1'})
   const METHOD = 'get'
   const PATH = 'some/path/to/resource'
 
-  t.is(client.params(METHOD, PATH).url, 'https://api.com/api/v1/some/path/to/resource')
+  t.is((await client.params(METHOD, PATH)).url, 'https://api.com/api/v1/some/path/to/resource')
   client.options.api += '/'
-  t.is(client.params(METHOD, PATH).url, 'https://api.com/api/v1/some/path/to/resource')
+  t.is((await client.params(METHOD, PATH)).url, 'https://api.com/api/v1/some/path/to/resource')
 })
 
-test('should generate auth header from API key', t => {
+test('should generate auth header from API key', async t => {
   const apiKey = 'some sample api key'
   const client = new Client({api: true, api_key: apiKey})
-  t.is(client.authHeader(), `Basic ${Buffer.from(apiKey).toString('base64')}`)
+  t.is(await client.authHeader(), `Basic ${Buffer.from(apiKey).toString('base64')}`)
+})
+
+test('should generate auth header from 3rd party authHandler plugin', async t => {
+  const authHandler = {
+    getAuthHeader: () => {
+      return Promise.resolve('Basic user:password')
+    }
+  }
+  const client = new Client({api: true, authHandler: authHandler})
+  t.is(await client.authHeader(), `Basic user:password`)
 })
 
 test('should return path and status code in error message', t => {


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services