@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/lib/index.cjs
CHANGED
|
@@ -34,258 +34,270 @@ var bent__default = /*#__PURE__*/_interopDefaultLegacy(bent);
|
|
|
34
34
|
|
|
35
35
|
// eslint-disable-next-line import/prefer-default-export
|
|
36
36
|
const constants = {
|
|
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
|
-
|
|
37
|
+
authTokenName: "tdx-token",
|
|
38
|
+
collectionOperation: {
|
|
39
|
+
add: "ADD",
|
|
40
|
+
remove: "REMOVE",
|
|
41
|
+
update: "UPDATE",
|
|
42
|
+
},
|
|
43
|
+
crypto: {
|
|
44
|
+
subjectDefaults: [
|
|
45
|
+
{
|
|
46
|
+
name: "countryName",
|
|
47
|
+
value: "GB",
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: "localityName",
|
|
51
|
+
value: "Southampton",
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: "organizationName",
|
|
55
|
+
value: "nquiringminds Ltd",
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
userIdOID: "0.9.2342.19200300.100.1.1",
|
|
59
|
+
},
|
|
60
|
+
defaultDIDHostName: "cloud.tdxvolt.com",
|
|
61
|
+
didServiceType: {
|
|
62
|
+
voltConfig: "tdx-volt-config",
|
|
63
|
+
},
|
|
64
|
+
identityType: {
|
|
65
|
+
did: "did",
|
|
66
|
+
volt: "volt",
|
|
67
|
+
},
|
|
68
|
+
onlineStatus: {
|
|
69
|
+
offline: "offline",
|
|
70
|
+
online: "online",
|
|
71
|
+
unknown: "unknown",
|
|
72
|
+
},
|
|
73
|
+
publicKeyPrefix: "-----BEGIN PUBLIC KEY-----",
|
|
74
|
+
privateKeyPrefix: "-----BEGIN RSA PRIVATE KEY-----",
|
|
75
|
+
privateEncryptedKeyPrefix: "-----BEGIN ENCRYPTED PRIVATE KEY-----",
|
|
76
|
+
resource: {
|
|
77
|
+
identity: "ident",
|
|
78
|
+
identityView: "ident-view",
|
|
79
|
+
proxy: "proxy",
|
|
80
|
+
proxyView: "proxy-view",
|
|
81
|
+
vc: "vc",
|
|
82
|
+
vcView: "vc-view",
|
|
83
|
+
},
|
|
84
|
+
voltStatus: {
|
|
85
|
+
pending: "",
|
|
86
|
+
online: "online",
|
|
87
|
+
offline: "offline",
|
|
88
|
+
deleted: "deleted",
|
|
89
|
+
revoked: "revoked",
|
|
90
|
+
},
|
|
91
|
+
/**
|
|
92
|
+
* These top-level service types should map to a protobuf definition
|
|
93
|
+
* in @tdxvolt/volt-proto.
|
|
94
|
+
*/
|
|
95
|
+
serviceType: {
|
|
96
|
+
fileAPI: "tdx.volt_api.volt.v1.FileAPI",
|
|
97
|
+
sqliteServerAPI: "tdx.volt_api.data.v1.SqliteServerAPI",
|
|
98
|
+
sqliteDatabaseAPI: "tdx.volt_api.data.v1.SqliteDatabaseAPI",
|
|
99
|
+
ssiAPI: "tdx.volt_api.ssi.v1.SsiAPI",
|
|
100
|
+
relayAPI: "tdx.volt_api.relay.v1.RelayAPI",
|
|
101
|
+
voltAPI: "tdx.volt_api.volt.v1.VoltAPI",
|
|
102
|
+
wireAPI: "tdx.volt_api.volt.v1.WireAPI",
|
|
103
|
+
},
|
|
104
|
+
tunnelInvokeMethod: "/tdx.volt_api.volt.v1.VoltAPI/Invoke",
|
|
105
|
+
tunnelPingInterval: 10000,
|
|
106
|
+
tunnelPingTimeout: 2500,
|
|
107
|
+
tunnelReconnectAfter: 30000,
|
|
108
|
+
tunnelTimeoutInterval: 10000 * 2, // This must always be greater than `tunnelPingInterval`.
|
|
109
109
|
};
|
|
110
110
|
|
|
111
|
-
const {pki: pki$2} = forge__default["default"];
|
|
111
|
+
const { pki: pki$2 } = forge__default["default"];
|
|
112
112
|
|
|
113
113
|
const createSecureContextOptions = (cryptoOptions) => {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
114
|
+
const tlsOptions = {
|
|
115
|
+
cert: Buffer.from(cryptoOptions.cert),
|
|
116
|
+
ca: Buffer.from(cryptoOptions.ca),
|
|
117
|
+
key: Buffer.from(cryptoOptions.key),
|
|
118
|
+
};
|
|
119
|
+
return tlsOptions;
|
|
120
120
|
};
|
|
121
121
|
|
|
122
122
|
const getServiceAddress = (mdnsService) => {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
123
|
+
let address;
|
|
124
|
+
|
|
125
|
+
if (mdnsService.txtRecord?.ip) {
|
|
126
|
+
// Use the advertised IP address if present.
|
|
127
|
+
address = `${mdnsService.txtRecord.ip}:${mdnsService.port}`;
|
|
128
|
+
} else {
|
|
129
|
+
// Find a valid ipv4 address.
|
|
130
|
+
const ipv4 = lodash__default["default"].find(mdnsService.addresses, (addy) => ip__default["default"].isV4Format(addy));
|
|
131
|
+
const serviceDetails = {
|
|
132
|
+
host: ipv4,
|
|
133
|
+
port: mdnsService.port,
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
address = `${serviceDetails.host}:${serviceDetails.port}`;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return address;
|
|
140
140
|
};
|
|
141
141
|
|
|
142
142
|
const privateKeyToPem = (keyPair) => {
|
|
143
|
-
|
|
143
|
+
return pki$2.privateKeyToPem(keyPair.privateKey);
|
|
144
144
|
};
|
|
145
145
|
|
|
146
146
|
const publicKeyToPem = (keyPair) => {
|
|
147
|
-
|
|
147
|
+
return pki$2.publicKeyToPem(keyPair.publicKey);
|
|
148
148
|
};
|
|
149
149
|
|
|
150
150
|
const deserialisePEM = (pem) => {
|
|
151
|
-
|
|
151
|
+
return pem.replace(/\\r\\n/g, "\r\n");
|
|
152
152
|
};
|
|
153
153
|
|
|
154
154
|
const removeCarriageReturn = (inp) => {
|
|
155
|
-
|
|
155
|
+
return inp.replace(/\r\n/g, "\n");
|
|
156
156
|
};
|
|
157
157
|
|
|
158
158
|
const createBase58Hash = (inp) => {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
159
|
+
const md = forge__default["default"].md.sha256.create();
|
|
160
|
+
md.update(inp);
|
|
161
|
+
const hashBuf = Buffer.from(md.digest().getBytes(), "binary");
|
|
162
|
+
return bs58__default["default"].encode(hashBuf);
|
|
163
163
|
};
|
|
164
164
|
|
|
165
165
|
const createPublicKeyFingerprint = (key) => {
|
|
166
|
-
|
|
167
|
-
|
|
166
|
+
// Remove carriage returns before creating hash to bring into line with openSSL / volt format.
|
|
167
|
+
return createBase58Hash(removeCarriageReturn(key));
|
|
168
168
|
};
|
|
169
169
|
|
|
170
170
|
const signBase64 = (key, msg) => {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
171
|
+
const md = forge__default["default"].md.sha256.create();
|
|
172
|
+
md.update(msg);
|
|
173
|
+
const signature = key.sign(md);
|
|
174
|
+
return forge__default["default"].util.encode64(signature);
|
|
175
175
|
};
|
|
176
176
|
|
|
177
177
|
const verifyBase64 = (key, msg, signature) => {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
178
|
+
const md = forge__default["default"].md.sha256.create();
|
|
179
|
+
md.update(msg);
|
|
180
|
+
return key.verify(md.digest().bytes(), forge__default["default"].util.decode64(signature));
|
|
181
181
|
};
|
|
182
182
|
|
|
183
183
|
const getDIDResolutionURL = (didIn) => {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
184
|
+
const did = didIn.toLowerCase().split(":");
|
|
185
|
+
if (did.length < 3) {
|
|
186
|
+
return "";
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (did[0] !== "did") {
|
|
190
|
+
return "";
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (did[1] !== "tdx") {
|
|
194
|
+
return "";
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
let hostName;
|
|
198
|
+
let didGUID;
|
|
199
|
+
if (did.length === 3) {
|
|
200
|
+
hostName = constants.defaultDIDHostName;
|
|
201
|
+
didGUID = did[2];
|
|
202
|
+
} else {
|
|
203
|
+
hostName = did[2];
|
|
204
|
+
didGUID = did[3];
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return `https://${hostName}/api/identity/${didGUID}`;
|
|
208
208
|
};
|
|
209
209
|
|
|
210
210
|
var utils = /*#__PURE__*/Object.freeze({
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
211
|
+
__proto__: null,
|
|
212
|
+
createSecureContextOptions: createSecureContextOptions,
|
|
213
|
+
getServiceAddress: getServiceAddress,
|
|
214
|
+
privateKeyToPem: privateKeyToPem,
|
|
215
|
+
publicKeyToPem: publicKeyToPem,
|
|
216
|
+
deserialisePEM: deserialisePEM,
|
|
217
|
+
removeCarriageReturn: removeCarriageReturn,
|
|
218
|
+
createBase58Hash: createBase58Hash,
|
|
219
|
+
createPublicKeyFingerprint: createPublicKeyFingerprint,
|
|
220
|
+
signBase64: signBase64,
|
|
221
|
+
verifyBase64: verifyBase64,
|
|
222
|
+
getDIDResolutionURL: getDIDResolutionURL
|
|
223
223
|
});
|
|
224
224
|
|
|
225
225
|
/* eslint-disable no-underscore-dangle */
|
|
226
226
|
|
|
227
|
-
const {forEach, isEmpty} = lodash__default["default"];
|
|
227
|
+
const { forEach, isEmpty } = lodash__default["default"];
|
|
228
228
|
|
|
229
229
|
const serialiseNOP = (arg) => {
|
|
230
|
-
|
|
230
|
+
return arg;
|
|
231
231
|
};
|
|
232
232
|
|
|
233
|
-
function createClient(
|
|
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
|
-
|
|
233
|
+
function createClient(
|
|
234
|
+
grpc,
|
|
235
|
+
serviceDescriptors,
|
|
236
|
+
address,
|
|
237
|
+
credentials,
|
|
238
|
+
noSerialise,
|
|
239
|
+
voltId,
|
|
240
|
+
serviceId,
|
|
241
|
+
) {
|
|
242
|
+
let grpcCredentials;
|
|
243
|
+
if (credentials) {
|
|
244
|
+
const ca = credentials.config?.relay?.ca_pem || credentials.cache.ca;
|
|
245
|
+
const cert = credentials.cache.cloud_cert || credentials.cache.cert;
|
|
246
|
+
const { key } = credentials.cache;
|
|
247
|
+
|
|
248
|
+
if (key && cert) {
|
|
249
|
+
const cryyptoOptions = { ca, cert, key };
|
|
250
|
+
const tlsOptions = createSecureContextOptions(cryyptoOptions);
|
|
251
|
+
grpcCredentials = grpc.credentials.createSsl(
|
|
252
|
+
tlsOptions.ca,
|
|
253
|
+
tlsOptions.key,
|
|
254
|
+
tlsOptions.cert,
|
|
255
|
+
);
|
|
256
|
+
} else {
|
|
257
|
+
// Cache has no private key, this implies we are connecting without supplying a client certificate.
|
|
258
|
+
// Simply connect using the CA certificate.
|
|
259
|
+
grpcCredentials = grpc.credentials.createSsl(Buffer.from(ca));
|
|
260
|
+
}
|
|
261
|
+
} else {
|
|
262
|
+
grpcCredentials = grpc.credentials.createInsecure();
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Force all methods to appear bi-directional, since we manually handle the call flow (see grpc-call.js).
|
|
266
|
+
forEach(serviceDescriptors, (method) => {
|
|
267
|
+
method.requestStream = true;
|
|
268
|
+
method.responseStream = true;
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
if (noSerialise || voltId) {
|
|
272
|
+
if (voltId && !serviceId) {
|
|
273
|
+
// We must have a service identifier id when tunnelling in order to be able to route the rpc.
|
|
274
|
+
throw new Error("createClient - no serviceId given");
|
|
275
|
+
}
|
|
276
|
+
forEach(serviceDescriptors, (method) => {
|
|
277
|
+
if (voltId) {
|
|
278
|
+
// Prepend the resource id to the method path if tunnelling.
|
|
279
|
+
if (voltId === serviceId) {
|
|
280
|
+
method.path = `/${voltId}${method.path}`;
|
|
281
|
+
} else {
|
|
282
|
+
method.path = `/${voltId}/${serviceId}${method.path}`;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
if (noSerialise) {
|
|
286
|
+
// Save original serialisation methods so we can encrypt/decrypt in the tunnel.
|
|
287
|
+
method.requestSerializeOriginal = method.requestSerialize;
|
|
288
|
+
method.responseDeserializeOriginal = method.responseDeserialize;
|
|
289
|
+
|
|
290
|
+
// Replace default serialisation with NOP, since when tunnelling we encrypt the payload
|
|
291
|
+
// before sending it to the channel, so we want it to be treated as a pure byte array (pass-through).
|
|
292
|
+
method.requestSerialize = serialiseNOP;
|
|
293
|
+
method.responseDeserialize = serialiseNOP;
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const SubClass = grpc.makeGenericClientConstructor(serviceDescriptors);
|
|
299
|
+
|
|
300
|
+
return new SubClass(address, grpcCredentials);
|
|
289
301
|
}
|
|
290
302
|
|
|
291
303
|
/**
|
|
@@ -293,424 +305,484 @@ function createClient(grpc, serviceDescriptors, address, cryptoOptions, noSerial
|
|
|
293
305
|
* @param {*} api
|
|
294
306
|
*/
|
|
295
307
|
const grpcWrap = (api) => {
|
|
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
|
-
|
|
308
|
+
const grpcRoutes = {};
|
|
309
|
+
|
|
310
|
+
// Don't bother trying to wrap class constructors.
|
|
311
|
+
const ignoreMethods = ["constructor"];
|
|
312
|
+
|
|
313
|
+
// Support class instances and PoJos
|
|
314
|
+
const isClass = api.constructor !== Object;
|
|
315
|
+
const iterate = Object.getOwnPropertyNames(
|
|
316
|
+
isClass ? api.constructor.prototype : api,
|
|
317
|
+
);
|
|
318
|
+
|
|
319
|
+
// Wrap each method on the api.
|
|
320
|
+
forEach(iterate, (name) => {
|
|
321
|
+
if (!ignoreMethods.includes(name)) {
|
|
322
|
+
const method = api[name];
|
|
323
|
+
grpcRoutes[name] = (call, callback) => {
|
|
324
|
+
// Execute the method in the context of the api.
|
|
325
|
+
const resultPromise = method.call(api, call.request, call);
|
|
326
|
+
|
|
327
|
+
// Enforce promise results.
|
|
328
|
+
if (!(resultPromise?.then && resultPromise?.catch)) {
|
|
329
|
+
throw new Error(
|
|
330
|
+
`implementation methods must return a Promise [${name}]`,
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
resultPromise
|
|
335
|
+
.then((response) => {
|
|
336
|
+
if (callback) {
|
|
337
|
+
callback(null, response);
|
|
338
|
+
}
|
|
339
|
+
})
|
|
340
|
+
.catch((err) => {
|
|
341
|
+
if (callback) {
|
|
342
|
+
callback(err);
|
|
343
|
+
} else {
|
|
344
|
+
// TODO - verify this => how to send error to stream clients?
|
|
345
|
+
// Destroy stream on error.
|
|
346
|
+
call.destroy(err);
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
return grpcRoutes;
|
|
338
354
|
};
|
|
339
355
|
|
|
340
356
|
class GrpcServer {
|
|
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
|
-
|
|
357
|
+
constructor(options) {
|
|
358
|
+
const { grpc, serviceDescriptors, routes, tlsOptions, requireClientCert } =
|
|
359
|
+
options;
|
|
360
|
+
this._grpc = grpc;
|
|
361
|
+
this._server = new this._grpc.Server({
|
|
362
|
+
"grpc.keepalive_time_ms": 500,
|
|
363
|
+
"grpc.keepalive_timeout_ms": 5000,
|
|
364
|
+
"grpc.http2.max_pings_without_data": 0,
|
|
365
|
+
"grpc.keepalive_permit_without_calls": 1,
|
|
366
|
+
"grpc.http2.min_ping_interval_without_data_ms": 250,
|
|
367
|
+
"grpc.http2.max_ping_strikes": 0,
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
if (tlsOptions) {
|
|
371
|
+
const caList = [];
|
|
372
|
+
const certChains = [];
|
|
373
|
+
[].concat(tlsOptions).forEach((tlsOption) => {
|
|
374
|
+
caList.push(tlsOption.ca);
|
|
375
|
+
certChains.push({
|
|
376
|
+
private_key: tlsOption.key,
|
|
377
|
+
cert_chain: tlsOption.cert,
|
|
378
|
+
});
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
// Create credentials from the volt CA and our own pub/priv key.
|
|
382
|
+
this._credentials = grpc.ServerCredentials.createSsl(
|
|
383
|
+
// The root CAs we trust when verifying client certificates.
|
|
384
|
+
Buffer.concat(caList),
|
|
385
|
+
// The certificate chain(s) we present to the client.
|
|
386
|
+
certChains,
|
|
387
|
+
// Flag indicating if we insist on a client certificate.
|
|
388
|
+
requireClientCert,
|
|
389
|
+
);
|
|
390
|
+
} else {
|
|
391
|
+
this._credentials = grpc.ServerCredentials.createInsecure();
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
if (serviceDescriptors && !isEmpty(routes)) {
|
|
395
|
+
// Enforce decoupling of the service implementation.
|
|
396
|
+
const router = grpcWrap(routes);
|
|
397
|
+
this._server.addService(serviceDescriptors, router);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
close(force = false) {
|
|
402
|
+
return new Promise((resolve) => {
|
|
403
|
+
if (force) {
|
|
404
|
+
resolve(this._server.forceShutdown());
|
|
405
|
+
} else {
|
|
406
|
+
this._server.tryShutdown(resolve);
|
|
407
|
+
}
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
listen(address) {
|
|
412
|
+
return new Promise((resolve, reject) => {
|
|
413
|
+
// If no port is specified, grpc will return the port number assigned.
|
|
414
|
+
this._server.bindAsync(address, this._credentials, (err, port) => {
|
|
415
|
+
if (err) {
|
|
416
|
+
reject(err);
|
|
417
|
+
} else {
|
|
418
|
+
this._port = port;
|
|
419
|
+
this._server.start();
|
|
420
|
+
resolve(port);
|
|
421
|
+
}
|
|
422
|
+
});
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
get server() {
|
|
427
|
+
return this._server;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
get port() {
|
|
431
|
+
return this._port;
|
|
432
|
+
}
|
|
409
433
|
}
|
|
410
434
|
|
|
411
|
-
function createServer(
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
435
|
+
function createServer(
|
|
436
|
+
grpc,
|
|
437
|
+
serviceDescriptors,
|
|
438
|
+
routes,
|
|
439
|
+
cryptoCache,
|
|
440
|
+
requireClientCert,
|
|
441
|
+
) {
|
|
442
|
+
let tlsOptions;
|
|
443
|
+
if (cryptoCache) {
|
|
444
|
+
tlsOptions = createSecureContextOptions(cryptoCache);
|
|
445
|
+
}
|
|
446
|
+
const serverArgs = {
|
|
447
|
+
grpc,
|
|
448
|
+
serviceDescriptors,
|
|
449
|
+
routes,
|
|
450
|
+
tlsOptions,
|
|
451
|
+
requireClientCert,
|
|
452
|
+
};
|
|
453
|
+
return new GrpcServer(serverArgs);
|
|
418
454
|
}
|
|
419
455
|
|
|
420
456
|
var grpcUtils = /*#__PURE__*/Object.freeze({
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
457
|
+
__proto__: null,
|
|
458
|
+
createClient: createClient,
|
|
459
|
+
createServer: createServer
|
|
424
460
|
});
|
|
425
461
|
|
|
426
462
|
/* eslint-disable no-underscore-dangle */
|
|
427
463
|
|
|
428
|
-
const {pki: pki$1} = forge__default["default"];
|
|
429
|
-
const log$4 = debug__default["default"]("volt-client-grpc:volt-
|
|
464
|
+
const { pki: pki$1 } = forge__default["default"];
|
|
465
|
+
const log$4 = debug__default["default"]("volt-client-grpc:volt-credential");
|
|
430
466
|
const aesAlgorithm = "aes-256-cbc";
|
|
431
467
|
const rs256Algorithm = "RS256";
|
|
432
468
|
|
|
433
|
-
class
|
|
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
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
469
|
+
class VoltCredential {
|
|
470
|
+
constructor(config, configPath) {
|
|
471
|
+
this._config = config;
|
|
472
|
+
this._voltConfig = config.volt;
|
|
473
|
+
this._configPath = configPath;
|
|
474
|
+
this._persistCache = !!configPath;
|
|
475
|
+
|
|
476
|
+
if (this._config.credential) {
|
|
477
|
+
this._cryptoCache = this._config.credential;
|
|
478
|
+
} else {
|
|
479
|
+
this._cryptoCache = {};
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// Determine if we're dealing with an encrypted key.
|
|
483
|
+
if (
|
|
484
|
+
this._cryptoCache.key?.startsWith(constants.privateEncryptedKeyPrefix)
|
|
485
|
+
) {
|
|
486
|
+
if (!config.p) {
|
|
487
|
+
throw new Error("encrypted key but no passphrase given");
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// Need to unencrypt the private key.
|
|
491
|
+
const decrypted = pki$1.decryptRsaPrivateKey(
|
|
492
|
+
this._cryptoCache.key,
|
|
493
|
+
config.p,
|
|
494
|
+
);
|
|
495
|
+
if (!decrypted) {
|
|
496
|
+
throw new Error("failed to decrypt key - check passphrase");
|
|
497
|
+
}
|
|
498
|
+
this._cryptoCache.key = pki$1.privateKeyToPem(decrypted);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
if (this._voltConfig.ca_pem) {
|
|
502
|
+
if (!this._cryptoCache.ca) {
|
|
503
|
+
// Copy the volt signing CA to the crypto cache.
|
|
504
|
+
this._cryptoCache.ca = this._voltConfig.ca_pem;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// Pre-cache the target Volt public key by extracting it from the signing certificate.
|
|
508
|
+
const voltCert = forge__default["default"].pki.certificateFromPem(this._voltConfig.ca_pem);
|
|
509
|
+
|
|
510
|
+
// Extract Volt public key (used for encrypting Relay payloads).
|
|
511
|
+
this._voltPublicKey = forge__default["default"].pki.publicKeyToPem(voltCert.publicKey);
|
|
512
|
+
this._voltFingerprint = createPublicKeyFingerprint(
|
|
513
|
+
this._voltPublicKey,
|
|
514
|
+
);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
get config() {
|
|
519
|
+
return this._config;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
get cache() {
|
|
523
|
+
return this._cryptoCache;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
get voltFingerprint() {
|
|
527
|
+
return this._voltFingerprint;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
getKey() {
|
|
531
|
+
const privateKey = pki$1.privateKeyFromPem(this._cryptoCache.key);
|
|
532
|
+
const publicKey = pki$1.rsa.setPublicKey(privateKey.n, privateKey.e);
|
|
533
|
+
return { privateKey, publicKey };
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
/**
|
|
537
|
+
* Retrieves and re-hydrates the pub/priv key data from the crypto cache, or creates one if not found.
|
|
538
|
+
*/
|
|
539
|
+
createKey() {
|
|
540
|
+
if (this._cryptoCache.key) {
|
|
541
|
+
// Already have key data in the cache in pem format => create fully-formed pki instances.
|
|
542
|
+
log$4("createKey - key already exists");
|
|
543
|
+
return Promise.resolve(this.getKey());
|
|
544
|
+
} else {
|
|
545
|
+
log$4("creating key");
|
|
546
|
+
return new Promise((resolve, reject) => {
|
|
547
|
+
pki$1.rsa.generateKeyPair({ bits: 2048, workers: -1 }, (err, keypair) => {
|
|
548
|
+
if (err) {
|
|
549
|
+
log$4("failure creating key [%s]", err.message);
|
|
550
|
+
reject(err);
|
|
551
|
+
} else {
|
|
552
|
+
log$4("successfully created key");
|
|
553
|
+
const pem = pki$1.privateKeyToPem(keypair.privateKey);
|
|
554
|
+
this._cryptoCache.key = pem;
|
|
555
|
+
resolve(keypair);
|
|
556
|
+
}
|
|
557
|
+
});
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* Create cryptocache from scratch.
|
|
564
|
+
*/
|
|
565
|
+
initialise() {
|
|
566
|
+
return this.createKey()
|
|
567
|
+
.then((/* keypair */) => {
|
|
568
|
+
return this.saveCache();
|
|
569
|
+
})
|
|
570
|
+
.then(() => {
|
|
571
|
+
return this._cryptoCache;
|
|
572
|
+
})
|
|
573
|
+
.catch((err) => {
|
|
574
|
+
log$4("failure initialising crypto [%s]", err.message);
|
|
575
|
+
return Promise.reject(err);
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
get isBound() {
|
|
580
|
+
return !!this._cryptoCache.cert;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
/**
|
|
584
|
+
* Signs the identity resource id using the private key.
|
|
585
|
+
* @param {*} audience the token audience (usually the target volt)
|
|
586
|
+
* @param {*} tunnelling flag indicating if the token is required for a tunnelled connection
|
|
587
|
+
* @param {*} ttl time to live in seconds (default to 1 minute)
|
|
588
|
+
*/
|
|
589
|
+
getIdentityToken(audience, tunnelling = false, ttl = 60) {
|
|
590
|
+
if (!this._cryptoCache.key) {
|
|
591
|
+
throw new Error("crypto cache invalid");
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
const keyPair = this.getKey();
|
|
595
|
+
const publicKeyPem = pki$1.publicKeyToPem(keyPair.publicKey);
|
|
596
|
+
const base58Key = createPublicKeyFingerprint(publicKeyPem);
|
|
597
|
+
|
|
598
|
+
log$4("base58 key is %s", base58Key);
|
|
599
|
+
|
|
600
|
+
// Allow TTL either side of the current time.
|
|
601
|
+
// @todo - fix with server time sync on volt connection.
|
|
602
|
+
const payload = {
|
|
603
|
+
aud: audience,
|
|
604
|
+
iat: Math.floor(Date.now() / 1000) - ttl,
|
|
605
|
+
exp: Math.floor(Date.now() / 1000) + ttl,
|
|
606
|
+
};
|
|
607
|
+
|
|
608
|
+
let sharedKey;
|
|
609
|
+
if (tunnelling) {
|
|
610
|
+
// Initialise the Relay encryption key.
|
|
611
|
+
sharedKey = VoltCredential.aesCreateKey();
|
|
612
|
+
|
|
613
|
+
// Encrypt the key details using the target Volt public key and include this in the JWT payload.
|
|
614
|
+
payload.sk = VoltCredential.rsaEncrypt(
|
|
615
|
+
this._voltPublicKey,
|
|
616
|
+
sharedKey.key,
|
|
617
|
+
);
|
|
618
|
+
payload.iv = VoltCredential.rsaEncrypt(this._voltPublicKey, sharedKey.iv);
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
// Sign synchronously.
|
|
622
|
+
const token = jwt__default["default"].sign(payload, this._cryptoCache.key, {
|
|
623
|
+
algorithm: rs256Algorithm,
|
|
624
|
+
keyid: base58Key,
|
|
625
|
+
});
|
|
626
|
+
|
|
627
|
+
// Return the token along with any encryption key details.
|
|
628
|
+
return {
|
|
629
|
+
token,
|
|
630
|
+
sharedKey,
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
/**
|
|
635
|
+
* Creates the metadata required for a grpc call, including the Volt authentication token
|
|
636
|
+
* and optionally tunnel encryption parameters.
|
|
637
|
+
* @param {*} grpc
|
|
638
|
+
* @param {*} audience
|
|
639
|
+
* @param {*} tunnelling
|
|
640
|
+
* @param {*} ttl
|
|
641
|
+
* @returns
|
|
642
|
+
*/
|
|
643
|
+
getIdentityMetadata(grpc, audience, tunnelling = false, ttl = 60) {
|
|
644
|
+
const identityToken = this.getIdentityToken(
|
|
645
|
+
audience || this._voltConfig.id,
|
|
646
|
+
tunnelling,
|
|
647
|
+
ttl,
|
|
648
|
+
);
|
|
649
|
+
const metadata = new grpc.Metadata();
|
|
650
|
+
metadata.add(constants.authTokenName, identityToken.token);
|
|
651
|
+
return { ...identityToken, metadata };
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
saveCache() {
|
|
655
|
+
if (this._persistCache) {
|
|
656
|
+
const clone = { ...this._config, credential: { ...this._cryptoCache } };
|
|
657
|
+
if (this._config.p) {
|
|
658
|
+
// A passphrase option exists => encrypt the key before writing the cache file.
|
|
659
|
+
const privateKey = pki$1.privateKeyFromPem(this._cryptoCache.key);
|
|
660
|
+
clone.credential.key = pki$1.encryptRsaPrivateKey(
|
|
661
|
+
privateKey,
|
|
662
|
+
this._config.p,
|
|
663
|
+
);
|
|
664
|
+
}
|
|
665
|
+
fs__default["default"].writeFileSync(this._configPath, JSON.stringify(clone, null, 2));
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
static aesCreateKey() {
|
|
670
|
+
// Generate random key for the AES-256 algorithm (32 bytes = 256 bits).
|
|
671
|
+
const key = crypto__default["default"].randomBytes(32);
|
|
672
|
+
const iv = crypto__default["default"].randomBytes(16);
|
|
673
|
+
return { key, iv };
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
static aesEncrypt(buffer, key, iv) {
|
|
677
|
+
const cipher = crypto__default["default"].createCipheriv(aesAlgorithm, key, iv);
|
|
678
|
+
const crypted = Buffer.concat([cipher.update(buffer), cipher.final()]);
|
|
679
|
+
return crypted;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
static aesDecrypt(buffer, key, iv) {
|
|
683
|
+
const cipher = crypto__default["default"].createDecipheriv(aesAlgorithm, key, iv);
|
|
684
|
+
const decrypted = Buffer.concat([cipher.update(buffer), cipher.final()]);
|
|
685
|
+
return decrypted;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
static rsaEncrypt(key, buffer) {
|
|
689
|
+
return crypto__default["default"].publicEncrypt(key, buffer).toString("base64");
|
|
690
|
+
}
|
|
631
691
|
}
|
|
632
692
|
|
|
633
|
-
const {get: getProp, snakeCase} = lodash__default["default"];
|
|
693
|
+
const { get: getProp, snakeCase } = lodash__default["default"];
|
|
634
694
|
const __dirname$1 = path.dirname(url.fileURLToPath((typeof document === 'undefined' ? new (require('u' + 'rl').URL)('file:' + __filename).href : (document.currentScript && document.currentScript.src || new URL('index.cjs', document.baseURI).href))));
|
|
635
695
|
|
|
636
696
|
const defaultProtoPath = path.join(__dirname$1, "../protobuf");
|
|
637
697
|
|
|
638
698
|
const defaultLoaderOptions = {
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
699
|
+
keepCase: true,
|
|
700
|
+
longs: String,
|
|
701
|
+
enums: String,
|
|
702
|
+
defaults: true,
|
|
703
|
+
oneofs: true,
|
|
704
|
+
includeDirs: [defaultProtoPath],
|
|
645
705
|
};
|
|
646
706
|
|
|
647
707
|
const getProtoDescriptors = (grpc, protoPath, opts) => {
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
708
|
+
const definition = protoLoader__default["default"].loadSync(
|
|
709
|
+
protoPath,
|
|
710
|
+
opts || defaultLoaderOptions,
|
|
711
|
+
);
|
|
712
|
+
const descriptors = grpc.loadPackageDefinition(definition);
|
|
713
|
+
return descriptors;
|
|
651
714
|
};
|
|
652
715
|
|
|
653
716
|
const getProtoService = (descriptors, servicePath) => {
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
717
|
+
const serviceDef = getProp(descriptors, servicePath);
|
|
718
|
+
if (serviceDef?.service) {
|
|
719
|
+
return serviceDef.service;
|
|
720
|
+
} else {
|
|
721
|
+
return null;
|
|
722
|
+
}
|
|
660
723
|
};
|
|
661
724
|
|
|
662
|
-
function getServiceDescriptorsFromPath(
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
725
|
+
function getServiceDescriptorsFromPath(
|
|
726
|
+
grpc,
|
|
727
|
+
protoPath,
|
|
728
|
+
servicePackageName,
|
|
729
|
+
opts,
|
|
730
|
+
) {
|
|
731
|
+
let serviceDescriptors;
|
|
732
|
+
const descriptors = getProtoDescriptors(grpc, protoPath, opts);
|
|
733
|
+
if (descriptors) {
|
|
734
|
+
serviceDescriptors = getProtoService(descriptors, servicePackageName);
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
return serviceDescriptors;
|
|
670
738
|
}
|
|
671
739
|
|
|
672
740
|
function getServiceDescriptors(grpc, servicePackageName, opts) {
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
741
|
+
// Extract the package components of the service proto package. The package name should be of the
|
|
742
|
+
// fully qualified proto path, e.g. tdx.volt_api.webcam.v1.WebcamControlAPI.
|
|
743
|
+
const protoServiceComponents = servicePackageName.split(".");
|
|
744
|
+
|
|
745
|
+
// The service name is the last component of the path.
|
|
746
|
+
const protoServiceName = protoServiceComponents.pop();
|
|
747
|
+
|
|
748
|
+
// The actual file name should match the snake case of the service name.
|
|
749
|
+
const protoFileName = `${snakeCase(protoServiceName).toLowerCase()}.proto`;
|
|
750
|
+
|
|
751
|
+
// The path to the proto file should match each component of the package name.
|
|
752
|
+
const protoPath = path.join(
|
|
753
|
+
defaultProtoPath,
|
|
754
|
+
...protoServiceComponents,
|
|
755
|
+
protoFileName,
|
|
756
|
+
);
|
|
757
|
+
|
|
758
|
+
let serviceDescriptors;
|
|
759
|
+
const descriptors = getProtoDescriptors(grpc, protoPath, opts);
|
|
760
|
+
if (descriptors) {
|
|
761
|
+
serviceDescriptors = getProtoService(descriptors, servicePackageName);
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
return serviceDescriptors;
|
|
693
765
|
}
|
|
694
766
|
|
|
695
767
|
function getProtoDescriptorMethods(protoDescriptor) {
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
768
|
+
const methods = [];
|
|
769
|
+
Object.keys(protoDescriptor).forEach((methodName) => {
|
|
770
|
+
const method = protoDescriptor[methodName];
|
|
771
|
+
methods.push({
|
|
772
|
+
path: method.path,
|
|
773
|
+
client_streaming: method.requestStream,
|
|
774
|
+
server_streaming: method.responseStream,
|
|
775
|
+
});
|
|
776
|
+
});
|
|
777
|
+
return methods;
|
|
706
778
|
}
|
|
707
779
|
|
|
708
780
|
var protoUtils = /*#__PURE__*/Object.freeze({
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
781
|
+
__proto__: null,
|
|
782
|
+
getServiceDescriptors: getServiceDescriptors,
|
|
783
|
+
getProtoDescriptors: getProtoDescriptors,
|
|
784
|
+
getProtoDescriptorMethods: getProtoDescriptorMethods,
|
|
785
|
+
getServiceDescriptorsFromPath: getServiceDescriptorsFromPath
|
|
714
786
|
});
|
|
715
787
|
|
|
716
788
|
/* eslint-disable no-underscore-dangle */
|
|
@@ -718,15 +790,15 @@ var protoUtils = /*#__PURE__*/Object.freeze({
|
|
|
718
790
|
const log$3 = debug__default["default"]("volt-client-grpc:grpc-call");
|
|
719
791
|
|
|
720
792
|
function _prepareInvokeRequest(request, method, methodType = "METHOD_TYPE_UNARY") {
|
|
721
|
-
const meta = this._voltClient.
|
|
793
|
+
const meta = this._voltClient.credential.getIdentityMetadata(
|
|
722
794
|
this._voltClient.grpc,
|
|
723
|
-
this._voltClient.
|
|
795
|
+
this._voltClient.voltConfig.id,
|
|
724
796
|
this._voltClient.isRemote
|
|
725
797
|
);
|
|
726
798
|
let invokeRequest;
|
|
727
799
|
let callMethod = method;
|
|
728
800
|
|
|
729
|
-
if (this._voltClient.
|
|
801
|
+
if (this._voltClient.isVoltRelay) {
|
|
730
802
|
let requestPayload;
|
|
731
803
|
if (request) {
|
|
732
804
|
requestPayload = this._grpcClient[method].requestSerialize(request);
|
|
@@ -747,23 +819,23 @@ function _prepareInvokeRequest(request, method, methodType = "METHOD_TYPE_UNARY"
|
|
|
747
819
|
},
|
|
748
820
|
};
|
|
749
821
|
|
|
750
|
-
const invokePayload =
|
|
822
|
+
const invokePayload = VoltCredential.aesEncrypt(
|
|
751
823
|
tunnelMethod.responseSerialize(tunnelResp),
|
|
752
824
|
meta.sharedKey.key,
|
|
753
825
|
meta.sharedKey.iv
|
|
754
826
|
);
|
|
755
827
|
|
|
756
|
-
log$3("target volt is %s", this._voltClient.
|
|
828
|
+
log$3("target volt is %s", this._voltClient.credential.voltFingerprint);
|
|
757
829
|
|
|
758
830
|
invokeRequest = {
|
|
759
|
-
identity_fingerprint: this._voltClient.
|
|
831
|
+
identity_fingerprint: this._voltClient.credential.voltFingerprint,
|
|
760
832
|
payload: invokePayload,
|
|
761
833
|
};
|
|
762
834
|
|
|
763
835
|
callMethod = "Invoke";
|
|
764
836
|
} else if (this._voltClient.isRemote && meta.sharedKey.key) {
|
|
765
837
|
const requestPayload = this._grpcClient[method].requestSerializeOriginal(request);
|
|
766
|
-
invokeRequest =
|
|
838
|
+
invokeRequest = VoltCredential.aesEncrypt(requestPayload, meta.sharedKey.key, meta.sharedKey.iv);
|
|
767
839
|
} else {
|
|
768
840
|
invokeRequest = request;
|
|
769
841
|
}
|
|
@@ -779,7 +851,7 @@ function _prepareInvokeRequest(request, method, methodType = "METHOD_TYPE_UNARY"
|
|
|
779
851
|
function _preparePayloadRequest(request, invokeInfo) {
|
|
780
852
|
let payloadRequest;
|
|
781
853
|
|
|
782
|
-
if (this._voltClient.
|
|
854
|
+
if (this._voltClient.isVoltRelay) {
|
|
783
855
|
const requestPayload = this._grpcClient[invokeInfo.methodName].requestSerialize(request);
|
|
784
856
|
const tunnelMethod = this._grpcClient.Tunnel;
|
|
785
857
|
|
|
@@ -789,7 +861,7 @@ function _preparePayloadRequest(request, invokeInfo) {
|
|
|
789
861
|
},
|
|
790
862
|
};
|
|
791
863
|
|
|
792
|
-
const invokePayload =
|
|
864
|
+
const invokePayload = VoltCredential.aesEncrypt(
|
|
793
865
|
tunnelMethod.responseSerialize(tunnelResp),
|
|
794
866
|
invokeInfo.meta.sharedKey.key,
|
|
795
867
|
invokeInfo.meta.sharedKey.iv
|
|
@@ -800,7 +872,11 @@ function _preparePayloadRequest(request, invokeInfo) {
|
|
|
800
872
|
};
|
|
801
873
|
} else if (this._voltClient.isRemote && invokeInfo.meta.sharedKey.key) {
|
|
802
874
|
const requestPayload = this._grpcClient[invokeInfo.methodName].requestSerializeOriginal(request);
|
|
803
|
-
payloadRequest =
|
|
875
|
+
payloadRequest = VoltCredential.aesEncrypt(
|
|
876
|
+
requestPayload,
|
|
877
|
+
invokeInfo.meta.sharedKey.key,
|
|
878
|
+
invokeInfo.meta.sharedKey.iv
|
|
879
|
+
);
|
|
804
880
|
} else {
|
|
805
881
|
payloadRequest = request;
|
|
806
882
|
}
|
|
@@ -811,9 +887,9 @@ function _preparePayloadRequest(request, invokeInfo) {
|
|
|
811
887
|
function _parseResponse(method, meta, response) {
|
|
812
888
|
let invokeResponse = {};
|
|
813
889
|
|
|
814
|
-
if (this._voltClient.
|
|
890
|
+
if (this._voltClient.isVoltRelay) {
|
|
815
891
|
if (response.payload) {
|
|
816
|
-
const decryptedPayload =
|
|
892
|
+
const decryptedPayload = VoltCredential.aesDecrypt(response.payload, meta.sharedKey.key, meta.sharedKey.iv);
|
|
817
893
|
const tunnelMethod = this._grpcClient.Tunnel;
|
|
818
894
|
const responsePayload = tunnelMethod.requestDeserialize(decryptedPayload);
|
|
819
895
|
if (responsePayload.payload === "method_payload") {
|
|
@@ -823,7 +899,7 @@ function _parseResponse(method, meta, response) {
|
|
|
823
899
|
} else {
|
|
824
900
|
log$3("unexpected tunnel payload type: %s", responsePayload.payload);
|
|
825
901
|
}
|
|
826
|
-
} else if (response.status
|
|
902
|
+
} else if (response.status?.message) {
|
|
827
903
|
// There's been an error in the tunnel.
|
|
828
904
|
log$3(
|
|
829
905
|
"Tunnel status received: %s, code %d, description: %s",
|
|
@@ -837,7 +913,7 @@ function _parseResponse(method, meta, response) {
|
|
|
837
913
|
log$3("started method %d for %s", invokeResponse.methodId, method);
|
|
838
914
|
}
|
|
839
915
|
} else if (this._voltClient.isRemote && meta.sharedKey.key) {
|
|
840
|
-
const decryptedPayload =
|
|
916
|
+
const decryptedPayload = VoltCredential.aesDecrypt(response, meta.sharedKey.key, meta.sharedKey.iv);
|
|
841
917
|
invokeResponse = {payload: this._grpcClient[method].responseDeserializeOriginal(decryptedPayload)};
|
|
842
918
|
} else {
|
|
843
919
|
invokeResponse = {payload: response};
|
|
@@ -943,28 +1019,28 @@ class GRPCCall extends EventEmitter__default["default"] {
|
|
|
943
1019
|
}
|
|
944
1020
|
|
|
945
1021
|
end() {
|
|
946
|
-
if (
|
|
947
|
-
throw new Error("call not initialised");
|
|
948
|
-
} else {
|
|
1022
|
+
if (this._call) {
|
|
949
1023
|
return this._call.end();
|
|
1024
|
+
} else {
|
|
1025
|
+
throw new Error("call not initialised");
|
|
950
1026
|
}
|
|
951
1027
|
}
|
|
952
1028
|
|
|
953
1029
|
get writable() {
|
|
954
|
-
return this._call
|
|
1030
|
+
return this._call?.writable;
|
|
955
1031
|
}
|
|
956
1032
|
|
|
957
1033
|
write(request) {
|
|
958
1034
|
if (!this._call) {
|
|
959
1035
|
log$3("ERROR - call not initialised");
|
|
960
1036
|
throw new Error("call not initialised");
|
|
961
|
-
} else if (
|
|
1037
|
+
} else if (this._initialRequest) {
|
|
1038
|
+
const payloadRequest = _preparePayloadRequest.call(this, request, this._initialRequest);
|
|
1039
|
+
return this._call.write(payloadRequest);
|
|
1040
|
+
} else {
|
|
962
1041
|
this._initialRequest = _prepareInvokeRequest.call(this, request, this._methodName, this._methodType);
|
|
963
1042
|
|
|
964
1043
|
return this._call.write(this._initialRequest.request);
|
|
965
|
-
} else {
|
|
966
|
-
const payloadRequest = _preparePayloadRequest.call(this, request, this._initialRequest);
|
|
967
|
-
return this._call.write(payloadRequest);
|
|
968
1044
|
}
|
|
969
1045
|
}
|
|
970
1046
|
}
|
|
@@ -974,924 +1050,1015 @@ class GRPCCall extends EventEmitter__default["default"] {
|
|
|
974
1050
|
const log$2 = debug__default["default"]("volt-client-grpc:volt-connection");
|
|
975
1051
|
|
|
976
1052
|
function _cleanUp(immediateReconnect) {
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1053
|
+
this._session = false;
|
|
1054
|
+
|
|
1055
|
+
if (this._pingTimer) {
|
|
1056
|
+
clearTimeout(this._pingTimer);
|
|
1057
|
+
this._pingTimer = 0;
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
if (this._pingTimeoutTimer) {
|
|
1061
|
+
clearTimeout(this._pingTimeoutTimer);
|
|
1062
|
+
this._pingTimeoutTimer = 0;
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
if (
|
|
1066
|
+
!this._dying &&
|
|
1067
|
+
(immediateReconnect || (this._autoRetry && !this._reconnectTimer))
|
|
1068
|
+
) {
|
|
1069
|
+
this._reconnectTimer = setTimeout(
|
|
1070
|
+
() => {
|
|
1071
|
+
this._reconnectTimer = 0;
|
|
1072
|
+
_doConnect.call(this);
|
|
1073
|
+
},
|
|
1074
|
+
immediateReconnect ? 0 : this._reconnectInterval,
|
|
1075
|
+
);
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
if (this.call?.writable) {
|
|
1079
|
+
this.call.end();
|
|
1080
|
+
}
|
|
1002
1081
|
}
|
|
1003
1082
|
|
|
1004
1083
|
function _connectionTimeoutHandler() {
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1084
|
+
log$2("server timed out");
|
|
1085
|
+
this.emit("connected", false);
|
|
1086
|
+
_cleanUp.call(this);
|
|
1008
1087
|
}
|
|
1009
1088
|
|
|
1010
1089
|
function _doConnect() {
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1090
|
+
this.call = new GRPCCall(this._voltClient, "Connect", "METHOD_TYPE_BIDI");
|
|
1091
|
+
|
|
1092
|
+
const grpcClient = this._voltClient.getVoltAPIClient();
|
|
1093
|
+
const helloPayload = {
|
|
1094
|
+
hello: {},
|
|
1095
|
+
};
|
|
1096
|
+
|
|
1097
|
+
this.call.start(grpcClient, helloPayload, (err, response) => {
|
|
1098
|
+
if (err) {
|
|
1099
|
+
log$2("error on connection stream [%s]", err.message);
|
|
1100
|
+
_cleanUp.call(this);
|
|
1101
|
+
this.emit("error", err);
|
|
1102
|
+
} else {
|
|
1103
|
+
switch (response.payload) {
|
|
1104
|
+
case "ping": {
|
|
1105
|
+
log$2("pinged");
|
|
1106
|
+
|
|
1107
|
+
// Received ping response from server => clear timeout.
|
|
1108
|
+
if (this._pingTimeoutTimer) {
|
|
1109
|
+
clearTimeout(this._pingTimeoutTimer);
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
// Schedule the next ping.
|
|
1113
|
+
_startPingTimer.call(this);
|
|
1114
|
+
break;
|
|
1115
|
+
}
|
|
1116
|
+
case "hello": {
|
|
1117
|
+
if (!this._session) {
|
|
1118
|
+
this._session = true;
|
|
1119
|
+
this.emit("connected", this._session);
|
|
1120
|
+
}
|
|
1121
|
+
break;
|
|
1122
|
+
}
|
|
1123
|
+
case "evt": {
|
|
1124
|
+
log$2("received event %s", response.evt.event_type);
|
|
1125
|
+
this.emit("evt", response.evt);
|
|
1126
|
+
break;
|
|
1127
|
+
}
|
|
1128
|
+
default:
|
|
1129
|
+
log$2("unimplemented connect response payload: %s", response.payload);
|
|
1130
|
+
break;
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
});
|
|
1134
|
+
|
|
1135
|
+
this.call
|
|
1136
|
+
.wait()
|
|
1137
|
+
.then(() => {
|
|
1138
|
+
if (this._session) {
|
|
1139
|
+
this.emit("connected", false);
|
|
1140
|
+
}
|
|
1141
|
+
})
|
|
1142
|
+
.catch((err) => {
|
|
1143
|
+
log$2("unexpected error on connect call [%s]", err.message);
|
|
1144
|
+
})
|
|
1145
|
+
.finally(() => {
|
|
1146
|
+
_cleanUp.call(this);
|
|
1147
|
+
});
|
|
1148
|
+
|
|
1149
|
+
_startPingTimer.call(this);
|
|
1069
1150
|
}
|
|
1070
1151
|
|
|
1071
1152
|
function _startPingTimer() {
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1153
|
+
if (this._pingTimer) {
|
|
1154
|
+
log$2("ping timer running");
|
|
1155
|
+
return;
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
this._pingTimer = setTimeout(() => {
|
|
1159
|
+
// Send next ping to server.
|
|
1160
|
+
const request = { ping: { timestamp: Date.now() } };
|
|
1161
|
+
|
|
1162
|
+
log$2("pinging Volt");
|
|
1163
|
+
this.call.write(request);
|
|
1164
|
+
|
|
1165
|
+
this._pingTimer = 0;
|
|
1166
|
+
|
|
1167
|
+
// Schedule a timeout if we don't hear back from the server.
|
|
1168
|
+
if (this._pingTimeoutTimer) {
|
|
1169
|
+
clearTimeout(this._pingTimeoutTimer);
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
this._pingTimeoutTimer = setTimeout(
|
|
1173
|
+
_connectionTimeoutHandler.bind(this),
|
|
1174
|
+
this._timeoutInterval,
|
|
1175
|
+
);
|
|
1176
|
+
}, this._pingInterval);
|
|
1093
1177
|
}
|
|
1094
1178
|
|
|
1095
1179
|
class VoltConnection extends EventEmitter__default["default"] {
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1180
|
+
constructor(voltClient, grpc) {
|
|
1181
|
+
super();
|
|
1182
|
+
|
|
1183
|
+
const config = voltClient.config;
|
|
1184
|
+
this._grpc = grpc;
|
|
1185
|
+
this._autoRetry =
|
|
1186
|
+
config.autoReconnect === undefined ? true : !!config.autoReconnect;
|
|
1187
|
+
this._dying = false;
|
|
1188
|
+
this._voltClient = voltClient;
|
|
1189
|
+
this._pingTimer = 0;
|
|
1190
|
+
this._pingTimeoutTimer = 0;
|
|
1191
|
+
this._session = false;
|
|
1192
|
+
this._pingInterval = config.pingInterval || 10000;
|
|
1193
|
+
this._reconnectInterval = config.reconnectInterval || 5000;
|
|
1194
|
+
this._timeoutInterval = config.timeoutInterval || 60000;
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
connect(throwOnFail) {
|
|
1198
|
+
if (throwOnFail) {
|
|
1199
|
+
return _doConnect.call(this);
|
|
1200
|
+
} else {
|
|
1201
|
+
return Promise.resolve(_cleanUp.call(this, true));
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
disconnect() {
|
|
1206
|
+
this._dying = true;
|
|
1207
|
+
_cleanUp.call(this);
|
|
1208
|
+
}
|
|
1124
1209
|
}
|
|
1125
1210
|
|
|
1126
1211
|
/* eslint-disable no-underscore-dangle */
|
|
1127
1212
|
|
|
1128
|
-
const {pki} = forge__default["default"];
|
|
1129
|
-
const {filter, pick} = lodash__default["default"];
|
|
1213
|
+
const { pki } = forge__default["default"];
|
|
1214
|
+
const { filter, pick } = lodash__default["default"];
|
|
1130
1215
|
|
|
1131
1216
|
const log$1 = debug__default["default"]("volt-client-grpc:volt-client-internal");
|
|
1132
1217
|
const voltServices = [
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1218
|
+
constants.serviceType.voltAPI,
|
|
1219
|
+
constants.serviceType.fileAPI,
|
|
1220
|
+
constants.serviceType.sqliteDatabaseAPI,
|
|
1221
|
+
constants.serviceType.sqliteServerAPI,
|
|
1222
|
+
constants.serviceType.ssiAPI,
|
|
1223
|
+
constants.serviceType.relayAPI,
|
|
1224
|
+
constants.serviceType.wireAPI,
|
|
1140
1225
|
];
|
|
1141
1226
|
|
|
1142
1227
|
function issueBind(bindRequest, ttl) {
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1228
|
+
// eslint-disable-next-line no-use-before-define
|
|
1229
|
+
return unaryCall.call(this, "Bind", bindRequest).then((bindResponse) => {
|
|
1230
|
+
log$1("got binding request response %j", bindResponse);
|
|
1231
|
+
if (bindResponse.status?.code) {
|
|
1232
|
+
log$1("error in bind response: %s", bindResponse.status.message);
|
|
1233
|
+
return Promise.reject(new Error(bindResponse.status.message));
|
|
1234
|
+
} else {
|
|
1235
|
+
switch (bindResponse.decision) {
|
|
1236
|
+
case "POLICY_DECISION_PERMIT": {
|
|
1237
|
+
//
|
|
1238
|
+
// The request status is permit => cache the bind response info.
|
|
1239
|
+
//
|
|
1240
|
+
|
|
1241
|
+
// This is the certificate assigned to us by the volt.
|
|
1242
|
+
this._credential.cache.cert = bindResponse.cert;
|
|
1243
|
+
|
|
1244
|
+
// This is the signing CA used by the volt (don't ov)
|
|
1245
|
+
this._credential.cache.ca = bindResponse.chain;
|
|
1246
|
+
|
|
1247
|
+
// Resource id is assigned by the volt.
|
|
1248
|
+
this._credential.cache.client_id = bindResponse.identity_id;
|
|
1249
|
+
this._credential.saveCache();
|
|
1250
|
+
break;
|
|
1251
|
+
}
|
|
1252
|
+
case "POLICY_DECISION_DENY": {
|
|
1253
|
+
log$1(">>>>>>>>>>>>>>> access request is DENIED <<<<<<<<<<<<<<<<<<<");
|
|
1254
|
+
break;
|
|
1255
|
+
}
|
|
1256
|
+
case "POLICY_DECISION_PROMPT":
|
|
1257
|
+
case "POLICY_DECISION_PENDING": {
|
|
1258
|
+
log$1("access pending approval - waiting...");
|
|
1259
|
+
break;
|
|
1260
|
+
}
|
|
1261
|
+
default:
|
|
1262
|
+
log$1("ignoring unknown bind descision %s", bindResponse.status);
|
|
1263
|
+
break;
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
return bindResponse.decision;
|
|
1268
|
+
});
|
|
1181
1269
|
}
|
|
1182
1270
|
|
|
1183
1271
|
function findDIDDocumentService(document, serviceType) {
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1272
|
+
if (!document.service) {
|
|
1273
|
+
return [];
|
|
1274
|
+
}
|
|
1187
1275
|
|
|
1188
|
-
|
|
1189
|
-
|
|
1276
|
+
const matches = filter(document.service, (svc) => svc.type === serviceType);
|
|
1277
|
+
return matches;
|
|
1190
1278
|
}
|
|
1191
1279
|
|
|
1192
1280
|
async function bindInternal() {
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1281
|
+
try {
|
|
1282
|
+
// Check if we need to resolve a Relay volt.
|
|
1283
|
+
if (
|
|
1284
|
+
this._config?.relay?.discovery_url ||
|
|
1285
|
+
typeof this._config?.relay === "string"
|
|
1286
|
+
) {
|
|
1287
|
+
const relayURL = this._config.relay?.discovery_url || this._config.relay;
|
|
1288
|
+
log$1("attempting to retrieve Relay information from %s", relayURL);
|
|
1289
|
+
const voltConfig = await fetchVoltConfig.call(this, relayURL);
|
|
1290
|
+
this._config.relay = voltConfig.volt;
|
|
1291
|
+
this._config.relay.discovery_url = relayURL;
|
|
1292
|
+
if (this._config.relay.ca_pem) {
|
|
1293
|
+
log$1(
|
|
1294
|
+
"Auto-fetched Relay CA %s, remote address %s",
|
|
1295
|
+
this._config.relay.ca_pem,
|
|
1296
|
+
this._config.relay.address,
|
|
1297
|
+
);
|
|
1298
|
+
} else {
|
|
1299
|
+
throw new Error("Unable to fetch Relay CA - cannot securely connect.");
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
if (!this._credential.cache.ca) {
|
|
1304
|
+
this._credential.cache.ca = this._voltConfig.ca_pem;
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
if (!this._credential.cache.ca) {
|
|
1308
|
+
throw new Error(
|
|
1309
|
+
"No certificate authority found in configuration for target Volt - check configuration",
|
|
1310
|
+
);
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
const keyPair = this._credential.getKey();
|
|
1314
|
+
const publicKeyPem = pki.publicKeyToPem(keyPair.publicKey);
|
|
1315
|
+
|
|
1316
|
+
if (!this._credential.cache.bindIp) {
|
|
1317
|
+
// Default the IP address if none is specified. This is used to set the SAN in the
|
|
1318
|
+
// certificate issued by the Volt.
|
|
1319
|
+
this._credential.cache.bindIp = ip__default["default"].address();
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
this._credential.saveCache();
|
|
1323
|
+
|
|
1324
|
+
// Form the request message.
|
|
1325
|
+
const bindRequest = {
|
|
1326
|
+
binding_name: this._config.client_name,
|
|
1327
|
+
public_key: publicKeyPem,
|
|
1328
|
+
host: this._credential.cache.bindIp,
|
|
1329
|
+
x509_credential: [],
|
|
1330
|
+
};
|
|
1331
|
+
|
|
1332
|
+
if (this._credential.cache.cert) {
|
|
1333
|
+
bindRequest.x509_credential = [
|
|
1334
|
+
this._credential.cache.cert + this._credential.cache.ca,
|
|
1335
|
+
];
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
if (this._voltConfig.challenge_code) {
|
|
1339
|
+
bindRequest.challenge = signBase64(
|
|
1340
|
+
keyPair.privateKey,
|
|
1341
|
+
this._voltConfig.challenge_code,
|
|
1342
|
+
);
|
|
1343
|
+
} else {
|
|
1344
|
+
log$1(
|
|
1345
|
+
"****no Volt challenge code available**** => not sending challenge signature",
|
|
1346
|
+
);
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
// For remote connections, add our cloud-issued certificate as an additional credential.
|
|
1350
|
+
if (this._config?.relay?.ca_pem && this._credential.cache.cloud_cert) {
|
|
1351
|
+
bindRequest.x509_credential = bindRequest.x509_credential.concat(
|
|
1352
|
+
this._credential.cache.cloud_cert + this._config.relay.ca_pem,
|
|
1353
|
+
);
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
log$1("sending %d x509 credentials", bindRequest.x509_credential.length);
|
|
1357
|
+
log$1("bind TTL is ", this._config.bindRequestTTL);
|
|
1358
|
+
|
|
1359
|
+
// This will block while the request is pending.
|
|
1360
|
+
const pollInterval = 10000;
|
|
1361
|
+
let decision = "";
|
|
1362
|
+
do {
|
|
1363
|
+
if (decision) {
|
|
1364
|
+
// Wait a while before retrying.
|
|
1365
|
+
// eslint-disable-next-line no-await-in-loop
|
|
1366
|
+
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
1367
|
+
}
|
|
1368
|
+
// eslint-disable-next-line no-await-in-loop
|
|
1369
|
+
decision = await issueBind.call(
|
|
1370
|
+
this,
|
|
1371
|
+
bindRequest,
|
|
1372
|
+
this._voltConfig.bindRequestTTL,
|
|
1373
|
+
);
|
|
1374
|
+
log$1("binding decision: %s", decision);
|
|
1375
|
+
} while (
|
|
1376
|
+
decision === "POLICY_DECISION_PROMPT" ||
|
|
1377
|
+
decision === "POLICY_DECISION_PENDING"
|
|
1378
|
+
);
|
|
1379
|
+
|
|
1380
|
+
return decision === "POLICY_DECISION_PERMIT";
|
|
1381
|
+
} catch (err) {
|
|
1382
|
+
log$1("failure binding to Volt [%s]", err.message);
|
|
1383
|
+
throw err;
|
|
1384
|
+
}
|
|
1280
1385
|
}
|
|
1281
1386
|
|
|
1282
1387
|
function connectInternal() {
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1388
|
+
try {
|
|
1389
|
+
if (this._voltConnection) {
|
|
1390
|
+
log$1("connectInternal - Volt connection already exists");
|
|
1391
|
+
return Promise.resolve();
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
const onDisconnected = () => {
|
|
1395
|
+
this._connected = false;
|
|
1396
|
+
this._session = null;
|
|
1397
|
+
|
|
1398
|
+
if (this._cachedClient) {
|
|
1399
|
+
this._grpc.closeClient(this._cachedClient);
|
|
1400
|
+
this._cachedClient = null;
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
if (this._cachedRemoteClient) {
|
|
1404
|
+
this._grpc.closeClient(this._cachedRemoteClient);
|
|
1405
|
+
this._cachedRemoteClient = null;
|
|
1406
|
+
}
|
|
1407
|
+
};
|
|
1408
|
+
|
|
1409
|
+
this._voltConnection = new VoltConnection(this, this._grpc);
|
|
1410
|
+
|
|
1411
|
+
this._voltConnection.on("connected", (session) => {
|
|
1412
|
+
this._session = session;
|
|
1413
|
+
this._connected = !!session;
|
|
1414
|
+
if (!this._connected) {
|
|
1415
|
+
onDisconnected();
|
|
1416
|
+
}
|
|
1417
|
+
this.emit("connected", !!session);
|
|
1418
|
+
});
|
|
1419
|
+
|
|
1420
|
+
this._voltConnection.on("error", (err) => {
|
|
1421
|
+
// Should auto-reconnect.
|
|
1422
|
+
log$1("Volt connection error [%s]", err.message);
|
|
1423
|
+
if (this._connected) {
|
|
1424
|
+
onDisconnected();
|
|
1425
|
+
this.emit("connected", false);
|
|
1426
|
+
}
|
|
1427
|
+
});
|
|
1428
|
+
|
|
1429
|
+
this._voltConnection.on("evt", (evt) => {
|
|
1430
|
+
this.emit("evt", evt);
|
|
1431
|
+
});
|
|
1432
|
+
|
|
1433
|
+
return this._voltConnection.connect();
|
|
1434
|
+
} catch (err) {
|
|
1435
|
+
log$1("connect - error [%s]", err.message);
|
|
1436
|
+
throw err;
|
|
1437
|
+
}
|
|
1333
1438
|
}
|
|
1334
1439
|
|
|
1335
1440
|
function getVoltAPIClientInternal() {
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1441
|
+
if (!this._cachedClient) {
|
|
1442
|
+
const serviceAddress = this.isRemote
|
|
1443
|
+
? this._config.relay.address
|
|
1444
|
+
: this._voltConfig.address;
|
|
1445
|
+
log$1("creating cached VoltAPI service client on %s", serviceAddress);
|
|
1446
|
+
let serviceDescriptors = {};
|
|
1447
|
+
voltServices.forEach((pkg) => {
|
|
1448
|
+
const packageDescriptors = getServiceDescriptors(this._grpc, pkg);
|
|
1449
|
+
serviceDescriptors = { ...serviceDescriptors, ...packageDescriptors };
|
|
1450
|
+
});
|
|
1451
|
+
|
|
1452
|
+
this._cachedClient = createClient(
|
|
1453
|
+
this._grpc,
|
|
1454
|
+
serviceDescriptors,
|
|
1455
|
+
serviceAddress,
|
|
1456
|
+
this._credential,
|
|
1457
|
+
this.isRemote && !this.isVoltRelay,
|
|
1458
|
+
this._config?.relay?.cloud_relay ? this._voltConfig.id : "",
|
|
1459
|
+
this._config?.relay?.cloud_relay ? this._voltConfig.id : "",
|
|
1460
|
+
);
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
return this._cachedClient;
|
|
1357
1464
|
}
|
|
1358
1465
|
|
|
1359
1466
|
function unaryCall(method, request) {
|
|
1360
|
-
|
|
1467
|
+
const grpcClient = this.getVoltAPIClient();
|
|
1361
1468
|
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1469
|
+
const call = new GRPCCall(this, method, "METHOD_TYPE_UNARY");
|
|
1470
|
+
call.start(grpcClient, request);
|
|
1471
|
+
return call.wait();
|
|
1365
1472
|
}
|
|
1366
1473
|
|
|
1367
|
-
async function fetchVoltConfig(
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
resolvedConfig,
|
|
1388
|
-
"id",
|
|
1389
|
-
"display_name",
|
|
1390
|
-
"public_key",
|
|
1391
|
-
"fingerprint",
|
|
1392
|
-
"local_address",
|
|
1393
|
-
"ca_pem",
|
|
1394
|
-
"challenge_code"
|
|
1395
|
-
);
|
|
1474
|
+
async function fetchVoltConfig(disovery_url) {
|
|
1475
|
+
const getJSON = bent__default["default"]("json");
|
|
1476
|
+
const resolvedConfig = await getJSON(disovery_url);
|
|
1477
|
+
|
|
1478
|
+
const voltConfig = pick(
|
|
1479
|
+
resolvedConfig,
|
|
1480
|
+
"id",
|
|
1481
|
+
"display_name",
|
|
1482
|
+
"public_key",
|
|
1483
|
+
"fingerprint",
|
|
1484
|
+
"address",
|
|
1485
|
+
"ca_pem",
|
|
1486
|
+
"challenge_code",
|
|
1487
|
+
"cloud_relay",
|
|
1488
|
+
);
|
|
1489
|
+
|
|
1490
|
+
const relayConfig = pick(resolvedConfig, "relay");
|
|
1491
|
+
|
|
1492
|
+
return { volt: voltConfig, ...relayConfig };
|
|
1493
|
+
}
|
|
1396
1494
|
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1495
|
+
async function fetchVoltConfigFromDID(volt_did) {
|
|
1496
|
+
const didResolution = getDIDResolutionURL(volt_did);
|
|
1497
|
+
if (!didResolution) {
|
|
1498
|
+
throw new Error(`Invalid Volt DID: ${volt_did}`);
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
const getJSON = bent__default["default"]("json");
|
|
1502
|
+
const didDocument = await getJSON(didResolution);
|
|
1503
|
+
const voltConfigServices = findDIDDocumentService(
|
|
1504
|
+
didDocument,
|
|
1505
|
+
constants.didServiceType.voltConfig,
|
|
1506
|
+
);
|
|
1507
|
+
if (voltConfigServices.length !== 1) {
|
|
1508
|
+
log$1(
|
|
1509
|
+
"Unexpected number of Volt configuration endpoints found in DID document for %s, services found: %d",
|
|
1510
|
+
volt_did,
|
|
1511
|
+
voltConfigServices.length,
|
|
1512
|
+
);
|
|
1513
|
+
}
|
|
1514
|
+
const serviceEndpoint = voltConfigServices[0].serviceEndpoint;
|
|
1515
|
+
return await fetchVoltConfig.call(this, serviceEndpoint);
|
|
1403
1516
|
}
|
|
1404
1517
|
|
|
1405
|
-
const {readFile} = fs__default["default"].promises;
|
|
1518
|
+
const { readFile } = fs__default["default"].promises;
|
|
1406
1519
|
const log = debug__default["default"]("volt-client-grpc:volt-client");
|
|
1407
1520
|
|
|
1408
1521
|
class VoltClient extends EventEmitter__default["default"] {
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1522
|
+
/**
|
|
1523
|
+
* @param {object} grpc - an instance of the grpc module (see https://www.npmjs.com/package/grpc)
|
|
1524
|
+
*/
|
|
1525
|
+
constructor(grpc) {
|
|
1526
|
+
super();
|
|
1527
|
+
|
|
1528
|
+
if (!grpc) {
|
|
1529
|
+
throw new Error("invalid arguments, grpc is required");
|
|
1530
|
+
}
|
|
1531
|
+
this._grpc = grpc;
|
|
1532
|
+
this._cachedClient = null;
|
|
1533
|
+
this._cachedRemoteClient = null;
|
|
1534
|
+
this._session = null;
|
|
1535
|
+
this._config = null;
|
|
1536
|
+
this._voltConfig = null;
|
|
1537
|
+
this._voltConnection = null;
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
get config() {
|
|
1541
|
+
return this._config;
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
get credential() {
|
|
1545
|
+
return this._credential;
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1548
|
+
get grpc() {
|
|
1549
|
+
return this._grpc;
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1552
|
+
get voltConfig() {
|
|
1553
|
+
return this._voltConfig;
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1556
|
+
/**
|
|
1557
|
+
* Initialises a binding and connection to the volt.
|
|
1558
|
+
* @param {string} [config] - the location of the Volt configuration file, or a configuration object
|
|
1559
|
+
*/
|
|
1560
|
+
async initialise(config, extras = {}) {
|
|
1561
|
+
try {
|
|
1562
|
+
if (!config) {
|
|
1563
|
+
throw new Error(
|
|
1564
|
+
"configPath argument is required to be a non-empty string",
|
|
1565
|
+
);
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1568
|
+
let configPath;
|
|
1569
|
+
let configJSON;
|
|
1570
|
+
if (typeof config === "string") {
|
|
1571
|
+
configPath = path__default["default"].resolve(config);
|
|
1572
|
+
log("Attempt to load config from file %s", configPath);
|
|
1573
|
+
try {
|
|
1574
|
+
const configContents = await readFile(
|
|
1575
|
+
new URL(configPath, (typeof document === 'undefined' ? new (require('u' + 'rl').URL)('file:' + __filename).href : (document.currentScript && document.currentScript.src || new URL('index.cjs', document.baseURI).href))),
|
|
1576
|
+
);
|
|
1577
|
+
configJSON = JSON.parse(configContents);
|
|
1578
|
+
} catch (err) {
|
|
1579
|
+
log("failure loading config file %s [%s]", configPath, err.message);
|
|
1580
|
+
throw new Error(
|
|
1581
|
+
`failed to load configuration - check JSON format in ${configPath}`,
|
|
1582
|
+
);
|
|
1583
|
+
}
|
|
1584
|
+
} else if (typeof config === "object") {
|
|
1585
|
+
configJSON = config;
|
|
1586
|
+
} else {
|
|
1587
|
+
throw new Error("config must be either a file path or a config object");
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1590
|
+
if (!configJSON || typeof configJSON !== "object") {
|
|
1591
|
+
throw new Error("config argument is required to be an object");
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1594
|
+
if (
|
|
1595
|
+
!configJSON.client_name ||
|
|
1596
|
+
typeof configJSON.client_name !== "string"
|
|
1597
|
+
) {
|
|
1598
|
+
throw new Error("client_name property required in config");
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1601
|
+
this._config = { ...configJSON, ...extras };
|
|
1602
|
+
|
|
1603
|
+
let voltDID = "";
|
|
1604
|
+
let voltDiscovery = "";
|
|
1605
|
+
if (typeof this._config.volt === "string") {
|
|
1606
|
+
if (this._config.volt.indexOf("did:") === 0) {
|
|
1607
|
+
voltDID = this._config.volt;
|
|
1608
|
+
} else {
|
|
1609
|
+
voltDiscovery = this._config.volt;
|
|
1610
|
+
}
|
|
1611
|
+
} else if (typeof this._config.volt !== "object") {
|
|
1612
|
+
throw new Error("configuration is missing the 'volt' object");
|
|
1613
|
+
} else {
|
|
1614
|
+
voltDID = this._config.volt.did;
|
|
1615
|
+
voltDiscovery = this._config.volt.discovery_url;
|
|
1616
|
+
}
|
|
1617
|
+
|
|
1618
|
+
if (voltDID) {
|
|
1619
|
+
const didConfig = await fetchVoltConfigFromDID.call(this, voltDID);
|
|
1620
|
+
this._config = { ...this._config, ...didConfig };
|
|
1621
|
+
log("resolved config from DID: %s", JSON.stringify(didConfig, null, 2));
|
|
1622
|
+
} else if (voltDiscovery) {
|
|
1623
|
+
const discoConfig = await fetchVoltConfig.call(this, voltDiscovery);
|
|
1624
|
+
this._config = { ...this._config, ...discoConfig };
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1627
|
+
this._voltConfig = this._config.volt;
|
|
1628
|
+
this._voltConfig.did = voltDID;
|
|
1629
|
+
this._voltConfig.discovery_url = voltDiscovery;
|
|
1630
|
+
|
|
1631
|
+
if (!this._voltConfig.id || typeof this._voltConfig.id !== "string") {
|
|
1632
|
+
throw new Error("'id' property is missing in Volt configuration");
|
|
1633
|
+
}
|
|
1634
|
+
|
|
1635
|
+
this._credential = new VoltCredential(this._config, configPath);
|
|
1636
|
+
|
|
1637
|
+
await this._credential.initialise();
|
|
1638
|
+
|
|
1639
|
+
if (!this.isRemote) {
|
|
1640
|
+
// If we're not connecting via a Relay, we must have the address and CA.
|
|
1641
|
+
if (
|
|
1642
|
+
!this._voltConfig.address ||
|
|
1643
|
+
typeof this._voltConfig.address !== "string"
|
|
1644
|
+
) {
|
|
1645
|
+
throw new Error("'address' property missing in Volt configuration");
|
|
1646
|
+
}
|
|
1647
|
+
|
|
1648
|
+
if (
|
|
1649
|
+
!this._voltConfig.ca_pem ||
|
|
1650
|
+
typeof this._voltConfig.ca_pem !== "string"
|
|
1651
|
+
) {
|
|
1652
|
+
throw new Error("'ca_pPem' property missing in Volt configuration");
|
|
1653
|
+
}
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1656
|
+
let isBound;
|
|
1657
|
+
if (this._credential._cryptoCache.cert) {
|
|
1658
|
+
isBound = true;
|
|
1659
|
+
} else {
|
|
1660
|
+
// No certificate present in the cache yet => we need to bind.
|
|
1661
|
+
try {
|
|
1662
|
+
isBound = await bindInternal.call(this);
|
|
1663
|
+
} catch (bindErr) {
|
|
1664
|
+
if (bindErr.message === "invalid arguments") {
|
|
1665
|
+
// This is usually the result of a missing challenge signature or credential.
|
|
1666
|
+
log(
|
|
1667
|
+
"failure binding to Volt - did you supply a challenge code or verified credential?",
|
|
1668
|
+
);
|
|
1669
|
+
throw bindErr;
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
|
|
1674
|
+
if (!isBound) {
|
|
1675
|
+
// This is usually because of request being denied.
|
|
1676
|
+
throw new Error("Volt binding failed");
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1679
|
+
return Promise.resolve();
|
|
1680
|
+
} catch (err) {
|
|
1681
|
+
log("Failure starting Volt client [%s]", err.message);
|
|
1682
|
+
throw err;
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
|
|
1686
|
+
initialiseAndConnect(configPath, extras = {}) {
|
|
1687
|
+
return this.initialise(configPath, extras)
|
|
1688
|
+
.then(() => {
|
|
1689
|
+
return connectInternal.call(this);
|
|
1690
|
+
})
|
|
1691
|
+
.catch((err) => {
|
|
1692
|
+
log("initialiseAndConnect failure: %s", err.message);
|
|
1693
|
+
return Promise.reject(err);
|
|
1694
|
+
});
|
|
1695
|
+
}
|
|
1696
|
+
|
|
1697
|
+
/**
|
|
1698
|
+
* Creates a GRPC client for the given Volt service description.
|
|
1699
|
+
*
|
|
1700
|
+
* Automatically handles remote connections.
|
|
1701
|
+
*
|
|
1702
|
+
* n.b. for this javascript client Protobuf definitions should conform to the recommended
|
|
1703
|
+
* structure, which is that a package proto file resides in a folder path that matches the
|
|
1704
|
+
* package name, and services contained in the package are named in in Pascal Case and using
|
|
1705
|
+
* the suffix 'API'. For example the WebcamControlAPI in package tdx.volt_api.webcam.v1 should reside
|
|
1706
|
+
* in a protobuf file at tdx/api/webcam/v1/webcam_control_api.proto
|
|
1707
|
+
*
|
|
1708
|
+
* @param {object} service a service description.
|
|
1709
|
+
* @param {object} descriptors the service descriptors, must be specified unless the service
|
|
1710
|
+
* packages are well-known Volt APIs.
|
|
1711
|
+
*/
|
|
1712
|
+
createServiceClient(service, descriptors) {
|
|
1713
|
+
const pkg = service.service_description.service_api;
|
|
1714
|
+
|
|
1715
|
+
let serviceDescriptors;
|
|
1716
|
+
if (descriptors) {
|
|
1717
|
+
serviceDescriptors = descriptors;
|
|
1718
|
+
} else {
|
|
1719
|
+
serviceDescriptors = {};
|
|
1720
|
+
pkg.forEach((svc) => {
|
|
1721
|
+
log("adding service %s to service client", svc);
|
|
1722
|
+
const packageDescriptors = getServiceDescriptors(this._grpc, svc);
|
|
1723
|
+
serviceDescriptors = { ...serviceDescriptors, ...packageDescriptors };
|
|
1724
|
+
});
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1727
|
+
if (this.isRemote) {
|
|
1728
|
+
if (!this._config?.relay?.address) {
|
|
1729
|
+
throw new Error(
|
|
1730
|
+
"Remote connection enabled but no Relay address - have you called start()?",
|
|
1731
|
+
);
|
|
1732
|
+
}
|
|
1733
|
+
|
|
1734
|
+
// In remote mode => create a client using the discovered Relay address **and** passing the service reourceId
|
|
1735
|
+
return createClient(
|
|
1736
|
+
this._grpc,
|
|
1737
|
+
serviceDescriptors,
|
|
1738
|
+
this._config.relay.address,
|
|
1739
|
+
this._credential,
|
|
1740
|
+
false,
|
|
1741
|
+
service.volt_id,
|
|
1742
|
+
service.id,
|
|
1743
|
+
);
|
|
1744
|
+
} else {
|
|
1745
|
+
return createClient(
|
|
1746
|
+
this._grpc,
|
|
1747
|
+
serviceDescriptors,
|
|
1748
|
+
service.service_description.address,
|
|
1749
|
+
this._credential,
|
|
1750
|
+
);
|
|
1751
|
+
}
|
|
1752
|
+
}
|
|
1753
|
+
|
|
1754
|
+
get isRemote() {
|
|
1755
|
+
return !!(this._config?.relay && this._voltConfig.id);
|
|
1756
|
+
}
|
|
1757
|
+
|
|
1758
|
+
get isVoltRelay() {
|
|
1759
|
+
return this.isRemote && !this._config?.relay.cloud_relay;
|
|
1760
|
+
}
|
|
1761
|
+
|
|
1762
|
+
connect() {
|
|
1763
|
+
return connectInternal.call(this);
|
|
1764
|
+
}
|
|
1765
|
+
|
|
1766
|
+
disconnect() {
|
|
1767
|
+
if (this._voltConnection) {
|
|
1768
|
+
this._voltConnection.disconnect();
|
|
1769
|
+
this._voltConnection = null;
|
|
1770
|
+
}
|
|
1771
|
+
}
|
|
1772
|
+
|
|
1773
|
+
getVoltAPIClient() {
|
|
1774
|
+
return getVoltAPIClientInternal.call(this);
|
|
1775
|
+
}
|
|
1776
|
+
|
|
1777
|
+
/**
|
|
1778
|
+
* Request resource access.
|
|
1779
|
+
* @param {*} targetResourceId
|
|
1780
|
+
* @param {*} accessType - see vault.proto for enum values.
|
|
1781
|
+
*/
|
|
1782
|
+
async RequestAccessBlocking(
|
|
1783
|
+
targetResourceId,
|
|
1784
|
+
accessType = "VOLT_ACCESS_READ",
|
|
1785
|
+
) {
|
|
1786
|
+
try {
|
|
1787
|
+
const accessRequest = {
|
|
1788
|
+
access: accessType,
|
|
1789
|
+
resource_id: targetResourceId,
|
|
1790
|
+
};
|
|
1791
|
+
|
|
1792
|
+
// This will block while the request is pending.
|
|
1793
|
+
// TODO - implement streamed rpc on volt? Not sure there is a need really...
|
|
1794
|
+
const pollInterval = 10000;
|
|
1795
|
+
let decision = "";
|
|
1796
|
+
do {
|
|
1797
|
+
if (decision) {
|
|
1798
|
+
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
1799
|
+
}
|
|
1800
|
+
const accessResponse = await this.RequestAccess(accessRequest);
|
|
1801
|
+
decision = accessResponse.decision;
|
|
1802
|
+
log("access decision: %s", decision);
|
|
1803
|
+
} while (
|
|
1804
|
+
decision === "POLICY_DECISION_PROMPT" ||
|
|
1805
|
+
decision === "POLICY_DECISION_PENDING"
|
|
1806
|
+
);
|
|
1807
|
+
|
|
1808
|
+
log("requestResourceAccess result is %j", decision);
|
|
1809
|
+
return decision === "POLICY_DECISION_PERMIT";
|
|
1810
|
+
} catch (err) {
|
|
1811
|
+
log("failure requesting resource access [%s]", err.message);
|
|
1812
|
+
return Promise.reject(err);
|
|
1813
|
+
}
|
|
1814
|
+
}
|
|
1815
|
+
|
|
1816
|
+
/**
|
|
1817
|
+
* VoltAPI - resource management
|
|
1818
|
+
*/
|
|
1819
|
+
|
|
1820
|
+
CanAccessResource(request) {
|
|
1821
|
+
return unaryCall.call(this, "CanAccessResource", request);
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
DeleteResource(request) {
|
|
1825
|
+
return unaryCall.call(this, "DeleteResource", request);
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1828
|
+
DiscoverServices(request) {
|
|
1829
|
+
return unaryCall.call(this, "DiscoverServices", request);
|
|
1830
|
+
}
|
|
1831
|
+
|
|
1832
|
+
GetResource(request) {
|
|
1833
|
+
return unaryCall.call(this, "GetResource", request);
|
|
1834
|
+
}
|
|
1835
|
+
|
|
1836
|
+
GetResources(request) {
|
|
1837
|
+
return unaryCall.call(this, "GetResources", request);
|
|
1838
|
+
}
|
|
1839
|
+
|
|
1840
|
+
GetResourceAncestors(request) {
|
|
1841
|
+
return unaryCall.call(this, "GetResourceAncestors", request);
|
|
1842
|
+
}
|
|
1843
|
+
|
|
1844
|
+
GetResourceDescendants(request) {
|
|
1845
|
+
return unaryCall.call(this, "GetResourceDescendants", request);
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1848
|
+
RequestAccess(request) {
|
|
1849
|
+
return unaryCall.call(this, "RequestAccess", request);
|
|
1850
|
+
}
|
|
1851
|
+
|
|
1852
|
+
SaveResource(request) {
|
|
1853
|
+
return unaryCall.call(this, "SaveResource", request);
|
|
1854
|
+
}
|
|
1855
|
+
|
|
1856
|
+
SaveResourceAttribute(request) {
|
|
1857
|
+
return unaryCall.call(this, "SaveResourceAttribute", request);
|
|
1858
|
+
}
|
|
1859
|
+
|
|
1860
|
+
SetServiceStatus(request) {
|
|
1861
|
+
return unaryCall.call(this, "SetServiceStatus", request);
|
|
1862
|
+
}
|
|
1863
|
+
|
|
1864
|
+
/**
|
|
1865
|
+
* VoltAPI - volt management
|
|
1866
|
+
*/
|
|
1867
|
+
|
|
1868
|
+
Bind(request) {
|
|
1869
|
+
return unaryCall.call(this, "Bind", request);
|
|
1870
|
+
}
|
|
1871
|
+
|
|
1872
|
+
DeleteAccess(request) {
|
|
1873
|
+
return unaryCall.call(this, "DeleteAccess", request);
|
|
1874
|
+
}
|
|
1875
|
+
|
|
1876
|
+
DeleteVolt(request) {
|
|
1877
|
+
return unaryCall.call(this, "DeleteVolt", request);
|
|
1878
|
+
}
|
|
1879
|
+
|
|
1880
|
+
GetAccess(request) {
|
|
1881
|
+
return unaryCall.call(this, "GetAccess", request);
|
|
1882
|
+
}
|
|
1883
|
+
|
|
1884
|
+
GetBindings(request) {
|
|
1885
|
+
return unaryCall.call(this, "GetBindings", request);
|
|
1886
|
+
}
|
|
1887
|
+
|
|
1888
|
+
GetIdentities(request) {
|
|
1889
|
+
return unaryCall.call(this, "GetIdentities", request);
|
|
1890
|
+
}
|
|
1891
|
+
|
|
1892
|
+
GetIdentity(request) {
|
|
1893
|
+
return unaryCall.call(this, "GetIdentity", request);
|
|
1894
|
+
}
|
|
1895
|
+
|
|
1896
|
+
GetIdentityToken(request) {
|
|
1897
|
+
return unaryCall.call(this, "GetIdentityToken", request);
|
|
1898
|
+
}
|
|
1899
|
+
|
|
1900
|
+
GetPolicy(request) {
|
|
1901
|
+
return unaryCall.call(this, "GetPolicy", request);
|
|
1902
|
+
}
|
|
1903
|
+
|
|
1904
|
+
GetSettings(request) {
|
|
1905
|
+
return unaryCall.call(this, "GetSettings", request);
|
|
1906
|
+
}
|
|
1907
|
+
|
|
1908
|
+
SaveAccess(request) {
|
|
1909
|
+
return unaryCall.call(this, "SaveAccess", request);
|
|
1910
|
+
}
|
|
1911
|
+
|
|
1912
|
+
SaveCloudConnection(request) {
|
|
1913
|
+
return unaryCall.call(this, "SaveCloudConnection", request);
|
|
1914
|
+
}
|
|
1915
|
+
|
|
1916
|
+
SaveIdentity(request) {
|
|
1917
|
+
return unaryCall.call(this, "SaveIdentity", request);
|
|
1918
|
+
}
|
|
1919
|
+
|
|
1920
|
+
SaveSettings(request) {
|
|
1921
|
+
return unaryCall.call(this, "SaveSettings", request);
|
|
1922
|
+
}
|
|
1923
|
+
|
|
1924
|
+
SetAccessRequestDecision(request) {
|
|
1925
|
+
return unaryCall.call(this, "SetAccessRequestDecision", request);
|
|
1926
|
+
}
|
|
1927
|
+
|
|
1928
|
+
SetBindingDecision(request) {
|
|
1929
|
+
return unaryCall.call(this, "SetBindingDecision", request);
|
|
1930
|
+
}
|
|
1931
|
+
|
|
1932
|
+
Shutdown(request) {
|
|
1933
|
+
return unaryCall.call(this, "Shutdown", request);
|
|
1934
|
+
}
|
|
1935
|
+
|
|
1936
|
+
SignVerify(request) {
|
|
1937
|
+
return unaryCall.call(this, "SignVerify", request);
|
|
1938
|
+
}
|
|
1939
|
+
|
|
1940
|
+
/**
|
|
1941
|
+
* FileAPI
|
|
1942
|
+
*/
|
|
1943
|
+
GetFileDescendants(request) {
|
|
1944
|
+
return unaryCall.call(this, "GetFileDescendants", request);
|
|
1945
|
+
}
|
|
1946
|
+
|
|
1947
|
+
/**
|
|
1948
|
+
* Asynchronous file download
|
|
1949
|
+
* @param {*} request
|
|
1950
|
+
* @returns stream
|
|
1951
|
+
*/
|
|
1952
|
+
DownloadFile(request) {
|
|
1953
|
+
const grpcClient = this.getVoltAPIClient();
|
|
1954
|
+
|
|
1955
|
+
return new Promise((resolve, reject) => {
|
|
1956
|
+
const meta = this._credential.getIdentityMetadata(
|
|
1957
|
+
this._grpc,
|
|
1958
|
+
this._voltConfig.id,
|
|
1959
|
+
this.isRemote,
|
|
1960
|
+
);
|
|
1961
|
+
resolve(grpcClient.DownloadFile(request, meta.metadata));
|
|
1962
|
+
}).catch((err) => {
|
|
1963
|
+
log("error during %s [%s]", method, err.message);
|
|
1964
|
+
return Promise.reject(err);
|
|
1965
|
+
});
|
|
1966
|
+
}
|
|
1967
|
+
|
|
1968
|
+
UploadFile(request, callback) {
|
|
1969
|
+
const grpcClient = this.getVoltAPIClient();
|
|
1970
|
+
|
|
1971
|
+
const uploadCall = new GRPCCall(this, "UploadFile", "METHOD_TYPE_BIDI");
|
|
1972
|
+
return uploadCall.start(grpcClient, request, callback);
|
|
1973
|
+
}
|
|
1974
|
+
|
|
1975
|
+
/**
|
|
1976
|
+
* Synchronous file download
|
|
1977
|
+
* @param {*} request
|
|
1978
|
+
* @returns file buffer, base64 encoded
|
|
1979
|
+
*/
|
|
1980
|
+
DownloadFileSync(request) {
|
|
1981
|
+
const grpcClient = this.getVoltAPIClient();
|
|
1982
|
+
|
|
1983
|
+
const blocks = [];
|
|
1984
|
+
|
|
1985
|
+
const call = new GRPCCall(
|
|
1986
|
+
this,
|
|
1987
|
+
"DownloadFile",
|
|
1988
|
+
"METHOD_TYPE_SERVER_STREAM",
|
|
1989
|
+
);
|
|
1990
|
+
call.start(grpcClient, request, (err, response) => {
|
|
1991
|
+
if (err) {
|
|
1992
|
+
return call.reject(err);
|
|
1993
|
+
}
|
|
1994
|
+
|
|
1995
|
+
switch (response.payload) {
|
|
1996
|
+
case "block": {
|
|
1997
|
+
blocks.push(response.block);
|
|
1998
|
+
break;
|
|
1999
|
+
}
|
|
2000
|
+
case "status": {
|
|
2001
|
+
if (response.status.message) {
|
|
2002
|
+
return call.reject(new Error(response.status.message));
|
|
2003
|
+
}
|
|
2004
|
+
break;
|
|
2005
|
+
}
|
|
2006
|
+
default:
|
|
2007
|
+
log("unrecognised response payload %s", response.payload);
|
|
2008
|
+
break;
|
|
2009
|
+
}
|
|
2010
|
+
});
|
|
2011
|
+
|
|
2012
|
+
return call
|
|
2013
|
+
.wait()
|
|
2014
|
+
.then(() => {
|
|
2015
|
+
log("Download finished");
|
|
2016
|
+
return { buffer: Buffer.concat(blocks).toString("base64") };
|
|
2017
|
+
})
|
|
2018
|
+
.catch((err) => {
|
|
2019
|
+
log("error during DownloadFile [%s]", err.message);
|
|
2020
|
+
return Promise.reject(err);
|
|
2021
|
+
});
|
|
2022
|
+
}
|
|
2023
|
+
|
|
2024
|
+
/**
|
|
2025
|
+
* SqliteDatabaseAPI
|
|
2026
|
+
*/
|
|
2027
|
+
SqlExecute(request, callback) {
|
|
2028
|
+
const grpcClient = this.getVoltAPIClient();
|
|
2029
|
+
|
|
2030
|
+
const subscribeCall = new GRPCCall(this, "Execute", "METHOD_TYPE_BIDI");
|
|
2031
|
+
return subscribeCall.start(grpcClient, request, callback);
|
|
2032
|
+
}
|
|
2033
|
+
|
|
2034
|
+
/**
|
|
2035
|
+
* WireAPI
|
|
2036
|
+
*/
|
|
2037
|
+
PublishWire(request, callback) {
|
|
2038
|
+
const grpcClient = this.getVoltAPIClient();
|
|
2039
|
+
|
|
2040
|
+
const publishCall = new GRPCCall(this, "PublishWire", "METHOD_TYPE_BIDI");
|
|
2041
|
+
return publishCall.start(grpcClient, request, callback);
|
|
2042
|
+
}
|
|
2043
|
+
|
|
2044
|
+
SubscribeWire(request, callback) {
|
|
2045
|
+
const grpcClient = this.getVoltAPIClient();
|
|
2046
|
+
|
|
2047
|
+
const subscribeCall = new GRPCCall(
|
|
2048
|
+
this,
|
|
2049
|
+
"SubscribeWire",
|
|
2050
|
+
"METHOD_TYPE_BIDI",
|
|
2051
|
+
);
|
|
2052
|
+
return subscribeCall.start(grpcClient, request, callback);
|
|
2053
|
+
}
|
|
1887
2054
|
}
|
|
1888
2055
|
|
|
1889
2056
|
Object.defineProperty(exports, 'generateId', {
|
|
1890
|
-
|
|
1891
|
-
|
|
2057
|
+
enumerable: true,
|
|
2058
|
+
get: function () { return uuid.v4; }
|
|
1892
2059
|
});
|
|
1893
2060
|
exports.VoltClient = VoltClient;
|
|
1894
|
-
exports.
|
|
2061
|
+
exports.VoltCredential = VoltCredential;
|
|
1895
2062
|
exports.constants = constants;
|
|
1896
2063
|
exports.grpcUtils = grpcUtils;
|
|
1897
2064
|
exports.protoUtils = protoUtils;
|