@tdxvolt/volt-client-grpc 0.17.0 → 0.18.1

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.
@@ -16,11 +16,9 @@ import { constants } from "./constants.js";
16
16
  import GRPCCall from "./grpc-call.js";
17
17
  import RpcInvocation from "./rpc-invocation.js";
18
18
  import { v4 as generateId } from "uuid";
19
- import {
20
- publicKeyToPem,
21
- removeCarriageReturn,
22
- signBase64
23
- } from "@tdxvolt/volt-client-web";
19
+ import { voltUtils as voltClientUtils } from "@tdxvolt/volt-client-web/js";
20
+
21
+ const { publicKeyToPem, removeCarriageReturn, signBase64 } = voltClientUtils;
24
22
 
25
23
  const { filter, pick } = lodash;
26
24
 
@@ -174,7 +172,7 @@ export async function authenticateInternal(
174
172
 
175
173
  if (this._voltConfig.challenge_code) {
176
174
  authenticateRequest.challenge = signBase64(
177
- keyPair.privateKey,
175
+ this._credential.cache.key,
178
176
  this._voltConfig.challenge_code
179
177
  );
180
178
  } else {
@@ -204,16 +202,18 @@ export async function authenticateInternal(
204
202
  // This is because the DID document is signed and the signature
205
203
  // is over the entire document.
206
204
  const didDocument = {
207
- "@context": "https://www.w3.org/ns/did/v1",
205
+ "@context": [
206
+ "https://www.w3.org/ns/did/v1",
207
+ "https://tdxvolt.com/ns/did/v1"
208
+ ],
208
209
  authentication: [`${did}#key-1`],
209
210
  controller: did,
210
- description: this._config.client_name,
211
211
  id: did,
212
212
  verificationMethod: [
213
213
  {
214
214
  id: `${did}#key-1`,
215
215
  publicKeyPem: publicKeyPem,
216
- type: "Ed25519VerificationKey2018"
216
+ type: "Ed25519Signature2018"
217
217
  }
218
218
  ]
219
219
  };
@@ -224,7 +224,7 @@ export async function authenticateInternal(
224
224
  // Now sign the entire DID document, including the proof and set it in the
225
225
  // bind request. This is required by the Volt to verify that we have the private key for the DID.
226
226
  authenticateRequest.did_document_signature = signBase64(
227
- keyPair.privateKey,
227
+ this._credential.cache.key,
228
228
  authenticateRequest.did_document
229
229
  );
230
230
 
@@ -262,7 +262,7 @@ export async function authenticateInternal(
262
262
 
263
263
  return decision === "POLICY_DECISION_PERMIT";
264
264
  } catch (err) {
265
- log("failure binding to Volt [%s]", err.message);
265
+ log("failure authenticating with Volt [%s]", err.message);
266
266
  throw err;
267
267
  }
268
268
  }
@@ -278,15 +278,7 @@ export function connectInternal(helloPayload) {
278
278
  this._connected = false;
279
279
  this._session = null;
280
280
 
281
- if (this._cachedClient) {
282
- this._grpc.closeClient(this._cachedClient);
283
- this._cachedClient = null;
284
- }
285
-
286
- if (this._cachedRemoteClient) {
287
- this._grpc.closeClient(this._cachedRemoteClient);
288
- this._cachedRemoteClient = null;
289
- }
281
+ this.close();
290
282
  };
291
283
 
292
284
  this._voltConnection = new VoltConnection(this, this._grpc);
@@ -375,7 +367,7 @@ export function connectInternal(helloPayload) {
375
367
 
376
368
  export function getVoltAPIClientInternal() {
377
369
  if (!this._cachedClient) {
378
- const serviceAddress = this.isRemote
370
+ const serviceAddress = this.isRelayed
379
371
  ? this._voltConfig.relay.address
380
372
  : this._voltConfig.address;
381
373
  log("creating cached VoltAPI service client on %s", serviceAddress);
@@ -390,7 +382,7 @@ export function getVoltAPIClientInternal() {
390
382
  serviceDescriptors,
391
383
  serviceAddress,
392
384
  this._credential,
393
- this.isRemote && !this.isVoltRelay,
385
+ this.isRelayed,
394
386
  this._voltConfig?.relay?.cloud ? this._voltConfig.id : "",
395
387
  this._voltConfig?.relay?.cloud ? this._voltConfig.id : ""
396
388
  );
@@ -401,7 +393,7 @@ export function getVoltAPIClientInternal() {
401
393
 
402
394
  export function getAPIClientInternal(service) {
403
395
  if (!this._cachedService[service.id]) {
404
- const serviceAddress = this.isRemote
396
+ const serviceAddress = this.isRelayed
405
397
  ? this._voltConfig.relay.address
406
398
  : service.service_description.host_address;
407
399
 
@@ -424,7 +416,7 @@ export function getAPIClientInternal(service) {
424
416
  serviceDescriptors,
425
417
  serviceAddress,
426
418
  this._credential,
427
- this.isRemote && !this.isVoltRelay,
419
+ this.isRelayed,
428
420
  this._voltConfig?.relay?.cloud ? this._voltConfig.id : "",
429
421
  this._voltConfig?.relay?.cloud ? this._voltConfig.id : ""
430
422
  );
@@ -519,9 +511,9 @@ export async function fetchVoltConfig(discovery_url) {
519
511
  }
520
512
  }
521
513
 
522
- export async function fetchVoltConfigFromDID(volt_did) {
514
+ async function fetchVoltConfigFromDIDInternal(volt_did, registryUrl) {
523
515
  try {
524
- const didResolution = voltUtils.getDIDResolutionURL(volt_did);
516
+ const didResolution = voltUtils.getDIDResolutionURL(volt_did, registryUrl);
525
517
  if (!didResolution) {
526
518
  throw new Error(`Invalid Volt DID: ${volt_did}`);
527
519
  }
@@ -540,6 +532,8 @@ export async function fetchVoltConfigFromDID(volt_did) {
540
532
  );
541
533
  }
542
534
 
535
+ log("resolved DID from %s", didResolution);
536
+
543
537
  const serviceEndpoint = voltConfigServices[0].serviceEndpoint;
544
538
  return await fetchVoltConfig.call(this, serviceEndpoint);
545
539
  } catch (err) {
@@ -548,3 +542,22 @@ export async function fetchVoltConfigFromDID(volt_did) {
548
542
  );
549
543
  }
550
544
  }
545
+
546
+ export async function fetchVoltConfigFromDID(
547
+ volt_did,
548
+ registryList = [constants.defaultDIDHostName]
549
+ ) {
550
+ for (let registryUrl of registryList) {
551
+ try {
552
+ return await fetchVoltConfigFromDIDInternal.call(
553
+ this,
554
+ volt_did,
555
+ registryUrl
556
+ );
557
+ } catch (err) {
558
+ log("Failed to fetch Volt config from %s: %s", registryUrl, err.message);
559
+ }
560
+ }
561
+
562
+ throw new Error("Failed to fetch Volt config from any registry");
563
+ }
@@ -31,7 +31,6 @@ export class VoltClient extends EventEmitter {
31
31
  }
32
32
  this._grpc = grpc;
33
33
  this._cachedClient = null;
34
- this._cachedRemoteClient = null;
35
34
  this._session = null;
36
35
  this._config = null;
37
36
  this._voltConfig = null;
@@ -56,12 +55,36 @@ export class VoltClient extends EventEmitter {
56
55
  return this._voltConfig;
57
56
  }
58
57
 
58
+ get connection() {
59
+ return this._voltConnection;
60
+ }
61
+
62
+ close() {
63
+ if (this._cachedClient) {
64
+ this._grpc.closeClient(this._cachedClient);
65
+ this._cachedClient = null;
66
+ }
67
+
68
+ for (let serviceKey in Object.keys(this._cachedService)) {
69
+ if (this._cachedService[serviceKey]) {
70
+ this._grpc.closeClient(this._cachedService[serviceKey]);
71
+ }
72
+ }
73
+
74
+ this._cachedService = {};
75
+ }
76
+
59
77
  /**
60
78
  * Initialises a binding and connection to the volt.
61
79
  * @param {string} [config] - the location of the Volt configuration file, or a configuration object
62
80
  * @param {object} [extras] - additional configuration properties
63
81
  */
64
- async initialise(config, extras = {}) {
82
+ async initialise(
83
+ config,
84
+ extras = {},
85
+ ownDID = false,
86
+ didRegistryList = undefined
87
+ ) {
65
88
  try {
66
89
  if (!config) {
67
90
  throw new Error(
@@ -126,7 +149,15 @@ export class VoltClient extends EventEmitter {
126
149
  }
127
150
 
128
151
  if (voltDID) {
129
- const didConfig = await fetchVoltConfigFromDID.call(this, voltDID);
152
+ const didConfig = await fetchVoltConfigFromDID.call(
153
+ this,
154
+ voltDID,
155
+ didRegistryList
156
+ );
157
+
158
+ // Remove the relay list from the DID config, as it would overwrite the relay in the supplied config.
159
+ delete didConfig.relay;
160
+
130
161
  this._config = { ...this._config, volt: didConfig };
131
162
  log("resolved config from DID: %s", JSON.stringify(didConfig, null, 2));
132
163
  } else if (voltHttpAddress && !this._config?.volt?.id) {
@@ -156,7 +187,7 @@ export class VoltClient extends EventEmitter {
156
187
 
157
188
  await this._credential.initialise();
158
189
 
159
- if (!this.isRemote) {
190
+ if (!this.isRelayed) {
160
191
  // If we're not connecting via a Relay, we must have the address and CA.
161
192
  if (
162
193
  !this._voltConfig.address ||
@@ -179,7 +210,7 @@ export class VoltClient extends EventEmitter {
179
210
  } else {
180
211
  // No certificate present in the cache yet => we need to authenticate.
181
212
  try {
182
- isBound = await authenticateInternal.call(this);
213
+ isBound = await authenticateInternal.call(this, false, ownDID);
183
214
  } catch (bindErr) {
184
215
  if (bindErr.message === "invalid arguments") {
185
216
  // This is usually the result of a missing challenge signature or credential.
@@ -196,7 +227,7 @@ export class VoltClient extends EventEmitter {
196
227
  throw new Error("Volt authentication failed");
197
228
  } else {
198
229
  // Perist the configuration.
199
- this._config = this._credential.saveCache();
230
+ this._credential.saveCache();
200
231
  }
201
232
 
202
233
  return Promise.resolve(this._config);
@@ -247,7 +278,7 @@ export class VoltClient extends EventEmitter {
247
278
  });
248
279
  }
249
280
 
250
- if (this.isRemote) {
281
+ if (this.isRelayed) {
251
282
  if (!this._voltConfig?.relay?.address) {
252
283
  throw new Error(
253
284
  "Remote connection enabled but no Relay address - have you called start()?"
@@ -274,14 +305,10 @@ export class VoltClient extends EventEmitter {
274
305
  }
275
306
  }
276
307
 
277
- get isRemote() {
308
+ get isRelayed() {
278
309
  return !!(this._voltConfig?.relay && this._voltConfig.id);
279
310
  }
280
311
 
281
- get isVoltRelay() {
282
- return this.isRemote && !this._voltConfig?.relay.cloud;
283
- }
284
-
285
312
  connect(helloPayload) {
286
313
  return connectInternal.call(this, helloPayload);
287
314
  }
@@ -613,6 +640,9 @@ export class VoltClient extends EventEmitter {
613
640
  */
614
641
  SqlExecuteJSON(request) {
615
642
  return new Promise((resolve, reject) => {
643
+ if (!request.start) {
644
+ request = { start: request };
645
+ }
616
646
  const call = this.SqlExecute(request);
617
647
 
618
648
  let header;
@@ -96,8 +96,18 @@ function _doConnect(connectHello) {
96
96
  break;
97
97
  }
98
98
  case "invoke_request": {
99
- log("received invoke request");
100
- this.emit("invoke_request", response.invoke_request);
99
+ log("received invoke request %s", response.invoke_request.invoke_id);
100
+ // Pause the call while we process the invoke request to avoid getting
101
+ // CANCELLED errors from grpc - we should perhaps leave this to the
102
+ // client to manage as it may be overkill in some cases.
103
+ this.call.pause();
104
+ try {
105
+ this.emit("invoke_request", response.invoke_request);
106
+ } catch (err) {
107
+ log("error processing invoke request [%s]", err.message);
108
+ } finally {
109
+ this.call.resume();
110
+ }
101
111
  break;
102
112
  }
103
113
  case "evt": {
@@ -169,6 +179,10 @@ export default class VoltConnection extends EventEmitter {
169
179
  this._helloPayload = undefined;
170
180
  }
171
181
 
182
+ get connectionId() {
183
+ return this._connectionId;
184
+ }
185
+
172
186
  connect(helloPayload, throwOnFail) {
173
187
  this._helloPayload = helloPayload;
174
188
  if (throwOnFail) {
@@ -2,14 +2,16 @@
2
2
  import debug from "debug";
3
3
  import fs from "fs";
4
4
  import { constants } from "./constants.js";
5
- import {
6
- createKey,
7
- fingerprintFromPublicKey,
5
+ import { voltUtils as voltClientUtils } from "@tdxvolt/volt-client-web/js";
6
+
7
+ const {
8
+ createSigningKey,
9
+ fingerprintFromPem,
8
10
  getIdentityToken,
9
11
  keyFromPem,
10
12
  privateKeyToPem,
11
13
  publicKeyToPem
12
- } from "@tdxvolt/volt-client-web";
14
+ } = voltClientUtils;
13
15
 
14
16
  const log = debug("volt-client-grpc:volt-credential");
15
17
 
@@ -50,11 +52,11 @@ export class VoltCredential {
50
52
  this._cryptoCache.ca = this._voltConfig.ca_pem;
51
53
  }
52
54
 
53
- // Extract Volt public key (used for encrypting Relay payloads).
55
+ // Extract Volt public key.
54
56
  this._voltPublicKey = this._voltConfig.public_key;
55
57
 
56
58
  const voltKey = keyFromPem(this._voltPublicKey).publicKey;
57
- this._voltFingerprint = fingerprintFromPublicKey(voltKey);
59
+ this._voltFingerprint = fingerprintFromPem(voltKey);
58
60
  }
59
61
  }
60
62
 
@@ -90,6 +92,10 @@ export class VoltCredential {
90
92
  return this._voltFingerprint;
91
93
  }
92
94
 
95
+ get key() {
96
+ return this.getKey();
97
+ }
98
+
93
99
  getKey() {
94
100
  const key = keyFromPem(this._cryptoCache.key);
95
101
  return key;
@@ -105,7 +111,7 @@ export class VoltCredential {
105
111
  return Promise.resolve(this.getKey());
106
112
  } else {
107
113
  log("creating key");
108
- this._cryptoCache.key = createKey();
114
+ this._cryptoCache.key = JSON.parse(createSigningKey()).private_pem;
109
115
  const key = keyFromPem(this._cryptoCache.key);
110
116
  log("successfully created key");
111
117
  return Promise.resolve(key);