Added auth functionality and interop tests

pull/590/head
murgatroid99 10 years ago
parent 47b30b0b26
commit 8c3ed009a8
  1. 32
      src/node/index.js
  2. 48
      src/node/interop/interop_client.js
  3. 10
      src/node/interop/messages.proto
  4. 3
      src/node/package.json
  5. 193
      src/node/src/client.js

@ -73,6 +73,36 @@ function load(filename) {
return loadObject(builder.ns); return loadObject(builder.ns);
} }
/**
* 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 * See docs for loadObject
*/ */
@ -106,3 +136,5 @@ exports.Credentials = grpc.Credentials;
* ServerCredentials factories * ServerCredentials factories
*/ */
exports.ServerCredentials = grpc.ServerCredentials; exports.ServerCredentials = grpc.ServerCredentials;
exports.getGoogleAuthDelegate = getGoogleAuthDelegate;

@ -35,9 +35,14 @@ var fs = require('fs');
var path = require('path'); var path = require('path');
var grpc = require('..'); var grpc = require('..');
var testProto = grpc.load(__dirname + '/test.proto').grpc.testing; var testProto = grpc.load(__dirname + '/test.proto').grpc.testing;
var GoogleAuth = require('googleauth');
var assert = require('assert'); 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 * Create a buffer filled with size zeroes
* @param {number} size The length of the buffer * @param {number} size The length of the buffer
@ -255,6 +260,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 * Map from test case names to test functions
*/ */
@ -266,7 +310,9 @@ var test_cases = {
ping_pong: pingPong, ping_pong: pingPong,
empty_stream: emptyStream, empty_stream: emptyStream,
cancel_after_begin: cancelAfterBegin, cancel_after_begin: cancelAfterBegin,
cancel_after_first_response: cancelAfterFirstResponse cancel_after_first_response: cancelAfterFirstResponse,
compute_engine_creds: authTest,
service_account_creds: authTest
}; };
/** /**

@ -36,6 +36,12 @@ message SimpleRequest {
// Optional input payload sent along with the request. // Optional input payload sent along with the request.
optional Payload payload = 3; 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. // Unary response, as configured by the request.
@ -44,7 +50,9 @@ message SimpleResponse {
optional Payload payload = 1; optional Payload payload = 1;
// The user the request came from, for verifying authentication was // The user the request came from, for verifying authentication was
// successful when the client expected it. // 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. // Client-streaming request.

@ -14,7 +14,8 @@
}, },
"devDependencies": { "devDependencies": {
"mocha": "~1.21.0", "mocha": "~1.21.0",
"minimist": "^1.1.0" "minimist": "^1.1.0",
"googleauth": "google/google-auth-library-nodejs"
}, },
"main": "index.js" "main": "index.js"
} }

@ -224,25 +224,32 @@ function makeUnaryRequestFunction(method, serialize, deserialize) {
emitter.cancel = function cancel() { emitter.cancel = function cancel() {
call.cancel(); call.cancel();
}; };
var client_batch = {}; this.updateMetadata(metadata, function(error, metadata) {
client_batch[grpc.opType.SEND_INITIAL_METADATA] = metadata; if (error) {
client_batch[grpc.opType.SEND_MESSAGE] = serialize(argument); call.cancel();
client_batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true; callback(error);
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; return;
} }
if (response.status.code != grpc.status.OK) { var client_batch = {};
callback(response.status); client_batch[grpc.opType.SEND_INITIAL_METADATA] = metadata;
return; client_batch[grpc.opType.SEND_MESSAGE] = serialize(argument);
} client_batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true;
emitter.emit('status', response.status); client_batch[grpc.opType.RECV_INITIAL_METADATA] = true;
emitter.emit('metadata', response.metadata); client_batch[grpc.opType.RECV_MESSAGE] = true;
callback(null, deserialize(response.read)); 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; return emitter;
} }
@ -279,30 +286,37 @@ function makeClientStreamRequestFunction(method, serialize, deserialize) {
metadata = {}; metadata = {};
} }
var stream = new ClientWritableStream(call, serialize); var stream = new ClientWritableStream(call, serialize);
var metadata_batch = {}; this.updateMetadata(metadata, function(error, metadata) {
metadata_batch[grpc.opType.SEND_INITIAL_METADATA] = metadata; if (error) {
metadata_batch[grpc.opType.RECV_INITIAL_METADATA] = true; call.cancel();
call.startBatch(metadata_batch, function(err, response) { callback(error);
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; return;
} }
stream.emit('status', response.status); var metadata_batch = {};
callback(null, deserialize(response.read)); 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; return stream;
} }
@ -339,24 +353,31 @@ function makeServerStreamRequestFunction(method, serialize, deserialize) {
metadata = {}; metadata = {};
} }
var stream = new ClientReadableStream(call, deserialize); var stream = new ClientReadableStream(call, deserialize);
var start_batch = {}; this.updateMetadata(metadata, function(error, metadata) {
start_batch[grpc.opType.SEND_INITIAL_METADATA] = metadata; if (error) {
start_batch[grpc.opType.RECV_INITIAL_METADATA] = true; call.cancel();
start_batch[grpc.opType.SEND_MESSAGE] = serialize(argument); stream.emit('error', error);
start_batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true; return;
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); 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; return stream;
} }
@ -391,22 +412,29 @@ function makeBidiStreamRequestFunction(method, serialize, deserialize) {
metadata = {}; metadata = {};
} }
var stream = new ClientDuplexStream(call, serialize, deserialize); var stream = new ClientDuplexStream(call, serialize, deserialize);
var start_batch = {}; this.updateMetadata(metadata, function(error, metadata) {
start_batch[grpc.opType.SEND_INITIAL_METADATA] = metadata; if (error) {
start_batch[grpc.opType.RECV_INITIAL_METADATA] = true; call.cancel();
call.startBatch(start_batch, function(err, response) { stream.emit('error', error);
if (err) { return;
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); 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; return stream;
} }
@ -438,8 +466,17 @@ function makeClientConstructor(service) {
* @constructor * @constructor
* @param {string} address The address of the server to connect to * @param {string} address The address of the server to connect to
* @param {Object} options Options to pass to the underlying channel * @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); this.channel = new grpc.Channel(address, options);
} }
@ -458,11 +495,13 @@ function makeClientConstructor(service) {
method_type = 'unary'; method_type = 'unary';
} }
} }
Client.prototype[decapitalize(method.name)] = var serialize = common.serializeCls(method.resolvedRequestType.build());
requester_makers[method_type]( var deserialize = common.deserializeCls(
prefix + capitalize(method.name), method.resolvedResponseType.build());
common.serializeCls(method.resolvedRequestType.build()), Client.prototype[decapitalize(method.name)] = requester_makers[method_type](
common.deserializeCls(method.resolvedResponseType.build())); prefix + capitalize(method.name), serialize, deserialize);
Client.prototype[decapitalize(method.name)].serialize = serialize;
Client.prototype[decapitalize(method.name)].deserialize = deserialize;
}); });
Client.service = service; Client.service = service;

Loading…
Cancel
Save