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

Support un-authenticated requests. #2712

Merged
Show file tree
Hide file tree
Changes from all 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
17 changes: 10 additions & 7 deletions packages/common/src/service.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,22 +48,25 @@ var PROJECT_ID_TOKEN = '{{projectId}}';
function Service(config, options) {
options = options || {};

this.baseUrl = config.baseUrl;
this.globalInterceptors = arrify(options.interceptors_);
this.interceptors = [];
this.packageJson = config.packageJson;
this.projectId = options.projectId || PROJECT_ID_TOKEN;
this.projectIdRequired = config.projectIdRequired !== false;
this.Promise = options.promise || Promise;

var reqCfg = extend({}, config, {
projectIdRequired: this.projectIdRequired,
projectId: this.projectId,
credentials: options.credentials,
keyFile: options.keyFilename,
email: options.email
});

this.makeAuthenticatedRequest = util.makeAuthenticatedRequestFactory(reqCfg);
this.authClient = this.makeAuthenticatedRequest.authClient;
this.baseUrl = config.baseUrl;
this.getCredentials = this.makeAuthenticatedRequest.getCredentials;
this.globalInterceptors = arrify(options.interceptors_);
this.interceptors = [];
this.packageJson = config.packageJson;
this.projectId = options.projectId || PROJECT_ID_TOKEN;
this.projectIdRequired = config.projectIdRequired !== false;
this.Promise = options.promise || Promise;

var isCloudFunctionEnv = !!process.env.FUNCTION_NAME;

Expand Down
39 changes: 28 additions & 11 deletions packages/common/src/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -346,14 +346,33 @@ function makeAuthenticatedRequestFactory(config) {
}

function onAuthenticated(err, authenticatedReqOpts) {
if (!err) {
var autoAuthFailed =
err &&
err.message.indexOf('Could not load the default credentials') > -1;

if (autoAuthFailed) {
// Even though authentication failed, the API might not actually care.
authenticatedReqOpts = reqOpts;
}

if (!err || autoAuthFailed) {
var projectId = authClient.projectId;

if (config.projectId && config.projectId !== '{{projectId}}') {
projectId = config.projectId;
}

try {
authenticatedReqOpts = util.decorateRequest(
authenticatedReqOpts,
extend({ projectId: authClient.projectId }, config)
projectId
);
err = null;
} catch(e) {
err = e;
// A projectId was required, but we don't have one.
// Re-use the "Could not load the default credentials error" if auto
// auth failed.
err = err || e;
}
}

Expand Down Expand Up @@ -473,29 +492,27 @@ util.makeRequest = makeRequest;
* Decorate the options about to be made in a request.
*
* @param {object} reqOpts - The options to be passed to `request`.
* @param {object} config - Service config.
* @param {string} projectId - The project ID.
* @return {object} reqOpts - The decorated reqOpts.
*/
function decorateRequest(reqOpts, config) {
config = config || {};

function decorateRequest(reqOpts, projectId) {
delete reqOpts.autoPaginate;
delete reqOpts.autoPaginateVal;
delete reqOpts.objectMode;

if (is.object(reqOpts.qs)) {
delete reqOpts.qs.autoPaginate;
delete reqOpts.qs.autoPaginateVal;
reqOpts.qs = util.replaceProjectIdToken(reqOpts.qs, config.projectId);
reqOpts.qs = util.replaceProjectIdToken(reqOpts.qs, projectId);
}

if (is.object(reqOpts.json)) {
delete reqOpts.json.autoPaginate;
delete reqOpts.json.autoPaginateVal;
reqOpts.json = util.replaceProjectIdToken(reqOpts.json, config.projectId);
reqOpts.json = util.replaceProjectIdToken(reqOpts.json, projectId);
}

reqOpts.uri = util.replaceProjectIdToken(reqOpts.uri, config.projectId);
reqOpts.uri = util.replaceProjectIdToken(reqOpts.uri, projectId);

return reqOpts;
}
Expand Down Expand Up @@ -528,7 +545,7 @@ function replaceProjectIdToken(value, projectId) {
}

if (is.string(value) && value.indexOf('{{projectId}}') > -1) {
if (!projectId) {
if (!projectId || projectId === '{{projectId}}') {
throw util.missingProjectIdError;
}
value = value.replace(/{{projectId}}/g, projectId);
Expand Down
4 changes: 3 additions & 1 deletion packages/common/test/service.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,9 @@ describe('Service', function() {
var expectedConfig = extend({}, CONFIG, {
credentials: OPTIONS.credentials,
keyFile: OPTIONS.keyFilename,
email: OPTIONS.email
email: OPTIONS.email,
projectIdRequired: CONFIG.projectIdRequired,
projectId: OPTIONS.projectId
});

assert.deepEqual(config, expectedConfig);
Expand Down
116 changes: 95 additions & 21 deletions packages/common/test/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -709,7 +709,7 @@ describe('common/util', function() {
var config = {
customEndpoint: true
};
var expectedConfig = extend({ projectId: authClient.projectId }, config);
var expectedProjectId = authClient.projectId;

beforeEach(function() {
makeAuthenticatedRequest = util.makeAuthenticatedRequestFactory(config);
Expand All @@ -719,9 +719,9 @@ describe('common/util', function() {
var reqOpts = { a: 'b', c: 'd' };
var decoratedRequest = {};

utilOverrides.decorateRequest = function(reqOpts_, config_) {
utilOverrides.decorateRequest = function(reqOpts_, projectId) {
assert.strictEqual(reqOpts_, reqOpts);
assert.deepEqual(config_, expectedConfig);
assert.deepEqual(projectId, expectedProjectId);
return decoratedRequest;
};

Expand Down Expand Up @@ -793,6 +793,47 @@ describe('common/util', function() {
assert(makeAuthenticatedRequest({}) instanceof stream.Stream);
});

describe('projectId', function() {
it('should default to authClient projectId', function(done) {
authClient.projectId = 'authclient-project-id';

utilOverrides.decorateRequest = function(reqOpts, projectId) {
assert.strictEqual(projectId, authClient.projectId);
setImmediate(done);
};

var makeAuthenticatedRequest = util.makeAuthenticatedRequestFactory({
customEndpoint: true
});

makeAuthenticatedRequest({}, {
onAuthenticated: assert.ifError
});
});

it('should use user-provided projectId', function(done) {
authClient.projectId = 'authclient-project-id';

var config = {
customEndpoint: true,
projectId: 'project-id'
};

utilOverrides.decorateRequest = function(reqOpts, projectId) {
assert.strictEqual(projectId, config.projectId);
setImmediate(done);
};

var makeAuthenticatedRequest = util.makeAuthenticatedRequestFactory(
config
);

makeAuthenticatedRequest({}, {
onAuthenticated: assert.ifError
});
});
});

describe('authentication errors', function() {
var error = new Error('Error.');

Expand All @@ -804,6 +845,45 @@ describe('common/util', function() {
};
});

it('should attempt request anyway', function(done) {
var makeAuthenticatedRequest = util.makeAuthenticatedRequestFactory();

var correctReqOpts = {};
var incorrectReqOpts = {};

authClient.authorizeRequest = function(rOpts, callback) {
var error = new Error('Could not load the default credentials');
callback(error, incorrectReqOpts);
};

makeAuthenticatedRequest(correctReqOpts, {
onAuthenticated: function(err, reqOpts) {
assert.ifError(err);

assert.strictEqual(reqOpts, correctReqOpts);
assert.notStrictEqual(reqOpts, incorrectReqOpts);

done();
}
});
});

it('should block decorateRequest error', function(done) {
var decorateRequestError = new Error('Error.');
utilOverrides.decorateRequest = function() {
throw decorateRequestError;
};

var makeAuthenticatedRequest = util.makeAuthenticatedRequestFactory();
makeAuthenticatedRequest({}, {
onAuthenticated: function(err) {
assert.notStrictEqual(err, decorateRequestError);
assert.strictEqual(err, error);
done();
}
});
});

it('should invoke the callback with error', function(done) {
var makeAuthenticatedRequest = util.makeAuthenticatedRequestFactory();
makeAuthenticatedRequest({}, function(err) {
Expand Down Expand Up @@ -1281,64 +1361,58 @@ describe('common/util', function() {
});

it('should replace project ID tokens for qs object', function() {
var config = {
projectId: 'project-id'
};
var projectId = 'project-id';
var reqOpts = {
uri: 'http://',
qs: {}
};
var decoratedQs = {};

utilOverrides.replaceProjectIdToken = function(qs, projectId) {
utilOverrides.replaceProjectIdToken = function(qs, projectId_) {
utilOverrides = {};
assert.strictEqual(qs, reqOpts.qs);
assert.strictEqual(projectId, config.projectId);
assert.strictEqual(projectId_, projectId);
return decoratedQs;
};

var decoratedRequest = util.decorateRequest(reqOpts, config);
var decoratedRequest = util.decorateRequest(reqOpts, projectId);
assert.strictEqual(decoratedRequest.qs, decoratedQs);
});

it('should replace project ID tokens for json object', function() {
var config = {
projectId: 'project-id'
};
var projectId = 'project-id';
var reqOpts = {
uri: 'http://',
json: {}
};
var decoratedJson = {};

utilOverrides.replaceProjectIdToken = function(json, projectId) {
utilOverrides.replaceProjectIdToken = function(json, projectId_) {
utilOverrides = {};
assert.strictEqual(reqOpts.json, json);
assert.strictEqual(projectId, config.projectId);
assert.strictEqual(projectId_, projectId);
return decoratedJson;
};

var decoratedRequest = util.decorateRequest(reqOpts, config);
var decoratedRequest = util.decorateRequest(reqOpts, projectId);
assert.strictEqual(decoratedRequest.json, decoratedJson);
});

it('should decorate the request', function() {
var config = {
projectId: 'project-id'
};
var projectId = 'project-id';
var reqOpts = {
uri: 'http://'
};
var decoratedUri = 'http://decorated';

utilOverrides.replaceProjectIdToken = function(uri, projectId) {
utilOverrides.replaceProjectIdToken = function(uri, projectId_) {
assert.strictEqual(uri, reqOpts.uri);
assert.strictEqual(projectId, config.projectId);
assert.strictEqual(projectId_, projectId);
return decoratedUri;
};

assert.deepEqual(
util.decorateRequest(reqOpts, config),
util.decorateRequest(reqOpts, projectId),
{ uri: decoratedUri }
);
});
Expand Down