aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/node
diff options
context:
space:
mode:
authorGravatar murgatroid99 <mlumish@google.com>2015-02-18 15:00:56 -0800
committerGravatar murgatroid99 <mlumish@google.com>2015-02-18 15:00:56 -0800
commit8c3ed009a835e01dcee690cecaeeaf7fecf3e98e (patch)
tree5671d82ee4371a66346d208a89b9dba9576f01c0 /src/node
parent47b30b0b26e676d78e2683d1f9bb710ab237f138 (diff)
Added auth functionality and interop tests
Diffstat (limited to 'src/node')
-rw-r--r--src/node/index.js32
-rw-r--r--src/node/interop/interop_client.js48
-rw-r--r--src/node/interop/messages.proto10
-rw-r--r--src/node/package.json3
-rw-r--r--src/node/src/client.js193
5 files changed, 206 insertions, 80 deletions
diff --git a/src/node/index.js b/src/node/index.js
index baef4d03c6..8b5b1ea9a6 100644
--- a/src/node/index.js
+++ b/src/node/index.js
@@ -74,6 +74,36 @@ function load(filename) {
}
/**
+ * Get a function that a client can use to update metadata with authentication
+ * information from a Google Auth credential object.
+ * @param {Object} credential The credential object to use
+ * @return {function(Object, callback)} Metadata updater function
+ */
+function getGoogleAuthDelegate(credential) {
+ /**
+ * Update a metadata object with authentication information.
+ * @param {Object} metadata Metadata object
+ * @param {function(Error, Object)} callback
+ */
+ return function updateMetadata(metadata, callback) {
+ metadata = _.clone(metadata);
+ if (metadata.Authorization) {
+ metadata.Authorization = _.clone(metadata.Authorization);
+ } else {
+ metadata.Authorization = [];
+ }
+ credential.getAccessToken(function(err, token) {
+ if (err) {
+ callback(err);
+ return;
+ }
+ metadata.Authorization.push('Bearer ' + token);
+ callback(null, metadata);
+ });
+ };
+}
+
+/**
* See docs for loadObject
*/
exports.loadObject = loadObject;
@@ -106,3 +136,5 @@ exports.Credentials = grpc.Credentials;
* ServerCredentials factories
*/
exports.ServerCredentials = grpc.ServerCredentials;
+
+exports.getGoogleAuthDelegate = getGoogleAuthDelegate;
diff --git a/src/node/interop/interop_client.js b/src/node/interop/interop_client.js
index 00284d0855..9a19a509f3 100644
--- a/src/node/interop/interop_client.js
+++ b/src/node/interop/interop_client.js
@@ -35,9 +35,14 @@ var fs = require('fs');
var path = require('path');
var grpc = require('..');
var testProto = grpc.load(__dirname + '/test.proto').grpc.testing;
+var GoogleAuth = require('googleauth');
var assert = require('assert');
+var AUTH_SCOPE = 'https://www.googleapis.com/auth/xapi.zoo';
+var AUTH_SCOPE_RESPONSE = 'xapi.zoo';
+var AUTH_USER = '155450119199-3psnrh1sdr3d8cpj1v46naggf81mhdnk@developer.gserviceaccount.com';
+
/**
* Create a buffer filled with size zeroes
* @param {number} size The length of the buffer
@@ -256,6 +261,45 @@ function cancelAfterFirstResponse(client, done) {
}
/**
+ * Run one of the authentication tests.
+ * @param {Client} client The client to test against
+ * @param {function} done Callback to call when the test is completed. Included
+ * primarily for use with mocha
+ */
+function authTest(client, done) {
+ (new GoogleAuth()).getApplicationDefault(function(err, credential) {
+ assert.ifError(err);
+ if (credential.createScopedRequired()) {
+ credential = credential.createScoped(AUTH_SCOPE);
+ }
+ client.updateMetadata = grpc.getGoogleAuthDelegate(credential);
+ var arg = {
+ response_type: testProto.PayloadType.COMPRESSABLE,
+ response_size: 314159,
+ payload: {
+ body: zeroBuffer(271828)
+ },
+ fill_username: true,
+ fill_oauth_scope: true
+ };
+ var call = client.unaryCall(arg, function(err, resp) {
+ assert.ifError(err);
+ assert.strictEqual(resp.payload.type, testProto.PayloadType.COMPRESSABLE);
+ assert.strictEqual(resp.payload.body.limit - resp.payload.body.offset,
+ 314159);
+ assert.strictEqual(resp.username, AUTH_USER);
+ assert.strictEqual(resp.oauth_scope, AUTH_SCOPE_RESPONSE);
+ });
+ call.on('status', function(status) {
+ assert.strictEqual(status.code, grpc.status.OK);
+ if (done) {
+ done();
+ }
+ });
+ });
+}
+
+/**
* Map from test case names to test functions
*/
var test_cases = {
@@ -266,7 +310,9 @@ var test_cases = {
ping_pong: pingPong,
empty_stream: emptyStream,
cancel_after_begin: cancelAfterBegin,
- cancel_after_first_response: cancelAfterFirstResponse
+ cancel_after_first_response: cancelAfterFirstResponse,
+ compute_engine_creds: authTest,
+ service_account_creds: authTest
};
/**
diff --git a/src/node/interop/messages.proto b/src/node/interop/messages.proto
index 29db0dd8b1..1d95154cf4 100644
--- a/src/node/interop/messages.proto
+++ b/src/node/interop/messages.proto
@@ -36,6 +36,12 @@ message SimpleRequest {
// Optional input payload sent along with the request.
optional Payload payload = 3;
+
+ // Whether SimpleResponse should include username.
+ optional bool fill_username = 4;
+
+ // Whether SimpleResponse should include OAuth scope.
+ optional bool fill_oauth_scope = 5;
}
// Unary response, as configured by the request.
@@ -44,7 +50,9 @@ message SimpleResponse {
optional Payload payload = 1;
// The user the request came from, for verifying authentication was
// successful when the client expected it.
- optional int64 effective_gaia_user_id = 2;
+ optional string username = 2;
+ // OAuth scope.
+ optional string oauth_scope = 3;
}
// Client-streaming request.
diff --git a/src/node/package.json b/src/node/package.json
index 8f81014c1e..821641ce19 100644
--- a/src/node/package.json
+++ b/src/node/package.json
@@ -14,7 +14,8 @@
},
"devDependencies": {
"mocha": "~1.21.0",
- "minimist": "^1.1.0"
+ "minimist": "^1.1.0",
+ "googleauth": "google/google-auth-library-nodejs"
},
"main": "index.js"
}
diff --git a/src/node/src/client.js b/src/node/src/client.js
index 81fa65eb26..19c3144c7d 100644
--- a/src/node/src/client.js
+++ b/src/node/src/client.js
@@ -224,25 +224,32 @@ function makeUnaryRequestFunction(method, serialize, deserialize) {
emitter.cancel = function cancel() {
call.cancel();
};
- var client_batch = {};
- client_batch[grpc.opType.SEND_INITIAL_METADATA] = metadata;
- client_batch[grpc.opType.SEND_MESSAGE] = serialize(argument);
- client_batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true;
- client_batch[grpc.opType.RECV_INITIAL_METADATA] = true;
- client_batch[grpc.opType.RECV_MESSAGE] = true;
- client_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true;
- call.startBatch(client_batch, function(err, response) {
- if (err) {
- callback(err);
+ this.updateMetadata(metadata, function(error, metadata) {
+ if (error) {
+ call.cancel();
+ callback(error);
return;
}
- if (response.status.code != grpc.status.OK) {
- callback(response.status);
- return;
- }
- emitter.emit('status', response.status);
- emitter.emit('metadata', response.metadata);
- callback(null, deserialize(response.read));
+ var client_batch = {};
+ client_batch[grpc.opType.SEND_INITIAL_METADATA] = metadata;
+ client_batch[grpc.opType.SEND_MESSAGE] = serialize(argument);
+ client_batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true;
+ client_batch[grpc.opType.RECV_INITIAL_METADATA] = true;
+ client_batch[grpc.opType.RECV_MESSAGE] = true;
+ client_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true;
+ call.startBatch(client_batch, function(err, response) {
+ if (err) {
+ callback(err);
+ return;
+ }
+ if (response.status.code != grpc.status.OK) {
+ callback(response.status);
+ return;
+ }
+ emitter.emit('status', response.status);
+ emitter.emit('metadata', response.metadata);
+ callback(null, deserialize(response.read));
+ });
});
return emitter;
}
@@ -279,30 +286,37 @@ function makeClientStreamRequestFunction(method, serialize, deserialize) {
metadata = {};
}
var stream = new ClientWritableStream(call, serialize);
- var metadata_batch = {};
- metadata_batch[grpc.opType.SEND_INITIAL_METADATA] = metadata;
- metadata_batch[grpc.opType.RECV_INITIAL_METADATA] = true;
- call.startBatch(metadata_batch, function(err, response) {
- if (err) {
- callback(err);
- return;
- }
- stream.emit('metadata', response.metadata);
- });
- var client_batch = {};
- client_batch[grpc.opType.RECV_MESSAGE] = true;
- client_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true;
- call.startBatch(client_batch, function(err, response) {
- if (err) {
- callback(err);
- return;
- }
- if (response.status.code != grpc.status.OK) {
- callback(response.status);
+ this.updateMetadata(metadata, function(error, metadata) {
+ if (error) {
+ call.cancel();
+ callback(error);
return;
}
- stream.emit('status', response.status);
- callback(null, deserialize(response.read));
+ var metadata_batch = {};
+ metadata_batch[grpc.opType.SEND_INITIAL_METADATA] = metadata;
+ metadata_batch[grpc.opType.RECV_INITIAL_METADATA] = true;
+ call.startBatch(metadata_batch, function(err, response) {
+ if (err) {
+ callback(err);
+ return;
+ }
+ stream.emit('metadata', response.metadata);
+ });
+ var client_batch = {};
+ client_batch[grpc.opType.RECV_MESSAGE] = true;
+ client_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true;
+ call.startBatch(client_batch, function(err, response) {
+ if (err) {
+ callback(err);
+ return;
+ }
+ if (response.status.code != grpc.status.OK) {
+ callback(response.status);
+ return;
+ }
+ stream.emit('status', response.status);
+ callback(null, deserialize(response.read));
+ });
});
return stream;
}
@@ -339,24 +353,31 @@ function makeServerStreamRequestFunction(method, serialize, deserialize) {
metadata = {};
}
var stream = new ClientReadableStream(call, deserialize);
- var start_batch = {};
- start_batch[grpc.opType.SEND_INITIAL_METADATA] = metadata;
- start_batch[grpc.opType.RECV_INITIAL_METADATA] = true;
- start_batch[grpc.opType.SEND_MESSAGE] = serialize(argument);
- start_batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true;
- call.startBatch(start_batch, function(err, response) {
- if (err) {
- throw err;
- }
- stream.emit('metadata', response.metadata);
- });
- var status_batch = {};
- status_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true;
- call.startBatch(status_batch, function(err, response) {
- if (err) {
- throw err;
+ this.updateMetadata(metadata, function(error, metadata) {
+ if (error) {
+ call.cancel();
+ stream.emit('error', error);
+ return;
}
- stream.emit('status', response.status);
+ var start_batch = {};
+ start_batch[grpc.opType.SEND_INITIAL_METADATA] = metadata;
+ start_batch[grpc.opType.RECV_INITIAL_METADATA] = true;
+ start_batch[grpc.opType.SEND_MESSAGE] = serialize(argument);
+ start_batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true;
+ call.startBatch(start_batch, function(err, response) {
+ if (err) {
+ throw err;
+ }
+ stream.emit('metadata', response.metadata);
+ });
+ var status_batch = {};
+ status_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true;
+ call.startBatch(status_batch, function(err, response) {
+ if (err) {
+ throw err;
+ }
+ stream.emit('status', response.status);
+ });
});
return stream;
}
@@ -391,22 +412,29 @@ function makeBidiStreamRequestFunction(method, serialize, deserialize) {
metadata = {};
}
var stream = new ClientDuplexStream(call, serialize, deserialize);
- var start_batch = {};
- start_batch[grpc.opType.SEND_INITIAL_METADATA] = metadata;
- start_batch[grpc.opType.RECV_INITIAL_METADATA] = true;
- call.startBatch(start_batch, function(err, response) {
- if (err) {
- throw err;
- }
- stream.emit('metadata', response.metadata);
- });
- var status_batch = {};
- status_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true;
- call.startBatch(status_batch, function(err, response) {
- if (err) {
- throw err;
+ this.updateMetadata(metadata, function(error, metadata) {
+ if (error) {
+ call.cancel();
+ stream.emit('error', error);
+ return;
}
- stream.emit('status', response.status);
+ var start_batch = {};
+ start_batch[grpc.opType.SEND_INITIAL_METADATA] = metadata;
+ start_batch[grpc.opType.RECV_INITIAL_METADATA] = true;
+ call.startBatch(start_batch, function(err, response) {
+ if (err) {
+ throw err;
+ }
+ stream.emit('metadata', response.metadata);
+ });
+ var status_batch = {};
+ status_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true;
+ call.startBatch(status_batch, function(err, response) {
+ if (err) {
+ throw err;
+ }
+ stream.emit('status', response.status);
+ });
});
return stream;
}
@@ -438,8 +466,17 @@ function makeClientConstructor(service) {
* @constructor
* @param {string} address The address of the server to connect to
* @param {Object} options Options to pass to the underlying channel
+ * @param {function(Object, function)=} updateMetadata function to update the
+ * metadata for each request
*/
- function Client(address, options) {
+ function Client(address, options, updateMetadata) {
+ if (updateMetadata) {
+ this.updateMetadata = updateMetadata;
+ } else {
+ this.updateMetadata = function(metadata, callback) {
+ callback(null, metadata);
+ };
+ }
this.channel = new grpc.Channel(address, options);
}
@@ -458,11 +495,13 @@ function makeClientConstructor(service) {
method_type = 'unary';
}
}
- Client.prototype[decapitalize(method.name)] =
- requester_makers[method_type](
- prefix + capitalize(method.name),
- common.serializeCls(method.resolvedRequestType.build()),
- common.deserializeCls(method.resolvedResponseType.build()));
+ var serialize = common.serializeCls(method.resolvedRequestType.build());
+ var deserialize = common.deserializeCls(
+ method.resolvedResponseType.build());
+ Client.prototype[decapitalize(method.name)] = requester_makers[method_type](
+ prefix + capitalize(method.name), serialize, deserialize);
+ Client.prototype[decapitalize(method.name)].serialize = serialize;
+ Client.prototype[decapitalize(method.name)].deserialize = deserialize;
});
Client.service = service;