Skip to content

Commit

Permalink
feat: allow IAM clientid/secret to be configured (#14)
Browse files Browse the repository at this point in the history
  • Loading branch information
padamstx authored and dpopp07 committed Apr 19, 2019
1 parent f7e65d8 commit ff8f2e7
Show file tree
Hide file tree
Showing 3 changed files with 198 additions and 3 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,5 @@ index.js
**/*.js.map
coverage.lcov
.swagger-codegen-ignore
/.settings/
/.project
51 changes: 48 additions & 3 deletions iam-token-manager/v1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,16 @@
* limitations under the License.
*/

import bufferFrom = require('buffer-from');
import extend = require('extend');
import { sendRequest } from '../lib/requestwrapper';

export type Options = {
iamApikey?: string;
iamAccessToken?: string;
iamUrl?: string;
iamClientId?: string;
iamSecret?: string;
}

// this interface is a representation of the response
Expand All @@ -41,6 +44,8 @@ export class IamTokenManagerV1 {
protected tokenInfo: IamTokenData;
private iamApikey: string;
private userAccessToken: string;
private iamClientId: string;
private iamSecret: string;

/**
* IAM Token Manager Service
Expand All @@ -54,14 +59,20 @@ export class IamTokenManagerV1 {
* @constructor
*/
constructor(options: Options) {
this.iamUrl = options.iamUrl || 'https://iam.bluemix.net/identity/token';
this.iamUrl = options.iamUrl || 'https://iam.cloud.ibm.com/identity/token';
this.tokenInfo = {} as IamTokenData;
if (options.iamApikey) {
this.iamApikey = options.iamApikey;
}
if (options.iamAccessToken) {
this.userAccessToken = options.iamAccessToken;
}
if (options.iamClientId) {
this.iamClientId = options.iamClientId;
}
if (options.iamSecret) {
this.iamSecret = options.iamSecret;
}
}

/**
Expand Down Expand Up @@ -96,6 +107,22 @@ export class IamTokenManagerV1 {
}
}

/**
* Set the IAM 'client_id' and 'secret' values.
* These values are used to compute the Authorization header used
* when retrieving or refreshing the IAM access token.
* If these values are not set, then a default Authorization header
* will be used when interacting with the IAM token server.
*
* @param {string} iamClientId - The client id
* @param {string} iamSecret - The secret
* @returns {void}
*/
public setIamAuthorizationInfo(iamClientId: string, iamSecret: string): void {
this.iamClientId = iamClientId;
this.iamSecret = iamSecret;
}

/**
* Set a self-managed IAM access token.
* The access token should be valid and not yet expired.
Expand Down Expand Up @@ -125,7 +152,7 @@ export class IamTokenManagerV1 {
method: 'POST',
headers: {
'Content-type': 'application/x-www-form-urlencoded',
Authorization: 'Basic Yng6Yng='
Authorization: this.computeIamAuthHeader()
},
form: {
grant_type: 'urn:ibm:params:oauth:grant-type:apikey',
Expand All @@ -150,7 +177,7 @@ export class IamTokenManagerV1 {
method: 'POST',
headers: {
'Content-type': 'application/x-www-form-urlencoded',
Authorization: 'Basic Yng6Yng='
Authorization: this.computeIamAuthHeader()
},
form: {
grant_type: 'refresh_token',
Expand Down Expand Up @@ -213,4 +240,22 @@ export class IamTokenManagerV1 {
private saveTokenInfo(tokenResponse: IamTokenData): void {
this.tokenInfo = extend({}, tokenResponse);
}

/**
* Compute and return the Authorization header to be used with the
* IAM token server interactions (retrieve and refresh access token).
*/
private computeIamAuthHeader(): string {
// Use bx:bx as default auth header creds.
let clientId = 'bx';
let secret = 'bx';

// If both the clientId and secret were specified by the user, then use them.
if (this.iamClientId && this.iamSecret) {
clientId = this.iamClientId;
secret = this.iamSecret;
}
const encodedCreds = bufferFrom(`${clientId}:${secret}`).toString('base64');
return `Basic ${encodedCreds}`;
}
}
148 changes: 148 additions & 0 deletions test/unit/iamTokenManager.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -199,4 +199,152 @@ describe('iam_token_manager_v1', function() {
done();
});
});

it('should use the default Authorization header - no clientid, no secret', function(done) {
const instance = new IamTokenManagerV1({ iamApikey: 'abcd-1234' });

requestWrapper.sendRequest.mockImplementation((parameters, _callback) => {
_callback();
});

instance.getToken(function() {
const sendRequestArgs = requestWrapper.sendRequest.mock.calls[0][0];
const authHeader = sendRequestArgs.options.headers.Authorization;
expect(authHeader).toBe('Basic Yng6Yng=');
done();
});
});

it('should use a non-default Authorization header - client id and secret via ctor', function(done) {
const instance = new IamTokenManagerV1({
iamApikey: 'abcd-1234',
iamClientId: 'foo',
iamSecret: 'bar',
});

requestWrapper.sendRequest.mockImplementation((parameters, _callback) => {
_callback();
});

instance.getToken(function() {
const sendRequestArgs = requestWrapper.sendRequest.mock.calls[0][0];
const authHeader = sendRequestArgs.options.headers.Authorization;
expect(authHeader).not.toBe('Basic Yng6Yng=');
done();
});
});

it('should use the default Authorization header - clientid only via ctor', function(done) {
const instance = new IamTokenManagerV1({
iamApikey: 'abcd-1234',
iamClientId: 'foo',
});

requestWrapper.sendRequest.mockImplementation((parameters, _callback) => {
_callback();
});

instance.getToken(function() {
const sendRequestArgs = requestWrapper.sendRequest.mock.calls[0][0];
const authHeader = sendRequestArgs.options.headers.Authorization;
expect(authHeader).toBe('Basic Yng6Yng=');
done();
});
});

it('should use the default Authorization header, secret only via ctor', function(done) {
const instance = new IamTokenManagerV1({
iamApikey: 'abcd-1234',
iamSecret: 'bar',
});

requestWrapper.sendRequest.mockImplementation((parameters, _callback) => {
_callback();
});

instance.getToken(function() {
const sendRequestArgs = requestWrapper.sendRequest.mock.calls[0][0];
const authHeader = sendRequestArgs.options.headers.Authorization;
expect(authHeader).toBe('Basic Yng6Yng=');
done();
});
});

it('should use a non-default Authorization header - client id and secret via setter', function(done) {
const instance = new IamTokenManagerV1({
iamApikey: 'abcd-1234',
});

instance.setIamAuthorizationInfo('foo', 'bar');

requestWrapper.sendRequest.mockImplementation((parameters, _callback) => {
_callback();
});

instance.getToken(function() {
const sendRequestArgs = requestWrapper.sendRequest.mock.calls[0][0];
const authHeader = sendRequestArgs.options.headers.Authorization;
expect(authHeader).not.toBe('Basic Yng6Yng=');
done();
});
});

it('should use the default Authorization header - clientid only via setter', function(done) {
const instance = new IamTokenManagerV1({
iamApikey: 'abcd-1234',
});

instance.setIamAuthorizationInfo('foo', null);

requestWrapper.sendRequest.mockImplementation((parameters, _callback) => {
_callback();
});

instance.getToken(function() {
const sendRequestArgs = requestWrapper.sendRequest.mock.calls[0][0];
const authHeader = sendRequestArgs.options.headers.Authorization;
expect(authHeader).toBe('Basic Yng6Yng=');
done();
});
});

it('should use the default Authorization header, secret only via ctor', function(done) {
const instance = new IamTokenManagerV1({
iamApikey: 'abcd-1234',
iamSecret: 'bar',
});

instance.setIamAuthorizationInfo(null, 'bar');

requestWrapper.sendRequest.mockImplementation((parameters, _callback) => {
_callback();
});

instance.getToken(function() {
const sendRequestArgs = requestWrapper.sendRequest.mock.calls[0][0];
const authHeader = sendRequestArgs.options.headers.Authorization;
expect(authHeader).toBe('Basic Yng6Yng=');
done();
});
});

it('should use the default Authorization header, nulls passed to setter', function(done) {
const instance = new IamTokenManagerV1({
iamApikey: 'abcd-1234',
iamSecret: 'bar',
});

instance.setIamAuthorizationInfo(null, null);

requestWrapper.sendRequest.mockImplementation((parameters, _callback) => {
_callback();
});

instance.getToken(function() {
const sendRequestArgs = requestWrapper.sendRequest.mock.calls[0][0];
const authHeader = sendRequestArgs.options.headers.Authorization;
expect(authHeader).toBe('Basic Yng6Yng=');
done();
});
});
});

0 comments on commit ff8f2e7

Please sign in to comment.