@tdxvolt/volt-client-grpc 0.1.1 → 0.1.2
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.
- package/lib/index.cjs +1641 -1474
- package/package.json +1 -1
- package/protobuf/tdx/volt_api/data/v1/sqlite_database_api.proto +8 -0
- package/protobuf/tdx/volt_api/data/v1/sqlite_server_api.proto +5 -0
- package/protobuf/tdx/volt_api/{tunnel → relay}/v1/proxy_api.proto +2 -2
- package/protobuf/tdx/volt_api/{tunnel/v1/tunnel_api.proto → relay/v1/relay_api.proto} +6 -5
- package/protobuf/tdx/volt_api/sync/v1/sync.proto +49 -6
- package/protobuf/tdx/volt_api/volt/v1/discovery_api.proto +3 -3
- package/protobuf/tdx/volt_api/volt/v1/file_api.proto +47 -1
- package/protobuf/tdx/volt_api/volt/v1/spark_api.proto +121 -0
- package/protobuf/tdx/volt_api/volt/v1/volt.proto +30 -25
- package/protobuf/tdx/volt_api/volt/v1/volt_api.proto +78 -45
- package/protobuf/tdx/volt_api/volt/v1/wire_api.proto +8 -6
- package/src/constants.js +72 -72
- package/src/grpc-call.js +27 -23
- package/src/grpc-utils.js +212 -176
- package/src/index.js +1 -1
- package/src/proto-utils.js +68 -51
- package/src/utils.js +66 -66
- package/src/volt-client-internal.js +281 -253
- package/src/volt-client.js +544 -489
- package/src/volt-connection.js +144 -135
- package/src/volt-credential.js +237 -0
- package/src/tunnel.pb.js +0 -0
- package/src/volt-crypto.js +0 -213
package/src/volt-client.js
CHANGED
|
@@ -1,501 +1,556 @@
|
|
|
1
1
|
import debug from "debug";
|
|
2
2
|
import EventEmitter from "events";
|
|
3
|
-
import {
|
|
3
|
+
import { VoltCredential } from "./volt-credential.js";
|
|
4
4
|
import path from "path";
|
|
5
|
-
import {getServiceDescriptors} from "./proto-utils.js";
|
|
6
|
-
import {createClient as createGrpcClient} from "./grpc-utils.js";
|
|
5
|
+
import { getServiceDescriptors } from "./proto-utils.js";
|
|
6
|
+
import { createClient as createGrpcClient } from "./grpc-utils.js";
|
|
7
7
|
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
bindInternal,
|
|
9
|
+
connectInternal,
|
|
10
|
+
fetchVoltConfig,
|
|
11
|
+
fetchVoltConfigFromDID,
|
|
12
|
+
getVoltAPIClientInternal,
|
|
13
|
+
unaryCall,
|
|
13
14
|
} from "./volt-client-internal.js";
|
|
14
15
|
import fs from "fs";
|
|
15
16
|
import GRPCCall from "./grpc-call.js";
|
|
16
17
|
|
|
17
|
-
const {readFile} = fs.promises;
|
|
18
|
+
const { readFile } = fs.promises;
|
|
18
19
|
const log = debug("volt-client-grpc:volt-client");
|
|
19
20
|
|
|
20
21
|
export class VoltClient extends EventEmitter {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
22
|
+
/**
|
|
23
|
+
* @param {object} grpc - an instance of the grpc module (see https://www.npmjs.com/package/grpc)
|
|
24
|
+
*/
|
|
25
|
+
constructor(grpc) {
|
|
26
|
+
super();
|
|
27
|
+
|
|
28
|
+
if (!grpc) {
|
|
29
|
+
throw new Error("invalid arguments, grpc is required");
|
|
30
|
+
}
|
|
31
|
+
this._grpc = grpc;
|
|
32
|
+
this._cachedClient = null;
|
|
33
|
+
this._cachedRemoteClient = null;
|
|
34
|
+
this._session = null;
|
|
35
|
+
this._config = null;
|
|
36
|
+
this._voltConfig = null;
|
|
37
|
+
this._voltConnection = null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
get config() {
|
|
41
|
+
return this._config;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
get credential() {
|
|
45
|
+
return this._credential;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
get grpc() {
|
|
49
|
+
return this._grpc;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
get voltConfig() {
|
|
53
|
+
return this._voltConfig;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Initialises a binding and connection to the volt.
|
|
58
|
+
* @param {string} [config] - the location of the Volt configuration file, or a configuration object
|
|
59
|
+
*/
|
|
60
|
+
async initialise(config, extras = {}) {
|
|
61
|
+
try {
|
|
62
|
+
if (!config) {
|
|
63
|
+
throw new Error(
|
|
64
|
+
"configPath argument is required to be a non-empty string",
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
let configPath;
|
|
69
|
+
let configJSON;
|
|
70
|
+
if (typeof config === "string") {
|
|
71
|
+
configPath = path.resolve(config);
|
|
72
|
+
log("Attempt to load config from file %s", configPath);
|
|
73
|
+
try {
|
|
74
|
+
const configContents = await readFile(
|
|
75
|
+
new URL(configPath, import.meta.url),
|
|
76
|
+
);
|
|
77
|
+
configJSON = JSON.parse(configContents);
|
|
78
|
+
} catch (err) {
|
|
79
|
+
log("failure loading config file %s [%s]", configPath, err.message);
|
|
80
|
+
throw new Error(
|
|
81
|
+
`failed to load configuration - check JSON format in ${configPath}`,
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
} else if (typeof config === "object") {
|
|
85
|
+
configJSON = config;
|
|
86
|
+
} else {
|
|
87
|
+
throw new Error("config must be either a file path or a config object");
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (!configJSON || typeof configJSON !== "object") {
|
|
91
|
+
throw new Error("config argument is required to be an object");
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (
|
|
95
|
+
!configJSON.client_name ||
|
|
96
|
+
typeof configJSON.client_name !== "string"
|
|
97
|
+
) {
|
|
98
|
+
throw new Error("client_name property required in config");
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
this._config = { ...configJSON, ...extras };
|
|
102
|
+
|
|
103
|
+
let voltDID = "";
|
|
104
|
+
let voltDiscovery = "";
|
|
105
|
+
if (typeof this._config.volt === "string") {
|
|
106
|
+
if (this._config.volt.indexOf("did:") === 0) {
|
|
107
|
+
voltDID = this._config.volt;
|
|
108
|
+
} else {
|
|
109
|
+
voltDiscovery = this._config.volt;
|
|
110
|
+
}
|
|
111
|
+
} else if (typeof this._config.volt !== "object") {
|
|
112
|
+
throw new Error("configuration is missing the 'volt' object");
|
|
113
|
+
} else {
|
|
114
|
+
voltDID = this._config.volt.did;
|
|
115
|
+
voltDiscovery = this._config.volt.discovery_url;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (voltDID) {
|
|
119
|
+
const didConfig = await fetchVoltConfigFromDID.call(this, voltDID);
|
|
120
|
+
this._config = { ...this._config, ...didConfig };
|
|
121
|
+
log("resolved config from DID: %s", JSON.stringify(didConfig, null, 2));
|
|
122
|
+
} else if (voltDiscovery) {
|
|
123
|
+
const discoConfig = await fetchVoltConfig.call(this, voltDiscovery);
|
|
124
|
+
this._config = { ...this._config, ...discoConfig };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
this._voltConfig = this._config.volt;
|
|
128
|
+
this._voltConfig.did = voltDID;
|
|
129
|
+
this._voltConfig.discovery_url = voltDiscovery;
|
|
130
|
+
|
|
131
|
+
if (!this._voltConfig.id || typeof this._voltConfig.id !== "string") {
|
|
132
|
+
throw new Error("'id' property is missing in Volt configuration");
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
this._credential = new VoltCredential(this._config, configPath);
|
|
136
|
+
|
|
137
|
+
await this._credential.initialise();
|
|
138
|
+
|
|
139
|
+
if (!this.isRemote) {
|
|
140
|
+
// If we're not connecting via a Relay, we must have the address and CA.
|
|
141
|
+
if (
|
|
142
|
+
!this._voltConfig.address ||
|
|
143
|
+
typeof this._voltConfig.address !== "string"
|
|
144
|
+
) {
|
|
145
|
+
throw new Error("'address' property missing in Volt configuration");
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (
|
|
149
|
+
!this._voltConfig.ca_pem ||
|
|
150
|
+
typeof this._voltConfig.ca_pem !== "string"
|
|
151
|
+
) {
|
|
152
|
+
throw new Error("'ca_pPem' property missing in Volt configuration");
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
let isBound;
|
|
157
|
+
if (this._credential._cryptoCache.cert) {
|
|
158
|
+
isBound = true;
|
|
159
|
+
} else {
|
|
160
|
+
// No certificate present in the cache yet => we need to bind.
|
|
161
|
+
try {
|
|
162
|
+
isBound = await bindInternal.call(this);
|
|
163
|
+
} catch (bindErr) {
|
|
164
|
+
if (bindErr.message === "invalid arguments") {
|
|
165
|
+
// This is usually the result of a missing challenge signature or credential.
|
|
166
|
+
log(
|
|
167
|
+
"failure binding to Volt - did you supply a challenge code or verified credential?",
|
|
168
|
+
);
|
|
169
|
+
throw bindErr;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (!isBound) {
|
|
175
|
+
// This is usually because of request being denied.
|
|
176
|
+
throw new Error("Volt binding failed");
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return Promise.resolve();
|
|
180
|
+
} catch (err) {
|
|
181
|
+
log("Failure starting Volt client [%s]", err.message);
|
|
182
|
+
throw err;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
initialiseAndConnect(configPath, extras = {}) {
|
|
187
|
+
return this.initialise(configPath, extras)
|
|
188
|
+
.then(() => {
|
|
189
|
+
return connectInternal.call(this);
|
|
190
|
+
})
|
|
191
|
+
.catch((err) => {
|
|
192
|
+
log("initialiseAndConnect failure: %s", err.message);
|
|
193
|
+
return Promise.reject(err);
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Creates a GRPC client for the given Volt service description.
|
|
199
|
+
*
|
|
200
|
+
* Automatically handles remote connections.
|
|
201
|
+
*
|
|
202
|
+
* n.b. for this javascript client Protobuf definitions should conform to the recommended
|
|
203
|
+
* structure, which is that a package proto file resides in a folder path that matches the
|
|
204
|
+
* package name, and services contained in the package are named in in Pascal Case and using
|
|
205
|
+
* the suffix 'API'. For example the WebcamControlAPI in package tdx.volt_api.webcam.v1 should reside
|
|
206
|
+
* in a protobuf file at tdx/api/webcam/v1/webcam_control_api.proto
|
|
207
|
+
*
|
|
208
|
+
* @param {object} service a service description.
|
|
209
|
+
* @param {object} descriptors the service descriptors, must be specified unless the service
|
|
210
|
+
* packages are well-known Volt APIs.
|
|
211
|
+
*/
|
|
212
|
+
createServiceClient(service, descriptors) {
|
|
213
|
+
const pkg = service.service_description.service_api;
|
|
214
|
+
|
|
215
|
+
let serviceDescriptors;
|
|
216
|
+
if (descriptors) {
|
|
217
|
+
serviceDescriptors = descriptors;
|
|
218
|
+
} else {
|
|
219
|
+
serviceDescriptors = {};
|
|
220
|
+
pkg.forEach((svc) => {
|
|
221
|
+
log("adding service %s to service client", svc);
|
|
222
|
+
const packageDescriptors = getServiceDescriptors(this._grpc, svc);
|
|
223
|
+
serviceDescriptors = { ...serviceDescriptors, ...packageDescriptors };
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (this.isRemote) {
|
|
228
|
+
if (!this._config?.relay?.address) {
|
|
229
|
+
throw new Error(
|
|
230
|
+
"Remote connection enabled but no Relay address - have you called start()?",
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// In remote mode => create a client using the discovered Relay address **and** passing the service reourceId
|
|
235
|
+
return createGrpcClient(
|
|
236
|
+
this._grpc,
|
|
237
|
+
serviceDescriptors,
|
|
238
|
+
this._config.relay.address,
|
|
239
|
+
this._credential,
|
|
240
|
+
false,
|
|
241
|
+
service.volt_id,
|
|
242
|
+
service.id,
|
|
243
|
+
);
|
|
244
|
+
} else {
|
|
245
|
+
return createGrpcClient(
|
|
246
|
+
this._grpc,
|
|
247
|
+
serviceDescriptors,
|
|
248
|
+
service.service_description.address,
|
|
249
|
+
this._credential,
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
get isRemote() {
|
|
255
|
+
return !!(this._config?.relay && this._voltConfig.id);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
get isVoltRelay() {
|
|
259
|
+
return this.isRemote && !this._config?.relay.cloud_relay;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
connect() {
|
|
263
|
+
return connectInternal.call(this);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
disconnect() {
|
|
267
|
+
if (this._voltConnection) {
|
|
268
|
+
this._voltConnection.disconnect();
|
|
269
|
+
this._voltConnection = null;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
getVoltAPIClient() {
|
|
274
|
+
return getVoltAPIClientInternal.call(this);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Request resource access.
|
|
279
|
+
* @param {*} targetResourceId
|
|
280
|
+
* @param {*} accessType - see vault.proto for enum values.
|
|
281
|
+
*/
|
|
282
|
+
async RequestAccessBlocking(
|
|
283
|
+
targetResourceId,
|
|
284
|
+
accessType = "VOLT_ACCESS_READ",
|
|
285
|
+
) {
|
|
286
|
+
try {
|
|
287
|
+
const accessRequest = {
|
|
288
|
+
access: accessType,
|
|
289
|
+
resource_id: targetResourceId,
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
// This will block while the request is pending.
|
|
293
|
+
// TODO - implement streamed rpc on volt? Not sure there is a need really...
|
|
294
|
+
const pollInterval = 10000;
|
|
295
|
+
let decision = "";
|
|
296
|
+
do {
|
|
297
|
+
if (decision) {
|
|
298
|
+
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
299
|
+
}
|
|
300
|
+
const accessResponse = await this.RequestAccess(accessRequest);
|
|
301
|
+
decision = accessResponse.decision;
|
|
302
|
+
log("access decision: %s", decision);
|
|
303
|
+
} while (
|
|
304
|
+
decision === "POLICY_DECISION_PROMPT" ||
|
|
305
|
+
decision === "POLICY_DECISION_PENDING"
|
|
306
|
+
);
|
|
307
|
+
|
|
308
|
+
log("requestResourceAccess result is %j", decision);
|
|
309
|
+
return decision === "POLICY_DECISION_PERMIT";
|
|
310
|
+
} catch (err) {
|
|
311
|
+
log("failure requesting resource access [%s]", err.message);
|
|
312
|
+
return Promise.reject(err);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* VoltAPI - resource management
|
|
318
|
+
*/
|
|
319
|
+
|
|
320
|
+
CanAccessResource(request) {
|
|
321
|
+
return unaryCall.call(this, "CanAccessResource", request);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
DeleteResource(request) {
|
|
325
|
+
return unaryCall.call(this, "DeleteResource", request);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
DiscoverServices(request) {
|
|
329
|
+
return unaryCall.call(this, "DiscoverServices", request);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
GetResource(request) {
|
|
333
|
+
return unaryCall.call(this, "GetResource", request);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
GetResources(request) {
|
|
337
|
+
return unaryCall.call(this, "GetResources", request);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
GetResourceAncestors(request) {
|
|
341
|
+
return unaryCall.call(this, "GetResourceAncestors", request);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
GetResourceDescendants(request) {
|
|
345
|
+
return unaryCall.call(this, "GetResourceDescendants", request);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
RequestAccess(request) {
|
|
349
|
+
return unaryCall.call(this, "RequestAccess", request);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
SaveResource(request) {
|
|
353
|
+
return unaryCall.call(this, "SaveResource", request);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
SaveResourceAttribute(request) {
|
|
357
|
+
return unaryCall.call(this, "SaveResourceAttribute", request);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
SetServiceStatus(request) {
|
|
361
|
+
return unaryCall.call(this, "SetServiceStatus", request);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* VoltAPI - volt management
|
|
366
|
+
*/
|
|
367
|
+
|
|
368
|
+
Bind(request) {
|
|
369
|
+
return unaryCall.call(this, "Bind", request);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
DeleteAccess(request) {
|
|
373
|
+
return unaryCall.call(this, "DeleteAccess", request);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
DeleteVolt(request) {
|
|
377
|
+
return unaryCall.call(this, "DeleteVolt", request);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
GetAccess(request) {
|
|
381
|
+
return unaryCall.call(this, "GetAccess", request);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
GetBindings(request) {
|
|
385
|
+
return unaryCall.call(this, "GetBindings", request);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
GetIdentities(request) {
|
|
389
|
+
return unaryCall.call(this, "GetIdentities", request);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
GetIdentity(request) {
|
|
393
|
+
return unaryCall.call(this, "GetIdentity", request);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
GetIdentityToken(request) {
|
|
397
|
+
return unaryCall.call(this, "GetIdentityToken", request);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
GetPolicy(request) {
|
|
401
|
+
return unaryCall.call(this, "GetPolicy", request);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
GetSettings(request) {
|
|
405
|
+
return unaryCall.call(this, "GetSettings", request);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
SaveAccess(request) {
|
|
409
|
+
return unaryCall.call(this, "SaveAccess", request);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
SaveCloudConnection(request) {
|
|
413
|
+
return unaryCall.call(this, "SaveCloudConnection", request);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
SaveIdentity(request) {
|
|
417
|
+
return unaryCall.call(this, "SaveIdentity", request);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
SaveSettings(request) {
|
|
421
|
+
return unaryCall.call(this, "SaveSettings", request);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
SetAccessRequestDecision(request) {
|
|
425
|
+
return unaryCall.call(this, "SetAccessRequestDecision", request);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
SetBindingDecision(request) {
|
|
429
|
+
return unaryCall.call(this, "SetBindingDecision", request);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
Shutdown(request) {
|
|
433
|
+
return unaryCall.call(this, "Shutdown", request);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
SignVerify(request) {
|
|
437
|
+
return unaryCall.call(this, "SignVerify", request);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* FileAPI
|
|
442
|
+
*/
|
|
443
|
+
GetFileDescendants(request) {
|
|
444
|
+
return unaryCall.call(this, "GetFileDescendants", request);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Asynchronous file download
|
|
449
|
+
* @param {*} request
|
|
450
|
+
* @returns stream
|
|
451
|
+
*/
|
|
452
|
+
DownloadFile(request) {
|
|
453
|
+
const grpcClient = this.getVoltAPIClient();
|
|
454
|
+
|
|
455
|
+
return new Promise((resolve, reject) => {
|
|
456
|
+
const meta = this._credential.getIdentityMetadata(
|
|
457
|
+
this._grpc,
|
|
458
|
+
this._voltConfig.id,
|
|
459
|
+
this.isRemote,
|
|
460
|
+
);
|
|
461
|
+
resolve(grpcClient.DownloadFile(request, meta.metadata));
|
|
462
|
+
}).catch((err) => {
|
|
463
|
+
log("error during %s [%s]", method, err.message);
|
|
464
|
+
return Promise.reject(err);
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
UploadFile(request, callback) {
|
|
469
|
+
const grpcClient = this.getVoltAPIClient();
|
|
470
|
+
|
|
471
|
+
const uploadCall = new GRPCCall(this, "UploadFile", "METHOD_TYPE_BIDI");
|
|
472
|
+
return uploadCall.start(grpcClient, request, callback);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Synchronous file download
|
|
477
|
+
* @param {*} request
|
|
478
|
+
* @returns file buffer, base64 encoded
|
|
479
|
+
*/
|
|
480
|
+
DownloadFileSync(request) {
|
|
481
|
+
const grpcClient = this.getVoltAPIClient();
|
|
482
|
+
|
|
483
|
+
const blocks = [];
|
|
484
|
+
|
|
485
|
+
const call = new GRPCCall(
|
|
486
|
+
this,
|
|
487
|
+
"DownloadFile",
|
|
488
|
+
"METHOD_TYPE_SERVER_STREAM",
|
|
489
|
+
);
|
|
490
|
+
call.start(grpcClient, request, (err, response) => {
|
|
491
|
+
if (err) {
|
|
492
|
+
return call.reject(err);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
switch (response.payload) {
|
|
496
|
+
case "block": {
|
|
497
|
+
blocks.push(response.block);
|
|
498
|
+
break;
|
|
499
|
+
}
|
|
500
|
+
case "status": {
|
|
501
|
+
if (response.status.message) {
|
|
502
|
+
return call.reject(new Error(response.status.message));
|
|
503
|
+
} else {
|
|
504
|
+
// Non-error response status => do nothing and wait for the stream to end.
|
|
505
|
+
}
|
|
506
|
+
break;
|
|
507
|
+
}
|
|
508
|
+
default:
|
|
509
|
+
log("unrecognised response payload %s", response.payload);
|
|
510
|
+
break;
|
|
511
|
+
}
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
return call
|
|
515
|
+
.wait()
|
|
516
|
+
.then(() => {
|
|
517
|
+
log("Download finished");
|
|
518
|
+
return { buffer: Buffer.concat(blocks).toString("base64") };
|
|
519
|
+
})
|
|
520
|
+
.catch((err) => {
|
|
521
|
+
log("error during DownloadFile [%s]", err.message);
|
|
522
|
+
return Promise.reject(err);
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
/**
|
|
527
|
+
* SqliteDatabaseAPI
|
|
528
|
+
*/
|
|
529
|
+
SqlExecute(request, callback) {
|
|
530
|
+
const grpcClient = this.getVoltAPIClient();
|
|
531
|
+
|
|
532
|
+
const subscribeCall = new GRPCCall(this, "Execute", "METHOD_TYPE_BIDI");
|
|
533
|
+
return subscribeCall.start(grpcClient, request, callback);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
/**
|
|
537
|
+
* WireAPI
|
|
538
|
+
*/
|
|
539
|
+
PublishWire(request, callback) {
|
|
540
|
+
const grpcClient = this.getVoltAPIClient();
|
|
541
|
+
|
|
542
|
+
const publishCall = new GRPCCall(this, "PublishWire", "METHOD_TYPE_BIDI");
|
|
543
|
+
return publishCall.start(grpcClient, request, callback);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
SubscribeWire(request, callback) {
|
|
547
|
+
const grpcClient = this.getVoltAPIClient();
|
|
548
|
+
|
|
549
|
+
const subscribeCall = new GRPCCall(
|
|
550
|
+
this,
|
|
551
|
+
"SubscribeWire",
|
|
552
|
+
"METHOD_TYPE_BIDI",
|
|
553
|
+
);
|
|
554
|
+
return subscribeCall.start(grpcClient, request, callback);
|
|
555
|
+
}
|
|
501
556
|
}
|