@tdxvolt/volt-client-grpc 0.1.2 → 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.1.2",
6
+ "version": "0.11.1",
7
7
  "description": "tdx Volt library for nodejs clients",
8
8
  "type": "module",
9
9
  "exports": {
@@ -3,19 +3,19 @@ syntax = "proto3";
3
3
  package tdx.volt_api.data.v1;
4
4
 
5
5
  // Just reflect SQLite types for now.
6
- enum ColumnType {
7
- COLUMN_TYPE_UNKNOWN = 0;
8
- COLUMN_TYPE_TEXT = 1;
9
- COLUMN_TYPE_INTEGER = 2;
10
- COLUMN_TYPE_REAL = 3;
11
- COLUMN_TYPE_BLOB = 4;
12
- COLUMN_TYPE_NULL = 5;
6
+ enum DataType {
7
+ DATA_TYPE_UNKNOWN = 0;
8
+ DATA_TYPE_TEXT = 1;
9
+ DATA_TYPE_INTEGER = 2;
10
+ DATA_TYPE_REAL = 3;
11
+ DATA_TYPE_BLOB = 4;
12
+ DATA_TYPE_NULL = 5;
13
13
  }
14
14
 
15
15
  message Column {
16
16
  string name = 1;
17
17
  string description = 2;
18
- ColumnType type = 3;
18
+ DataType type = 3;
19
19
  }
20
20
 
21
21
  message Schema {
@@ -25,12 +25,12 @@ message Schema {
25
25
  }
26
26
 
27
27
  message Variant {
28
- ColumnType type = 1;
29
28
  oneof data {
30
- string text = 2;
31
- int64 integer = 3;
32
- double real = 4;
33
- bytes blob = 5;
29
+ string text = 1;
30
+ int64 integer = 2;
31
+ double real = 3;
32
+ bytes blob = 4;
33
+ bool null = 5;
34
34
  }
35
35
  }
36
36
 
@@ -82,6 +82,9 @@ message ProxyConnection {
82
82
 
83
83
  // Set to indicate the Volt API itself is not automatically exposed to the connection.
84
84
  bool disable_volt_api = 9;
85
+
86
+ // Optional challenge that can be presented in the bind phase when initialising the connection.
87
+ string challenge = 10;
85
88
  }
86
89
 
87
90
  enum SecureMode {
@@ -14,7 +14,7 @@ service WireAPI {
14
14
  rpc SubscribeWire(stream SubscribeWireRequest) returns (stream SubscribeWireResponse);
15
15
  }
16
16
 
17
- // Because the publish RPC is client-streaming, the policy will not be checked until the first message arrives. This can mean that the `PublishWire` call appears to succeed but then fails after the first attempt to publish a message.
17
+ // Because the publish RPC is streaming, the policy will not be checked until the first message arrives. This can mean that the `PublishWire` call appears to succeed but then fails after the first attempt to publish a message.
18
18
  // To avoid this, clients can immediately send a message with the `wire_id` set and no `chunk` set. This will establish that the correct permissions are in place and fail fast if not.
19
19
  message PublishWireRequest {
20
20
  // Only necessary in the first payload.
package/src/constants.js CHANGED
@@ -1,75 +1,75 @@
1
1
  // eslint-disable-next-line import/prefer-default-export
2
2
  export const constants = {
3
- authTokenName: "tdx-token",
4
- collectionOperation: {
5
- add: "ADD",
6
- remove: "REMOVE",
7
- update: "UPDATE",
8
- },
9
- crypto: {
10
- subjectDefaults: [
11
- {
12
- name: "countryName",
13
- value: "GB",
14
- },
15
- {
16
- name: "localityName",
17
- value: "Southampton",
18
- },
19
- {
20
- name: "organizationName",
21
- value: "nquiringminds Ltd",
22
- },
23
- ],
24
- userIdOID: "0.9.2342.19200300.100.1.1",
25
- },
26
- defaultDIDHostName: "cloud.tdxvolt.com",
27
- didServiceType: {
28
- voltConfig: "tdx-volt-config",
29
- },
30
- identityType: {
31
- did: "did",
32
- volt: "volt",
33
- },
34
- onlineStatus: {
35
- offline: "offline",
36
- online: "online",
37
- unknown: "unknown",
38
- },
39
- publicKeyPrefix: "-----BEGIN PUBLIC KEY-----",
40
- privateKeyPrefix: "-----BEGIN RSA PRIVATE KEY-----",
41
- privateEncryptedKeyPrefix: "-----BEGIN ENCRYPTED PRIVATE KEY-----",
42
- resource: {
43
- identity: "ident",
44
- identityView: "ident-view",
45
- proxy: "proxy",
46
- proxyView: "proxy-view",
47
- vc: "vc",
48
- vcView: "vc-view",
49
- },
50
- voltStatus: {
51
- pending: "",
52
- online: "online",
53
- offline: "offline",
54
- deleted: "deleted",
55
- revoked: "revoked",
56
- },
57
- /**
58
- * These top-level service types should map to a protobuf definition
59
- * in @tdxvolt/volt-proto.
60
- */
61
- serviceType: {
62
- fileAPI: "tdx.volt_api.volt.v1.FileAPI",
63
- sqliteServerAPI: "tdx.volt_api.data.v1.SqliteServerAPI",
64
- sqliteDatabaseAPI: "tdx.volt_api.data.v1.SqliteDatabaseAPI",
65
- ssiAPI: "tdx.volt_api.ssi.v1.SsiAPI",
66
- relayAPI: "tdx.volt_api.relay.v1.RelayAPI",
67
- voltAPI: "tdx.volt_api.volt.v1.VoltAPI",
68
- wireAPI: "tdx.volt_api.volt.v1.WireAPI",
69
- },
70
- tunnelInvokeMethod: "/tdx.volt_api.volt.v1.VoltAPI/Invoke",
71
- tunnelPingInterval: 10000,
72
- tunnelPingTimeout: 2500,
73
- tunnelReconnectAfter: 30000,
74
- tunnelTimeoutInterval: 10000 * 2, // This must always be greater than `tunnelPingInterval`.
3
+ authTokenName: "tdx-token",
4
+ collectionOperation: {
5
+ add: "ADD",
6
+ remove: "REMOVE",
7
+ update: "UPDATE",
8
+ },
9
+ crypto: {
10
+ subjectDefaults: [
11
+ {
12
+ name: "countryName",
13
+ value: "GB",
14
+ },
15
+ {
16
+ name: "localityName",
17
+ value: "Southampton",
18
+ },
19
+ {
20
+ name: "organizationName",
21
+ value: "nquiringminds Ltd",
22
+ },
23
+ ],
24
+ userIdOID: "0.9.2342.19200300.100.1.1",
25
+ },
26
+ defaultDIDHostName: "cloud.tdxvolt.com",
27
+ didServiceType: {
28
+ voltConfig: "tdx-volt-config",
29
+ },
30
+ identityType: {
31
+ did: "did",
32
+ volt: "volt",
33
+ },
34
+ onlineStatus: {
35
+ offline: "offline",
36
+ online: "online",
37
+ unknown: "unknown",
38
+ },
39
+ publicKeyPrefix: "-----BEGIN PUBLIC KEY-----",
40
+ privateKeyPrefix: "-----BEGIN RSA PRIVATE KEY-----",
41
+ privateEncryptedKeyPrefix: "-----BEGIN ENCRYPTED PRIVATE KEY-----",
42
+ resource: {
43
+ identity: "ident",
44
+ identityView: "ident-view",
45
+ proxy: "proxy",
46
+ proxyView: "proxy-view",
47
+ vc: "vc",
48
+ vcView: "vc-view",
49
+ },
50
+ voltStatus: {
51
+ pending: "",
52
+ online: "online",
53
+ offline: "offline",
54
+ deleted: "deleted",
55
+ revoked: "revoked",
56
+ },
57
+ /**
58
+ * These top-level service types should map to a protobuf definition
59
+ * in @tdxvolt/volt-proto.
60
+ */
61
+ serviceType: {
62
+ fileAPI: "tdx.volt_api.volt.v1.FileAPI",
63
+ sqliteServerAPI: "tdx.volt_api.data.v1.SqliteServerAPI",
64
+ sqliteDatabaseAPI: "tdx.volt_api.data.v1.SqliteDatabaseAPI",
65
+ ssiAPI: "tdx.volt_api.ssi.v1.SsiAPI",
66
+ relayAPI: "tdx.volt_api.relay.v1.RelayAPI",
67
+ voltAPI: "tdx.volt_api.volt.v1.VoltAPI",
68
+ wireAPI: "tdx.volt_api.volt.v1.WireAPI",
69
+ },
70
+ tunnelInvokeMethod: "/tdx.volt_api.volt.v1.VoltAPI/Invoke",
71
+ tunnelPingInterval: 10000,
72
+ tunnelPingTimeout: 2500,
73
+ tunnelReconnectAfter: 30000,
74
+ tunnelTimeoutInterval: 10000 * 2, // This must always be greater than `tunnelPingInterval`.
75
75
  };
package/src/grpc-call.js CHANGED
@@ -1,15 +1,19 @@
1
1
  /* eslint-disable no-underscore-dangle */
2
2
  import debug from "debug";
3
3
  import EventEmitter from "events";
4
- import {VoltCredential} from "./volt-credential.js";
4
+ import { VoltCredential } from "./volt-credential.js";
5
5
 
6
6
  const log = debug("volt-client-grpc:grpc-call");
7
7
 
8
- function _prepareInvokeRequest(request, method, methodType = "METHOD_TYPE_UNARY") {
8
+ function _prepareInvokeRequest(
9
+ request,
10
+ method,
11
+ methodType = "METHOD_TYPE_UNARY",
12
+ ) {
9
13
  const meta = this._voltClient.credential.getIdentityMetadata(
10
14
  this._voltClient.grpc,
11
15
  this._voltClient.voltConfig.id,
12
- this._voltClient.isRemote
16
+ this._voltClient.isRemote,
13
17
  );
14
18
  let invokeRequest;
15
19
  let callMethod = method;
@@ -38,7 +42,7 @@ function _prepareInvokeRequest(request, method, methodType = "METHOD_TYPE_UNARY"
38
42
  const invokePayload = VoltCredential.aesEncrypt(
39
43
  tunnelMethod.responseSerialize(tunnelResp),
40
44
  meta.sharedKey.key,
41
- meta.sharedKey.iv
45
+ meta.sharedKey.iv,
42
46
  );
43
47
 
44
48
  log("target volt is %s", this._voltClient.credential.voltFingerprint);
@@ -50,8 +54,13 @@ function _prepareInvokeRequest(request, method, methodType = "METHOD_TYPE_UNARY"
50
54
 
51
55
  callMethod = "Invoke";
52
56
  } else if (this._voltClient.isRemote && meta.sharedKey.key) {
53
- const requestPayload = this._grpcClient[method].requestSerializeOriginal(request);
54
- invokeRequest = VoltCredential.aesEncrypt(requestPayload, meta.sharedKey.key, meta.sharedKey.iv);
57
+ const requestPayload =
58
+ this._grpcClient[method].requestSerializeOriginal(request);
59
+ invokeRequest = VoltCredential.aesEncrypt(
60
+ requestPayload,
61
+ meta.sharedKey.key,
62
+ meta.sharedKey.iv,
63
+ );
55
64
  } else {
56
65
  invokeRequest = request;
57
66
  }
@@ -68,7 +77,8 @@ function _preparePayloadRequest(request, invokeInfo) {
68
77
  let payloadRequest;
69
78
 
70
79
  if (this._voltClient.isVoltRelay) {
71
- const requestPayload = this._grpcClient[invokeInfo.methodName].requestSerialize(request);
80
+ const requestPayload =
81
+ this._grpcClient[invokeInfo.methodName].requestSerialize(request);
72
82
  const tunnelMethod = this._grpcClient.Tunnel;
73
83
 
74
84
  const tunnelResp = {
@@ -80,18 +90,19 @@ function _preparePayloadRequest(request, invokeInfo) {
80
90
  const invokePayload = VoltCredential.aesEncrypt(
81
91
  tunnelMethod.responseSerialize(tunnelResp),
82
92
  invokeInfo.meta.sharedKey.key,
83
- invokeInfo.meta.sharedKey.iv
93
+ invokeInfo.meta.sharedKey.iv,
84
94
  );
85
95
 
86
96
  payloadRequest = {
87
97
  payload: invokePayload,
88
98
  };
89
99
  } else if (this._voltClient.isRemote && invokeInfo.meta.sharedKey.key) {
90
- const requestPayload = this._grpcClient[invokeInfo.methodName].requestSerializeOriginal(request);
100
+ const requestPayload =
101
+ this._grpcClient[invokeInfo.methodName].requestSerializeOriginal(request);
91
102
  payloadRequest = VoltCredential.aesEncrypt(
92
103
  requestPayload,
93
104
  invokeInfo.meta.sharedKey.key,
94
- invokeInfo.meta.sharedKey.iv
105
+ invokeInfo.meta.sharedKey.iv,
95
106
  );
96
107
  } else {
97
108
  payloadRequest = request;
@@ -105,11 +116,17 @@ function _parseResponse(method, meta, response) {
105
116
 
106
117
  if (this._voltClient.isVoltRelay) {
107
118
  if (response.payload) {
108
- const decryptedPayload = VoltCredential.aesDecrypt(response.payload, meta.sharedKey.key, meta.sharedKey.iv);
119
+ const decryptedPayload = VoltCredential.aesDecrypt(
120
+ response.payload,
121
+ meta.sharedKey.key,
122
+ meta.sharedKey.iv,
123
+ );
109
124
  const tunnelMethod = this._grpcClient.Tunnel;
110
125
  const responsePayload = tunnelMethod.requestDeserialize(decryptedPayload);
111
126
  if (responsePayload.payload === "method_payload") {
112
- invokeResponse.payload = this._grpcClient[method].responseDeserialize(responsePayload.method_payload.payload);
127
+ invokeResponse.payload = this._grpcClient[method].responseDeserialize(
128
+ responsePayload.method_payload.payload,
129
+ );
113
130
  } else if (responsePayload.payload === "method_end") {
114
131
  invokeResponse = responsePayload.method_end;
115
132
  } else {
@@ -121,18 +138,28 @@ function _parseResponse(method, meta, response) {
121
138
  "Tunnel status received: %s, code %d, description: %s",
122
139
  response.status.message,
123
140
  response.status.code,
124
- response.status.description || "n/a"
141
+ response.status.description || "n/a",
125
142
  );
126
- invokeResponse = {ended: true, error: `Error in tunnel: ${response.status.message}`};
143
+ invokeResponse = {
144
+ ended: true,
145
+ error: `Error in tunnel: ${response.status.message}`,
146
+ };
127
147
  } else {
128
148
  invokeResponse.methodId = response.invoke_id;
129
149
  log("started method %d for %s", invokeResponse.methodId, method);
130
150
  }
131
151
  } else if (this._voltClient.isRemote && meta.sharedKey.key) {
132
- const decryptedPayload = VoltCredential.aesDecrypt(response, meta.sharedKey.key, meta.sharedKey.iv);
133
- invokeResponse = {payload: this._grpcClient[method].responseDeserializeOriginal(decryptedPayload)};
152
+ const decryptedPayload = VoltCredential.aesDecrypt(
153
+ response,
154
+ meta.sharedKey.key,
155
+ meta.sharedKey.iv,
156
+ );
157
+ invokeResponse = {
158
+ payload:
159
+ this._grpcClient[method].responseDeserializeOriginal(decryptedPayload),
160
+ };
134
161
  } else {
135
- invokeResponse = {payload: response};
162
+ invokeResponse = { payload: response };
136
163
  }
137
164
 
138
165
  return invokeResponse;
@@ -145,92 +172,79 @@ export default class GRPCCall extends EventEmitter {
145
172
  this._methodType = methodType;
146
173
  this._call = null;
147
174
  this._voltClient = voltClient;
148
- this._lastResponse = null;
149
- this._promise = null;
150
- this._reject = null;
151
175
  }
152
176
 
153
- reject(err) {
154
- if (this._reject) {
155
- this._reject(err);
177
+ start(grpcClient, request) {
178
+ this._grpcClient = grpcClient;
179
+
180
+ this._initialRequest = _prepareInvokeRequest.call(
181
+ this,
182
+ request,
183
+ this._methodName,
184
+ this._methodType,
185
+ );
186
+
187
+ if (!this._grpcClient[this._initialRequest.method]) {
188
+ return reject(
189
+ new Error(`method not found: '${this._initialRequest.method}'`),
190
+ );
156
191
  }
157
- }
158
192
 
159
- wait() {
160
- return this._promise;
161
- }
193
+ this._call = this._grpcClient[this._initialRequest.method](
194
+ this._initialRequest.meta.metadata,
195
+ );
162
196
 
163
- start(grpcClient, request, responseCB) {
164
- this._promise = new Promise((resolve, reject) => {
165
- this._grpcClient = grpcClient;
166
- this._reject = reject;
167
-
168
- const callbackIntercept = (err, response) => {
169
- log("received callback for method %s", this._methodName);
170
- if (err) {
171
- reject(err);
172
- } else if (response) {
173
- this._lastResponse = response;
174
- if (responseCB) {
175
- responseCB(err, response);
197
+ this._call.on("data", (streamResponse) => {
198
+ try {
199
+ const parsedResponse = _parseResponse.call(
200
+ this,
201
+ this._methodName,
202
+ this._initialRequest.meta,
203
+ streamResponse,
204
+ );
205
+ if (parsedResponse.payload) {
206
+ this.emit("data", parsedResponse.payload);
207
+ } else if (parsedResponse.ended) {
208
+ if (parsedResponse.error) {
209
+ this.emit("error", new Error(parsedResponse.error));
210
+ } else {
211
+ // Not sure this is necessary - we should receive "end" from grpc soon...
212
+ this.emit("end");
176
213
  }
177
214
  } else {
178
- this.end();
179
- resolve(this._lastResponse);
215
+ log("method id is %s", parsedResponse.methodId);
180
216
  }
181
- };
217
+ } catch (err) {
218
+ log("failure processing connect response: %s", err.message);
219
+ this.emit("error", err);
220
+ }
221
+ });
182
222
 
183
- this._initialRequest = _prepareInvokeRequest.call(this, request, this._methodName, this._methodType);
223
+ this._call.on("error", (err) => {
224
+ log("ERROR - intercepted stream error %s", err.message);
225
+ this.emit("error", err);
226
+ });
184
227
 
185
- if (!this._grpcClient[this._initialRequest.method]) {
186
- return reject(new Error(`method not found: '${this._initialRequest.method}'`));
187
- }
228
+ this._call.on("finish", () => {
229
+ // The read side has ended (i.e. we called end()).
230
+ log("finished call %s", this._methodName);
231
+ this.emit("finish");
232
+ });
188
233
 
189
- this._call = this._grpcClient[this._initialRequest.method](this._initialRequest.meta.metadata, callbackIntercept);
190
-
191
- this._call.on("data", (streamResponse) => {
192
- try {
193
- const parsedResponse = _parseResponse.call(this, this._methodName, this._initialRequest.meta, streamResponse);
194
- if (parsedResponse.payload) {
195
- callbackIntercept(null, parsedResponse.payload);
196
- } else if (parsedResponse.ended) {
197
- if (parsedResponse.error) {
198
- callbackIntercept(new Error(parsedResponse.error), null);
199
- } else {
200
- callbackIntercept(null, null);
201
- }
202
- } else {
203
- log("method id is %s", parsedResponse.methodId);
204
- }
205
- } catch (err) {
206
- log("failure processing connect response: %s", err.message);
207
- this.emit("error", err);
208
- }
209
- });
210
-
211
- this._call.on("error", (err) => {
212
- log("ERROR - intercepted stream error %s", err.message);
213
- callbackIntercept(err, null);
214
- });
215
-
216
- this._call.on("finish", () => {
217
- // The read side has ended (i.e. we called end()).
218
- log("finished call %s", this._methodName);
219
- this.emit("finish");
220
- });
221
-
222
- this._call.on("end", () => {
223
- // The remote peer ended the stream.
224
- log("ended call %s, method id: %s", this._methodName, this._initialRequest.methodId || "n/a - not tunnelling");
225
- this.emit("end");
226
- callbackIntercept(null, null);
227
- });
228
-
229
- if (this._initialRequest.request) {
230
- this._call.write(this._initialRequest.request);
231
- }
234
+ this._call.on("end", () => {
235
+ // The remote peer ended the stream.
236
+ log(
237
+ "ended call %s, method id: %s",
238
+ this._methodName,
239
+ this._initialRequest.methodId || "n/a - not tunnelling",
240
+ );
241
+ this.emit("end");
232
242
  });
233
243
 
244
+ if (this._initialRequest.request) {
245
+ this._call.write(this._initialRequest.request);
246
+ }
247
+
234
248
  return this;
235
249
  }
236
250
 
@@ -251,10 +265,19 @@ export default class GRPCCall extends EventEmitter {
251
265
  log("ERROR - call not initialised");
252
266
  throw new Error("call not initialised");
253
267
  } else if (this._initialRequest) {
254
- const payloadRequest = _preparePayloadRequest.call(this, request, this._initialRequest);
268
+ const payloadRequest = _preparePayloadRequest.call(
269
+ this,
270
+ request,
271
+ this._initialRequest,
272
+ );
255
273
  return this._call.write(payloadRequest);
256
274
  } else {
257
- this._initialRequest = _prepareInvokeRequest.call(this, request, this._methodName, this._methodType);
275
+ this._initialRequest = _prepareInvokeRequest.call(
276
+ this,
277
+ request,
278
+ this._methodName,
279
+ this._methodType,
280
+ );
258
281
 
259
282
  return this._call.write(this._initialRequest.request);
260
283
  }