diff --git a/iam-token-manager/v1.ts b/iam-token-manager/v1.ts index febdce06f..6d2b27d81 100644 --- a/iam-token-manager/v1.ts +++ b/iam-token-manager/v1.ts @@ -17,6 +17,22 @@ import extend = require('extend'); import { sendRequest } from '../lib/requestwrapper'; +/** + * Check for only one of two elements being defined. + * Returns true if a is defined and b is undefined, + * or vice versa. Returns false if both are defined + * or both are undefined. + * + * @param {any} a - The first object + * @param {any} b - The second object + * @returns {boolean} + */ +function onlyOne(a: any, b: any): boolean { + return Boolean((a && !b) || (b && !a)); +} + +const CLIENT_ID_SECRET_WARNING = 'Warning: Client ID and Secret must BOTH be given, or the defaults will be used.'; + export type Options = { iamApikey?: string; iamAccessToken?: string; @@ -72,6 +88,10 @@ export class IamTokenManagerV1 { if (options.iamSecret) { this.iamSecret = options.iamSecret; } + if (onlyOne(options.iamClientId, options.iamSecret)) { + // tslint:disable-next-line + console.log(CLIENT_ID_SECRET_WARNING); + } } /** @@ -120,6 +140,10 @@ export class IamTokenManagerV1 { public setIamAuthorizationInfo(iamClientId: string, iamSecret: string): void { this.iamClientId = iamClientId; this.iamSecret = iamSecret; + if (onlyOne(iamClientId, iamSecret)) { + // tslint:disable-next-line + console.log(CLIENT_ID_SECRET_WARNING); + } } /** diff --git a/lib/base_service.ts b/lib/base_service.ts index b6bda4a4f..906a330e4 100644 --- a/lib/base_service.ts +++ b/lib/base_service.ts @@ -40,6 +40,8 @@ export interface UserOptions { iam_access_token?: string; iam_apikey?: string; iam_url?: string; + iam_client_id?: string; + iam_secret?: string; disable_ssl_verification?: boolean; } @@ -121,6 +123,11 @@ export class BaseService { /** * Internal base class that other services inherit from * @param {UserOptions} options + * @param {string} [options.iam_apikey] - api key used to retrieve an iam access token + * @param {string} [options.iam_access_token] - iam access token provided and managed by user + * @param {string} [options.iam_url] - url for iam service api, needed for services in staging + * @param {string} [options.iam_client_id] - client id (username) for request to iam service + * @param {string} [options.iam_secret] - secret (password) for request to iam service * @param {string} [options.username] - required unless use_unauthenticated is set * @param {string} [options.password] - required unless use_unauthenticated is set * @param {boolean} [options.use_unauthenticated] - skip credential requirement @@ -158,12 +165,16 @@ export class BaseService { this.tokenManager = new IamTokenManagerV1({ iamApikey: _options.iam_apikey, iamAccessToken: _options.iam_access_token, - iamUrl: _options.iam_url + iamUrl: _options.iam_url, + iamClientId: _options.iam_client_id, + iamSecret: _options.iam_secret }); } else if (usesBasicForIam(_options)) { this.tokenManager = new IamTokenManagerV1({ iamApikey: _options.password, - iamUrl: _options.iam_url + iamUrl: _options.iam_url, + iamClientId: _options.iam_client_id, + iamSecret: _options.iam_secret }); } else { this.tokenManager = null; diff --git a/test/unit/baseService.test.js b/test/unit/baseService.test.js index 7dabad6ba..cb3c06744 100644 --- a/test/unit/baseService.test.js +++ b/test/unit/baseService.test.js @@ -285,6 +285,41 @@ describe('BaseService', function() { }); }); + it('should pass all credentials to token manager when given iam creds', function() { + const instance = new TestService({ + iam_apikey: 'key1234', + iam_access_token: 'real-token-84', + iam_url: 'iam.com/api', + iam_client_id: 'abc', + iam_secret: 'abc', + }); + + expect(instance.tokenManager).toBeDefined(); + expect(instance.tokenManager).not.toBeNull(); + expect(instance.tokenManager.iamApikey).toBeDefined(); + expect(instance.tokenManager.userAccessToken).toBeDefined(); + expect(instance.tokenManager.iamUrl).toBeDefined(); + expect(instance.tokenManager.iamClientId).toBeDefined(); + expect(instance.tokenManager.iamSecret).toBeDefined(); + }); + + it('should pass all credentials to token manager when given iam with basic', function() { + const instance = new TestService({ + username: 'apikey', + password: 'key1234', + iam_url: 'iam.com/api', + iam_client_id: 'abc', + iam_secret: 'abc', + }); + + expect(instance.tokenManager).toBeDefined(); + expect(instance.tokenManager).not.toBeNull(); + expect(instance.tokenManager.iamApikey).toBeDefined(); + expect(instance.tokenManager.iamUrl).toBeDefined(); + expect(instance.tokenManager.iamClientId).toBeDefined(); + expect(instance.tokenManager.iamSecret).toBeDefined(); + }); + it('should not fail if setAccessToken is called and token manager is null', function() { const instance = new TestService({ username: 'user', password: 'pass' }); expect(instance.tokenManager).toBeNull(); diff --git a/test/unit/iamTokenManager.test.js b/test/unit/iamTokenManager.test.js index 409016c01..1146032dc 100644 --- a/test/unit/iamTokenManager.test.js +++ b/test/unit/iamTokenManager.test.js @@ -1,3 +1,4 @@ +/* eslint-disable no-alert, no-console */ 'use strict'; const requestWrapper = require('../../lib/requestwrapper'); @@ -5,6 +6,9 @@ requestWrapper.sendRequest = jest.fn(); const IamTokenManagerV1 = require('../../iam-token-manager/v1').IamTokenManagerV1; +const CLIENT_ID_SECRET_WARNING = + 'Warning: Client ID and Secret must BOTH be given, or the defaults will be used.'; + describe('iam_token_manager_v1', function() { beforeEach(() => { requestWrapper.sendRequest.mockReset(); @@ -235,11 +239,18 @@ describe('iam_token_manager_v1', function() { }); it('should use the default Authorization header - clientid only via ctor', function(done) { + jest.spyOn(console, 'log').mockImplementation(() => {}); + const instance = new IamTokenManagerV1({ iamApikey: 'abcd-1234', iamClientId: 'foo', }); + // verify warning was triggered + expect(console.log).toHaveBeenCalled(); + expect(console.log.mock.calls[0][0]).toBe(CLIENT_ID_SECRET_WARNING); + console.log.mockRestore(); + requestWrapper.sendRequest.mockImplementation((parameters, _callback) => { _callback(); }); @@ -253,11 +264,17 @@ describe('iam_token_manager_v1', function() { }); it('should use the default Authorization header, secret only via ctor', function(done) { + jest.spyOn(console, 'log').mockImplementation(() => {}); const instance = new IamTokenManagerV1({ iamApikey: 'abcd-1234', iamSecret: 'bar', }); + // verify warning was triggered + expect(console.log).toHaveBeenCalled(); + expect(console.log.mock.calls[0][0]).toBe(CLIENT_ID_SECRET_WARNING); + console.log.mockRestore(); + requestWrapper.sendRequest.mockImplementation((parameters, _callback) => { _callback(); }); @@ -294,8 +311,15 @@ describe('iam_token_manager_v1', function() { iamApikey: 'abcd-1234', }); + jest.spyOn(console, 'log').mockImplementation(() => {}); + instance.setIamAuthorizationInfo('foo', null); + // verify warning was triggered + expect(console.log).toHaveBeenCalled(); + expect(console.log.mock.calls[0][0]).toBe(CLIENT_ID_SECRET_WARNING); + console.log.mockRestore(); + requestWrapper.sendRequest.mockImplementation((parameters, _callback) => { _callback(); }); @@ -308,14 +332,20 @@ describe('iam_token_manager_v1', function() { }); }); - it('should use the default Authorization header, secret only via ctor', function(done) { + it('should use the default Authorization header, secret only via setter', function(done) { const instance = new IamTokenManagerV1({ iamApikey: 'abcd-1234', - iamSecret: 'bar', }); + jest.spyOn(console, 'log').mockImplementation(() => {}); + instance.setIamAuthorizationInfo(null, 'bar'); + // verify warning was triggered + expect(console.log).toHaveBeenCalled(); + expect(console.log.mock.calls[0][0]).toBe(CLIENT_ID_SECRET_WARNING); + console.log.mockRestore(); + requestWrapper.sendRequest.mockImplementation((parameters, _callback) => { _callback(); }); @@ -331,7 +361,6 @@ describe('iam_token_manager_v1', function() { 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);