credentials_test.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  1. /*
  2. *
  3. * Copyright 2015 gRPC authors.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. *
  17. */
  18. 'use strict';
  19. var assert = require('assert');
  20. var fs = require('fs');
  21. var path = require('path');
  22. var grpc = require('..');
  23. /**
  24. * This is used for testing functions with multiple asynchronous calls that
  25. * can happen in different orders. This should be passed the number of async
  26. * function invocations that can occur last, and each of those should call this
  27. * function's return value
  28. * @param {function()} done The function that should be called when a test is
  29. * complete.
  30. * @param {number} count The number of calls to the resulting function if the
  31. * test passes.
  32. * @return {function()} The function that should be called at the end of each
  33. * sequence of asynchronous functions.
  34. */
  35. function multiDone(done, count) {
  36. return function() {
  37. count -= 1;
  38. if (count <= 0) {
  39. done();
  40. }
  41. };
  42. }
  43. var fakeSuccessfulGoogleCredentials = {
  44. getRequestMetadata: function(service_url, callback) {
  45. setTimeout(function() {
  46. callback(null, {Authorization: 'success'});
  47. }, 0);
  48. }
  49. };
  50. var fakeFailingGoogleCredentials = {
  51. getRequestMetadata: function(service_url, callback) {
  52. setTimeout(function() {
  53. // Google credentials currently adds string error codes to auth errors
  54. var error = new Error('Authentication failure');
  55. error.code = 'ENOENT';
  56. callback(error);
  57. }, 0);
  58. }
  59. };
  60. var key_data, pem_data, ca_data;
  61. before(function() {
  62. var key_path = path.join(__dirname, './data/server1.key');
  63. var pem_path = path.join(__dirname, './data/server1.pem');
  64. var ca_path = path.join(__dirname, '../test/data/ca.pem');
  65. key_data = fs.readFileSync(key_path);
  66. pem_data = fs.readFileSync(pem_path);
  67. ca_data = fs.readFileSync(ca_path);
  68. });
  69. describe('channel credentials', function() {
  70. describe('#createSsl', function() {
  71. it('works with no arguments', function() {
  72. var creds;
  73. assert.doesNotThrow(function() {
  74. creds = grpc.credentials.createSsl();
  75. });
  76. assert.notEqual(creds, null);
  77. });
  78. it('works with just one Buffer argument', function() {
  79. var creds;
  80. assert.doesNotThrow(function() {
  81. creds = grpc.credentials.createSsl(ca_data);
  82. });
  83. assert.notEqual(creds, null);
  84. });
  85. it('works with 3 Buffer arguments', function() {
  86. var creds;
  87. assert.doesNotThrow(function() {
  88. creds = grpc.credentials.createSsl(ca_data, key_data, pem_data);
  89. });
  90. assert.notEqual(creds, null);
  91. });
  92. it('works if the first argument is null', function() {
  93. var creds;
  94. assert.doesNotThrow(function() {
  95. creds = grpc.credentials.createSsl(null, key_data, pem_data);
  96. });
  97. assert.notEqual(creds, null);
  98. });
  99. it('fails if the first argument is a non-Buffer value', function() {
  100. assert.throws(function() {
  101. grpc.credentials.createSsl('test');
  102. }, TypeError);
  103. });
  104. it('fails if the second argument is a non-Buffer value', function() {
  105. assert.throws(function() {
  106. grpc.credentials.createSsl(null, 'test', pem_data);
  107. }, TypeError);
  108. });
  109. it('fails if the third argument is a non-Buffer value', function() {
  110. assert.throws(function() {
  111. grpc.credentials.createSsl(null, key_data, 'test');
  112. }, TypeError);
  113. });
  114. it('fails if only 1 of the last 2 arguments is provided', function() {
  115. assert.throws(function() {
  116. grpc.credentials.createSsl(null, key_data);
  117. });
  118. assert.throws(function() {
  119. grpc.credentials.createSsl(null, null, pem_data);
  120. });
  121. });
  122. });
  123. });
  124. describe('server credentials', function() {
  125. describe('#createSsl', function() {
  126. it('accepts a buffer and array as the first 2 arguments', function() {
  127. var creds;
  128. assert.doesNotThrow(function() {
  129. creds = grpc.ServerCredentials.createSsl(ca_data, []);
  130. });
  131. assert.notEqual(creds, null);
  132. });
  133. it('accepts a boolean as the third argument', function() {
  134. var creds;
  135. assert.doesNotThrow(function() {
  136. creds = grpc.ServerCredentials.createSsl(ca_data, [], true);
  137. });
  138. assert.notEqual(creds, null);
  139. });
  140. it('accepts an object with two buffers in the second argument', function() {
  141. var creds;
  142. assert.doesNotThrow(function() {
  143. creds = grpc.ServerCredentials.createSsl(null,
  144. [{private_key: key_data,
  145. cert_chain: pem_data}]);
  146. });
  147. assert.notEqual(creds, null);
  148. });
  149. it('accepts multiple objects in the second argument', function() {
  150. var creds;
  151. assert.doesNotThrow(function() {
  152. creds = grpc.ServerCredentials.createSsl(null,
  153. [{private_key: key_data,
  154. cert_chain: pem_data},
  155. {private_key: key_data,
  156. cert_chain: pem_data}]);
  157. });
  158. assert.notEqual(creds, null);
  159. });
  160. it('fails if the second argument is not an Array', function() {
  161. assert.throws(function() {
  162. grpc.ServerCredentials.createSsl(ca_data, 'test');
  163. }, TypeError);
  164. });
  165. it('fails if the first argument is a non-Buffer value', function() {
  166. assert.throws(function() {
  167. grpc.ServerCredentials.createSsl('test', []);
  168. }, TypeError);
  169. });
  170. it('fails if the third argument is a non-boolean value', function() {
  171. assert.throws(function() {
  172. grpc.ServerCredentials.createSsl(ca_data, [], 'test');
  173. }, TypeError);
  174. });
  175. it('fails if the array elements are not objects', function() {
  176. assert.throws(function() {
  177. grpc.ServerCredentials.createSsl(ca_data, 'test');
  178. }, TypeError);
  179. });
  180. it('fails if the object does not have a Buffer private_key', function() {
  181. assert.throws(function() {
  182. grpc.ServerCredentials.createSsl(null,
  183. [{private_key: 'test',
  184. cert_chain: pem_data}]);
  185. }, TypeError);
  186. });
  187. it('fails if the object does not have a Buffer cert_chain', function() {
  188. assert.throws(function() {
  189. grpc.ServerCredentials.createSsl(null,
  190. [{private_key: key_data,
  191. cert_chain: 'test'}]);
  192. }, TypeError);
  193. });
  194. });
  195. });
  196. describe('client credentials', function() {
  197. var Client;
  198. var server;
  199. var port;
  200. var client_ssl_creds;
  201. var client_options = {};
  202. before(function() {
  203. var proto = grpc.load(__dirname + '/test_service.proto');
  204. server = new grpc.Server();
  205. server.addService(proto.TestService.service, {
  206. unary: function(call, cb) {
  207. call.sendMetadata(call.metadata);
  208. cb(null, {});
  209. },
  210. clientStream: function(stream, cb){
  211. stream.on('data', function(data) {});
  212. stream.on('end', function() {
  213. stream.sendMetadata(stream.metadata);
  214. cb(null, {});
  215. });
  216. },
  217. serverStream: function(stream) {
  218. stream.sendMetadata(stream.metadata);
  219. stream.end();
  220. },
  221. bidiStream: function(stream) {
  222. stream.on('data', function(data) {});
  223. stream.on('end', function() {
  224. stream.sendMetadata(stream.metadata);
  225. stream.end();
  226. });
  227. }
  228. });
  229. var creds = grpc.ServerCredentials.createSsl(null,
  230. [{private_key: key_data,
  231. cert_chain: pem_data}]);
  232. port = server.bind('localhost:0', creds);
  233. server.start();
  234. Client = proto.TestService;
  235. client_ssl_creds = grpc.credentials.createSsl(ca_data);
  236. var host_override = 'foo.test.google.fr';
  237. client_options['grpc.ssl_target_name_override'] = host_override;
  238. client_options['grpc.default_authority'] = host_override;
  239. });
  240. after(function() {
  241. server.forceShutdown();
  242. });
  243. it('Should accept SSL creds for a client', function(done) {
  244. var client = new Client('localhost:' + port, client_ssl_creds,
  245. client_options);
  246. client.unary({}, function(err, data) {
  247. assert.ifError(err);
  248. done();
  249. });
  250. });
  251. it('Should update metadata with SSL creds', function(done) {
  252. var metadataUpdater = function(service_url, callback) {
  253. var metadata = new grpc.Metadata();
  254. metadata.set('plugin_key', 'plugin_value');
  255. callback(null, metadata);
  256. };
  257. var creds = grpc.credentials.createFromMetadataGenerator(metadataUpdater);
  258. var combined_creds = grpc.credentials.combineChannelCredentials(
  259. client_ssl_creds, creds);
  260. var client = new Client('localhost:' + port, combined_creds,
  261. client_options);
  262. var call = client.unary({}, function(err, data) {
  263. assert.ifError(err);
  264. });
  265. call.on('metadata', function(metadata) {
  266. assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']);
  267. done();
  268. });
  269. });
  270. it('Should update metadata for two simultaneous calls', function(done) {
  271. done = multiDone(done, 2);
  272. var metadataUpdater = function(service_url, callback) {
  273. var metadata = new grpc.Metadata();
  274. metadata.set('plugin_key', 'plugin_value');
  275. callback(null, metadata);
  276. };
  277. var creds = grpc.credentials.createFromMetadataGenerator(metadataUpdater);
  278. var combined_creds = grpc.credentials.combineChannelCredentials(
  279. client_ssl_creds, creds);
  280. var client = new Client('localhost:' + port, combined_creds,
  281. client_options);
  282. var call = client.unary({}, function(err, data) {
  283. assert.ifError(err);
  284. });
  285. call.on('metadata', function(metadata) {
  286. assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']);
  287. done();
  288. });
  289. var call2 = client.unary({}, function(err, data) {
  290. assert.ifError(err);
  291. });
  292. call2.on('metadata', function(metadata) {
  293. assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']);
  294. done();
  295. });
  296. });
  297. it('should propagate errors that the updater emits', function(done) {
  298. var metadataUpdater = function(service_url, callback) {
  299. var error = new Error('Authentication error');
  300. error.code = grpc.status.UNAUTHENTICATED;
  301. callback(error);
  302. };
  303. var creds = grpc.credentials.createFromMetadataGenerator(metadataUpdater);
  304. var combined_creds = grpc.credentials.combineChannelCredentials(
  305. client_ssl_creds, creds);
  306. var client = new Client('localhost:' + port, combined_creds,
  307. client_options);
  308. client.unary({}, function(err, data) {
  309. assert(err);
  310. assert.strictEqual(err.message,
  311. 'Getting metadata from plugin failed with error: ' +
  312. 'Authentication error');
  313. assert.strictEqual(err.code, grpc.status.UNAUTHENTICATED);
  314. done();
  315. });
  316. });
  317. it('should successfully wrap a Google credential', function(done) {
  318. var creds = grpc.credentials.createFromGoogleCredential(
  319. fakeSuccessfulGoogleCredentials);
  320. var combined_creds = grpc.credentials.combineChannelCredentials(
  321. client_ssl_creds, creds);
  322. var client = new Client('localhost:' + port, combined_creds,
  323. client_options);
  324. var call = client.unary({}, function(err, data) {
  325. assert.ifError(err);
  326. });
  327. call.on('metadata', function(metadata) {
  328. assert.deepEqual(metadata.get('authorization'), ['success']);
  329. done();
  330. });
  331. });
  332. it('Should not add metadata with just SSL credentials', function(done) {
  333. // Tests idempotency of credentials composition
  334. var metadataUpdater = function(service_url, callback) {
  335. var metadata = new grpc.Metadata();
  336. metadata.set('plugin_key', 'plugin_value');
  337. callback(null, metadata);
  338. };
  339. var creds = grpc.credentials.createFromMetadataGenerator(metadataUpdater);
  340. grpc.credentials.combineChannelCredentials(client_ssl_creds, creds);
  341. var client = new Client('localhost:' + port, client_ssl_creds,
  342. client_options);
  343. var call = client.unary({}, function(err, data) {
  344. assert.ifError(err);
  345. });
  346. call.on('metadata', function(metadata) {
  347. assert.deepEqual(metadata.get('plugin_key'), []);
  348. done();
  349. });
  350. });
  351. it('should get an error from a Google credential', function(done) {
  352. var creds = grpc.credentials.createFromGoogleCredential(
  353. fakeFailingGoogleCredentials);
  354. var combined_creds = grpc.credentials.combineChannelCredentials(
  355. client_ssl_creds, creds);
  356. var client = new Client('localhost:' + port, combined_creds,
  357. client_options);
  358. client.unary({}, function(err, data) {
  359. assert(err);
  360. assert.strictEqual(err.message,
  361. 'Getting metadata from plugin failed with error: ' +
  362. 'Authentication failure');
  363. done();
  364. });
  365. });
  366. describe('Per-rpc creds', function() {
  367. var client;
  368. var updater_creds;
  369. before(function() {
  370. client = new Client('localhost:' + port, client_ssl_creds,
  371. client_options);
  372. var metadataUpdater = function(service_url, callback) {
  373. var metadata = new grpc.Metadata();
  374. metadata.set('plugin_key', 'plugin_value');
  375. callback(null, metadata);
  376. };
  377. updater_creds = grpc.credentials.createFromMetadataGenerator(
  378. metadataUpdater);
  379. });
  380. it('Should update metadata on a unary call', function(done) {
  381. var call = client.unary({}, {credentials: updater_creds},
  382. function(err, data) {
  383. assert.ifError(err);
  384. });
  385. call.on('metadata', function(metadata) {
  386. assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']);
  387. done();
  388. });
  389. });
  390. it('should update metadata on a client streaming call', function(done) {
  391. var call = client.clientStream({credentials: updater_creds},
  392. function(err, data) {
  393. assert.ifError(err);
  394. });
  395. call.on('metadata', function(metadata) {
  396. assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']);
  397. done();
  398. });
  399. call.end();
  400. });
  401. it('should update metadata on a server streaming call', function(done) {
  402. var call = client.serverStream({}, {credentials: updater_creds});
  403. call.on('data', function() {});
  404. call.on('metadata', function(metadata) {
  405. assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']);
  406. done();
  407. });
  408. });
  409. it('should update metadata on a bidi streaming call', function(done) {
  410. var call = client.bidiStream({credentials: updater_creds});
  411. call.on('data', function() {});
  412. call.on('metadata', function(metadata) {
  413. assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']);
  414. done();
  415. });
  416. call.end();
  417. });
  418. it('should be able to use multiple plugin credentials', function(done) {
  419. var altMetadataUpdater = function(service_url, callback) {
  420. var metadata = new grpc.Metadata();
  421. metadata.set('other_plugin_key', 'other_plugin_value');
  422. callback(null, metadata);
  423. };
  424. var alt_updater_creds = grpc.credentials.createFromMetadataGenerator(
  425. altMetadataUpdater);
  426. var combined_updater = grpc.credentials.combineCallCredentials(
  427. updater_creds, alt_updater_creds);
  428. var call = client.unary({}, {credentials: combined_updater},
  429. function(err, data) {
  430. assert.ifError(err);
  431. });
  432. call.on('metadata', function(metadata) {
  433. assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']);
  434. assert.deepEqual(metadata.get('other_plugin_key'),
  435. ['other_plugin_value']);
  436. done();
  437. });
  438. });
  439. });
  440. });