Skip to content
Snippets Groups Projects
Commit 8c3ed009 authored by murgatroid99's avatar murgatroid99
Browse files

Added auth functionality and interop tests

parent 47b30b0b
No related branches found
No related tags found
No related merge requests found
...@@ -73,6 +73,36 @@ function load(filename) { ...@@ -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; ...@@ -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'); ...@@ -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) { ...@@ -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 = { ...@@ -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 { ...@@ -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 { ...@@ -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 @@ ...@@ -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) { ...@@ -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) { ...@@ -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) { ...@@ -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) { ...@@ -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) { ...@@ -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) { ...@@ -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;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment