Skip to content

Commit

Permalink
refactor: move iam-specific functionality to a shared class (#150)
Browse files Browse the repository at this point in the history
This will allow other token managers that interact with the IAM service to share configuration and functionality. This change is being made in advance of a new container-based token manager being added that will utilize this shared functionality, as it will interact with the IAM service as well.
  • Loading branch information
dpopp07 authored Aug 3, 2021
1 parent 50c2e89 commit 796a4eb
Show file tree
Hide file tree
Showing 9 changed files with 2,064 additions and 2,771 deletions.
47 changes: 26 additions & 21 deletions .secrets.baseline
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"files": "package-lock.json|^.secrets.baseline$",
"lines": null
},
"generated_at": "2021-06-29T19:23:55Z",
"generated_at": "2021-08-03T20:56:36Z",
"plugins_used": [
{
"name": "AWSKeyDetector"
Expand All @@ -25,6 +25,7 @@
"name": "CloudantDetector"
},
{
"ghe_instance": "github.ibm.com",
"name": "GheDetector"
},
{
Expand Down Expand Up @@ -204,52 +205,54 @@
"verified_result": null
}
],
"auth/token-managers/iam-token-manager.ts": [
"auth/token-managers/iam-request-based-token-manager.ts": [
{
"hashed_secret": "8f4bfc22c4fd7cb884f94ec175ff4a3284a174a1",
"hashed_secret": "f84f793e0af9ade37c8b927bc5091e98f35bf821",
"is_secret": false,
"is_verified": false,
"line_number": 107,
"line_number": 81,
"type": "Secret Keyword",
"verified_result": null
},
{
"hashed_secret": "f84f793e0af9ade37c8b927bc5091e98f35bf821",
"hashed_secret": "45c43fe97e3a06ab078b0eeff6fbe622cc417a25",
"is_secret": false,
"is_verified": false,
"line_number": 116,
"line_number": 118,
"type": "Secret Keyword",
"verified_result": null
},
{
"hashed_secret": "45c43fe97e3a06ab078b0eeff6fbe622cc417a25",
"hashed_secret": "99833a8b234b57b886a9aef1dba187fdd7ceece8",
"is_secret": false,
"is_verified": false,
"line_number": 150,
"line_number": 120,
"type": "Secret Keyword",
"verified_result": null
},
}
],
"auth/token-managers/iam-token-manager.ts": [
{
"hashed_secret": "99833a8b234b57b886a9aef1dba187fdd7ceece8",
"hashed_secret": "8f4bfc22c4fd7cb884f94ec175ff4a3284a174a1",
"is_secret": false,
"is_verified": false,
"line_number": 152,
"line_number": 60,
"type": "Secret Keyword",
"verified_result": null
},
{
"hashed_secret": "c563e15142a4d1ab66222756647ec74606793569",
"hashed_secret": "0358c67856fb6a21c4767daf02fcb8fe4dc0a318",
"is_secret": false,
"is_verified": false,
"line_number": 208,
"line_number": 63,
"type": "Secret Keyword",
"verified_result": null
},
{
"hashed_secret": "65e622227634e8876cfa733000233fb80c6f0473",
"hashed_secret": "dbb19b8ae3b78f908e1467721fe4c9f0b0529d9b",
"is_secret": false,
"is_verified": false,
"line_number": 209,
"line_number": 64,
"type": "Secret Keyword",
"verified_result": null
}
Expand Down Expand Up @@ -424,20 +427,22 @@
"verified_result": null
}
],
"test/unit/iam-token-manager.test.js": [
"test/unit/iam-request-based-token-manager.test.js": [
{
"hashed_secret": "62cdb7020ff920e5aa642c3d4066950dd1f01f4d",
"hashed_secret": "43ed4c2d8375dfc89e3dc8c917f404b9481d355b",
"is_secret": false,
"is_verified": false,
"line_number": 201,
"line_number": 13,
"type": "Secret Keyword",
"verified_result": null
},
}
],
"test/unit/iam-token-manager.test.js": [
{
"hashed_secret": "a7ef1be18bb8d37af79f3d87761a203378bf26a2",
"is_secret": false,
"is_verified": false,
"line_number": 400,
"line_number": 129,
"type": "Secret Keyword",
"verified_result": null
}
Expand Down Expand Up @@ -477,7 +482,7 @@
}
]
},
"version": "0.13.1+ibm.35.dss",
"version": "0.13.1+ibm.40.dss",
"word_list": {
"file": null,
"hash": null
Expand Down
159 changes: 159 additions & 0 deletions auth/token-managers/iam-request-based-token-manager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
/**
* Copyright 2019 IBM Corp. All Rights Reserved.
*
* 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.
*/

import extend = require('extend');
import { OutgoingHttpHeaders } from 'http';
import logger from '../../lib/logger';
import { computeBasicAuthHeader, onlyOne, removeSuffix } from '../utils';
import { JwtTokenManager, JwtTokenManagerOptions } from './jwt-token-manager';

const CLIENT_ID_SECRET_WARNING =
'Warning: Client ID and Secret must BOTH be given, or the header will not be included.';
const DEFAULT_IAM_URL = 'https://iam.cloud.ibm.com';
const OPERATION_PATH = '/identity/token';

/** Configuration options for IAM token retrieval. */
export interface IamRequestOptions extends JwtTokenManagerOptions {
clientId?: string;
clientSecret?: string;
scope?: string;
}

/**
* The IamRequestBasedTokenManager class contains code relevant to any token manager that
* interacts with the IAM service to manage a token. It stores information relevant to all
* IAM requests, such as the client ID and secret, and performs the token request with a set
* of request options common to any IAM token management scheme. It is intended that this
* class be extended with specific implementations.
*/
export class IamRequestBasedTokenManager extends JwtTokenManager {
private clientId: string;

private clientSecret: string;

private scope: string;

protected formData: any;

/**
*
* Create a new [[IamRequestBasedTokenManager]] instance.
*
* @param {object} options Configuration options.
* @param {string} [options.clientId] The `clientId` and `clientSecret` fields are used to form a "basic"
* authorization header for IAM token requests.
* @param {string} [options.clientSecret] The `clientId` and `clientSecret` fields are used to form a "basic"
* authorization header for IAM token requests.
* @param {string} [url='https://iam.cloud.ibm.com'] The IAM endpoint for token requests.
* @param {boolean} [options.disableSslVerification] A flag that indicates
* whether verification of the token server's SSL certificate should be
* disabled or not.
* @param {object<string, string>} [options.headers] Headers to be sent with every
* outbound HTTP requests to token services.
* @constructor
*/
constructor(options: IamRequestOptions) {
// all parameters are optional
options = options || ({} as IamRequestOptions);

super(options);

// Canonicalize the URL by removing the operation path if it was specified by the user.
this.url = this.url ? removeSuffix(this.url, OPERATION_PATH) : DEFAULT_IAM_URL;

if (options.clientId) {
this.clientId = options.clientId;
}
if (options.clientSecret) {
this.clientSecret = options.clientSecret;
}
if (options.scope) {
this.scope = options.scope;
}
if (onlyOne(options.clientId, options.clientSecret)) {
// tslint:disable-next-line
logger.warn(CLIENT_ID_SECRET_WARNING);
}

// initialize the form data object
this.formData = {};
}

/**
* Set the IAM `scope` value.
* This value is the form parameter to use when fetching the bearer token
* from the IAM token server.
*
* @param {string} scope - A space seperated string that makes up the scope parameter.
* @returns {void}
*/
public setScope(scope: string): void {
this.scope = scope;
}

/**
* Set the IAM `clientId` and `clientSecret` values.
* These values are used to compute the Authorization header used
* when retrieving the IAM access token.
* If these values are not set, no Authorization header will be
* set on the request (it is not required).
*
* @param {string} clientId - The client id.
* @param {string} clientSecret - The client secret.
* @returns {void}
*/
public setClientIdAndSecret(clientId: string, clientSecret: string): void {
this.clientId = clientId;
this.clientSecret = clientSecret;
if (onlyOne(clientId, clientSecret)) {
// tslint:disable-next-line
logger.warn(CLIENT_ID_SECRET_WARNING);
}
}

/**
* Request an IAM token using an API key.
*
* @returns {Promise}
*/
protected requestToken(): Promise<any> {
// these cannot be overwritten
const requiredHeaders = {
'Content-type': 'application/x-www-form-urlencoded',
} as OutgoingHttpHeaders;

// If both the clientId and secret were specified by the user, then use them.
if (this.clientId && this.clientSecret) {
requiredHeaders.Authorization = computeBasicAuthHeader(this.clientId, this.clientSecret);
}

if (this.scope) {
this.formData.scope = this.scope;
}

const parameters = {
options: {
url: this.url + OPERATION_PATH,
method: 'POST',
headers: extend(true, {}, this.headers, requiredHeaders),
form: this.formData,
rejectUnauthorized: !this.disableSslVerification,
},
};

return this.requestWrapperInstance.sendRequest(parameters);
}
}
Loading

0 comments on commit 796a4eb

Please sign in to comment.