| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457 | /* * * Copyright 2015, Google Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * *     * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. *     * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. *     * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */'use strict';var assert = require('assert');var fs = require('fs');var path = require('path');var grpc = require('..');/** * This is used for testing functions with multiple asynchronous calls that * can happen in different orders. This should be passed the number of async * function invocations that can occur last, and each of those should call this * function's return value * @param {function()} done The function that should be called when a test is *     complete. * @param {number} count The number of calls to the resulting function if the *     test passes. * @return {function()} The function that should be called at the end of each *     sequence of asynchronous functions. */function multiDone(done, count) {  return function() {    count -= 1;    if (count <= 0) {      done();    }  };}var fakeSuccessfulGoogleCredentials = {  getRequestMetadata: function(service_url, callback) {    setTimeout(function() {      callback(null, {Authorization: 'success'});    }, 0);  }};var fakeFailingGoogleCredentials = {  getRequestMetadata: function(service_url, callback) {    setTimeout(function() {      callback(new Error('Authentication failure'));    }, 0);  }};var key_data, pem_data, ca_data;before(function() {  var key_path = path.join(__dirname, './data/server1.key');  var pem_path = path.join(__dirname, './data/server1.pem');  var ca_path = path.join(__dirname, '../test/data/ca.pem');  key_data = fs.readFileSync(key_path);  pem_data = fs.readFileSync(pem_path);  ca_data = fs.readFileSync(ca_path);});describe('channel credentials', function() {  describe('#createSsl', function() {    it('works with no arguments', function() {      var creds;      assert.doesNotThrow(function() {        creds = grpc.credentials.createSsl();      });      assert.notEqual(creds, null);    });    it('works with just one Buffer argument', function() {      var creds;      assert.doesNotThrow(function() {        creds = grpc.credentials.createSsl(ca_data);      });      assert.notEqual(creds, null);    });    it('works with 3 Buffer arguments', function() {      var creds;      assert.doesNotThrow(function() {        creds = grpc.credentials.createSsl(ca_data, key_data, pem_data);      });      assert.notEqual(creds, null);    });    it('works if the first argument is null', function() {      var creds;      assert.doesNotThrow(function() {        creds = grpc.credentials.createSsl(null, key_data, pem_data);      });      assert.notEqual(creds, null);    });    it('fails if the first argument is a non-Buffer value', function() {      assert.throws(function() {        grpc.credentials.createSsl('test');      }, TypeError);    });    it('fails if the second argument is a non-Buffer value', function() {      assert.throws(function() {        grpc.credentials.createSsl(null, 'test', pem_data);      }, TypeError);    });    it('fails if the third argument is a non-Buffer value', function() {      assert.throws(function() {        grpc.credentials.createSsl(null, key_data, 'test');      }, TypeError);    });    it('fails if only 1 of the last 2 arguments is provided', function() {      assert.throws(function() {        grpc.credentials.createSsl(null, key_data);      });      assert.throws(function() {        grpc.credentials.createSsl(null, null, pem_data);      });    });  });});describe('server credentials', function() {  describe('#createSsl', function() {    it('accepts a buffer and array as the first 2 arguments', function() {      var creds;      assert.doesNotThrow(function() {        creds = grpc.ServerCredentials.createSsl(ca_data, []);      });      assert.notEqual(creds, null);    });    it('accepts a boolean as the third argument', function() {      var creds;      assert.doesNotThrow(function() {        creds = grpc.ServerCredentials.createSsl(ca_data, [], true);      });      assert.notEqual(creds, null);    });    it('accepts an object with two buffers in the second argument', function() {      var creds;      assert.doesNotThrow(function() {        creds = grpc.ServerCredentials.createSsl(null,                                                 [{private_key: key_data,                                                   cert_chain: pem_data}]);      });      assert.notEqual(creds, null);    });    it('accepts multiple objects in the second argument', function() {      var creds;      assert.doesNotThrow(function() {        creds = grpc.ServerCredentials.createSsl(null,                                                 [{private_key: key_data,                                                   cert_chain: pem_data},                                                  {private_key: key_data,                                                   cert_chain: pem_data}]);      });      assert.notEqual(creds, null);    });    it('fails if the second argument is not an Array', function() {      assert.throws(function() {        grpc.ServerCredentials.createSsl(ca_data, 'test');      }, TypeError);    });    it('fails if the first argument is a non-Buffer value', function() {      assert.throws(function() {        grpc.ServerCredentials.createSsl('test', []);      }, TypeError);    });    it('fails if the third argument is a non-boolean value', function() {      assert.throws(function() {        grpc.ServerCredentials.createSsl(ca_data, [], 'test');      }, TypeError);    });    it('fails if the array elements are not objects', function() {      assert.throws(function() {        grpc.ServerCredentials.createSsl(ca_data, 'test');      }, TypeError);    });    it('fails if the object does not have a Buffer private_key', function() {      assert.throws(function() {        grpc.ServerCredentials.createSsl(null,                                         [{private_key: 'test',                                           cert_chain: pem_data}]);      }, TypeError);    });    it('fails if the object does not have a Buffer cert_chain', function() {      assert.throws(function() {        grpc.ServerCredentials.createSsl(null,                                         [{private_key: key_data,                                           cert_chain: 'test'}]);      }, TypeError);    });  });});describe('client credentials', function() {  var Client;  var server;  var port;  var client_ssl_creds;  var client_options = {};  before(function() {    var proto = grpc.load(__dirname + '/test_service.proto');    server = new grpc.Server();    server.addProtoService(proto.TestService.service, {      unary: function(call, cb) {        call.sendMetadata(call.metadata);        cb(null, {});      },      clientStream: function(stream, cb){        stream.on('data', function(data) {});        stream.on('end', function() {          stream.sendMetadata(stream.metadata);          cb(null, {});        });      },      serverStream: function(stream) {        stream.sendMetadata(stream.metadata);        stream.end();      },      bidiStream: function(stream) {        stream.on('data', function(data) {});        stream.on('end', function() {          stream.sendMetadata(stream.metadata);          stream.end();        });      }    });    var creds = grpc.ServerCredentials.createSsl(null,                                                 [{private_key: key_data,                                                   cert_chain: pem_data}]);    port = server.bind('localhost:0', creds);    server.start();    Client = proto.TestService;    client_ssl_creds = grpc.credentials.createSsl(ca_data);    var host_override = 'foo.test.google.fr';    client_options['grpc.ssl_target_name_override'] = host_override;    client_options['grpc.default_authority'] = host_override;  });  after(function() {    server.forceShutdown();  });  it('Should accept SSL creds for a client', function(done) {    var client = new Client('localhost:' + port, client_ssl_creds,                            client_options);    client.unary({}, function(err, data) {      assert.ifError(err);      done();    });  });  it('Should update metadata with SSL creds', function(done) {    var metadataUpdater = function(service_url, callback) {      var metadata = new grpc.Metadata();      metadata.set('plugin_key', 'plugin_value');      callback(null, metadata);    };    var creds = grpc.credentials.createFromMetadataGenerator(metadataUpdater);    var combined_creds = grpc.credentials.combineChannelCredentials(        client_ssl_creds, creds);    var client = new Client('localhost:' + port, combined_creds,                            client_options);    var call = client.unary({}, function(err, data) {      assert.ifError(err);    });    call.on('metadata', function(metadata) {      assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']);      done();    });  });  it('Should update metadata for two simultaneous calls', function(done) {    done = multiDone(done, 2);    var metadataUpdater = function(service_url, callback) {      var metadata = new grpc.Metadata();      metadata.set('plugin_key', 'plugin_value');      callback(null, metadata);    };    var creds = grpc.credentials.createFromMetadataGenerator(metadataUpdater);    var combined_creds = grpc.credentials.combineChannelCredentials(        client_ssl_creds, creds);    var client = new Client('localhost:' + port, combined_creds,                            client_options);    var call = client.unary({}, function(err, data) {      assert.ifError(err);    });    call.on('metadata', function(metadata) {      assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']);      done();    });    var call2 = client.unary({}, function(err, data) {      assert.ifError(err);    });    call2.on('metadata', function(metadata) {      assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']);      done();    });  });  it.skip('should propagate errors that the updater emits', function(done) {    var metadataUpdater = function(service_url, callback) {      var error = new Error('Authentication error');      error.code = grpc.status.UNAUTHENTICATED;      callback(error);    };    var creds = grpc.credentials.createFromMetadataGenerator(metadataUpdater);    var combined_creds = grpc.credentials.combineChannelCredentials(        client_ssl_creds, creds);    var client = new Client('localhost:' + port, combined_creds,                            client_options);    client.unary({}, function(err, data) {      assert(err);      assert.strictEqual(err.message, 'Authentication error');      assert.strictEqual(err.code, grpc.status.UNAUTHENTICATED);      done();    });  });  it('should successfully wrap a Google credential', function(done) {    var creds = grpc.credentials.createFromGoogleCredential(        fakeSuccessfulGoogleCredentials);    var combined_creds = grpc.credentials.combineChannelCredentials(        client_ssl_creds, creds);    var client = new Client('localhost:' + port, combined_creds,                            client_options);    var call = client.unary({}, function(err, data) {      assert.ifError(err);    });    call.on('metadata', function(metadata) {      assert.deepEqual(metadata.get('authorization'), ['success']);      done();    });  });  it('Should not add metadata with just SSL credentials', function(done) {    // Tests idempotency of credentials composition    var metadataUpdater = function(service_url, callback) {      var metadata = new grpc.Metadata();      metadata.set('plugin_key', 'plugin_value');      callback(null, metadata);    };    var creds = grpc.credentials.createFromMetadataGenerator(metadataUpdater);    grpc.credentials.combineChannelCredentials(client_ssl_creds, creds);    var client = new Client('localhost:' + port, client_ssl_creds,                            client_options);    var call = client.unary({}, function(err, data) {      assert.ifError(err);    });    call.on('metadata', function(metadata) {      assert.deepEqual(metadata.get('plugin_key'), []);      done();    });  });  it.skip('should get an error from a Google credential', function(done) {    var creds = grpc.credentials.createFromGoogleCredential(        fakeFailingGoogleCredentials);    var combined_creds = grpc.credentials.combineChannelCredentials(        client_ssl_creds, creds);    var client = new Client('localhost:' + port, combined_creds,                            client_options);    client.unary({}, function(err, data) {      assert(err);      assert.strictEqual(err.message, 'Authentication failure');      done();    });  });  describe('Per-rpc creds', function() {    var client;    var updater_creds;    before(function() {      client = new Client('localhost:' + port, client_ssl_creds,                          client_options);      var metadataUpdater = function(service_url, callback) {        var metadata = new grpc.Metadata();        metadata.set('plugin_key', 'plugin_value');        callback(null, metadata);      };      updater_creds = grpc.credentials.createFromMetadataGenerator(          metadataUpdater);    });    it('Should update metadata on a unary call', function(done) {      var call = client.unary({}, function(err, data) {        assert.ifError(err);      }, null, {credentials: updater_creds});      call.on('metadata', function(metadata) {        assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']);        done();      });    });    it('should update metadata on a client streaming call', function(done) {      var call = client.clientStream(function(err, data) {        assert.ifError(err);      }, null, {credentials: updater_creds});      call.on('metadata', function(metadata) {        assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']);        done();      });      call.end();    });    it('should update metadata on a server streaming call', function(done) {      var call = client.serverStream({}, null, {credentials: updater_creds});      call.on('data', function() {});      call.on('metadata', function(metadata) {        assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']);        done();      });    });    it('should update metadata on a bidi streaming call', function(done) {      var call = client.bidiStream(null, {credentials: updater_creds});      call.on('data', function() {});      call.on('metadata', function(metadata) {        assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']);        done();      });      call.end();    });    it('should be able to use multiple plugin credentials', function(done) {      var altMetadataUpdater = function(service_url, callback) {        var metadata = new grpc.Metadata();        metadata.set('other_plugin_key', 'other_plugin_value');        callback(null, metadata);      };      var alt_updater_creds = grpc.credentials.createFromMetadataGenerator(          altMetadataUpdater);      var combined_updater = grpc.credentials.combineCallCredentials(          updater_creds, alt_updater_creds);      var call = client.unary({}, function(err, data) {        assert.ifError(err);      }, null, {credentials: combined_updater});      call.on('metadata', function(metadata) {        assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']);        assert.deepEqual(metadata.get('other_plugin_key'),                         ['other_plugin_value']);        done();      });    });  });});
 |