Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: deprecate the getDefaultProjectId method #402

Merged
merged 5 commits into from
Jul 4, 2018
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,13 @@
],
"dependencies": {
"axios": "^0.18.0",
"gcp-metadata": "^0.6.3",
"gtoken": "^2.3.0",
"jws": "^3.1.5",
"lodash.isstring": "^4.0.1",
"lru-cache": "^4.1.3",
"retry-axios": "^0.3.2",
"gcp-metadata": "^0.6.3"
"semver": "^5.5.0"
},
"devDependencies": {
"@justinbeckwith/typedoc": "^0.10.1",
Expand All @@ -37,8 +38,9 @@
"@types/mv": "^2.1.0",
"@types/ncp": "^2.0.1",
"@types/nock": "^9.1.3",
"@types/node": "^10.3.0",
"@types/node": "^10.5.1",
"@types/pify": "^3.0.2",
"@types/semver": "^5.5.0",
"@types/sinon": "^5.0.1",
"@types/tmp": "^0.0.33",
"clang-format": "^1.2.3",
Expand Down
42 changes: 26 additions & 16 deletions src/auth/googleauth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,6 @@ export const CLOUD_SDK_CLIENT_ID =
export class GoogleAuth {
transporter?: Transporter;

// This shim is in place for compatibility with google-auto-auth.
getProjectId = this.getDefaultProjectId;

/**
* Caches a value indicating whether the auth layer is running on Google
* Compute Engine.
Expand Down Expand Up @@ -126,23 +123,36 @@ export class GoogleAuth {
}

/**
* Obtains the default project ID for the application.
* @param callback Optional callback
* @returns Promise that resolves with project Id (if used without callback)
* THIS METHOD HAS BEEN DEPRECATED.
* It will be removed in 3.0. Please use getProjectId instead.
*/
getDefaultProjectId(): Promise<string>;
getDefaultProjectId(callback: ProjectIdCallback): void;
getDefaultProjectId(callback?: ProjectIdCallback): Promise<string|null>|void {
messages.warn(messages.DEFAULT_PROJECT_ID_DEPRECATED);
if (callback) {
this.getDefaultProjectIdAsync()
.then(r => callback(null, r))
.catch(callback);
this.getProjectIdAsync().then(r => callback(null, r)).catch(callback);

This comment was marked as spam.

This comment was marked as spam.

} else {
return this.getProjectIdAsync();
}
}

/**
* Obtains the default project ID for the application.
* @param callback Optional callback
* @returns Promise that resolves with project Id (if used without callback)
*/
getProjectId(): Promise<string>;
getProjectId(callback: ProjectIdCallback): void;
getProjectId(callback?: ProjectIdCallback): Promise<string|null>|void {
if (callback) {
this.getProjectIdAsync().then(r => callback(null, r)).catch(callback);

This comment was marked as spam.

} else {
return this.getDefaultProjectIdAsync();
return this.getProjectIdAsync();
}
}

private getDefaultProjectIdAsync(): Promise<string|null> {
private getProjectIdAsync(): Promise<string|null> {
if (this._cachedProjectId) {
return Promise.resolve(this._cachedProjectId);
}
Expand Down Expand Up @@ -205,7 +215,7 @@ export class GoogleAuth {
if (this.cachedCredential) {
return {
credential: this.cachedCredential as JWT | UserRefreshClient,
projectId: await this.getDefaultProjectIdAsync()
projectId: await this.getProjectIdAsync()
};
}

Expand All @@ -222,7 +232,7 @@ export class GoogleAuth {
credential.scopes = this.scopes;
}
this.cachedCredential = credential;
projectId = await this.getDefaultProjectId();
projectId = await this.getProjectId();
return {credential, projectId};
}

Expand All @@ -234,7 +244,7 @@ export class GoogleAuth {
credential.scopes = this.scopes;
}
this.cachedCredential = credential;
projectId = await this.getDefaultProjectId();
projectId = await this.getProjectId();
return {credential, projectId};
}

Expand All @@ -256,7 +266,7 @@ export class GoogleAuth {
// For GCE, just return a default ComputeClient. It will take care of
// the rest.
this.cachedCredential = new Compute(options);
projectId = await this.getDefaultProjectId();
projectId = await this.getProjectId();
return {projectId, credential: this.cachedCredential};
}

Expand Down Expand Up @@ -382,7 +392,7 @@ export class GoogleAuth {
*/
protected warnOnProblematicCredentials(client: JWT) {
if (client.email === CLOUD_SDK_CLIENT_ID) {
process.emitWarning(messages.PROBLEMATIC_CREDENTIALS_WARNING);
messages.warn(messages.PROBLEMATIC_CREDENTIALS_WARNING);
}
}

Expand Down
52 changes: 45 additions & 7 deletions src/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,48 @@
* limitations under the License.
*/

export const PROBLEMATIC_CREDENTIALS_WARNING =
`Your application has authenticated using end user credentials from Google
Cloud SDK. We recommend that most server applications use service accounts
instead. If your application continues to use end user credentials from Cloud
SDK, you might receive a "quota exceeded" or "API not enabled" error. For
more information about service accounts, see
https://cloud.google.com/docs/authentication/.`;
import * as semver from 'semver';

export function warn(warning: Warning) {
if (semver.satisfies(process.version, '>=8')) {
// @types/node doesn't recognize the emitWarning syntax which
// accepts a config object, so `as any` it is
// https://nodejs.org/docs/latest-v8.x/api/process.html#process_process_emitwarning_warning_options
// tslint:disable-next-line no-any
process.emitWarning(warning.message, warning as any);
} else {
// This path can be removed once we drop support for Node 6.
// https://nodejs.org/docs/latest-v6.x/api/process.html#process_process_emitwarning_warning_name_ctor
process.emitWarning(warning.message, warning.type);
}
}

export interface Warning {
code: string;
type: string;
message: string;
}

export const PROBLEMATIC_CREDENTIALS_WARNING = {
code: 'google-auth-library:DEP001',
type: 'DeprecationWarning',

This comment was marked as spam.

message: [
'Your application has authenticated using end user credentials from Google',
'Cloud SDK. We recommend that most server applications use service accounts',
'instead. If your application continues to use end user credentials from',
'Cloud SDK, you might receive a "quota exceeded" or "API not enabled" error.',
'For more information about service accounts, see',
'https://cloud.google.com/docs/authentication/.'
].join(' ')
};


export const DEFAULT_PROJECT_ID_DEPRECATED = {
code: 'google-auth-library:DEP002',
type: 'DeprecationWarning',
message: [
'The `getDefaultProjectId` method has been deprecated, and will be removed',
'in the 3.0 release of google-auth-library. Please use the `getProjectId`',
'method instead.'
].join(' ')
};
2 changes: 1 addition & 1 deletion test/fixtures/kitchen/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const jwt = new JWT();
const auth = new GoogleAuth();
async function getToken() {
const token = await jwt.getToken('token');
const projectId = await auth.getDefaultProjectId();
const projectId = await auth.getProjectId();
const creds = await auth.getApplicationDefault();
return token;
}
Expand Down
60 changes: 40 additions & 20 deletions test/test.googleauth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -745,7 +745,7 @@ it('_tryGetApplicationCredentialsFromWellKnownFile should pass along a failure o
assert.fail('failed to throw');
});

it('getDefaultProjectId should return a new projectId the first time and a cached projectId the second time',
it('getProjectId should return a new projectId the first time and a cached projectId the second time',
async () => {
// Create a function which will set up a GoogleAuth instance to match
// on an environment variable json file, but not on anything else.
Expand All @@ -757,7 +757,7 @@ it('getDefaultProjectId should return a new projectId the first time and a cache
setUpAuthForEnvironmentVariable(auth);

// Ask for credentials, the first time.
const projectIdPromise = auth.getDefaultProjectId();
const projectIdPromise = auth.getProjectId();
const projectId = await projectIdPromise;
assert.equal(projectId, fixedProjectId);

Expand All @@ -771,7 +771,7 @@ it('getDefaultProjectId should return a new projectId the first time and a cache

// Ask for projectId again, from the same auth instance. If it isn't
// cached, this will crash.
const projectId2 = await auth.getDefaultProjectId();
const projectId2 = await auth.getProjectId();

// Make sure we get the original cached projectId back
assert.equal(fixedProjectId, projectId2);
Expand All @@ -781,34 +781,34 @@ it('getDefaultProjectId should return a new projectId the first time and a cache
const auth2 = new GoogleAuth();
setUpAuthForEnvironmentVariable(auth2);

const getProjectIdPromise = auth2.getDefaultProjectId();
const getProjectIdPromise = auth2.getProjectId();
assert.notEqual(getProjectIdPromise, projectIdPromise);
});

it('getDefaultProjectId should use GCLOUD_PROJECT environment variable when it is set',
it('getProjectId should use GCLOUD_PROJECT environment variable when it is set',
async () => {
mockEnvVar('GCLOUD_PROJECT', fixedProjectId);
const projectId = await auth.getDefaultProjectId();
const projectId = await auth.getProjectId();
assert.equal(projectId, fixedProjectId);
});

it('getDefaultProjectId should use GOOGLE_CLOUD_PROJECT environment variable when it is set',
it('getProjectId should use GOOGLE_CLOUD_PROJECT environment variable when it is set',
async () => {
mockEnvVar('GOOGLE_CLOUD_PROJECT', fixedProjectId);
const projectId = await auth.getDefaultProjectId();
const projectId = await auth.getProjectId();
assert.equal(projectId, fixedProjectId);
});

it('getDefaultProjectId should use GOOGLE_APPLICATION_CREDENTIALS file when it is available',
it('getProjectId should use GOOGLE_APPLICATION_CREDENTIALS file when it is available',
async () => {
mockEnvVar(
'GOOGLE_APPLICATION_CREDENTIALS',
path.join(__dirname, '../../test/fixtures/private2.json'));
const projectId = await auth.getDefaultProjectId();
const projectId = await auth.getProjectId();
assert.equal(projectId, fixedProjectId);
});

it('getDefaultProjectId should prefer configured projectId', async () => {
it('getProjectId should prefer configured projectId', async () => {
mockEnvVar('GCLOUD_PROJECT', fixedProjectId);
mockEnvVar('GOOGLE_CLOUD_PROJECT', fixedProjectId);
mockEnvVar(
Expand All @@ -818,19 +818,19 @@ it('getDefaultProjectId should prefer configured projectId', async () => {

const PROJECT_ID = 'configured-project-id-should-be-preferred';
const auth = new GoogleAuth({projectId: PROJECT_ID});
const projectId = await auth.getDefaultProjectId();
const projectId = await auth.getProjectId();
assert.strictEqual(projectId, PROJECT_ID);
});

it('getProjectId should work the same as getDefaultProjectId', async () => {
it('getProjectId should work the same as getProjectId', async () => {
mockEnvVar(
'GOOGLE_APPLICATION_CREDENTIALS',
path.join(__dirname, '../../test/fixtures/private2.json'));
const projectId = await auth.getProjectId();
assert.equal(projectId, fixedProjectId);
});

it('getDefaultProjectId should use Cloud SDK when it is available and env vars are not set',
it('getProjectId should use Cloud SDK when it is available and env vars are not set',
async () => {
// Set up the creds.
// * Environment variable is not set.
Expand All @@ -842,19 +842,19 @@ it('getDefaultProjectId should use Cloud SDK when it is available and env vars a
{configuration: {properties: {core: {project: fixedProjectId}}}});
const stub = sandbox.stub(child_process, 'exec')
.callsArgWith(1, null, stdout, null);
const projectId = await auth.getDefaultProjectId();
const projectId = await auth.getProjectId();
assert(stub.calledOnce);
assert.equal(projectId, fixedProjectId);
});

it('getDefaultProjectId should use GCE when well-known file and env const are not set',
it('getProjectId should use GCE when well-known file and env const are not set',
async () => {
blockGoogleApplicationCredentialEnvironmentVariable();
sandbox = sinon.createSandbox();
const stub =
sandbox.stub(child_process, 'exec').callsArgWith(1, null, '', null);
const scope = createGetProjectIdNock(fixedProjectId);
const projectId = await auth.getDefaultProjectId();
const projectId = await auth.getProjectId();
assert(stub.calledOnce);
assert.equal(projectId, fixedProjectId);
scope.done();
Expand Down Expand Up @@ -1389,14 +1389,34 @@ it('should warn the user if using default Cloud SDK credentials', done => {
return Promise.resolve(new JWT(CLOUD_SDK_CLIENT_ID));
};
let warned = false;
process.on('warning', (warning) => {
assert.equal(warning.message, messages.PROBLEMATIC_CREDENTIALS_WARNING);
const onWarning = (warning: Error) => {
assert.equal(
warning.message, messages.PROBLEMATIC_CREDENTIALS_WARNING.message);
warned = true;
});
process.removeListener('warning', onWarning);
};
process.on('warning', onWarning);
auth._tryGetApplicationCredentialsFromWellKnownFile().then(() => {
setImmediate(() => {
assert(warned);
done();
});
});
});

it('should warn the user if using the getDefaultProjectId method', done => {
mockEnvVar('GCLOUD_PROJECT', fixedProjectId);
let warned = false;
const onWarning = (warning: Error) => {
assert.equal(
warning.message, messages.DEFAULT_PROJECT_ID_DEPRECATED.message);
warned = true;
process.removeListener('warning', onWarning);
};
process.on('warning', onWarning);
auth.getDefaultProjectId().then(projectId => {
assert.equal(projectId, fixedProjectId);
assert(warned);
done();
});
});