UNPKG

7.48 kBJavaScriptView Raw
1"use strict";
2// Copyright 2021 Google LLC
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15var _a, _b, _c;
16Object.defineProperty(exports, "__esModule", { value: true });
17exports.IdentityPoolClient = void 0;
18const fs = require("fs");
19const util_1 = require("util");
20const baseexternalclient_1 = require("./baseexternalclient");
21// fs.readfile is undefined in browser karma tests causing
22// `npm run browser-test` to fail as test.oauth2.ts imports this file via
23// src/index.ts.
24// Fallback to void function to avoid promisify throwing a TypeError.
25const readFile = util_1.promisify((_a = fs.readFile) !== null && _a !== void 0 ? _a : (() => { }));
26const realpath = util_1.promisify((_b = fs.realpath) !== null && _b !== void 0 ? _b : (() => { }));
27const lstat = util_1.promisify((_c = fs.lstat) !== null && _c !== void 0 ? _c : (() => { }));
28/**
29 * Defines the Url-sourced and file-sourced external account clients mainly
30 * used for K8s and Azure workloads.
31 */
32class IdentityPoolClient extends baseexternalclient_1.BaseExternalAccountClient {
33 /**
34 * Instantiate an IdentityPoolClient instance using the provided JSON
35 * object loaded from an external account credentials file.
36 * An error is thrown if the credential is not a valid file-sourced or
37 * url-sourced credential or a workforce pool user project is provided
38 * with a non workforce audience.
39 * @param options The external account options object typically loaded
40 * from the external account JSON credential file.
41 * @param additionalOptions Optional additional behavior customization
42 * options. These currently customize expiration threshold time and
43 * whether to retry on 401/403 API request errors.
44 */
45 constructor(options, additionalOptions) {
46 var _a, _b;
47 super(options, additionalOptions);
48 this.file = options.credential_source.file;
49 this.url = options.credential_source.url;
50 this.headers = options.credential_source.headers;
51 if (!this.file && !this.url) {
52 throw new Error('No valid Identity Pool "credential_source" provided');
53 }
54 // Text is the default format type.
55 this.formatType = ((_a = options.credential_source.format) === null || _a === void 0 ? void 0 : _a.type) || 'text';
56 this.formatSubjectTokenFieldName = (_b = options.credential_source.format) === null || _b === void 0 ? void 0 : _b.subject_token_field_name;
57 if (this.formatType !== 'json' && this.formatType !== 'text') {
58 throw new Error(`Invalid credential_source format "${this.formatType}"`);
59 }
60 if (this.formatType === 'json' && !this.formatSubjectTokenFieldName) {
61 throw new Error('Missing subject_token_field_name for JSON credential_source format');
62 }
63 }
64 /**
65 * Triggered when a external subject token is needed to be exchanged for a GCP
66 * access token via GCP STS endpoint.
67 * This uses the `options.credential_source` object to figure out how
68 * to retrieve the token using the current environment. In this case,
69 * this either retrieves the local credential from a file location (k8s
70 * workload) or by sending a GET request to a local metadata server (Azure
71 * workloads).
72 * @return A promise that resolves with the external subject token.
73 */
74 async retrieveSubjectToken() {
75 if (this.file) {
76 return await this.getTokenFromFile(this.file, this.formatType, this.formatSubjectTokenFieldName);
77 }
78 return await this.getTokenFromUrl(this.url, this.formatType, this.formatSubjectTokenFieldName, this.headers);
79 }
80 /**
81 * Looks up the external subject token in the file path provided and
82 * resolves with that token.
83 * @param file The file path where the external credential is located.
84 * @param formatType The token file or URL response type (JSON or text).
85 * @param formatSubjectTokenFieldName For JSON response types, this is the
86 * subject_token field name. For Azure, this is access_token. For text
87 * response types, this is ignored.
88 * @return A promise that resolves with the external subject token.
89 */
90 async getTokenFromFile(filePath, formatType, formatSubjectTokenFieldName) {
91 // Make sure there is a file at the path. lstatSync will throw if there is
92 // nothing there.
93 try {
94 // Resolve path to actual file in case of symlink. Expect a thrown error
95 // if not resolvable.
96 filePath = await realpath(filePath);
97 if (!(await lstat(filePath)).isFile()) {
98 throw new Error();
99 }
100 }
101 catch (err) {
102 err.message = `The file at ${filePath} does not exist, or it is not a file. ${err.message}`;
103 throw err;
104 }
105 let subjectToken;
106 const rawText = await readFile(filePath, { encoding: 'utf8' });
107 if (formatType === 'text') {
108 subjectToken = rawText;
109 }
110 else if (formatType === 'json' && formatSubjectTokenFieldName) {
111 const json = JSON.parse(rawText);
112 subjectToken = json[formatSubjectTokenFieldName];
113 }
114 if (!subjectToken) {
115 throw new Error('Unable to parse the subject_token from the credential_source file');
116 }
117 return subjectToken;
118 }
119 /**
120 * Sends a GET request to the URL provided and resolves with the returned
121 * external subject token.
122 * @param url The URL to call to retrieve the subject token. This is typically
123 * a local metadata server.
124 * @param formatType The token file or URL response type (JSON or text).
125 * @param formatSubjectTokenFieldName For JSON response types, this is the
126 * subject_token field name. For Azure, this is access_token. For text
127 * response types, this is ignored.
128 * @param headers The optional additional headers to send with the request to
129 * the metadata server url.
130 * @return A promise that resolves with the external subject token.
131 */
132 async getTokenFromUrl(url, formatType, formatSubjectTokenFieldName, headers) {
133 const opts = {
134 url,
135 method: 'GET',
136 headers,
137 responseType: formatType,
138 };
139 let subjectToken;
140 if (formatType === 'text') {
141 const response = await this.transporter.request(opts);
142 subjectToken = response.data;
143 }
144 else if (formatType === 'json' && formatSubjectTokenFieldName) {
145 const response = await this.transporter.request(opts);
146 subjectToken = response.data[formatSubjectTokenFieldName];
147 }
148 if (!subjectToken) {
149 throw new Error('Unable to parse the subject_token from the credential_source URL');
150 }
151 return subjectToken;
152 }
153}
154exports.IdentityPoolClient = IdentityPoolClient;
155//# sourceMappingURL=identitypoolclient.js.map
\No newline at end of file