owltech
Version:
This a backend for OwlTech Company
658 lines • 25.2 kB
JavaScript
"use strict";
/**
* Copyright 2019 Google LLC. 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.
*/
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const child_process_1 = require("child_process");
const fs = require("fs");
const gcpMetadata = require("gcp-metadata");
const os = require("os");
const path = require("path");
const crypto_1 = require("../crypto/crypto");
const isbrowser_1 = require("../isbrowser");
const messages = require("../messages");
const transporters_1 = require("../transporters");
const computeclient_1 = require("./computeclient");
const envDetect_1 = require("./envDetect");
const jwtclient_1 = require("./jwtclient");
const refreshclient_1 = require("./refreshclient");
exports.CLOUD_SDK_CLIENT_ID = '764086051850-6qr4p6gpi6hn506pt8ejuq83di341hur.apps.googleusercontent.com';
class GoogleAuth {
constructor(opts) {
/**
* Caches a value indicating whether the auth layer is running on Google
* Compute Engine.
* @private
*/
this.checkIsGCE = undefined;
// To save the contents of the JSON credential file
this.jsonContent = null;
this.cachedCredential = null;
opts = opts || {};
this._cachedProjectId = opts.projectId || null;
this.keyFilename = opts.keyFilename || opts.keyFile;
this.scopes = opts.scopes;
this.jsonContent = opts.credentials || null;
this.clientOptions = opts.clientOptions;
}
// Note: this properly is only public to satisify unit tests.
// https://github.com/Microsoft/TypeScript/issues/5228
get isGCE() {
return this.checkIsGCE;
}
getDefaultProjectId(callback) {
messages.warn(messages.DEFAULT_PROJECT_ID_DEPRECATED);
if (callback) {
this.getProjectIdAsync().then(r => callback(null, r), callback);
}
else {
return this.getProjectIdAsync();
}
}
getProjectId(callback) {
if (callback) {
this.getProjectIdAsync().then(r => callback(null, r), callback);
}
else {
return this.getProjectIdAsync();
}
}
getProjectIdAsync() {
if (this._cachedProjectId) {
return Promise.resolve(this._cachedProjectId);
}
// In implicit case, supports three environments. In order of precedence,
// the implicit environments are:
// - GCLOUD_PROJECT or GOOGLE_CLOUD_PROJECT environment variable
// - GOOGLE_APPLICATION_CREDENTIALS JSON file
// - Cloud SDK: `gcloud config config-helper --format json`
// - GCE project ID from metadata server)
if (!this._getDefaultProjectIdPromise) {
this._getDefaultProjectIdPromise =
new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
try {
const projectId = this.getProductionProjectId() ||
(yield this.getFileProjectId()) ||
(yield this.getDefaultServiceProjectId()) ||
(yield this.getGCEProjectId());
this._cachedProjectId = projectId;
resolve(projectId);
}
catch (e) {
reject(e);
}
}));
}
return this._getDefaultProjectIdPromise;
}
getApplicationDefault(optionsOrCallback = {}, callback) {
let options;
if (typeof optionsOrCallback === 'function') {
callback = optionsOrCallback;
}
else {
options = optionsOrCallback;
}
if (callback) {
this.getApplicationDefaultAsync(options).then(r => callback(null, r.credential, r.projectId), callback);
}
else {
return this.getApplicationDefaultAsync(options);
}
}
getApplicationDefaultAsync(options) {
return __awaiter(this, void 0, void 0, function* () {
// If we've already got a cached credential, just return it.
if (this.cachedCredential) {
return {
credential: this.cachedCredential,
projectId: yield this.getProjectIdAsync()
};
}
let credential;
let projectId;
// Check for the existence of a local environment variable pointing to the
// location of the credential file. This is typically used in local
// developer scenarios.
credential =
yield this._tryGetApplicationCredentialsFromEnvironmentVariable(options);
if (credential) {
if (credential instanceof jwtclient_1.JWT) {
credential.scopes = this.scopes;
}
this.cachedCredential = credential;
projectId = yield this.getProjectId();
return { credential, projectId };
}
// Look in the well-known credential file location.
credential =
yield this._tryGetApplicationCredentialsFromWellKnownFile(options);
if (credential) {
if (credential instanceof jwtclient_1.JWT) {
credential.scopes = this.scopes;
}
this.cachedCredential = credential;
projectId = yield this.getProjectId();
return { credential, projectId };
}
// Determine if we're running on GCE.
let isGCE;
try {
isGCE = yield this._checkIsGCE();
}
catch (e) {
throw new Error('Unexpected error determining execution environment: ' + e.message);
}
if (!isGCE) {
// We failed to find the default credentials. Bail out with an error.
throw new Error('Could not load the default credentials. Browse to https://cloud.google.com/docs/authentication/getting-started for more information.');
}
// For GCE, just return a default ComputeClient. It will take care of
// the rest.
this.cachedCredential = new computeclient_1.Compute(options);
projectId = yield this.getProjectId();
return { projectId, credential: this.cachedCredential };
});
}
/**
* Determines whether the auth layer is running on Google Compute Engine.
* @returns A promise that resolves with the boolean.
* @api private
*/
_checkIsGCE() {
return __awaiter(this, void 0, void 0, function* () {
if (this.checkIsGCE === undefined) {
this.checkIsGCE = yield gcpMetadata.isAvailable();
}
return this.checkIsGCE;
});
}
/**
* Attempts to load default credentials from the environment variable path..
* @returns Promise that resolves with the OAuth2Client or null.
* @api private
*/
_tryGetApplicationCredentialsFromEnvironmentVariable(options) {
return __awaiter(this, void 0, void 0, function* () {
const credentialsPath = process.env['GOOGLE_APPLICATION_CREDENTIALS'] ||
process.env['google_application_credentials'];
if (!credentialsPath || credentialsPath.length === 0) {
return null;
}
try {
return this._getApplicationCredentialsFromFilePath(credentialsPath, options);
}
catch (e) {
throw this.createError('Unable to read the credential file specified by the GOOGLE_APPLICATION_CREDENTIALS environment variable.', e);
}
});
}
/**
* Attempts to load default credentials from a well-known file location
* @return Promise that resolves with the OAuth2Client or null.
* @api private
*/
_tryGetApplicationCredentialsFromWellKnownFile(options) {
return __awaiter(this, void 0, void 0, function* () {
// First, figure out the location of the file, depending upon the OS type.
let location = null;
if (this._isWindows()) {
// Windows
location = process.env['APPDATA'];
}
else {
// Linux or Mac
const home = process.env['HOME'];
if (home) {
location = this._pathJoin(home, '.config');
}
}
// If we found the root path, expand it.
if (location) {
location = this._pathJoin(location, 'gcloud');
location =
this._pathJoin(location, 'application_default_credentials.json');
location = this._mockWellKnownFilePath(location);
// Check whether the file exists.
if (!this._fileExists(location)) {
location = null;
}
}
// The file does not exist.
if (!location) {
return null;
}
// The file seems to exist. Try to use it.
const client = yield this._getApplicationCredentialsFromFilePath(location, options);
this.warnOnProblematicCredentials(client);
return client;
});
}
/**
* Attempts to load default credentials from a file at the given path..
* @param filePath The path to the file to read.
* @returns Promise that resolves with the OAuth2Client
* @api private
*/
_getApplicationCredentialsFromFilePath(filePath, options = {}) {
return __awaiter(this, void 0, void 0, function* () {
// Make sure the path looks like a string.
if (!filePath || filePath.length === 0) {
throw new Error('The file path is invalid.');
}
// Make sure there is a file at the path. lstatSync will throw if there is
// nothing there.
try {
// Resolve path to actual file in case of symlink. Expect a thrown error
// if not resolvable.
filePath = fs.realpathSync(filePath);
if (!fs.lstatSync(filePath).isFile()) {
throw new Error();
}
}
catch (err) {
throw this.createError(`The file at ${filePath} does not exist, or it is not a file.`, err);
}
// Now open a read stream on the file, and parse it.
try {
const readStream = this._createReadStream(filePath);
return this.fromStream(readStream, options);
}
catch (err) {
throw this.createError(`Unable to read the file at ${filePath}.`, err);
}
});
}
/**
* Credentials from the Cloud SDK that are associated with Cloud SDK's project
* are problematic because they may not have APIs enabled and have limited
* quota. If this is the case, warn about it.
*/
warnOnProblematicCredentials(client) {
if (client.email === exports.CLOUD_SDK_CLIENT_ID) {
messages.warn(messages.PROBLEMATIC_CREDENTIALS_WARNING);
}
}
/**
* Create a credentials instance using the given input options.
* @param json The input object.
* @param options The JWT or UserRefresh options for the client
* @returns JWT or UserRefresh Client with data
*/
fromJSON(json, options) {
let client;
if (!json) {
throw new Error('Must pass in a JSON object containing the Google auth settings.');
}
this.jsonContent = json;
options = options || {};
if (json.type === 'authorized_user') {
client = new refreshclient_1.UserRefreshClient(options);
}
else {
options.scopes = this.scopes;
client = new jwtclient_1.JWT(options);
}
client.fromJSON(json);
return client;
}
fromStream(inputStream, optionsOrCallback = {}, callback) {
let options = {};
if (typeof optionsOrCallback === 'function') {
callback = optionsOrCallback;
}
else {
options = optionsOrCallback;
}
if (callback) {
this.fromStreamAsync(inputStream, options)
.then(r => callback(null, r), callback);
}
else {
return this.fromStreamAsync(inputStream, options);
}
}
fromStreamAsync(inputStream, options) {
return new Promise((resolve, reject) => {
if (!inputStream) {
throw new Error('Must pass in a stream containing the Google auth settings.');
}
let s = '';
inputStream.setEncoding('utf8')
.on('error', reject)
.on('data', (chunk) => s += chunk)
.on('end', () => {
try {
const data = JSON.parse(s);
const r = this.fromJSON(data, options);
return resolve(r);
}
catch (err) {
return reject(err);
}
});
});
}
/**
* Create a credentials instance using the given API key string.
* @param apiKey The API key string
* @param options An optional options object.
* @returns A JWT loaded from the key
*/
fromAPIKey(apiKey, options) {
options = options || {};
const client = new jwtclient_1.JWT(options);
client.fromAPIKey(apiKey);
return client;
}
/**
* Determines whether the current operating system is Windows.
* @api private
*/
_isWindows() {
const sys = this._osPlatform();
if (sys && sys.length >= 3) {
if (sys.substring(0, 3).toLowerCase() === 'win') {
return true;
}
}
return false;
}
/**
* Creates a file stream. Allows mocking.
* @api private
*/
_createReadStream(filePath) {
return fs.createReadStream(filePath);
}
/**
* Gets the current operating system platform. Allows mocking.
* @api private
*/
_osPlatform() {
return os.platform();
}
/**
* Determines whether a file exists. Allows mocking.
* @api private
*/
_fileExists(filePath) {
return fs.existsSync(filePath);
}
/**
* Joins two parts of a path. Allows mocking.
* @api private
*/
_pathJoin(item1, item2) {
return path.join(item1, item2);
}
/**
* Allows mocking of the path to a well-known file.
* @api private
*/
_mockWellKnownFilePath(filePath) {
return filePath;
}
// Creates an Error containing the given message, and includes the message
// from the optional err passed in.
createError(message, err) {
let s = message || '';
if (err) {
const errorMessage = String(err);
if (errorMessage && errorMessage.length > 0) {
if (s.length > 0) {
s += ' ';
}
s += errorMessage;
}
}
return Error(s);
}
/**
* Run the Google Cloud SDK command that prints the default project ID
*/
getDefaultServiceProjectId() {
return __awaiter(this, void 0, void 0, function* () {
return new Promise(resolve => {
child_process_1.exec('gcloud config config-helper --format json', (err, stdout, stderr) => {
if (!err && stdout) {
try {
const projectId = JSON.parse(stdout).configuration.properties.core.project;
resolve(projectId);
return;
}
catch (e) {
// ignore errors
}
}
resolve(null);
});
});
});
}
/**
* Loads the project id from environment variables.
* @api private
*/
getProductionProjectId() {
return process.env['GCLOUD_PROJECT'] ||
process.env['GOOGLE_CLOUD_PROJECT'] || process.env['gcloud_project'] ||
process.env['google_cloud_project'];
}
/**
* Loads the project id from the GOOGLE_APPLICATION_CREDENTIALS json file.
* @api private
*/
getFileProjectId() {
return __awaiter(this, void 0, void 0, function* () {
if (this.cachedCredential) {
// Try to read the project ID from the cached credentials file
return this.cachedCredential.projectId;
}
// Ensure the projectId is loaded from the keyFile if available.
if (this.keyFilename) {
const creds = yield this.getClient();
if (creds && creds.projectId) {
return creds.projectId;
}
}
// Try to load a credentials file and read its project ID
const r = yield this._tryGetApplicationCredentialsFromEnvironmentVariable();
if (r) {
return r.projectId;
}
else {
return null;
}
});
}
/**
* Gets the Compute Engine project ID if it can be inferred.
*/
getGCEProjectId() {
return __awaiter(this, void 0, void 0, function* () {
try {
const r = yield gcpMetadata.project('project-id');
return r;
}
catch (e) {
// Ignore any errors
return null;
}
});
}
getCredentials(callback) {
if (callback) {
this.getCredentialsAsync().then(r => callback(null, r), callback);
}
else {
return this.getCredentialsAsync();
}
}
getCredentialsAsync() {
return __awaiter(this, void 0, void 0, function* () {
yield this.getClient();
if (this.jsonContent) {
const credential = {
client_email: this.jsonContent.client_email,
private_key: this.jsonContent.private_key
};
return credential;
}
const isGCE = yield this._checkIsGCE();
if (!isGCE) {
throw new Error('Unknown error.');
}
// For GCE, return the service account details from the metadata server
// NOTE: The trailing '/' at the end of service-accounts/ is very important!
// The GCF metadata server doesn't respect querystring params if this / is
// not included.
const data = yield gcpMetadata.instance({ property: 'service-accounts/', params: { recursive: 'true' } });
if (!data || !data.default || !data.default.email) {
throw new Error('Failure from metadata server.');
}
return { client_email: data.default.email };
});
}
/**
* Automatically obtain a client based on the provided configuration. If no
* options were passed, use Application Default Credentials.
*/
getClient(options) {
return __awaiter(this, void 0, void 0, function* () {
if (options) {
this.keyFilename =
options.keyFilename || options.keyFile || this.keyFilename;
this.scopes = options.scopes || this.scopes;
this.jsonContent = options.credentials || this.jsonContent;
this.clientOptions = options.clientOptions;
}
if (!this.cachedCredential) {
if (this.jsonContent) {
this.cachedCredential =
yield this.fromJSON(this.jsonContent, this.clientOptions);
}
else if (this.keyFilename) {
const filePath = path.resolve(this.keyFilename);
const stream = fs.createReadStream(filePath);
this.cachedCredential =
yield this.fromStreamAsync(stream, this.clientOptions);
}
else {
yield this.getApplicationDefaultAsync(this.clientOptions);
}
}
return this.cachedCredential;
});
}
/**
* Automatically obtain application default credentials, and return
* an access token for making requests.
*/
getAccessToken() {
return __awaiter(this, void 0, void 0, function* () {
const client = yield this.getClient();
return (yield client.getAccessToken()).token;
});
}
/**
* Obtain the HTTP headers that will provide authorization for a given
* request.
*/
getRequestHeaders(url) {
return __awaiter(this, void 0, void 0, function* () {
const client = yield this.getClient();
return client.getRequestHeaders(url);
});
}
/**
* Obtain credentials for a request, then attach the appropriate headers to
* the request options.
* @param opts Axios or Request options on which to attach the headers
*/
authorizeRequest(opts) {
return __awaiter(this, void 0, void 0, function* () {
opts = opts || {};
const url = opts.url || opts.uri;
const client = yield this.getClient();
const headers = yield client.getRequestHeaders(url);
opts.headers = Object.assign(opts.headers || {}, headers);
return opts;
});
}
/**
* Automatically obtain application default credentials, and make an
* HTTP request using the given options.
* @param opts Axios request options for the HTTP request.
*/
// tslint:disable-next-line no-any
request(opts) {
return __awaiter(this, void 0, void 0, function* () {
const client = yield this.getClient();
return client.request(opts);
});
}
/**
* Determine the compute environment in which the code is running.
*/
getEnv() {
return envDetect_1.getEnv();
}
/**
* Sign the given data with the current private key, or go out
* to the IAM API to sign it.
* @param data The data to be signed.
*/
sign(data) {
return __awaiter(this, void 0, void 0, function* () {
const client = yield this.getClient();
const crypto = crypto_1.createCrypto();
if (client instanceof jwtclient_1.JWT && client.key && !isbrowser_1.isBrowser()) {
const sign = crypto.createSign('RSA-SHA256');
sign.update(data);
return sign.sign(client.key, 'base64');
}
const projectId = yield this.getProjectId();
if (!projectId) {
throw new Error('Cannot sign data without a project ID.');
}
const creds = yield this.getCredentials();
if (!creds.client_email) {
throw new Error('Cannot sign data without `client_email`.');
}
const id = `projects/${projectId}/serviceAccounts/${creds.client_email}`;
const res = yield this.request({
method: 'POST',
url: `https://iam.googleapis.com/v1/${id}:signBlob`,
data: { bytesToSign: crypto.encodeBase64StringUtf8(data) }
});
return res.data.signature;
});
}
}
/**
* Export DefaultTransporter as a static property of the class.
*/
GoogleAuth.DefaultTransporter = transporters_1.DefaultTransporter;
exports.GoogleAuth = GoogleAuth;
//# sourceMappingURL=googleauth.js.map