Skip to content

Commit

Permalink
fix: throw when adc cannot acquire a projectId (#658)
Browse files Browse the repository at this point in the history
  • Loading branch information
JustinBeckwith authored Apr 2, 2019
1 parent 48a1c81 commit ba48164
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 34 deletions.
31 changes: 18 additions & 13 deletions src/auth/googleauth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,19 +170,24 @@ export class GoogleAuth {
// - Cloud SDK: `gcloud config config-helper --format json`
// - GCE project ID from metadata server)
if (!this._getDefaultProjectIdPromise) {
this._getDefaultProjectIdPromise =
new Promise(async (resolve, reject) => {
try {
const projectId = this.getProductionProjectId() ||
await this.getFileProjectId() ||
await this.getDefaultServiceProjectId() ||
await this.getGCEProjectId();
this._cachedProjectId = projectId;
resolve(projectId);
} catch (e) {
reject(e);
}
});
this._getDefaultProjectIdPromise = new Promise(async (resolve, reject) => {
try {
const projectId = this.getProductionProjectId() ||
await this.getFileProjectId() ||
await this.getDefaultServiceProjectId() ||
await this.getGCEProjectId();
this._cachedProjectId = projectId;
if (!projectId) {
throw new Error(
'Unable to detect a Project Id in the current environment. \n' +
'To learn more about authentication and Google APIs, visit: \n' +
'https://cloud.google.com/docs/authentication/getting-started');
}
resolve(projectId);
} catch (e) {
reject(e);
}
});
}
return this._getDefaultProjectIdPromise;
}
Expand Down
3 changes: 2 additions & 1 deletion test/fixtures/private.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
"private_key": "privatekey",
"client_email": "hello@youarecool.com",
"client_id": "client123",
"type": "service_account"
"type": "service_account",
"project_id": "not-a-project-id"
}
49 changes: 29 additions & 20 deletions test/test.googleauth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ describe('googleauth', () => {
return nock(host).get(instancePath).reply(404);
}

function createGetProjectIdNock(projectId: string) {
function createGetProjectIdNock(projectId = 'not-real') {
return nock(host)
.get(`${BASE_PATH}/project/project-id`)
.reply(200, projectId, HEADERS);
Expand Down Expand Up @@ -108,11 +108,6 @@ describe('googleauth', () => {
return {auth, scopes: [scope1, scope2]};
}

// Matches the ending of a string.
function stringEndsWith(str: string, suffix: string) {
return str.indexOf(suffix, str.length - suffix.length) !== -1;
}

// Simulates a path join.
function pathJoin(item1: string, item2: string) {
return item1 + ':' + item2;
Expand Down Expand Up @@ -871,16 +866,13 @@ describe('googleauth', () => {

it('getApplicationDefault should return a new credential the first time and a cached credential the second time',
async () => {
const scope = nockNotGCE();
// Create a function which will set up a GoogleAuth instance to match
// on an environment variable json file, but not on anything else.
mockEnvVar(
'GOOGLE_APPLICATION_CREDENTIALS', './test/fixtures/private.json');
auth._fileExists = () => false;

// Ask for credentials, the first time.
const result = await auth.getApplicationDefault();
scope.isDone();
assert.notEqual(null, result);

// Capture the returned credential.
Expand Down Expand Up @@ -931,11 +923,11 @@ describe('googleauth', () => {
async () => {
blockGoogleApplicationCredentialEnvironmentVariable();
auth._fileExists = () => false;
const scope = nockIsGCE();
const scopes = [nockIsGCE(), createGetProjectIdNock()];

// Ask for credentials, the first time.
const result = await auth.getApplicationDefault();
scope.done();
scopes.forEach(x => x.done());
assert.notEqual(null, result);

// Capture the returned credential.
Expand Down Expand Up @@ -1012,9 +1004,9 @@ describe('googleauth', () => {
auth._pathJoin = pathJoin;
auth._osPlatform = () => 'win32';
auth._fileExists = () => false;
const scope = nockIsGCE();
const scopes = [nockIsGCE(), createGetProjectIdNock()];
const res = await auth.getApplicationDefault();
scope.done();
scopes.forEach(x => x.done());
// This indicates that we got a ComputeClient instance back, rather than
// a JWTClient.
assert.strictEqual(
Expand Down Expand Up @@ -1141,7 +1133,7 @@ describe('googleauth', () => {
}
};
const scopes = [
nockIsGCE(),
nockIsGCE(), createGetProjectIdNock(),
nock(host).get(svcAccountPath).reply(200, response, HEADERS)
];
blockGoogleApplicationCredentialEnvironmentVariable();
Expand All @@ -1157,8 +1149,10 @@ describe('googleauth', () => {
});

it('getCredentials should error if metadata server is not reachable', async () => {
const scopes =
[nockIsGCE(), nock(HOST_ADDRESS).get(svcAccountPath).reply(404)];
const scopes = [
nockIsGCE(), createGetProjectIdNock(),
nock(HOST_ADDRESS).get(svcAccountPath).reply(404)
];
blockGoogleApplicationCredentialEnvironmentVariable();
auth._fileExists = () => false;
await auth._checkIsGCE();
Expand All @@ -1172,8 +1166,10 @@ describe('googleauth', () => {
it('getCredentials should error if body is empty', async () => {
blockGoogleApplicationCredentialEnvironmentVariable();
auth._fileExists = () => false;
const scopes =
[nockIsGCE(), nock(HOST_ADDRESS).get(svcAccountPath).reply(200, {})];
const scopes = [
nockIsGCE(), createGetProjectIdNock(),
nock(HOST_ADDRESS).get(svcAccountPath).reply(200, {})
];
await auth._checkIsGCE();
assert.strictEqual(true, auth.isGCE);
await assertRejects(
Expand Down Expand Up @@ -1305,20 +1301,23 @@ describe('googleauth', () => {

it('should get an access token', async () => {
const {auth, scopes} = mockGCE();
scopes.push(createGetProjectIdNock());
const token = await auth.getAccessToken();
scopes.forEach(s => s.done());
assert.strictEqual(token, 'abc123');
});

it('should get request headers', async () => {
const {auth, scopes} = mockGCE();
scopes.push(createGetProjectIdNock());
const headers = await auth.getRequestHeaders();
scopes.forEach(s => s.done());
assert.deepStrictEqual(headers, {Authorization: 'Bearer abc123'});
});

it('should authorize the request', async () => {
const {auth, scopes} = mockGCE();
scopes.push(createGetProjectIdNock());
const opts = await auth.authorizeRequest({url: 'http://example.com'});
scopes.forEach(s => s.done());
assert.deepStrictEqual(opts.headers, {Authorization: 'Bearer abc123'});
Expand Down Expand Up @@ -1359,9 +1358,9 @@ describe('googleauth', () => {
it('should make the request', async () => {
const url = 'http://example.com';
const {auth, scopes} = mockGCE();
scopes.push(createGetProjectIdNock());
const data = {breakfast: 'coffee'};
const scope = nock(url).get('/').reply(200, data);
scopes.push(scope);
scopes.push(nock(url).get('/').reply(200, data));
const res = await auth.request({url});
scopes.forEach(s => s.done());
assert.deepStrictEqual(res.data, data);
Expand Down Expand Up @@ -1467,4 +1466,14 @@ describe('googleauth', () => {
const client = await auth.getClient() as JWT;
assert.strictEqual(client.subject, subject);
});

it('should throw if getProjectId cannot find a projectId', async () => {
blockGoogleApplicationCredentialEnvironmentVariable();
auth._fileExists = () => false;
// tslint:disable-next-line no-any
sinon.stub(auth as any, 'getDefaultServiceProjectId').resolves();
await assertRejects(
auth.getProjectId(),
/Unable to detect a Project Id in the current environment/);
});
});

0 comments on commit ba48164

Please sign in to comment.