diff options
author | murgatroid99 <mlumish@google.com> | 2015-02-18 15:00:56 -0800 |
---|---|---|
committer | murgatroid99 <mlumish@google.com> | 2015-02-18 15:00:56 -0800 |
commit | 8c3ed009a835e01dcee690cecaeeaf7fecf3e98e (patch) | |
tree | 5671d82ee4371a66346d208a89b9dba9576f01c0 /src/node | |
parent | 47b30b0b26e676d78e2683d1f9bb710ab237f138 (diff) |
Added auth functionality and interop tests
Diffstat (limited to 'src/node')
-rw-r--r-- | src/node/index.js | 32 | ||||
-rw-r--r-- | src/node/interop/interop_client.js | 48 | ||||
-rw-r--r-- | src/node/interop/messages.proto | 10 | ||||
-rw-r--r-- | src/node/package.json | 3 | ||||
-rw-r--r-- | src/node/src/client.js | 193 |
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; |