@tdxvolt/volt-client-grpc 0.14.61 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,7 +10,8 @@ import {
10
10
  fetchVoltConfig,
11
11
  fetchVoltConfigFromDID,
12
12
  getVoltAPIClientInternal,
13
- unaryCall,
13
+ unaryCallInternal,
14
+ streamingCallInternal
14
15
  } from "./volt-client-internal.js";
15
16
  import fs from "fs";
16
17
  import GRPCCall from "./grpc-call.js";
@@ -35,6 +36,8 @@ export class VoltClient extends EventEmitter {
35
36
  this._config = null;
36
37
  this._voltConfig = null;
37
38
  this._voltConnection = null;
39
+ this._cachedService = {};
40
+ this._activeRPC = {};
38
41
  }
39
42
 
40
43
  get config() {
@@ -61,7 +64,7 @@ export class VoltClient extends EventEmitter {
61
64
  try {
62
65
  if (!config) {
63
66
  throw new Error(
64
- "configPath argument is required to be a non-empty string",
67
+ "configPath argument is required to be a non-empty string"
65
68
  );
66
69
  }
67
70
 
@@ -72,13 +75,13 @@ export class VoltClient extends EventEmitter {
72
75
  log("Attempt to load config from file %s", configPath);
73
76
  try {
74
77
  const configContents = await readFile(
75
- new URL(configPath, import.meta.url),
78
+ new URL(configPath, import.meta.url)
76
79
  );
77
80
  configJSON = JSON.parse(configContents);
78
81
  } catch (err) {
79
82
  log("failure loading config file %s [%s]", configPath, err.message);
80
83
  throw new Error(
81
- `failed to load configuration - check JSON format in ${configPath}`,
84
+ `failed to load configuration - check JSON format in ${configPath}`
82
85
  );
83
86
  }
84
87
  } else if (typeof config === "object") {
@@ -119,14 +122,14 @@ export class VoltClient extends EventEmitter {
119
122
  const didConfig = await fetchVoltConfigFromDID.call(this, voltDID);
120
123
  this._config = { ...this._config, volt: didConfig };
121
124
  log("resolved config from DID: %s", JSON.stringify(didConfig, null, 2));
122
- } else if (voltHttpAddress) {
125
+ } else if (voltHttpAddress && !this._config?.volt?.id) {
123
126
  const discoConfig = await fetchVoltConfig.call(
124
127
  this,
125
- `${voltHttpAddress}/discovery`,
128
+ `${voltHttpAddress}/discovery`
126
129
  );
127
130
  this._config = {
128
131
  ...this._config,
129
- volt: { ...this._config.volt, ...discoConfig },
132
+ volt: { ...this._config.volt, ...discoConfig }
130
133
  };
131
134
  }
132
135
 
@@ -170,7 +173,7 @@ export class VoltClient extends EventEmitter {
170
173
  if (bindErr.message === "invalid arguments") {
171
174
  // This is usually the result of a missing challenge signature or credential.
172
175
  log(
173
- "failure binding to Volt - did you supply a challenge code or verified credential?",
176
+ "failure binding to Volt - did you supply a challenge code or verified credential?"
174
177
  );
175
178
  throw bindErr;
176
179
  }
@@ -233,7 +236,7 @@ export class VoltClient extends EventEmitter {
233
236
  if (this.isRemote) {
234
237
  if (!this._voltConfig?.relay?.address) {
235
238
  throw new Error(
236
- "Remote connection enabled but no Relay address - have you called start()?",
239
+ "Remote connection enabled but no Relay address - have you called start()?"
237
240
  );
238
241
  }
239
242
 
@@ -245,14 +248,14 @@ export class VoltClient extends EventEmitter {
245
248
  this._credential,
246
249
  false,
247
250
  service.volt_id,
248
- service.id,
251
+ service.id
249
252
  );
250
253
  } else {
251
254
  return createGrpcClient(
252
255
  this._grpc,
253
256
  serviceDescriptors,
254
257
  service.service_description.address,
255
- this._credential,
258
+ this._credential
256
259
  );
257
260
  }
258
261
  }
@@ -280,6 +283,40 @@ export class VoltClient extends EventEmitter {
280
283
  return getVoltAPIClientInternal.call(this);
281
284
  }
282
285
 
286
+ unaryCall(method, request, service) {
287
+ return unaryCallInternal.call(this, method, request, service);
288
+ }
289
+
290
+ bidiStreamingCall(method, request, service) {
291
+ return streamingCallInternal.call(
292
+ this,
293
+ "METHOD_TYPE_BIDI",
294
+ method,
295
+ request,
296
+ service
297
+ );
298
+ }
299
+
300
+ clientStreamingCall(method, request, service) {
301
+ return streamingCallInternal.call(
302
+ this,
303
+ "METHOD_TYPE_CLIENT_STREAM",
304
+ method,
305
+ request,
306
+ service
307
+ );
308
+ }
309
+
310
+ serverStreamingCall(method, request, service) {
311
+ return streamingCallInternal.call(
312
+ this,
313
+ "METHOD_TYPE_SERVER_STREAM",
314
+ method,
315
+ request,
316
+ service
317
+ );
318
+ }
319
+
283
320
  /**
284
321
  * Request resource access.
285
322
  * @param {*} targetResourceId
@@ -287,12 +324,12 @@ export class VoltClient extends EventEmitter {
287
324
  */
288
325
  async RequestAccessBlocking(
289
326
  targetResourceId,
290
- accessType = "VOLT_ACCESS_READ",
327
+ accessType = "VOLT_ACCESS_READ"
291
328
  ) {
292
329
  try {
293
330
  const accessRequest = {
294
331
  access: accessType,
295
- resource_id: targetResourceId,
332
+ resource_id: targetResourceId
296
333
  };
297
334
 
298
335
  // This will block while the request is pending.
@@ -324,47 +361,47 @@ export class VoltClient extends EventEmitter {
324
361
  */
325
362
 
326
363
  CanAccessResource(request) {
327
- return unaryCall.call(this, "CanAccessResource", request);
364
+ return unaryCallInternal.call(this, "CanAccessResource", request);
328
365
  }
329
366
 
330
367
  DeleteResource(request) {
331
- return unaryCall.call(this, "DeleteResource", request);
368
+ return unaryCallInternal.call(this, "DeleteResource", request);
332
369
  }
333
370
 
334
371
  DiscoverServices(request) {
335
- return unaryCall.call(this, "DiscoverServices", request);
372
+ return unaryCallInternal.call(this, "DiscoverServices", request);
336
373
  }
337
374
 
338
375
  GetResource(request) {
339
- return unaryCall.call(this, "GetResource", request);
376
+ return unaryCallInternal.call(this, "GetResource", request);
340
377
  }
341
378
 
342
379
  GetResources(request) {
343
- return unaryCall.call(this, "GetResources", request);
380
+ return unaryCallInternal.call(this, "GetResources", request);
344
381
  }
345
382
 
346
383
  GetResourceAncestors(request) {
347
- return unaryCall.call(this, "GetResourceAncestors", request);
384
+ return unaryCallInternal.call(this, "GetResourceAncestors", request);
348
385
  }
349
386
 
350
387
  GetResourceDescendants(request) {
351
- return unaryCall.call(this, "GetResourceDescendants", request);
388
+ return unaryCallInternal.call(this, "GetResourceDescendants", request);
352
389
  }
353
390
 
354
391
  RequestAccess(request) {
355
- return unaryCall.call(this, "RequestAccess", request);
392
+ return unaryCallInternal.call(this, "RequestAccess", request);
356
393
  }
357
394
 
358
395
  SaveResource(request) {
359
- return unaryCall.call(this, "SaveResource", request);
396
+ return unaryCallInternal.call(this, "SaveResource", request);
360
397
  }
361
398
 
362
399
  SaveResourceAttribute(request) {
363
- return unaryCall.call(this, "SaveResourceAttribute", request);
400
+ return unaryCallInternal.call(this, "SaveResourceAttribute", request);
364
401
  }
365
402
 
366
403
  SetServiceStatus(request) {
367
- return unaryCall.call(this, "SetServiceStatus", request);
404
+ return unaryCallInternal.call(this, "SetServiceStatus", request);
368
405
  }
369
406
 
370
407
  /**
@@ -372,82 +409,82 @@ export class VoltClient extends EventEmitter {
372
409
  */
373
410
 
374
411
  Bind(request) {
375
- return unaryCall.call(this, "Bind", request);
412
+ return unaryCallInternal.call(this, "Bind", request);
376
413
  }
377
414
 
378
415
  DeleteAccess(request) {
379
- return unaryCall.call(this, "DeleteAccess", request);
416
+ return unaryCallInternal.call(this, "DeleteAccess", request);
380
417
  }
381
418
 
382
419
  DeleteVolt(request) {
383
- return unaryCall.call(this, "DeleteVolt", request);
420
+ return unaryCallInternal.call(this, "DeleteVolt", request);
384
421
  }
385
422
 
386
423
  GetAccess(request) {
387
- return unaryCall.call(this, "GetAccess", request);
424
+ return unaryCallInternal.call(this, "GetAccess", request);
388
425
  }
389
426
 
390
427
  GetBindings(request) {
391
- return unaryCall.call(this, "GetBindings", request);
428
+ return unaryCallInternal.call(this, "GetBindings", request);
392
429
  }
393
430
 
394
431
  GetIdentities(request) {
395
- return unaryCall.call(this, "GetIdentities", request);
432
+ return unaryCallInternal.call(this, "GetIdentities", request);
396
433
  }
397
434
 
398
435
  GetIdentity(request) {
399
- return unaryCall.call(this, "GetIdentity", request);
436
+ return unaryCallInternal.call(this, "GetIdentity", request);
400
437
  }
401
438
 
402
439
  GetIdentityToken(request) {
403
- return unaryCall.call(this, "GetIdentityToken", request);
440
+ return unaryCallInternal.call(this, "GetIdentityToken", request);
404
441
  }
405
442
 
406
443
  GetPolicy(request) {
407
- return unaryCall.call(this, "GetPolicy", request);
444
+ return unaryCallInternal.call(this, "GetPolicy", request);
408
445
  }
409
446
 
410
447
  GetSettings(request) {
411
- return unaryCall.call(this, "GetSettings", request);
448
+ return unaryCallInternal.call(this, "GetSettings", request);
412
449
  }
413
450
 
414
451
  SaveAccess(request) {
415
- return unaryCall.call(this, "SaveAccess", request);
452
+ return unaryCallInternal.call(this, "SaveAccess", request);
416
453
  }
417
454
 
418
455
  SaveCloudConnection(request) {
419
- return unaryCall.call(this, "SaveCloudConnection", request);
456
+ return unaryCallInternal.call(this, "SaveCloudConnection", request);
420
457
  }
421
458
 
422
459
  SaveIdentity(request) {
423
- return unaryCall.call(this, "SaveIdentity", request);
460
+ return unaryCallInternal.call(this, "SaveIdentity", request);
424
461
  }
425
462
 
426
463
  SaveSettings(request) {
427
- return unaryCall.call(this, "SaveSettings", request);
464
+ return unaryCallInternal.call(this, "SaveSettings", request);
428
465
  }
429
466
 
430
467
  SetAccessRequestDecision(request) {
431
- return unaryCall.call(this, "SetAccessRequestDecision", request);
468
+ return unaryCallInternal.call(this, "SetAccessRequestDecision", request);
432
469
  }
433
470
 
434
471
  SetBindingDecision(request) {
435
- return unaryCall.call(this, "SetBindingDecision", request);
472
+ return unaryCallInternal.call(this, "SetBindingDecision", request);
436
473
  }
437
474
 
438
475
  Shutdown(request) {
439
- return unaryCall.call(this, "Shutdown", request);
476
+ return unaryCallInternal.call(this, "Shutdown", request);
440
477
  }
441
478
 
442
479
  SignVerify(request) {
443
- return unaryCall.call(this, "SignVerify", request);
480
+ return unaryCallInternal.call(this, "SignVerify", request);
444
481
  }
445
482
 
446
483
  /**
447
484
  * FileAPI
448
485
  */
449
486
  GetFileDescendants(request) {
450
- return unaryCall.call(this, "GetFileDescendants", request);
487
+ return unaryCallInternal.call(this, "GetFileDescendants", request);
451
488
  }
452
489
 
453
490
  /**
@@ -461,11 +498,19 @@ export class VoltClient extends EventEmitter {
461
498
  const downloadCall = new GRPCCall(
462
499
  this,
463
500
  "DownloadFile",
464
- "METHOD_TYPE_SERVER_STREAM",
501
+ "METHOD_TYPE_SERVER_STREAM"
465
502
  );
466
503
  return downloadCall.start(grpcClient, request);
467
504
  }
468
505
 
506
+ GetFileContent(request) {
507
+ return unaryCallInternal.call(this, "GetFileContent", request);
508
+ }
509
+
510
+ SetFileContent(request) {
511
+ return unaryCallInternal.call(this, "SetFileContent", request);
512
+ }
513
+
469
514
  UploadFile(request) {
470
515
  const grpcClient = this.getVoltAPIClient();
471
516
 
@@ -487,7 +532,7 @@ export class VoltClient extends EventEmitter {
487
532
  const call = new GRPCCall(
488
533
  this,
489
534
  "DownloadFile",
490
- "METHOD_TYPE_SERVER_STREAM",
535
+ "METHOD_TYPE_SERVER_STREAM"
491
536
  );
492
537
 
493
538
  call.on("data", (response) => {
@@ -527,6 +572,10 @@ export class VoltClient extends EventEmitter {
527
572
  /**
528
573
  * SqliteDatabaseAPI
529
574
  */
575
+ BulkUpdate(request) {
576
+ return unaryCallInternal.call(this, "BulkUpdate", request);
577
+ }
578
+
530
579
  SqlExecute(request) {
531
580
  const grpcClient = this.getVoltAPIClient();
532
581
 
@@ -610,7 +659,7 @@ export class VoltClient extends EventEmitter {
610
659
  const subscribeCall = new GRPCCall(
611
660
  this,
612
661
  "SubscribeWire",
613
- "METHOD_TYPE_BIDI",
662
+ "METHOD_TYPE_BIDI"
614
663
  );
615
664
  return subscribeCall.start(grpcClient, request);
616
665
  }
@@ -28,7 +28,7 @@ function _cleanUp(immediateReconnect) {
28
28
  this._reconnectTimer = 0;
29
29
  _doConnect.call(this, this._helloPayload);
30
30
  },
31
- immediateReconnect ? 0 : this._reconnectInterval,
31
+ immediateReconnect ? 0 : this._reconnectInterval
32
32
  );
33
33
  }
34
34
 
@@ -49,10 +49,10 @@ function _doConnect(connectHello) {
49
49
  const grpcClient = this._voltClient.getVoltAPIClient();
50
50
 
51
51
  let helloPayload = connectHello;
52
- if (!connectHello) {
52
+ if (!helloPayload) {
53
53
  log("defaulting hello payload");
54
54
  helloPayload = {
55
- hello: {},
55
+ hello: {}
56
56
  };
57
57
  }
58
58
 
@@ -74,6 +74,8 @@ function _doConnect(connectHello) {
74
74
 
75
75
  // Schedule the next ping.
76
76
  _startPingTimer.call(this);
77
+
78
+ this.emit("ping", response.ping.timestamp);
77
79
  break;
78
80
  }
79
81
  case "acknowledge": {
@@ -84,8 +86,13 @@ function _doConnect(connectHello) {
84
86
  }
85
87
  break;
86
88
  }
89
+ case "invoke_request": {
90
+ log("received invoke request");
91
+ this.emit("invoke_request", response.invoke_request);
92
+ break;
93
+ }
87
94
  case "evt": {
88
- log("received event %s", response.evt.event_type);
95
+ log("received event %s", Object.keys(response.evt)[0]);
89
96
  this.emit("evt", response.evt);
90
97
  break;
91
98
  }
@@ -129,7 +136,7 @@ function _startPingTimer() {
129
136
 
130
137
  this._pingTimeoutTimer = setTimeout(
131
138
  _connectionTimeoutHandler.bind(this),
132
- this._timeoutInterval,
139
+ this._timeoutInterval
133
140
  );
134
141
  }, this._pingInterval);
135
142
  }
@@ -166,4 +173,8 @@ export default class VoltConnection extends EventEmitter {
166
173
  this._dying = true;
167
174
  _cleanUp.call(this);
168
175
  }
176
+
177
+ send(request) {
178
+ this.call.write(request);
179
+ }
169
180
  }
@@ -36,7 +36,7 @@ export class VoltCredential {
36
36
  // Need to unencrypt the private key.
37
37
  const decrypted = pki.decryptRsaPrivateKey(
38
38
  this._cryptoCache.key,
39
- config.p,
39
+ config.p
40
40
  );
41
41
  if (!decrypted) {
42
42
  throw new Error("failed to decrypt key - check passphrase");
@@ -56,7 +56,7 @@ export class VoltCredential {
56
56
  // Extract Volt public key (used for encrypting Relay payloads).
57
57
  this._voltPublicKey = forge.pki.publicKeyToPem(voltCert.publicKey);
58
58
  this._voltFingerprint = voltUtils.createPublicKeyFingerprint(
59
- this._voltPublicKey,
59
+ this._voltPublicKey
60
60
  );
61
61
  }
62
62
  }
@@ -73,6 +73,10 @@ export class VoltCredential {
73
73
  return this._cryptoCache.client_id;
74
74
  }
75
75
 
76
+ get voltPublicKey() {
77
+ return this._voltPublicKey;
78
+ }
79
+
76
80
  get publicKey() {
77
81
  return pki.publicKeyToPem(this.getKey().publicKey);
78
82
  }
@@ -97,19 +101,17 @@ export class VoltCredential {
97
101
  return Promise.resolve(this.getKey());
98
102
  } else {
99
103
  log("creating key");
100
- return new Promise((resolve, reject) => {
101
- pki.rsa.generateKeyPair({ bits: 2048, workers: -1 }, (err, keypair) => {
102
- if (err) {
103
- log("failure creating key [%s]", err.message);
104
- reject(err);
105
- } else {
106
- log("successfully created key");
107
- const pem = pki.privateKeyToPem(keypair.privateKey);
108
- this._cryptoCache.key = pem;
109
- resolve(keypair);
110
- }
104
+ return voltUtils
105
+ .createRSAKey()
106
+ .then((keyInfo) => {
107
+ log("successfully created key");
108
+ this._cryptoCache.key = keyInfo.pem;
109
+ return keyInfo.keypair;
110
+ })
111
+ .catch((err) => {
112
+ log("failure creating key [%s]", err.message);
113
+ return Promise.reject(err);
111
114
  });
112
- });
113
115
  }
114
116
  }
115
117
 
@@ -140,7 +142,7 @@ export class VoltCredential {
140
142
  * @param {*} tunnelling flag indicating if the token is required for a tunnelled connection
141
143
  * @param {*} ttl time to live in seconds (default to 1 minute)
142
144
  */
143
- getIdentityToken(audience, tunnelling = false, ttl = 60) {
145
+ getIdentityToken(audience, publicKey, tunnelling = false, ttl = 60) {
144
146
  if (!this._cryptoCache.key) {
145
147
  throw new Error("crypto cache invalid");
146
148
  }
@@ -157,7 +159,7 @@ export class VoltCredential {
157
159
  aud: audience,
158
160
  iat: Math.floor(Date.now() / 1000) - ttl,
159
161
  exp: Math.floor(Date.now() / 1000) + ttl,
160
- sub: this._cryptoCache.client_id,
162
+ sub: this._cryptoCache.client_id
161
163
  };
162
164
 
163
165
  let sharedKey;
@@ -166,23 +168,20 @@ export class VoltCredential {
166
168
  sharedKey = VoltCredential.aesCreateKey();
167
169
 
168
170
  // Encrypt the key details using the target Volt public key and include this in the JWT payload.
169
- payload.sk = VoltCredential.rsaEncrypt(
170
- this._voltPublicKey,
171
- sharedKey.key,
172
- );
173
- payload.iv = VoltCredential.rsaEncrypt(this._voltPublicKey, sharedKey.iv);
171
+ payload.sk = VoltCredential.rsaEncrypt(publicKey, sharedKey.key);
172
+ payload.iv = VoltCredential.rsaEncrypt(publicKey, sharedKey.iv);
174
173
  }
175
174
 
176
175
  // Sign synchronously.
177
176
  const token = jwt.sign(payload, this._cryptoCache.key, {
178
177
  algorithm: rs256Algorithm,
179
- keyid: base58Key,
178
+ keyid: base58Key
180
179
  });
181
180
 
182
181
  // Return the token along with any encryption key details.
183
182
  return {
184
183
  token,
185
- sharedKey,
184
+ sharedKey
186
185
  };
187
186
  }
188
187
 
@@ -195,11 +194,12 @@ export class VoltCredential {
195
194
  * @param {*} ttl
196
195
  * @returns
197
196
  */
198
- getIdentityMetadata(grpc, audience, tunnelling = false, ttl = 60) {
197
+ getIdentityMetadata(grpc, audience, publicKey, tunnelling = false, ttl = 60) {
199
198
  const identityToken = this.getIdentityToken(
200
199
  audience || this._voltConfig.id,
200
+ publicKey,
201
201
  tunnelling,
202
- ttl,
202
+ ttl
203
203
  );
204
204
  const metadata = new grpc.Metadata();
205
205
  metadata.add(constants.authTokenName, identityToken.token);
@@ -214,7 +214,7 @@ export class VoltCredential {
214
214
  const privateKey = pki.privateKeyFromPem(this._cryptoCache.key);
215
215
  clone.credential.key = pki.encryptRsaPrivateKey(
216
216
  privateKey,
217
- this._config.p,
217
+ this._config.p
218
218
  );
219
219
  }
220
220
  fs.writeFileSync(this._configPath, JSON.stringify(clone, null, 2));
@@ -243,4 +243,10 @@ export class VoltCredential {
243
243
  static rsaEncrypt(key, buffer) {
244
244
  return crypto.publicEncrypt(key, buffer).toString("base64");
245
245
  }
246
+
247
+ static rsaDecrypt(key, buffer) {
248
+ const keyObj = crypto.createPrivateKey(key);
249
+ const cipher = Buffer.from(buffer, "base64");
250
+ return Buffer.from(crypto.privateDecrypt(keyObj, cipher));
251
+ }
246
252
  }