@twin.org/api-service 0.0.3-next.28 → 0.0.3-next.29
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/dist/es/hostingService.js +208 -5
- package/dist/es/hostingService.js.map +1 -1
- package/dist/es/models/IHostingServiceConfig.js.map +1 -1
- package/dist/es/models/IHostingServiceConstructorOptions.js.map +1 -1
- package/dist/types/hostingService.d.ts +62 -1
- package/dist/types/models/IHostingServiceConfig.d.ts +10 -0
- package/dist/types/models/IHostingServiceConstructorOptions.d.ts +5 -0
- package/docs/changelog.md +14 -0
- package/docs/reference/classes/HostingService.md +266 -0
- package/docs/reference/interfaces/IHostingServiceConfig.md +28 -0
- package/docs/reference/interfaces/IHostingServiceConstructorOptions.md +14 -0
- package/locales/en.json +10 -1
- package/package.json +3 -2
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0.
|
|
3
3
|
import { HttpUrlHelper } from "@twin.org/api-models";
|
|
4
4
|
import { ContextIdKeys, ContextIdStore } from "@twin.org/context";
|
|
5
|
-
import { ComponentFactory, Guards, Is } from "@twin.org/core";
|
|
5
|
+
import { BaseError, ComponentFactory, Converter, GeneralError, Guards, Is, ObjectHelper, RandomHelper, Uint8ArrayHelper } from "@twin.org/core";
|
|
6
|
+
import { VaultConnectorFactory, VaultEncryptionType } from "@twin.org/vault-models";
|
|
6
7
|
/**
|
|
7
8
|
* The hosting service for the server.
|
|
8
9
|
*/
|
|
@@ -11,11 +12,33 @@ export class HostingService {
|
|
|
11
12
|
* Runtime name for the class.
|
|
12
13
|
*/
|
|
13
14
|
static CLASS_NAME = "HostingService";
|
|
15
|
+
/**
|
|
16
|
+
* The prefix to use for encrypted query parameters when encrypting/decrypting.
|
|
17
|
+
* This is used to identify which query parameters have been encrypted.
|
|
18
|
+
* The actual key used in the query will be "x-enc-{originalKey}".
|
|
19
|
+
* @internal
|
|
20
|
+
*/
|
|
21
|
+
static _KEY_PREFIX = "x-enc-";
|
|
22
|
+
/**
|
|
23
|
+
* The default name for the tenant token query parameter.
|
|
24
|
+
* @internal
|
|
25
|
+
*/
|
|
26
|
+
static _DEFAULT_TENANT_TOKEN_NAME = "tenant-token";
|
|
27
|
+
/**
|
|
28
|
+
* The default name for the parameter encryption key query parameter.
|
|
29
|
+
* @internal
|
|
30
|
+
*/
|
|
31
|
+
static _DEFAULT_PARAM_ENCRYPTION_KEY_NAME = "param-encryption";
|
|
14
32
|
/**
|
|
15
33
|
* The tenant admin component.
|
|
16
34
|
* @internal
|
|
17
35
|
*/
|
|
18
|
-
|
|
36
|
+
_tenantAdminComponentType;
|
|
37
|
+
/**
|
|
38
|
+
* The vault connector type.
|
|
39
|
+
* @internal
|
|
40
|
+
*/
|
|
41
|
+
_vaultConnectorType;
|
|
19
42
|
/**
|
|
20
43
|
* The local origin URL e.g. "http://localhost:3000".
|
|
21
44
|
* @internal
|
|
@@ -26,6 +49,21 @@ export class HostingService {
|
|
|
26
49
|
* @internal
|
|
27
50
|
*/
|
|
28
51
|
_publicOrigin;
|
|
52
|
+
/**
|
|
53
|
+
* The name of the key to retrieve from the vault for encryption/decryption of parameters.
|
|
54
|
+
* @internal
|
|
55
|
+
*/
|
|
56
|
+
_paramEncryptionKeyName;
|
|
57
|
+
/**
|
|
58
|
+
* The query param name carrying the encrypted tenant token.
|
|
59
|
+
* @internal
|
|
60
|
+
*/
|
|
61
|
+
_tenantTokenName;
|
|
62
|
+
/**
|
|
63
|
+
* The node identity, captured at start.
|
|
64
|
+
* @internal
|
|
65
|
+
*/
|
|
66
|
+
_nodeId;
|
|
29
67
|
/**
|
|
30
68
|
* Create a new instance of HostingService.
|
|
31
69
|
* @param options The options to create the service.
|
|
@@ -34,9 +72,14 @@ export class HostingService {
|
|
|
34
72
|
Guards.object(HostingService.CLASS_NAME, "options", options);
|
|
35
73
|
Guards.object(HostingService.CLASS_NAME, "options.config", options.config);
|
|
36
74
|
Guards.stringValue(HostingService.CLASS_NAME, "options.config.localOrigin", options.config.localOrigin);
|
|
37
|
-
this.
|
|
75
|
+
this._tenantAdminComponentType = options?.tenantAdminComponentType ?? "tenant-admin";
|
|
76
|
+
this._vaultConnectorType = options?.vaultConnectorType ?? "vault";
|
|
38
77
|
this._localOrigin = options.config.localOrigin;
|
|
39
78
|
this._publicOrigin = options.config.publicOrigin;
|
|
79
|
+
this._paramEncryptionKeyName =
|
|
80
|
+
options.config.paramEncryptionKeyName ?? HostingService._DEFAULT_PARAM_ENCRYPTION_KEY_NAME;
|
|
81
|
+
this._tenantTokenName =
|
|
82
|
+
options.config.tenantTokenName ?? HostingService._DEFAULT_TENANT_TOKEN_NAME;
|
|
40
83
|
}
|
|
41
84
|
/**
|
|
42
85
|
* Returns the class name of the component.
|
|
@@ -45,6 +88,15 @@ export class HostingService {
|
|
|
45
88
|
className() {
|
|
46
89
|
return HostingService.CLASS_NAME;
|
|
47
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* The component needs to be started when the node is initialized.
|
|
93
|
+
* @param nodeLoggingComponentType The node logging component type.
|
|
94
|
+
* @returns Nothing.
|
|
95
|
+
*/
|
|
96
|
+
async start(nodeLoggingComponentType) {
|
|
97
|
+
const contextIds = await ContextIdStore.getContextIds();
|
|
98
|
+
this._nodeId = contextIds?.[ContextIdKeys.Node];
|
|
99
|
+
}
|
|
48
100
|
/**
|
|
49
101
|
* Get the public origin for the hosting.
|
|
50
102
|
* @param serverRequestUrl The url of the current server request if there is one.
|
|
@@ -75,10 +127,11 @@ export class HostingService {
|
|
|
75
127
|
*/
|
|
76
128
|
async getTenantOrigin(tenantId) {
|
|
77
129
|
Guards.stringHexLength(HostingService.CLASS_NAME, "tenantId", tenantId, 32);
|
|
78
|
-
|
|
130
|
+
const tenantAdminComponent = ComponentFactory.getIfExists(this._tenantAdminComponentType);
|
|
131
|
+
if (Is.empty(tenantAdminComponent)) {
|
|
79
132
|
return undefined;
|
|
80
133
|
}
|
|
81
|
-
const tenant = await
|
|
134
|
+
const tenant = await tenantAdminComponent.get(tenantId);
|
|
82
135
|
return tenant?.publicOrigin;
|
|
83
136
|
}
|
|
84
137
|
/**
|
|
@@ -90,5 +143,155 @@ export class HostingService {
|
|
|
90
143
|
const publicOrigin = await this.getPublicOrigin(url);
|
|
91
144
|
return HttpUrlHelper.replaceOrigin(url, publicOrigin);
|
|
92
145
|
}
|
|
146
|
+
/**
|
|
147
|
+
* Add encrypted key/value pairs to a URL's query string.
|
|
148
|
+
* Existing query parameters on the URL are preserved; the provided params are
|
|
149
|
+
* merged in and then encrypted before being written back to the URL.
|
|
150
|
+
* @param url The base URL to add parameters to.
|
|
151
|
+
* @param params The key/value pairs to encrypt and append.
|
|
152
|
+
* @returns The URL with the encrypted parameters added.
|
|
153
|
+
*/
|
|
154
|
+
async addEncryptedParamsToUrl(url, params) {
|
|
155
|
+
const urlObj = new URL(url);
|
|
156
|
+
const query = {};
|
|
157
|
+
for (const [key, value] of urlObj.searchParams.entries()) {
|
|
158
|
+
query[key] = value;
|
|
159
|
+
}
|
|
160
|
+
const keysToEncrypt = Object.keys(params);
|
|
161
|
+
for (const [key, value] of Object.entries(params)) {
|
|
162
|
+
query[key] = value;
|
|
163
|
+
}
|
|
164
|
+
await this.encryptQueryParams(query, keysToEncrypt);
|
|
165
|
+
urlObj.search = "";
|
|
166
|
+
for (const [key, value] of Object.entries(query)) {
|
|
167
|
+
urlObj.searchParams.set(key, value);
|
|
168
|
+
}
|
|
169
|
+
return urlObj.toString();
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Decrypt specified keys from a query parameter object and return their plain-text values.
|
|
173
|
+
* @param queryParams The HTTP request query containing the encrypted parameters.
|
|
174
|
+
* @param keys The keys to decrypt.
|
|
175
|
+
* @returns A map of the decrypted key/value pairs that were present.
|
|
176
|
+
*/
|
|
177
|
+
async getDecryptedParamsFromQueryParams(queryParams, keys) {
|
|
178
|
+
const queryParamsClone = ObjectHelper.clone(queryParams) ?? {};
|
|
179
|
+
await this.decryptQueryParams(queryParamsClone, keys);
|
|
180
|
+
const result = {};
|
|
181
|
+
for (const key of keys) {
|
|
182
|
+
if (Is.stringValue(queryParamsClone[key])) {
|
|
183
|
+
result[key] = queryParamsClone[key];
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return result;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Encrypt the tenant id and append it as a query parameter to the given URL.
|
|
190
|
+
* @param url The URL to append the encrypted tenant token to.
|
|
191
|
+
* @param tenantId The tenant identifier to encrypt and add.
|
|
192
|
+
* @returns The URL with the encrypted tenant token added as a query parameter.
|
|
193
|
+
*/
|
|
194
|
+
async addTenantTokenToUrl(url, tenantId) {
|
|
195
|
+
return this.addEncryptedParamsToUrl(url, { [this._tenantTokenName]: tenantId });
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Get the tenant token from the query parameters.
|
|
199
|
+
* @param queryParams The HTTP request query containing the parameters.
|
|
200
|
+
* @returns The tenant token if it exists.
|
|
201
|
+
*/
|
|
202
|
+
async getTenantTokenFromQueryParams(queryParams) {
|
|
203
|
+
const decrypted = await this.getDecryptedParamsFromQueryParams(queryParams, [
|
|
204
|
+
this._tenantTokenName
|
|
205
|
+
]);
|
|
206
|
+
return decrypted[this._tenantTokenName];
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Encrypt query parameters using the hosting component's encryption mechanism.
|
|
210
|
+
* @param httpRequestQuery The HTTP request query containing the parameters to encrypt.
|
|
211
|
+
* @param keys The keys of the parameters to encrypt.
|
|
212
|
+
* @returns A promise that resolves when the query parameters have been encrypted.
|
|
213
|
+
*/
|
|
214
|
+
async encryptQueryParams(httpRequestQuery, keys) {
|
|
215
|
+
if (Is.empty(httpRequestQuery)) {
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
for (const key of keys) {
|
|
219
|
+
if (Is.stringValue(httpRequestQuery[key])) {
|
|
220
|
+
const encryptedValue = await this.encryptParam(httpRequestQuery[key]);
|
|
221
|
+
httpRequestQuery[`${HostingService._KEY_PREFIX}${key}`] = encryptedValue;
|
|
222
|
+
delete httpRequestQuery[key];
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Decrypt query parameters using the hosting component's encryption mechanism.
|
|
228
|
+
* @param httpRequestQuery The HTTP request query containing the encrypted values.
|
|
229
|
+
* @param keys The keys of the parameters to decrypt.
|
|
230
|
+
* @returns A promise that resolves when the query parameters have been decrypted.
|
|
231
|
+
*/
|
|
232
|
+
async decryptQueryParams(httpRequestQuery, keys) {
|
|
233
|
+
if (Is.empty(httpRequestQuery)) {
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
for (const key of Object.keys(httpRequestQuery)) {
|
|
237
|
+
if (key.startsWith(HostingService._KEY_PREFIX)) {
|
|
238
|
+
const originalKey = key.slice(HostingService._KEY_PREFIX.length);
|
|
239
|
+
if (keys.includes(originalKey)) {
|
|
240
|
+
const decryptedValue = await this.decryptParam(httpRequestQuery[key]);
|
|
241
|
+
httpRequestQuery[originalKey] = decryptedValue;
|
|
242
|
+
delete httpRequestQuery[key];
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Encrypt a parameter value using the hosting component's encryption mechanism.
|
|
249
|
+
* @param paramValue The value of the parameter to encrypt.
|
|
250
|
+
* @returns A promise that resolves to the encrypted value of the parameter.
|
|
251
|
+
*/
|
|
252
|
+
async encryptParam(paramValue) {
|
|
253
|
+
Guards.stringValue(HostingService.CLASS_NAME, "paramValue", paramValue);
|
|
254
|
+
const vaultConnector = VaultConnectorFactory.getIfExists(this._vaultConnectorType);
|
|
255
|
+
if (Is.empty(vaultConnector) || Is.empty(this._nodeId)) {
|
|
256
|
+
throw new GeneralError(HostingService.CLASS_NAME, "encryptionUnavailable");
|
|
257
|
+
}
|
|
258
|
+
try {
|
|
259
|
+
// Add a salt to the encryption to ensure that the same value encrypted multiple times will yield different results,
|
|
260
|
+
// preventing rainbow table attacks.
|
|
261
|
+
const salt = RandomHelper.generate(8);
|
|
262
|
+
const encryptedParamValue = await vaultConnector.encrypt(`${this._nodeId}/${this._paramEncryptionKeyName}`, VaultEncryptionType.ChaCha20Poly1305, Uint8ArrayHelper.concat([salt, Converter.utf8ToBytes(paramValue)]));
|
|
263
|
+
if (!Is.uint8Array(encryptedParamValue)) {
|
|
264
|
+
throw new GeneralError(HostingService.CLASS_NAME, "encryptionFailed");
|
|
265
|
+
}
|
|
266
|
+
return Converter.bytesToBase64Url(encryptedParamValue);
|
|
267
|
+
}
|
|
268
|
+
catch (err) {
|
|
269
|
+
throw new GeneralError(HostingService.CLASS_NAME, "encryptionFailed", undefined, BaseError.fromError(err));
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Decrypt a parameter value using the hosting component's encryption mechanism.
|
|
274
|
+
* @param encryptedValue The encrypted value of the parameter.
|
|
275
|
+
* @returns A promise that resolves to the decrypted value of the parameter.
|
|
276
|
+
*/
|
|
277
|
+
async decryptParam(encryptedValue) {
|
|
278
|
+
Guards.stringValue(HostingService.CLASS_NAME, "encryptedValue", encryptedValue);
|
|
279
|
+
const vaultConnector = VaultConnectorFactory.getIfExists(this._vaultConnectorType);
|
|
280
|
+
if (Is.empty(vaultConnector) || Is.empty(this._nodeId)) {
|
|
281
|
+
throw new GeneralError(HostingService.CLASS_NAME, "decryptionUnavailable");
|
|
282
|
+
}
|
|
283
|
+
try {
|
|
284
|
+
const encryptedBytes = Converter.base64UrlToBytes(encryptedValue);
|
|
285
|
+
const decryptedBytes = await vaultConnector.decrypt(`${this._nodeId}/${this._paramEncryptionKeyName}`, VaultEncryptionType.ChaCha20Poly1305, encryptedBytes);
|
|
286
|
+
if (!Is.uint8Array(decryptedBytes)) {
|
|
287
|
+
throw new GeneralError(HostingService.CLASS_NAME, "decryptionFailed");
|
|
288
|
+
}
|
|
289
|
+
// The decrypted value is expected to have the salt as the first 8 bytes, which we need to remove before returning the original value.
|
|
290
|
+
return Converter.bytesToUtf8(decryptedBytes.slice(8));
|
|
291
|
+
}
|
|
292
|
+
catch (err) {
|
|
293
|
+
throw new GeneralError(HostingService.CLASS_NAME, "decryptionFailed", undefined, BaseError.fromError(err));
|
|
294
|
+
}
|
|
295
|
+
}
|
|
93
296
|
}
|
|
94
297
|
//# sourceMappingURL=hostingService.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hostingService.js","sourceRoot":"","sources":["../../src/hostingService.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,OAAO,EACN,aAAa,EAGb,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAClE,OAAO,EAAE,gBAAgB,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,gBAAgB,CAAC;AAI9D;;GAEG;AACH,MAAM,OAAO,cAAc;IAC1B;;OAEG;IACI,MAAM,CAAU,UAAU,oBAAoC;IAErE;;;OAGG;IACc,qBAAqB,CAAyB;IAE/D;;;OAGG;IACc,YAAY,CAAS;IAEtC;;;OAGG;IACc,aAAa,CAAU;IAExC;;;OAGG;IACH,YAAY,OAA0C;QACrD,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,UAAU,aAAmB,OAAO,CAAC,CAAC;QACnE,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,UAAU,oBAA0B,OAAO,CAAC,MAAM,CAAC,CAAC;QACjF,MAAM,CAAC,WAAW,CACjB,cAAc,CAAC,UAAU,gCAEzB,OAAO,CAAC,MAAM,CAAC,WAAW,CAC1B,CAAC;QACF,IAAI,CAAC,qBAAqB,GAAG,gBAAgB,CAAC,WAAW,CACxD,OAAO,EAAE,wBAAwB,IAAI,cAAc,CACnD,CAAC;QACF,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC;QAC/C,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC;IAClD,CAAC;IAED;;;OAGG;IACI,SAAS;QACf,OAAO,cAAc,CAAC,UAAU,CAAC;IAClC,CAAC;IAED;;;;OAIG;IACI,KAAK,CAAC,eAAe,CAAC,gBAAyB;QACrD,6EAA6E;QAC7E,kDAAkD;QAClD,MAAM,UAAU,GAAG,MAAM,cAAc,CAAC,aAAa,EAAE,CAAC;QACxD,MAAM,QAAQ,GAAG,UAAU,EAAE,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;QACpD,IAAI,kBAAkB,CAAC;QACvB,IAAI,EAAE,CAAC,WAAW,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC9B,kBAAkB,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC;QAC3D,CAAC;QAED,MAAM,mBAAmB,GAAG,EAAE,CAAC,WAAW,CAAC,gBAAgB,CAAC;YAC3D,CAAC,CAAC,aAAa,CAAC,aAAa,CAAC,gBAAgB,CAAC;YAC/C,CAAC,CAAC,SAAS,CAAC;QAEb,iDAAiD;QACjD,wDAAwD;QACxD,oDAAoD;QACpD,iCAAiC;QACjC,OAAO,kBAAkB,IAAI,IAAI,CAAC,aAAa,IAAI,mBAAmB,IAAI,IAAI,CAAC,YAAY,CAAC;IAC7F,CAAC;IAED;;;;OAIG;IACI,KAAK,CAAC,eAAe,CAAC,QAAgB;QAC5C,MAAM,CAAC,eAAe,CAAC,cAAc,CAAC,UAAU,cAAoB,QAAQ,EAAE,EAAE,CAAC,CAAC;QAElF,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,qBAAqB,CAAC,EAAE,CAAC;YAC1C,OAAO,SAAS,CAAC;QAClB,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,qBAAqB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC9D,OAAO,MAAM,EAAE,YAAY,CAAC;IAC7B,CAAC;IAED;;;;OAIG;IACI,KAAK,CAAC,cAAc,CAAC,GAAW;QACtC,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;QACrD,OAAO,aAAa,CAAC,aAAa,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;IACvD,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport {\n\tHttpUrlHelper,\n\ttype IHostingComponent,\n\ttype ITenantAdminComponent\n} from \"@twin.org/api-models\";\nimport { ContextIdKeys, ContextIdStore } from \"@twin.org/context\";\nimport { ComponentFactory, Guards, Is } from \"@twin.org/core\";\nimport { nameof } from \"@twin.org/nameof\";\nimport type { IHostingServiceConstructorOptions } from \"./models/IHostingServiceConstructorOptions.js\";\n\n/**\n * The hosting service for the server.\n */\nexport class HostingService implements IHostingComponent {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<HostingService>();\n\n\t/**\n\t * The tenant admin component.\n\t * @internal\n\t */\n\tprivate readonly _tenantAdminComponent?: ITenantAdminComponent;\n\n\t/**\n\t * The local origin URL e.g. \"http://localhost:3000\".\n\t * @internal\n\t */\n\tprivate readonly _localOrigin: string;\n\n\t/**\n\t * The APIs public base URL e.g. \"https://api.example.com:1234\".\n\t * @internal\n\t */\n\tprivate readonly _publicOrigin?: string;\n\n\t/**\n\t * Create a new instance of HostingService.\n\t * @param options The options to create the service.\n\t */\n\tconstructor(options: IHostingServiceConstructorOptions) {\n\t\tGuards.object(HostingService.CLASS_NAME, nameof(options), options);\n\t\tGuards.object(HostingService.CLASS_NAME, nameof(options.config), options.config);\n\t\tGuards.stringValue(\n\t\t\tHostingService.CLASS_NAME,\n\t\t\tnameof(options.config.localOrigin),\n\t\t\toptions.config.localOrigin\n\t\t);\n\t\tthis._tenantAdminComponent = ComponentFactory.getIfExists(\n\t\t\toptions?.tenantAdminComponentType ?? \"tenant-admin\"\n\t\t);\n\t\tthis._localOrigin = options.config.localOrigin;\n\t\tthis._publicOrigin = options.config.publicOrigin;\n\t}\n\n\t/**\n\t * Returns the class name of the component.\n\t * @returns The class name of the component.\n\t */\n\tpublic className(): string {\n\t\treturn HostingService.CLASS_NAME;\n\t}\n\n\t/**\n\t * Get the public origin for the hosting.\n\t * @param serverRequestUrl The url of the current server request if there is one.\n\t * @returns The public origin.\n\t */\n\tpublic async getPublicOrigin(serverRequestUrl?: string): Promise<string> {\n\t\t// If there is a tenant admin component, and the context has a tenant id set.\n\t\t// set if the tenant has a specific public origin.\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tconst tenantId = contextIds?.[ContextIdKeys.Tenant];\n\t\tlet tenantPublicOrigin;\n\t\tif (Is.stringValue(tenantId)) {\n\t\t\ttenantPublicOrigin = await this.getTenantOrigin(tenantId);\n\t\t}\n\n\t\tconst serverRequestOrigin = Is.stringValue(serverRequestUrl)\n\t\t\t? HttpUrlHelper.extractOrigin(serverRequestUrl)\n\t\t\t: undefined;\n\n\t\t// If there is a tenant public origin, return it.\n\t\t// If not and the config has a public origin, return it.\n\t\t// Otherwise, use the server request URL if provided\n\t\t// else fallback to local origin.\n\t\treturn tenantPublicOrigin ?? this._publicOrigin ?? serverRequestOrigin ?? this._localOrigin;\n\t}\n\n\t/**\n\t * Get the public origin for the tenant if one exists.\n\t * @param tenantId The tenant identifier.\n\t * @returns The public origin for the tenant.\n\t */\n\tpublic async getTenantOrigin(tenantId: string): Promise<string | undefined> {\n\t\tGuards.stringHexLength(HostingService.CLASS_NAME, nameof(tenantId), tenantId, 32);\n\n\t\tif (Is.empty(this._tenantAdminComponent)) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst tenant = await this._tenantAdminComponent.get(tenantId);\n\t\treturn tenant?.publicOrigin;\n\t}\n\n\t/**\n\t * Build a public url based on the public origin and the url provided.\n\t * @param url The url to build upon the public origin.\n\t * @returns The full url based on the public origin.\n\t */\n\tpublic async buildPublicUrl(url: string): Promise<string> {\n\t\tconst publicOrigin = await this.getPublicOrigin(url);\n\t\treturn HttpUrlHelper.replaceOrigin(url, publicOrigin);\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"hostingService.js","sourceRoot":"","sources":["../../src/hostingService.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport {\n\tHttpUrlHelper,\n\ttype IHostingComponent,\n\ttype IHttpRequestQuery,\n\ttype ITenantAdminComponent\n} from \"@twin.org/api-models\";\nimport { ContextIdKeys, ContextIdStore } from \"@twin.org/context\";\nimport {\n\tBaseError,\n\tComponentFactory,\n\tConverter,\n\tGeneralError,\n\tGuards,\n\tIs,\n\tObjectHelper,\n\tRandomHelper,\n\tUint8ArrayHelper\n} from \"@twin.org/core\";\nimport { nameof } from \"@twin.org/nameof\";\nimport { VaultConnectorFactory, VaultEncryptionType } from \"@twin.org/vault-models\";\nimport type { IHostingServiceConstructorOptions } from \"./models/IHostingServiceConstructorOptions.js\";\n\n/**\n * The hosting service for the server.\n */\nexport class HostingService implements IHostingComponent {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<HostingService>();\n\n\t/**\n\t * The prefix to use for encrypted query parameters when encrypting/decrypting.\n\t * This is used to identify which query parameters have been encrypted.\n\t * The actual key used in the query will be \"x-enc-{originalKey}\".\n\t * @internal\n\t */\n\tprivate static readonly _KEY_PREFIX = \"x-enc-\";\n\n\t/**\n\t * The default name for the tenant token query parameter.\n\t * @internal\n\t */\n\tprivate static readonly _DEFAULT_TENANT_TOKEN_NAME: string = \"tenant-token\";\n\n\t/**\n\t * The default name for the parameter encryption key query parameter.\n\t * @internal\n\t */\n\tprivate static readonly _DEFAULT_PARAM_ENCRYPTION_KEY_NAME: string = \"param-encryption\";\n\n\t/**\n\t * The tenant admin component.\n\t * @internal\n\t */\n\tprivate readonly _tenantAdminComponentType?: string;\n\n\t/**\n\t * The vault connector type.\n\t * @internal\n\t */\n\tprivate readonly _vaultConnectorType?: string;\n\n\t/**\n\t * The local origin URL e.g. \"http://localhost:3000\".\n\t * @internal\n\t */\n\tprivate readonly _localOrigin: string;\n\n\t/**\n\t * The APIs public base URL e.g. \"https://api.example.com:1234\".\n\t * @internal\n\t */\n\tprivate readonly _publicOrigin?: string;\n\n\t/**\n\t * The name of the key to retrieve from the vault for encryption/decryption of parameters.\n\t * @internal\n\t */\n\tprivate readonly _paramEncryptionKeyName: string;\n\n\t/**\n\t * The query param name carrying the encrypted tenant token.\n\t * @internal\n\t */\n\tprivate readonly _tenantTokenName: string;\n\n\t/**\n\t * The node identity, captured at start.\n\t * @internal\n\t */\n\tprivate _nodeId?: string;\n\n\t/**\n\t * Create a new instance of HostingService.\n\t * @param options The options to create the service.\n\t */\n\tconstructor(options: IHostingServiceConstructorOptions) {\n\t\tGuards.object(HostingService.CLASS_NAME, nameof(options), options);\n\t\tGuards.object(HostingService.CLASS_NAME, nameof(options.config), options.config);\n\t\tGuards.stringValue(\n\t\t\tHostingService.CLASS_NAME,\n\t\t\tnameof(options.config.localOrigin),\n\t\t\toptions.config.localOrigin\n\t\t);\n\t\tthis._tenantAdminComponentType = options?.tenantAdminComponentType ?? \"tenant-admin\";\n\t\tthis._vaultConnectorType = options?.vaultConnectorType ?? \"vault\";\n\t\tthis._localOrigin = options.config.localOrigin;\n\t\tthis._publicOrigin = options.config.publicOrigin;\n\t\tthis._paramEncryptionKeyName =\n\t\t\toptions.config.paramEncryptionKeyName ?? HostingService._DEFAULT_PARAM_ENCRYPTION_KEY_NAME;\n\t\tthis._tenantTokenName =\n\t\t\toptions.config.tenantTokenName ?? HostingService._DEFAULT_TENANT_TOKEN_NAME;\n\t}\n\n\t/**\n\t * Returns the class name of the component.\n\t * @returns The class name of the component.\n\t */\n\tpublic className(): string {\n\t\treturn HostingService.CLASS_NAME;\n\t}\n\n\t/**\n\t * The component needs to be started when the node is initialized.\n\t * @param nodeLoggingComponentType The node logging component type.\n\t * @returns Nothing.\n\t */\n\tpublic async start(nodeLoggingComponentType?: string): Promise<void> {\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tthis._nodeId = contextIds?.[ContextIdKeys.Node];\n\t}\n\n\t/**\n\t * Get the public origin for the hosting.\n\t * @param serverRequestUrl The url of the current server request if there is one.\n\t * @returns The public origin.\n\t */\n\tpublic async getPublicOrigin(serverRequestUrl?: string): Promise<string> {\n\t\t// If there is a tenant admin component, and the context has a tenant id set.\n\t\t// set if the tenant has a specific public origin.\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tconst tenantId = contextIds?.[ContextIdKeys.Tenant];\n\t\tlet tenantPublicOrigin;\n\t\tif (Is.stringValue(tenantId)) {\n\t\t\ttenantPublicOrigin = await this.getTenantOrigin(tenantId);\n\t\t}\n\n\t\tconst serverRequestOrigin = Is.stringValue(serverRequestUrl)\n\t\t\t? HttpUrlHelper.extractOrigin(serverRequestUrl)\n\t\t\t: undefined;\n\n\t\t// If there is a tenant public origin, return it.\n\t\t// If not and the config has a public origin, return it.\n\t\t// Otherwise, use the server request URL if provided\n\t\t// else fallback to local origin.\n\t\treturn tenantPublicOrigin ?? this._publicOrigin ?? serverRequestOrigin ?? this._localOrigin;\n\t}\n\n\t/**\n\t * Get the public origin for the tenant if one exists.\n\t * @param tenantId The tenant identifier.\n\t * @returns The public origin for the tenant.\n\t */\n\tpublic async getTenantOrigin(tenantId: string): Promise<string | undefined> {\n\t\tGuards.stringHexLength(HostingService.CLASS_NAME, nameof(tenantId), tenantId, 32);\n\n\t\tconst tenantAdminComponent = ComponentFactory.getIfExists<ITenantAdminComponent>(\n\t\t\tthis._tenantAdminComponentType\n\t\t);\n\n\t\tif (Is.empty(tenantAdminComponent)) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst tenant = await tenantAdminComponent.get(tenantId);\n\t\treturn tenant?.publicOrigin;\n\t}\n\n\t/**\n\t * Build a public url based on the public origin and the url provided.\n\t * @param url The url to build upon the public origin.\n\t * @returns The full url based on the public origin.\n\t */\n\tpublic async buildPublicUrl(url: string): Promise<string> {\n\t\tconst publicOrigin = await this.getPublicOrigin(url);\n\t\treturn HttpUrlHelper.replaceOrigin(url, publicOrigin);\n\t}\n\n\t/**\n\t * Add encrypted key/value pairs to a URL's query string.\n\t * Existing query parameters on the URL are preserved; the provided params are\n\t * merged in and then encrypted before being written back to the URL.\n\t * @param url The base URL to add parameters to.\n\t * @param params The key/value pairs to encrypt and append.\n\t * @returns The URL with the encrypted parameters added.\n\t */\n\tpublic async addEncryptedParamsToUrl(url: string, params: IHttpRequestQuery): Promise<string> {\n\t\tconst urlObj = new URL(url);\n\t\tconst query: IHttpRequestQuery = {};\n\t\tfor (const [key, value] of urlObj.searchParams.entries()) {\n\t\t\tquery[key] = value;\n\t\t}\n\t\tconst keysToEncrypt = Object.keys(params);\n\t\tfor (const [key, value] of Object.entries(params)) {\n\t\t\tquery[key] = value;\n\t\t}\n\t\tawait this.encryptQueryParams(query, keysToEncrypt);\n\t\turlObj.search = \"\";\n\t\tfor (const [key, value] of Object.entries(query)) {\n\t\t\turlObj.searchParams.set(key, value);\n\t\t}\n\t\treturn urlObj.toString();\n\t}\n\n\t/**\n\t * Decrypt specified keys from a query parameter object and return their plain-text values.\n\t * @param queryParams The HTTP request query containing the encrypted parameters.\n\t * @param keys The keys to decrypt.\n\t * @returns A map of the decrypted key/value pairs that were present.\n\t */\n\tpublic async getDecryptedParamsFromQueryParams(\n\t\tqueryParams: IHttpRequestQuery | undefined,\n\t\tkeys: string[]\n\t): Promise<IHttpRequestQuery> {\n\t\tconst queryParamsClone = ObjectHelper.clone(queryParams) ?? {};\n\t\tawait this.decryptQueryParams(queryParamsClone, keys);\n\t\tconst result: IHttpRequestQuery = {};\n\t\tfor (const key of keys) {\n\t\t\tif (Is.stringValue(queryParamsClone[key])) {\n\t\t\t\tresult[key] = queryParamsClone[key];\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * Encrypt the tenant id and append it as a query parameter to the given URL.\n\t * @param url The URL to append the encrypted tenant token to.\n\t * @param tenantId The tenant identifier to encrypt and add.\n\t * @returns The URL with the encrypted tenant token added as a query parameter.\n\t */\n\tpublic async addTenantTokenToUrl(url: string, tenantId: string): Promise<string> {\n\t\treturn this.addEncryptedParamsToUrl(url, { [this._tenantTokenName]: tenantId });\n\t}\n\n\t/**\n\t * Get the tenant token from the query parameters.\n\t * @param queryParams The HTTP request query containing the parameters.\n\t * @returns The tenant token if it exists.\n\t */\n\tpublic async getTenantTokenFromQueryParams(\n\t\tqueryParams: IHttpRequestQuery | undefined\n\t): Promise<string | undefined> {\n\t\tconst decrypted = await this.getDecryptedParamsFromQueryParams(queryParams, [\n\t\t\tthis._tenantTokenName\n\t\t]);\n\t\treturn decrypted[this._tenantTokenName];\n\t}\n\n\t/**\n\t * Encrypt query parameters using the hosting component's encryption mechanism.\n\t * @param httpRequestQuery The HTTP request query containing the parameters to encrypt.\n\t * @param keys The keys of the parameters to encrypt.\n\t * @returns A promise that resolves when the query parameters have been encrypted.\n\t */\n\tpublic async encryptQueryParams(\n\t\thttpRequestQuery: IHttpRequestQuery | undefined,\n\t\tkeys: string[]\n\t): Promise<void> {\n\t\tif (Is.empty(httpRequestQuery)) {\n\t\t\treturn;\n\t\t}\n\n\t\tfor (const key of keys) {\n\t\t\tif (Is.stringValue(httpRequestQuery[key])) {\n\t\t\t\tconst encryptedValue = await this.encryptParam(httpRequestQuery[key]);\n\t\t\t\thttpRequestQuery[`${HostingService._KEY_PREFIX}${key}`] = encryptedValue;\n\t\t\t\tdelete httpRequestQuery[key];\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Decrypt query parameters using the hosting component's encryption mechanism.\n\t * @param httpRequestQuery The HTTP request query containing the encrypted values.\n\t * @param keys The keys of the parameters to decrypt.\n\t * @returns A promise that resolves when the query parameters have been decrypted.\n\t */\n\tpublic async decryptQueryParams(\n\t\thttpRequestQuery: IHttpRequestQuery | undefined,\n\t\tkeys: string[]\n\t): Promise<void> {\n\t\tif (Is.empty(httpRequestQuery)) {\n\t\t\treturn;\n\t\t}\n\t\tfor (const key of Object.keys(httpRequestQuery)) {\n\t\t\tif (key.startsWith(HostingService._KEY_PREFIX)) {\n\t\t\t\tconst originalKey = key.slice(HostingService._KEY_PREFIX.length);\n\n\t\t\t\tif (keys.includes(originalKey)) {\n\t\t\t\t\tconst decryptedValue = await this.decryptParam(httpRequestQuery[key]);\n\t\t\t\t\thttpRequestQuery[originalKey] = decryptedValue;\n\t\t\t\t\tdelete httpRequestQuery[key];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Encrypt a parameter value using the hosting component's encryption mechanism.\n\t * @param paramValue The value of the parameter to encrypt.\n\t * @returns A promise that resolves to the encrypted value of the parameter.\n\t */\n\tpublic async encryptParam(paramValue: string): Promise<string> {\n\t\tGuards.stringValue(HostingService.CLASS_NAME, nameof(paramValue), paramValue);\n\n\t\tconst vaultConnector = VaultConnectorFactory.getIfExists(this._vaultConnectorType);\n\t\tif (Is.empty(vaultConnector) || Is.empty(this._nodeId)) {\n\t\t\tthrow new GeneralError(HostingService.CLASS_NAME, \"encryptionUnavailable\");\n\t\t}\n\n\t\ttry {\n\t\t\t// Add a salt to the encryption to ensure that the same value encrypted multiple times will yield different results,\n\t\t\t// preventing rainbow table attacks.\n\t\t\tconst salt = RandomHelper.generate(8);\n\n\t\t\tconst encryptedParamValue = await vaultConnector.encrypt(\n\t\t\t\t`${this._nodeId}/${this._paramEncryptionKeyName}`,\n\t\t\t\tVaultEncryptionType.ChaCha20Poly1305,\n\t\t\t\tUint8ArrayHelper.concat([salt, Converter.utf8ToBytes(paramValue)])\n\t\t\t);\n\n\t\t\tif (!Is.uint8Array(encryptedParamValue)) {\n\t\t\t\tthrow new GeneralError(HostingService.CLASS_NAME, \"encryptionFailed\");\n\t\t\t}\n\n\t\t\treturn Converter.bytesToBase64Url(encryptedParamValue);\n\t\t} catch (err) {\n\t\t\tthrow new GeneralError(\n\t\t\t\tHostingService.CLASS_NAME,\n\t\t\t\t\"encryptionFailed\",\n\t\t\t\tundefined,\n\t\t\t\tBaseError.fromError(err)\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Decrypt a parameter value using the hosting component's encryption mechanism.\n\t * @param encryptedValue The encrypted value of the parameter.\n\t * @returns A promise that resolves to the decrypted value of the parameter.\n\t */\n\tpublic async decryptParam(encryptedValue: string): Promise<string> {\n\t\tGuards.stringValue(HostingService.CLASS_NAME, nameof(encryptedValue), encryptedValue);\n\n\t\tconst vaultConnector = VaultConnectorFactory.getIfExists(this._vaultConnectorType);\n\t\tif (Is.empty(vaultConnector) || Is.empty(this._nodeId)) {\n\t\t\tthrow new GeneralError(HostingService.CLASS_NAME, \"decryptionUnavailable\");\n\t\t}\n\n\t\ttry {\n\t\t\tconst encryptedBytes = Converter.base64UrlToBytes(encryptedValue);\n\t\t\tconst decryptedBytes = await vaultConnector.decrypt(\n\t\t\t\t`${this._nodeId}/${this._paramEncryptionKeyName}`,\n\t\t\t\tVaultEncryptionType.ChaCha20Poly1305,\n\t\t\t\tencryptedBytes\n\t\t\t);\n\n\t\t\tif (!Is.uint8Array(decryptedBytes)) {\n\t\t\t\tthrow new GeneralError(HostingService.CLASS_NAME, \"decryptionFailed\");\n\t\t\t}\n\n\t\t\t// The decrypted value is expected to have the salt as the first 8 bytes, which we need to remove before returning the original value.\n\t\t\treturn Converter.bytesToUtf8(decryptedBytes.slice(8));\n\t\t} catch (err) {\n\t\t\tthrow new GeneralError(\n\t\t\t\tHostingService.CLASS_NAME,\n\t\t\t\t\"decryptionFailed\",\n\t\t\t\tundefined,\n\t\t\t\tBaseError.fromError(err)\n\t\t\t);\n\t\t}\n\t}\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IHostingServiceConfig.js","sourceRoot":"","sources":["../../../src/models/IHostingServiceConfig.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\n\n/**\n * Configuration for the hosting service.\n */\nexport interface IHostingServiceConfig {\n\t/**\n\t * The local origin, must be provided as a fallback e.g. http://localhost:1234.\n\t */\n\tlocalOrigin: string;\n\n\t/**\n\t * The APIs public base URL e.g. \"https://api.example.com:1234\".\n\t */\n\tpublicOrigin?: string;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"IHostingServiceConfig.js","sourceRoot":"","sources":["../../../src/models/IHostingServiceConfig.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\n\n/**\n * Configuration for the hosting service.\n */\nexport interface IHostingServiceConfig {\n\t/**\n\t * The local origin, must be provided as a fallback e.g. http://localhost:1234.\n\t */\n\tlocalOrigin: string;\n\n\t/**\n\t * The APIs public base URL e.g. \"https://api.example.com:1234\".\n\t */\n\tpublicOrigin?: string;\n\n\t/**\n\t * The name of the key to retrieve from the vault for encryption/decryption of parameters.\n\t * @default param-encryption\n\t */\n\tparamEncryptionKeyName?: string;\n\n\t/**\n\t * The query param name to look for the encrypted tenant token.\n\t * @default tenant-token\n\t */\n\ttenantTokenName?: string;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IHostingServiceConstructorOptions.js","sourceRoot":"","sources":["../../../src/models/IHostingServiceConstructorOptions.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IHostingServiceConfig } from \"./IHostingServiceConfig.js\";\n\n/**\n * Options for the IHostingService constructor.\n */\nexport interface IHostingServiceConstructorOptions {\n\t/**\n\t * The tenant admin component type.\n\t * @default tenant-admin\n\t */\n\ttenantAdminComponentType?: string;\n\n\t/**\n\t * The configuration for the service.\n\t */\n\tconfig: IHostingServiceConfig;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"IHostingServiceConstructorOptions.js","sourceRoot":"","sources":["../../../src/models/IHostingServiceConstructorOptions.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IHostingServiceConfig } from \"./IHostingServiceConfig.js\";\n\n/**\n * Options for the IHostingService constructor.\n */\nexport interface IHostingServiceConstructorOptions {\n\t/**\n\t * The tenant admin component type.\n\t * @default tenant-admin\n\t */\n\ttenantAdminComponentType?: string;\n\n\t/**\n\t * The vault connector type.\n\t * @default vault\n\t */\n\tvaultConnectorType?: string;\n\n\t/**\n\t * The configuration for the service.\n\t */\n\tconfig: IHostingServiceConfig;\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type IHostingComponent } from "@twin.org/api-models";
|
|
1
|
+
import { type IHostingComponent, type IHttpRequestQuery } from "@twin.org/api-models";
|
|
2
2
|
import type { IHostingServiceConstructorOptions } from "./models/IHostingServiceConstructorOptions.js";
|
|
3
3
|
/**
|
|
4
4
|
* The hosting service for the server.
|
|
@@ -18,6 +18,12 @@ export declare class HostingService implements IHostingComponent {
|
|
|
18
18
|
* @returns The class name of the component.
|
|
19
19
|
*/
|
|
20
20
|
className(): string;
|
|
21
|
+
/**
|
|
22
|
+
* The component needs to be started when the node is initialized.
|
|
23
|
+
* @param nodeLoggingComponentType The node logging component type.
|
|
24
|
+
* @returns Nothing.
|
|
25
|
+
*/
|
|
26
|
+
start(nodeLoggingComponentType?: string): Promise<void>;
|
|
21
27
|
/**
|
|
22
28
|
* Get the public origin for the hosting.
|
|
23
29
|
* @param serverRequestUrl The url of the current server request if there is one.
|
|
@@ -36,4 +42,59 @@ export declare class HostingService implements IHostingComponent {
|
|
|
36
42
|
* @returns The full url based on the public origin.
|
|
37
43
|
*/
|
|
38
44
|
buildPublicUrl(url: string): Promise<string>;
|
|
45
|
+
/**
|
|
46
|
+
* Add encrypted key/value pairs to a URL's query string.
|
|
47
|
+
* Existing query parameters on the URL are preserved; the provided params are
|
|
48
|
+
* merged in and then encrypted before being written back to the URL.
|
|
49
|
+
* @param url The base URL to add parameters to.
|
|
50
|
+
* @param params The key/value pairs to encrypt and append.
|
|
51
|
+
* @returns The URL with the encrypted parameters added.
|
|
52
|
+
*/
|
|
53
|
+
addEncryptedParamsToUrl(url: string, params: IHttpRequestQuery): Promise<string>;
|
|
54
|
+
/**
|
|
55
|
+
* Decrypt specified keys from a query parameter object and return their plain-text values.
|
|
56
|
+
* @param queryParams The HTTP request query containing the encrypted parameters.
|
|
57
|
+
* @param keys The keys to decrypt.
|
|
58
|
+
* @returns A map of the decrypted key/value pairs that were present.
|
|
59
|
+
*/
|
|
60
|
+
getDecryptedParamsFromQueryParams(queryParams: IHttpRequestQuery | undefined, keys: string[]): Promise<IHttpRequestQuery>;
|
|
61
|
+
/**
|
|
62
|
+
* Encrypt the tenant id and append it as a query parameter to the given URL.
|
|
63
|
+
* @param url The URL to append the encrypted tenant token to.
|
|
64
|
+
* @param tenantId The tenant identifier to encrypt and add.
|
|
65
|
+
* @returns The URL with the encrypted tenant token added as a query parameter.
|
|
66
|
+
*/
|
|
67
|
+
addTenantTokenToUrl(url: string, tenantId: string): Promise<string>;
|
|
68
|
+
/**
|
|
69
|
+
* Get the tenant token from the query parameters.
|
|
70
|
+
* @param queryParams The HTTP request query containing the parameters.
|
|
71
|
+
* @returns The tenant token if it exists.
|
|
72
|
+
*/
|
|
73
|
+
getTenantTokenFromQueryParams(queryParams: IHttpRequestQuery | undefined): Promise<string | undefined>;
|
|
74
|
+
/**
|
|
75
|
+
* Encrypt query parameters using the hosting component's encryption mechanism.
|
|
76
|
+
* @param httpRequestQuery The HTTP request query containing the parameters to encrypt.
|
|
77
|
+
* @param keys The keys of the parameters to encrypt.
|
|
78
|
+
* @returns A promise that resolves when the query parameters have been encrypted.
|
|
79
|
+
*/
|
|
80
|
+
encryptQueryParams(httpRequestQuery: IHttpRequestQuery | undefined, keys: string[]): Promise<void>;
|
|
81
|
+
/**
|
|
82
|
+
* Decrypt query parameters using the hosting component's encryption mechanism.
|
|
83
|
+
* @param httpRequestQuery The HTTP request query containing the encrypted values.
|
|
84
|
+
* @param keys The keys of the parameters to decrypt.
|
|
85
|
+
* @returns A promise that resolves when the query parameters have been decrypted.
|
|
86
|
+
*/
|
|
87
|
+
decryptQueryParams(httpRequestQuery: IHttpRequestQuery | undefined, keys: string[]): Promise<void>;
|
|
88
|
+
/**
|
|
89
|
+
* Encrypt a parameter value using the hosting component's encryption mechanism.
|
|
90
|
+
* @param paramValue The value of the parameter to encrypt.
|
|
91
|
+
* @returns A promise that resolves to the encrypted value of the parameter.
|
|
92
|
+
*/
|
|
93
|
+
encryptParam(paramValue: string): Promise<string>;
|
|
94
|
+
/**
|
|
95
|
+
* Decrypt a parameter value using the hosting component's encryption mechanism.
|
|
96
|
+
* @param encryptedValue The encrypted value of the parameter.
|
|
97
|
+
* @returns A promise that resolves to the decrypted value of the parameter.
|
|
98
|
+
*/
|
|
99
|
+
decryptParam(encryptedValue: string): Promise<string>;
|
|
39
100
|
}
|
|
@@ -10,4 +10,14 @@ export interface IHostingServiceConfig {
|
|
|
10
10
|
* The APIs public base URL e.g. "https://api.example.com:1234".
|
|
11
11
|
*/
|
|
12
12
|
publicOrigin?: string;
|
|
13
|
+
/**
|
|
14
|
+
* The name of the key to retrieve from the vault for encryption/decryption of parameters.
|
|
15
|
+
* @default param-encryption
|
|
16
|
+
*/
|
|
17
|
+
paramEncryptionKeyName?: string;
|
|
18
|
+
/**
|
|
19
|
+
* The query param name to look for the encrypted tenant token.
|
|
20
|
+
* @default tenant-token
|
|
21
|
+
*/
|
|
22
|
+
tenantTokenName?: string;
|
|
13
23
|
}
|
|
@@ -8,6 +8,11 @@ export interface IHostingServiceConstructorOptions {
|
|
|
8
8
|
* @default tenant-admin
|
|
9
9
|
*/
|
|
10
10
|
tenantAdminComponentType?: string;
|
|
11
|
+
/**
|
|
12
|
+
* The vault connector type.
|
|
13
|
+
* @default vault
|
|
14
|
+
*/
|
|
15
|
+
vaultConnectorType?: string;
|
|
11
16
|
/**
|
|
12
17
|
* The configuration for the service.
|
|
13
18
|
*/
|
package/docs/changelog.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.0.3-next.29](https://github.com/twinfoundation/twin-api/compare/api-service-v0.0.3-next.28...api-service-v0.0.3-next.29) (2026-05-01)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* hosting service ([#109](https://github.com/twinfoundation/twin-api/issues/109)) ([985bf1f](https://github.com/twinfoundation/twin-api/commit/985bf1f5c07b09ecb800df7120bc2422ac7a6d25))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Dependencies
|
|
12
|
+
|
|
13
|
+
* The following workspace dependencies were updated
|
|
14
|
+
* dependencies
|
|
15
|
+
* @twin.org/api-models bumped from 0.0.3-next.28 to 0.0.3-next.29
|
|
16
|
+
|
|
3
17
|
## [0.0.3-next.28](https://github.com/twinfoundation/twin-api/compare/api-service-v0.0.3-next.27...api-service-v0.0.3-next.28) (2026-04-30)
|
|
4
18
|
|
|
5
19
|
|
|
@@ -54,6 +54,32 @@ The class name of the component.
|
|
|
54
54
|
|
|
55
55
|
***
|
|
56
56
|
|
|
57
|
+
### start() {#start}
|
|
58
|
+
|
|
59
|
+
> **start**(`nodeLoggingComponentType?`): `Promise`\<`void`\>
|
|
60
|
+
|
|
61
|
+
The component needs to be started when the node is initialized.
|
|
62
|
+
|
|
63
|
+
#### Parameters
|
|
64
|
+
|
|
65
|
+
##### nodeLoggingComponentType?
|
|
66
|
+
|
|
67
|
+
`string`
|
|
68
|
+
|
|
69
|
+
The node logging component type.
|
|
70
|
+
|
|
71
|
+
#### Returns
|
|
72
|
+
|
|
73
|
+
`Promise`\<`void`\>
|
|
74
|
+
|
|
75
|
+
Nothing.
|
|
76
|
+
|
|
77
|
+
#### Implementation of
|
|
78
|
+
|
|
79
|
+
`IHostingComponent.start`
|
|
80
|
+
|
|
81
|
+
***
|
|
82
|
+
|
|
57
83
|
### getPublicOrigin() {#getpublicorigin}
|
|
58
84
|
|
|
59
85
|
> **getPublicOrigin**(`serverRequestUrl?`): `Promise`\<`string`\>
|
|
@@ -129,3 +155,243 @@ The full url based on the public origin.
|
|
|
129
155
|
#### Implementation of
|
|
130
156
|
|
|
131
157
|
`IHostingComponent.buildPublicUrl`
|
|
158
|
+
|
|
159
|
+
***
|
|
160
|
+
|
|
161
|
+
### addEncryptedParamsToUrl() {#addencryptedparamstourl}
|
|
162
|
+
|
|
163
|
+
> **addEncryptedParamsToUrl**(`url`, `params`): `Promise`\<`string`\>
|
|
164
|
+
|
|
165
|
+
Add encrypted key/value pairs to a URL's query string.
|
|
166
|
+
Existing query parameters on the URL are preserved; the provided params are
|
|
167
|
+
merged in and then encrypted before being written back to the URL.
|
|
168
|
+
|
|
169
|
+
#### Parameters
|
|
170
|
+
|
|
171
|
+
##### url
|
|
172
|
+
|
|
173
|
+
`string`
|
|
174
|
+
|
|
175
|
+
The base URL to add parameters to.
|
|
176
|
+
|
|
177
|
+
##### params
|
|
178
|
+
|
|
179
|
+
`IHttpRequestQuery`
|
|
180
|
+
|
|
181
|
+
The key/value pairs to encrypt and append.
|
|
182
|
+
|
|
183
|
+
#### Returns
|
|
184
|
+
|
|
185
|
+
`Promise`\<`string`\>
|
|
186
|
+
|
|
187
|
+
The URL with the encrypted parameters added.
|
|
188
|
+
|
|
189
|
+
#### Implementation of
|
|
190
|
+
|
|
191
|
+
`IHostingComponent.addEncryptedParamsToUrl`
|
|
192
|
+
|
|
193
|
+
***
|
|
194
|
+
|
|
195
|
+
### getDecryptedParamsFromQueryParams() {#getdecryptedparamsfromqueryparams}
|
|
196
|
+
|
|
197
|
+
> **getDecryptedParamsFromQueryParams**(`queryParams`, `keys`): `Promise`\<`IHttpRequestQuery`\>
|
|
198
|
+
|
|
199
|
+
Decrypt specified keys from a query parameter object and return their plain-text values.
|
|
200
|
+
|
|
201
|
+
#### Parameters
|
|
202
|
+
|
|
203
|
+
##### queryParams
|
|
204
|
+
|
|
205
|
+
`IHttpRequestQuery` \| `undefined`
|
|
206
|
+
|
|
207
|
+
The HTTP request query containing the encrypted parameters.
|
|
208
|
+
|
|
209
|
+
##### keys
|
|
210
|
+
|
|
211
|
+
`string`[]
|
|
212
|
+
|
|
213
|
+
The keys to decrypt.
|
|
214
|
+
|
|
215
|
+
#### Returns
|
|
216
|
+
|
|
217
|
+
`Promise`\<`IHttpRequestQuery`\>
|
|
218
|
+
|
|
219
|
+
A map of the decrypted key/value pairs that were present.
|
|
220
|
+
|
|
221
|
+
#### Implementation of
|
|
222
|
+
|
|
223
|
+
`IHostingComponent.getDecryptedParamsFromQueryParams`
|
|
224
|
+
|
|
225
|
+
***
|
|
226
|
+
|
|
227
|
+
### addTenantTokenToUrl() {#addtenanttokentourl}
|
|
228
|
+
|
|
229
|
+
> **addTenantTokenToUrl**(`url`, `tenantId`): `Promise`\<`string`\>
|
|
230
|
+
|
|
231
|
+
Encrypt the tenant id and append it as a query parameter to the given URL.
|
|
232
|
+
|
|
233
|
+
#### Parameters
|
|
234
|
+
|
|
235
|
+
##### url
|
|
236
|
+
|
|
237
|
+
`string`
|
|
238
|
+
|
|
239
|
+
The URL to append the encrypted tenant token to.
|
|
240
|
+
|
|
241
|
+
##### tenantId
|
|
242
|
+
|
|
243
|
+
`string`
|
|
244
|
+
|
|
245
|
+
The tenant identifier to encrypt and add.
|
|
246
|
+
|
|
247
|
+
#### Returns
|
|
248
|
+
|
|
249
|
+
`Promise`\<`string`\>
|
|
250
|
+
|
|
251
|
+
The URL with the encrypted tenant token added as a query parameter.
|
|
252
|
+
|
|
253
|
+
#### Implementation of
|
|
254
|
+
|
|
255
|
+
`IHostingComponent.addTenantTokenToUrl`
|
|
256
|
+
|
|
257
|
+
***
|
|
258
|
+
|
|
259
|
+
### getTenantTokenFromQueryParams() {#gettenanttokenfromqueryparams}
|
|
260
|
+
|
|
261
|
+
> **getTenantTokenFromQueryParams**(`queryParams`): `Promise`\<`string` \| `undefined`\>
|
|
262
|
+
|
|
263
|
+
Get the tenant token from the query parameters.
|
|
264
|
+
|
|
265
|
+
#### Parameters
|
|
266
|
+
|
|
267
|
+
##### queryParams
|
|
268
|
+
|
|
269
|
+
`IHttpRequestQuery` \| `undefined`
|
|
270
|
+
|
|
271
|
+
The HTTP request query containing the parameters.
|
|
272
|
+
|
|
273
|
+
#### Returns
|
|
274
|
+
|
|
275
|
+
`Promise`\<`string` \| `undefined`\>
|
|
276
|
+
|
|
277
|
+
The tenant token if it exists.
|
|
278
|
+
|
|
279
|
+
#### Implementation of
|
|
280
|
+
|
|
281
|
+
`IHostingComponent.getTenantTokenFromQueryParams`
|
|
282
|
+
|
|
283
|
+
***
|
|
284
|
+
|
|
285
|
+
### encryptQueryParams() {#encryptqueryparams}
|
|
286
|
+
|
|
287
|
+
> **encryptQueryParams**(`httpRequestQuery`, `keys`): `Promise`\<`void`\>
|
|
288
|
+
|
|
289
|
+
Encrypt query parameters using the hosting component's encryption mechanism.
|
|
290
|
+
|
|
291
|
+
#### Parameters
|
|
292
|
+
|
|
293
|
+
##### httpRequestQuery
|
|
294
|
+
|
|
295
|
+
`IHttpRequestQuery` \| `undefined`
|
|
296
|
+
|
|
297
|
+
The HTTP request query containing the parameters to encrypt.
|
|
298
|
+
|
|
299
|
+
##### keys
|
|
300
|
+
|
|
301
|
+
`string`[]
|
|
302
|
+
|
|
303
|
+
The keys of the parameters to encrypt.
|
|
304
|
+
|
|
305
|
+
#### Returns
|
|
306
|
+
|
|
307
|
+
`Promise`\<`void`\>
|
|
308
|
+
|
|
309
|
+
A promise that resolves when the query parameters have been encrypted.
|
|
310
|
+
|
|
311
|
+
#### Implementation of
|
|
312
|
+
|
|
313
|
+
`IHostingComponent.encryptQueryParams`
|
|
314
|
+
|
|
315
|
+
***
|
|
316
|
+
|
|
317
|
+
### decryptQueryParams() {#decryptqueryparams}
|
|
318
|
+
|
|
319
|
+
> **decryptQueryParams**(`httpRequestQuery`, `keys`): `Promise`\<`void`\>
|
|
320
|
+
|
|
321
|
+
Decrypt query parameters using the hosting component's encryption mechanism.
|
|
322
|
+
|
|
323
|
+
#### Parameters
|
|
324
|
+
|
|
325
|
+
##### httpRequestQuery
|
|
326
|
+
|
|
327
|
+
`IHttpRequestQuery` \| `undefined`
|
|
328
|
+
|
|
329
|
+
The HTTP request query containing the encrypted values.
|
|
330
|
+
|
|
331
|
+
##### keys
|
|
332
|
+
|
|
333
|
+
`string`[]
|
|
334
|
+
|
|
335
|
+
The keys of the parameters to decrypt.
|
|
336
|
+
|
|
337
|
+
#### Returns
|
|
338
|
+
|
|
339
|
+
`Promise`\<`void`\>
|
|
340
|
+
|
|
341
|
+
A promise that resolves when the query parameters have been decrypted.
|
|
342
|
+
|
|
343
|
+
#### Implementation of
|
|
344
|
+
|
|
345
|
+
`IHostingComponent.decryptQueryParams`
|
|
346
|
+
|
|
347
|
+
***
|
|
348
|
+
|
|
349
|
+
### encryptParam() {#encryptparam}
|
|
350
|
+
|
|
351
|
+
> **encryptParam**(`paramValue`): `Promise`\<`string`\>
|
|
352
|
+
|
|
353
|
+
Encrypt a parameter value using the hosting component's encryption mechanism.
|
|
354
|
+
|
|
355
|
+
#### Parameters
|
|
356
|
+
|
|
357
|
+
##### paramValue
|
|
358
|
+
|
|
359
|
+
`string`
|
|
360
|
+
|
|
361
|
+
The value of the parameter to encrypt.
|
|
362
|
+
|
|
363
|
+
#### Returns
|
|
364
|
+
|
|
365
|
+
`Promise`\<`string`\>
|
|
366
|
+
|
|
367
|
+
A promise that resolves to the encrypted value of the parameter.
|
|
368
|
+
|
|
369
|
+
#### Implementation of
|
|
370
|
+
|
|
371
|
+
`IHostingComponent.encryptParam`
|
|
372
|
+
|
|
373
|
+
***
|
|
374
|
+
|
|
375
|
+
### decryptParam() {#decryptparam}
|
|
376
|
+
|
|
377
|
+
> **decryptParam**(`encryptedValue`): `Promise`\<`string`\>
|
|
378
|
+
|
|
379
|
+
Decrypt a parameter value using the hosting component's encryption mechanism.
|
|
380
|
+
|
|
381
|
+
#### Parameters
|
|
382
|
+
|
|
383
|
+
##### encryptedValue
|
|
384
|
+
|
|
385
|
+
`string`
|
|
386
|
+
|
|
387
|
+
The encrypted value of the parameter.
|
|
388
|
+
|
|
389
|
+
#### Returns
|
|
390
|
+
|
|
391
|
+
`Promise`\<`string`\>
|
|
392
|
+
|
|
393
|
+
A promise that resolves to the decrypted value of the parameter.
|
|
394
|
+
|
|
395
|
+
#### Implementation of
|
|
396
|
+
|
|
397
|
+
`IHostingComponent.decryptParam`
|
|
@@ -17,3 +17,31 @@ The local origin, must be provided as a fallback e.g. http://localhost:1234.
|
|
|
17
17
|
> `optional` **publicOrigin?**: `string`
|
|
18
18
|
|
|
19
19
|
The APIs public base URL e.g. "https://api.example.com:1234".
|
|
20
|
+
|
|
21
|
+
***
|
|
22
|
+
|
|
23
|
+
### paramEncryptionKeyName? {#paramencryptionkeyname}
|
|
24
|
+
|
|
25
|
+
> `optional` **paramEncryptionKeyName?**: `string`
|
|
26
|
+
|
|
27
|
+
The name of the key to retrieve from the vault for encryption/decryption of parameters.
|
|
28
|
+
|
|
29
|
+
#### Default
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
param-encryption
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
***
|
|
36
|
+
|
|
37
|
+
### tenantTokenName? {#tenanttokenname}
|
|
38
|
+
|
|
39
|
+
> `optional` **tenantTokenName?**: `string`
|
|
40
|
+
|
|
41
|
+
The query param name to look for the encrypted tenant token.
|
|
42
|
+
|
|
43
|
+
#### Default
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
tenant-token
|
|
47
|
+
```
|
|
@@ -18,6 +18,20 @@ tenant-admin
|
|
|
18
18
|
|
|
19
19
|
***
|
|
20
20
|
|
|
21
|
+
### vaultConnectorType? {#vaultconnectortype}
|
|
22
|
+
|
|
23
|
+
> `optional` **vaultConnectorType?**: `string`
|
|
24
|
+
|
|
25
|
+
The vault connector type.
|
|
26
|
+
|
|
27
|
+
#### Default
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
vault
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
***
|
|
34
|
+
|
|
21
35
|
### config {#config}
|
|
22
36
|
|
|
23
37
|
> **config**: [`IHostingServiceConfig`](IHostingServiceConfig.md)
|
package/locales/en.json
CHANGED
|
@@ -1 +1,10 @@
|
|
|
1
|
-
{
|
|
1
|
+
{
|
|
2
|
+
"error": {
|
|
3
|
+
"hostingService": {
|
|
4
|
+
"encryptionUnavailable": "Encryption is unavailable because no vault connector is configured",
|
|
5
|
+
"decryptionUnavailable": "Decryption is unavailable because no vault connector is configured",
|
|
6
|
+
"encryptionFailed": "An error occurred during encryption",
|
|
7
|
+
"decryptionFailed": "An error occurred during decryption"
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@twin.org/api-service",
|
|
3
|
-
"version": "0.0.3-next.
|
|
3
|
+
"version": "0.0.3-next.29",
|
|
4
4
|
"description": "Information and hosting service implementations with generated REST route handlers.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -14,10 +14,11 @@
|
|
|
14
14
|
"node": ">=20.0.0"
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@twin.org/api-models": "0.0.3-next.
|
|
17
|
+
"@twin.org/api-models": "0.0.3-next.29",
|
|
18
18
|
"@twin.org/context": "next",
|
|
19
19
|
"@twin.org/core": "next",
|
|
20
20
|
"@twin.org/nameof": "next",
|
|
21
|
+
"@twin.org/vault-models": "next",
|
|
21
22
|
"@twin.org/web": "next"
|
|
22
23
|
},
|
|
23
24
|
"main": "./dist/es/index.js",
|