@twin.org/node-core 0.0.2-next.1 â 0.0.2-next.10
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/cjs/index.cjs +438 -218
- package/dist/esm/index.mjs +438 -220
- package/dist/types/bootstrap.d.ts +9 -9
- package/dist/types/builders/engineServerEnvBuilder.d.ts +2 -1
- package/dist/types/models/IEngineEnvironmentVariables.d.ts +115 -21
- package/dist/types/models/INodeOptions.d.ts +4 -0
- package/dist/types/models/nodeFeatures.d.ts +4 -0
- package/dist/types/node.d.ts +5 -0
- package/dist/types/utils.d.ts +6 -0
- package/docs/changelog.md +66 -0
- package/docs/reference/functions/{bootstrapAttestationMethod.md â bootstrapSynchronisedStorage.md} +3 -3
- package/docs/reference/functions/buildEngineServerConfiguration.md +7 -1
- package/docs/reference/functions/loadTextFile.md +19 -0
- package/docs/reference/functions/overrideModuleImport.md +17 -0
- package/docs/reference/index.md +3 -1
- package/docs/reference/interfaces/IEngineEnvironmentVariables.md +222 -32
- package/docs/reference/interfaces/INodeEnvironmentVariables.md +309 -43
- package/docs/reference/interfaces/INodeOptions.md +8 -0
- package/docs/reference/variables/NodeFeatures.md +7 -1
- package/locales/en.json +6 -4
- package/package.json +5 -2
package/dist/cjs/index.cjs
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
var apiAuthEntityStorageService = require('@twin.org/api-auth-entity-storage-service');
|
|
4
4
|
var core = require('@twin.org/core');
|
|
5
5
|
var crypto = require('@twin.org/crypto');
|
|
6
|
+
var engineServerTypes = require('@twin.org/engine-server-types');
|
|
6
7
|
var engineTypes = require('@twin.org/engine-types');
|
|
7
8
|
var entityStorageModels = require('@twin.org/entity-storage-models');
|
|
8
9
|
var identityModels = require('@twin.org/identity-models');
|
|
@@ -10,8 +11,9 @@ var vaultModels = require('@twin.org/vault-models');
|
|
|
10
11
|
var walletModels = require('@twin.org/wallet-models');
|
|
11
12
|
var promises = require('node:fs/promises');
|
|
12
13
|
var path = require('node:path');
|
|
14
|
+
var rightsManagementRestClient = require('@twin.org/rights-management-rest-client');
|
|
13
15
|
var engineServer = require('@twin.org/engine-server');
|
|
14
|
-
var
|
|
16
|
+
var modules = require('@twin.org/modules');
|
|
15
17
|
var dotenv = require('dotenv');
|
|
16
18
|
var engine = require('@twin.org/engine');
|
|
17
19
|
var engineCore = require('@twin.org/engine-core');
|
|
@@ -50,7 +52,11 @@ const NodeFeatures = {
|
|
|
50
52
|
/**
|
|
51
53
|
* NodeUser - generates a user for the node if not provided in config.
|
|
52
54
|
*/
|
|
53
|
-
NodeUser: "node-user"
|
|
55
|
+
NodeUser: "node-user",
|
|
56
|
+
/**
|
|
57
|
+
* NodeWallet - generates a wallet for the node and funds it when there is a faucet available.
|
|
58
|
+
*/
|
|
59
|
+
NodeWallet: "node-wallet"
|
|
54
60
|
};
|
|
55
61
|
|
|
56
62
|
// Copyright 2024 IOTA Stiftung.
|
|
@@ -92,13 +98,21 @@ async function fileExists(filename) {
|
|
|
92
98
|
return false;
|
|
93
99
|
}
|
|
94
100
|
}
|
|
101
|
+
/**
|
|
102
|
+
* Load the text file.
|
|
103
|
+
* @param filename The filename of the text file to load.
|
|
104
|
+
* @returns The contents of the text file if it could not be loaded.
|
|
105
|
+
*/
|
|
106
|
+
async function loadTextFile(filename) {
|
|
107
|
+
return promises.readFile(filename, "utf8");
|
|
108
|
+
}
|
|
95
109
|
/**
|
|
96
110
|
* Load the JSON file.
|
|
97
111
|
* @param filename The filename of the JSON file to load.
|
|
98
112
|
* @returns The contents of the JSON file or null if it could not be loaded.
|
|
99
113
|
*/
|
|
100
114
|
async function loadJsonFile(filename) {
|
|
101
|
-
const content = await
|
|
115
|
+
const content = await loadTextFile(filename);
|
|
102
116
|
return JSON.parse(content);
|
|
103
117
|
}
|
|
104
118
|
/**
|
|
@@ -137,8 +151,12 @@ async function bootstrap(engineCore, context, envVars) {
|
|
|
137
151
|
await bootstrapNodeUser(engineCore, context, envVars, features);
|
|
138
152
|
await bootstrapAuth(engineCore, context, envVars);
|
|
139
153
|
await bootstrapBlobEncryption(engineCore, context, envVars);
|
|
140
|
-
await
|
|
141
|
-
await
|
|
154
|
+
await addVerificationMethod(engineCore, context, "attestation", envVars.attestationVerificationMethodId);
|
|
155
|
+
await addVerificationMethod(engineCore, context, "immutable proof", envVars.immutableProofVerificationMethodId);
|
|
156
|
+
if (core.Coerce.boolean(envVars.rightsManagementEnabled) ?? false) {
|
|
157
|
+
await addVerificationMethod(engineCore, context, "rights management", envVars.rightsManagementNegotiationMethodId);
|
|
158
|
+
}
|
|
159
|
+
await bootstrapSynchronisedStorage(engineCore, context, envVars);
|
|
142
160
|
}
|
|
143
161
|
/**
|
|
144
162
|
* Bootstrap the node creating any necessary resources.
|
|
@@ -153,23 +171,21 @@ async function bootstrapNodeIdentity(engineCore, context, envVars, features) {
|
|
|
153
171
|
// But we have a chicken and egg problem in that we can't create the identity
|
|
154
172
|
// to store the mnemonic in the vault without an identity. We use a temporary identity
|
|
155
173
|
// and then replace it with the new identity later in the process.
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
}));
|
|
172
|
-
}
|
|
174
|
+
const defaultVaultConnectorType = engineCore.getRegisteredInstanceType("vaultConnector");
|
|
175
|
+
const vaultConnector = vaultModels.VaultConnectorFactory.get(defaultVaultConnectorType);
|
|
176
|
+
const workingIdentity = envVars.identity ??
|
|
177
|
+
context.state.nodeIdentity ??
|
|
178
|
+
`bootstrap-temp-${core.Converter.bytesToHex(core.RandomHelper.generate(16))}`;
|
|
179
|
+
await bootstrapMnemonic(engineCore, envVars, features, vaultConnector, workingIdentity);
|
|
180
|
+
const addresses = await bootstrapWallet(engineCore, envVars, features, workingIdentity);
|
|
181
|
+
const finalIdentity = await bootstrapIdentity(engineCore, envVars, features, workingIdentity);
|
|
182
|
+
await finaliseWallet(engineCore, envVars, features, finalIdentity, addresses);
|
|
183
|
+
await finaliseMnemonic(vaultConnector, workingIdentity, finalIdentity);
|
|
184
|
+
context.state.nodeIdentity = finalIdentity;
|
|
185
|
+
context.stateDirty = true;
|
|
186
|
+
engineCore.logInfo(core.I18n.formatMessage("node.nodeIdentity", {
|
|
187
|
+
identity: context.state.nodeIdentity
|
|
188
|
+
}));
|
|
173
189
|
}
|
|
174
190
|
}
|
|
175
191
|
/**
|
|
@@ -181,12 +197,13 @@ async function bootstrapNodeIdentity(engineCore, context, envVars, features) {
|
|
|
181
197
|
* @returns The addresses for the wallet.
|
|
182
198
|
*/
|
|
183
199
|
async function bootstrapIdentity(engineCore, envVars, features, nodeIdentity) {
|
|
184
|
-
const
|
|
200
|
+
const defaultIdentityConnectorType = engineCore.getRegisteredInstanceType("identityConnector");
|
|
185
201
|
// Now create an identity for the node controlled by the address we just funded
|
|
186
|
-
const identityConnector = identityModels.IdentityConnectorFactory.get(
|
|
202
|
+
const identityConnector = identityModels.IdentityConnectorFactory.get(defaultIdentityConnectorType);
|
|
187
203
|
let identityDocument;
|
|
188
204
|
try {
|
|
189
|
-
const
|
|
205
|
+
const defaultIdentityResolverConnectorType = engineCore.getRegisteredInstanceType("identityResolverConnector");
|
|
206
|
+
const identityResolverConnector = identityModels.IdentityResolverConnectorFactory.get(defaultIdentityResolverConnectorType);
|
|
190
207
|
identityDocument = await identityResolverConnector.resolveDocument(nodeIdentity);
|
|
191
208
|
engineCore.logInfo(core.I18n.formatMessage("node.existingNodeIdentity", { identity: nodeIdentity }));
|
|
192
209
|
}
|
|
@@ -196,7 +213,7 @@ async function bootstrapIdentity(engineCore, envVars, features, nodeIdentity) {
|
|
|
196
213
|
identityDocument = await identityConnector.createDocument(nodeIdentity);
|
|
197
214
|
engineCore.logInfo(core.I18n.formatMessage("node.createdNodeIdentity", { identity: identityDocument.id }));
|
|
198
215
|
}
|
|
199
|
-
if (
|
|
216
|
+
if (defaultIdentityConnectorType.startsWith(engineTypes.IdentityConnectorType.Iota)) {
|
|
200
217
|
const didUrn = core.Urn.fromValidString(identityDocument.id);
|
|
201
218
|
const didParts = didUrn.parts();
|
|
202
219
|
const objectId = didParts[3];
|
|
@@ -215,23 +232,26 @@ async function bootstrapIdentity(engineCore, envVars, features, nodeIdentity) {
|
|
|
215
232
|
* @returns The addresses for the wallet.
|
|
216
233
|
*/
|
|
217
234
|
async function bootstrapWallet(engineCore, envVars, features, nodeIdentity) {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
235
|
+
if (features.includes(NodeFeatures.NodeWallet)) {
|
|
236
|
+
const defaultWalletConnectorType = engineCore.getRegisteredInstanceType("walletConnector");
|
|
237
|
+
const walletConnector = walletModels.WalletConnectorFactory.get(defaultWalletConnectorType);
|
|
238
|
+
const addresses = await walletConnector.getAddresses(nodeIdentity, 0, 0, 5);
|
|
239
|
+
const balance = await walletConnector.getBalance(nodeIdentity, addresses[0]);
|
|
240
|
+
if (balance === 0n) {
|
|
241
|
+
let address0 = addresses[0];
|
|
242
|
+
if (defaultWalletConnectorType.startsWith(engineTypes.WalletConnectorType.Iota)) {
|
|
243
|
+
address0 = `${envVars.iotaExplorerEndpoint}address/${address0}?network=${envVars.iotaNetwork}`;
|
|
244
|
+
}
|
|
245
|
+
engineCore.logInfo(core.I18n.formatMessage("node.fundingWallet", { address: address0 }));
|
|
246
|
+
// Add some funds to the wallet from the faucet
|
|
247
|
+
await walletConnector.ensureBalance(nodeIdentity, addresses[0], 1000000000n);
|
|
226
248
|
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
else {
|
|
232
|
-
engineCore.logInfo(core.I18n.formatMessage("node.fundedWallet"));
|
|
249
|
+
else {
|
|
250
|
+
engineCore.logInfo(core.I18n.formatMessage("node.fundedWallet"));
|
|
251
|
+
}
|
|
252
|
+
return addresses;
|
|
233
253
|
}
|
|
234
|
-
return
|
|
254
|
+
return [];
|
|
235
255
|
}
|
|
236
256
|
/**
|
|
237
257
|
* Bootstrap the identity for the node.
|
|
@@ -242,15 +262,17 @@ async function bootstrapWallet(engineCore, envVars, features, nodeIdentity) {
|
|
|
242
262
|
* @param addresses The addresses for the wallet.
|
|
243
263
|
*/
|
|
244
264
|
async function finaliseWallet(engineCore, envVars, features, finalIdentity, addresses) {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
addr
|
|
253
|
-
|
|
265
|
+
if (features.includes(NodeFeatures.NodeWallet)) {
|
|
266
|
+
const defaultWalletConnectorType = engineCore.getRegisteredInstanceType("walletConnector");
|
|
267
|
+
// If we are using entity storage for wallet the identity associated with the
|
|
268
|
+
// address will be wrong, so fix it
|
|
269
|
+
if (defaultWalletConnectorType.startsWith(engineTypes.WalletConnectorType.EntityStorage)) {
|
|
270
|
+
const walletAddress = entityStorageModels.EntityStorageConnectorFactory.get(core.StringHelper.kebabCase("WalletAddress"));
|
|
271
|
+
const addr = await walletAddress.get(addresses[0]);
|
|
272
|
+
if (!core.Is.empty(addr)) {
|
|
273
|
+
addr.identity = finalIdentity;
|
|
274
|
+
await walletAddress.set(addr);
|
|
275
|
+
}
|
|
254
276
|
}
|
|
255
277
|
}
|
|
256
278
|
}
|
|
@@ -312,8 +334,8 @@ async function finaliseMnemonic(vaultConnector, workingIdentity, finalIdentity)
|
|
|
312
334
|
*/
|
|
313
335
|
async function bootstrapNodeUser(engineCore, context, envVars, features) {
|
|
314
336
|
if (features.includes(NodeFeatures.NodeUser)) {
|
|
315
|
-
const
|
|
316
|
-
if (
|
|
337
|
+
const defaultAuthenticationComponentType = engineCore.getRegisteredInstanceType("authenticationComponent");
|
|
338
|
+
if (defaultAuthenticationComponentType.startsWith(engineServerTypes.AuthenticationComponentType.EntityStorage) &&
|
|
317
339
|
core.Is.stringValue(context.state.nodeIdentity)) {
|
|
318
340
|
const authUserEntityStorage = entityStorageModels.EntityStorageConnectorFactory.get(core.StringHelper.kebabCase("AuthenticationUser"));
|
|
319
341
|
const email = envVars.username ?? DEFAULT_NODE_USERNAME;
|
|
@@ -356,7 +378,8 @@ async function bootstrapNodeUser(engineCore, context, envVars, features) {
|
|
|
356
378
|
}
|
|
357
379
|
}
|
|
358
380
|
// We have create a node user, now we need to create a profile for the user
|
|
359
|
-
const
|
|
381
|
+
const defaultIdentityConnectorType = engineCore.getRegisteredInstanceType("identityConnector");
|
|
382
|
+
const identityProfileConnector = identityModels.IdentityProfileConnectorFactory.get(defaultIdentityConnectorType);
|
|
360
383
|
if (identityProfileConnector) {
|
|
361
384
|
let userProfile;
|
|
362
385
|
try {
|
|
@@ -386,43 +409,6 @@ async function bootstrapNodeUser(engineCore, context, envVars, features) {
|
|
|
386
409
|
}
|
|
387
410
|
}
|
|
388
411
|
}
|
|
389
|
-
/**
|
|
390
|
-
* Bootstrap the attestation verification methods.
|
|
391
|
-
* @param engineCore The engine core for the node.
|
|
392
|
-
* @param context The context for the node.
|
|
393
|
-
* @param envVars The environment variables for the node.
|
|
394
|
-
* @param features The features that are enabled on the node.
|
|
395
|
-
*/
|
|
396
|
-
async function bootstrapAttestationMethod(engineCore, context, envVars, features) {
|
|
397
|
-
if (core.Is.stringValue(context.state.nodeIdentity) &&
|
|
398
|
-
core.Is.arrayValue(context.config.types.identityConnector) &&
|
|
399
|
-
core.Is.stringValue(envVars.attestationVerificationMethodId)) {
|
|
400
|
-
const engineDefaultTypes = engineCore.getDefaultTypes();
|
|
401
|
-
const identityConnector = identityModels.IdentityConnectorFactory.get(engineDefaultTypes.identityConnector);
|
|
402
|
-
const identityResolverConnector = identityModels.IdentityResolverConnectorFactory.get(engineDefaultTypes.identityResolverConnector);
|
|
403
|
-
const identityDocument = await identityResolverConnector.resolveDocument(context.state.nodeIdentity);
|
|
404
|
-
const fullMethodId = `${identityDocument.id}#${envVars.attestationVerificationMethodId}`;
|
|
405
|
-
let createVm = true;
|
|
406
|
-
try {
|
|
407
|
-
identityModels.DocumentHelper.getVerificationMethod(identityDocument, fullMethodId, "assertionMethod");
|
|
408
|
-
createVm = false;
|
|
409
|
-
}
|
|
410
|
-
catch { }
|
|
411
|
-
if (createVm) {
|
|
412
|
-
// Add attestation verification method to DID, the correct node context is now in place
|
|
413
|
-
// so the keys for the verification method will be stored correctly
|
|
414
|
-
engineCore.logInfo(core.I18n.formatMessage("node.addingAttestation", {
|
|
415
|
-
methodId: fullMethodId
|
|
416
|
-
}));
|
|
417
|
-
await identityConnector.addVerificationMethod(context.state.nodeIdentity, context.state.nodeIdentity, "assertionMethod", envVars.attestationVerificationMethodId);
|
|
418
|
-
}
|
|
419
|
-
else {
|
|
420
|
-
engineCore.logInfo(core.I18n.formatMessage("node.existingAttestation", {
|
|
421
|
-
methodId: fullMethodId
|
|
422
|
-
}));
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
412
|
/**
|
|
427
413
|
* Bootstrap the immutable proof verification methods.
|
|
428
414
|
* @param engineCore The engine core for the node.
|
|
@@ -430,36 +416,7 @@ async function bootstrapAttestationMethod(engineCore, context, envVars, features
|
|
|
430
416
|
* @param envVars The environment variables for the node.
|
|
431
417
|
* @param features The features that are enabled on the node.
|
|
432
418
|
*/
|
|
433
|
-
async function bootstrapImmutableProofMethod(engineCore, context, envVars, features) {
|
|
434
|
-
const engineDefaultTypes = engineCore.getDefaultTypes();
|
|
435
|
-
if (core.Is.stringValue(context.state.nodeIdentity) &&
|
|
436
|
-
core.Is.arrayValue(context.config.types.identityConnector) &&
|
|
437
|
-
core.Is.stringValue(envVars.immutableProofVerificationMethodId)) {
|
|
438
|
-
const identityConnector = identityModels.IdentityConnectorFactory.get(engineDefaultTypes.identityConnector);
|
|
439
|
-
const identityResolverConnector = identityModels.IdentityResolverConnectorFactory.get(engineDefaultTypes.identityResolverConnector);
|
|
440
|
-
const identityDocument = await identityResolverConnector.resolveDocument(context.state.nodeIdentity);
|
|
441
|
-
const fullMethodId = `${identityDocument.id}#${envVars.immutableProofVerificationMethodId}`;
|
|
442
|
-
let createVm = true;
|
|
443
|
-
try {
|
|
444
|
-
identityModels.DocumentHelper.getVerificationMethod(identityDocument, fullMethodId, "assertionMethod");
|
|
445
|
-
createVm = false;
|
|
446
|
-
}
|
|
447
|
-
catch { }
|
|
448
|
-
if (createVm) {
|
|
449
|
-
// Add AIG verification method to DID, the correct node context is now in place
|
|
450
|
-
// so the keys for the verification method will be stored correctly
|
|
451
|
-
engineCore.logInfo(core.I18n.formatMessage("node.addingImmutableProof", {
|
|
452
|
-
methodId: fullMethodId
|
|
453
|
-
}));
|
|
454
|
-
await identityConnector.addVerificationMethod(context.state.nodeIdentity, context.state.nodeIdentity, "assertionMethod", envVars.immutableProofVerificationMethodId);
|
|
455
|
-
}
|
|
456
|
-
else {
|
|
457
|
-
engineCore.logInfo(core.I18n.formatMessage("node.existingImmutableProof", {
|
|
458
|
-
methodId: fullMethodId
|
|
459
|
-
}));
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
}
|
|
419
|
+
async function bootstrapImmutableProofMethod(engineCore, context, envVars, features) { }
|
|
463
420
|
/**
|
|
464
421
|
* Bootstrap the keys for blob encryption.
|
|
465
422
|
* @param engineCore The engine core for the node.
|
|
@@ -470,18 +427,28 @@ async function bootstrapImmutableProofMethod(engineCore, context, envVars, featu
|
|
|
470
427
|
async function bootstrapBlobEncryption(engineCore, context, envVars, features) {
|
|
471
428
|
if ((core.Coerce.boolean(envVars.blobStorageEnableEncryption) ?? false) &&
|
|
472
429
|
core.Is.stringValue(context.state.nodeIdentity)) {
|
|
473
|
-
const engineDefaultTypes = engineCore.getDefaultTypes();
|
|
474
430
|
// Create a new key for encrypting blobs
|
|
475
|
-
const
|
|
476
|
-
const
|
|
431
|
+
const defaultVaultConnectorType = engineCore.getRegisteredInstanceType("vaultConnector");
|
|
432
|
+
const vaultConnector = vaultModels.VaultConnectorFactory.get(defaultVaultConnectorType);
|
|
433
|
+
const keyName = `${context.state.nodeIdentity}/${envVars.blobStorageEncryptionKeyId}`;
|
|
477
434
|
let existingKey;
|
|
478
435
|
try {
|
|
479
436
|
existingKey = await vaultConnector.getKey(keyName);
|
|
480
437
|
}
|
|
481
438
|
catch { }
|
|
482
439
|
if (core.Is.empty(existingKey)) {
|
|
483
|
-
|
|
484
|
-
|
|
440
|
+
if (core.Is.stringBase64(envVars.blobStorageSymmetricEncryptionKey)) {
|
|
441
|
+
engineCore.logInfo(core.I18n.formatMessage("node.addingBlobEncryptionKey", { keyName }));
|
|
442
|
+
await vaultConnector.addKey(keyName, vaultModels.VaultKeyType.ChaCha20Poly1305, core.Converter.base64ToBytes(envVars.blobStorageSymmetricEncryptionKey));
|
|
443
|
+
}
|
|
444
|
+
else {
|
|
445
|
+
engineCore.logInfo(core.I18n.formatMessage("node.creatingBlobEncryptionKey", { keyName }));
|
|
446
|
+
const key = await vaultConnector.createKey(keyName, vaultModels.VaultKeyType.ChaCha20Poly1305);
|
|
447
|
+
engineCore.logInfo(core.I18n.formatMessage("node.createdBlobEncryptionKey", {
|
|
448
|
+
keyName,
|
|
449
|
+
keyValue: core.Converter.bytesToBase64(key)
|
|
450
|
+
}));
|
|
451
|
+
}
|
|
485
452
|
}
|
|
486
453
|
else {
|
|
487
454
|
engineCore.logInfo(core.I18n.formatMessage("node.existingBlobEncryptionKey", { keyName }));
|
|
@@ -496,11 +463,13 @@ async function bootstrapBlobEncryption(engineCore, context, envVars, features) {
|
|
|
496
463
|
* @param features The features that are enabled on the node.
|
|
497
464
|
*/
|
|
498
465
|
async function bootstrapAuth(engineCore, context, envVars, features) {
|
|
499
|
-
const
|
|
500
|
-
if (
|
|
466
|
+
const defaultAuthenticationComponentType = engineCore.getRegisteredInstanceTypeOptional("authenticationComponent");
|
|
467
|
+
if (core.Is.stringValue(defaultAuthenticationComponentType) &&
|
|
468
|
+
defaultAuthenticationComponentType.startsWith(engineServerTypes.AuthenticationComponentType.EntityStorage) &&
|
|
501
469
|
core.Is.stringValue(context.state.nodeIdentity)) {
|
|
502
470
|
// Create a new JWT signing key and a user login for the node
|
|
503
|
-
const
|
|
471
|
+
const defaultVaultConnectorType = engineCore.getRegisteredInstanceType("vaultConnector");
|
|
472
|
+
const vaultConnector = vaultModels.VaultConnectorFactory.get(defaultVaultConnectorType);
|
|
504
473
|
const keyName = `${context.state.nodeIdentity}/${envVars.authSigningKeyId}`;
|
|
505
474
|
let existingKey;
|
|
506
475
|
try {
|
|
@@ -516,6 +485,76 @@ async function bootstrapAuth(engineCore, context, envVars, features) {
|
|
|
516
485
|
}
|
|
517
486
|
}
|
|
518
487
|
}
|
|
488
|
+
/**
|
|
489
|
+
* Bootstrap the synchronised storage blob encryption and verification methods.
|
|
490
|
+
* @param engineCore The engine core for the node.
|
|
491
|
+
* @param context The context for the node.
|
|
492
|
+
* @param envVars The environment variables for the node.
|
|
493
|
+
* @param features The features that are enabled on the node.
|
|
494
|
+
*/
|
|
495
|
+
async function bootstrapSynchronisedStorage(engineCore, context, envVars, features) {
|
|
496
|
+
if (core.Coerce.boolean(envVars.synchronisedStorageEnabled) ?? false) {
|
|
497
|
+
// Add the verification method to the identity if it doesn't exist
|
|
498
|
+
await addVerificationMethod(engineCore, context, "synchronised storage", envVars.synchronisedStorageVerificationMethodId);
|
|
499
|
+
// If this is a trusted node we need to add the blob encryption key pair
|
|
500
|
+
if (core.Is.stringValue(envVars.synchronisedStorageBlobStorageEncryptionKeyId) &&
|
|
501
|
+
core.Is.stringBase64(envVars.synchronisedStorageBlobStorageKey)) {
|
|
502
|
+
const defaultVaultConnectorType = engineCore.getRegisteredInstanceType("vaultConnector");
|
|
503
|
+
const vaultConnector = vaultModels.VaultConnectorFactory.get(defaultVaultConnectorType);
|
|
504
|
+
const keyName = envVars.synchronisedStorageBlobStorageEncryptionKeyId;
|
|
505
|
+
let existingKey;
|
|
506
|
+
try {
|
|
507
|
+
existingKey = await vaultConnector.getKey(keyName);
|
|
508
|
+
}
|
|
509
|
+
catch { }
|
|
510
|
+
if (core.Is.empty(existingKey)) {
|
|
511
|
+
engineCore.logInfo(core.I18n.formatMessage("node.addingSynchronisedStorageBlobEncryptionKey", { keyName }));
|
|
512
|
+
await vaultConnector.addKey(keyName, vaultModels.VaultKeyType.ChaCha20Poly1305, core.Converter.base64ToBytes(envVars.synchronisedStorageBlobStorageKey));
|
|
513
|
+
}
|
|
514
|
+
else {
|
|
515
|
+
engineCore.logInfo(core.I18n.formatMessage("node.existingSynchronisedStorageBlobEncryptionKey", { keyName }));
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
/**
|
|
521
|
+
* Add a verification method if it doesn't exist.
|
|
522
|
+
* @param engineCore The engine core for the node.
|
|
523
|
+
* @param context The context for the node.
|
|
524
|
+
* @param verificationMethodTitle The verification method title.
|
|
525
|
+
* @param verificationMethodId The verification method ID.
|
|
526
|
+
*/
|
|
527
|
+
async function addVerificationMethod(engineCore, context, verificationMethodTitle, verificationMethodId) {
|
|
528
|
+
if (core.Is.stringValue(context.state.nodeIdentity) &&
|
|
529
|
+
core.Is.arrayValue(context.config.types.identityConnector) &&
|
|
530
|
+
core.Is.stringValue(verificationMethodId)) {
|
|
531
|
+
const defaultIdentityConnectorType = engineCore.getRegisteredInstanceType("identityConnector");
|
|
532
|
+
const identityConnector = identityModels.IdentityConnectorFactory.get(defaultIdentityConnectorType);
|
|
533
|
+
const defaultIdentityResolverConnectorType = engineCore.getRegisteredInstanceType("identityResolverConnector");
|
|
534
|
+
const identityResolverConnector = identityModels.IdentityResolverConnectorFactory.get(defaultIdentityResolverConnectorType);
|
|
535
|
+
const identityDocument = await identityResolverConnector.resolveDocument(context.state.nodeIdentity);
|
|
536
|
+
const fullMethodId = `${identityDocument.id}#${verificationMethodId}`;
|
|
537
|
+
let exists = false;
|
|
538
|
+
try {
|
|
539
|
+
identityModels.DocumentHelper.getVerificationMethod(identityDocument, fullMethodId, "assertionMethod");
|
|
540
|
+
exists = true;
|
|
541
|
+
}
|
|
542
|
+
catch { }
|
|
543
|
+
if (!exists) {
|
|
544
|
+
engineCore.logInfo(core.I18n.formatMessage("node.addingVerificationMethod", {
|
|
545
|
+
title: verificationMethodTitle,
|
|
546
|
+
methodId: fullMethodId
|
|
547
|
+
}));
|
|
548
|
+
await identityConnector.addVerificationMethod(context.state.nodeIdentity, context.state.nodeIdentity, "assertionMethod", verificationMethodId);
|
|
549
|
+
}
|
|
550
|
+
else {
|
|
551
|
+
engineCore.logInfo(core.I18n.formatMessage("node.existingVerificationMethod", {
|
|
552
|
+
title: verificationMethodTitle,
|
|
553
|
+
methodId: fullMethodId
|
|
554
|
+
}));
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
}
|
|
519
558
|
|
|
520
559
|
// Copyright 2024 IOTA Stiftung.
|
|
521
560
|
// SPDX-License-Identifier: Apache-2.0.
|
|
@@ -533,7 +572,10 @@ function buildEngineConfiguration(envVars) {
|
|
|
533
572
|
envVars.attestationVerificationMethodId ??= "attestation-assertion";
|
|
534
573
|
envVars.immutableProofVerificationMethodId ??= "immutable-proof-assertion";
|
|
535
574
|
envVars.blobStorageEnableEncryption ??= "false";
|
|
536
|
-
envVars.
|
|
575
|
+
envVars.blobStorageEncryptionKeyId ??= "blob-encryption";
|
|
576
|
+
envVars.synchronisedStorageBlobStorageEncryptionKeyId ??= "synchronised-storage-blob-encryption";
|
|
577
|
+
envVars.synchronisedStorageVerificationMethodId ??= "synchronised-storage-assertion";
|
|
578
|
+
envVars.rightsManagementNegotiationMethodId ??= "policy-negotiation-assertion";
|
|
537
579
|
const coreConfig = {
|
|
538
580
|
debug: core.Coerce.boolean(envVars.debug) ?? false,
|
|
539
581
|
types: {}
|
|
@@ -544,6 +586,7 @@ function buildEngineConfiguration(envVars) {
|
|
|
544
586
|
configureDlt(coreConfig, envVars);
|
|
545
587
|
configureLogging(coreConfig, envVars);
|
|
546
588
|
configureBackgroundTask(coreConfig, envVars);
|
|
589
|
+
configureTaskScheduler(coreConfig, envVars);
|
|
547
590
|
configureEventBus(coreConfig, envVars);
|
|
548
591
|
configureTelemetry(coreConfig, envVars);
|
|
549
592
|
configureMessaging(coreConfig, envVars);
|
|
@@ -559,9 +602,10 @@ function buildEngineConfiguration(envVars) {
|
|
|
559
602
|
configureAuditableItemGraph(coreConfig);
|
|
560
603
|
configureAuditableItemStream(coreConfig);
|
|
561
604
|
configureDocumentManagement(coreConfig);
|
|
562
|
-
configureFederatedCatalogue(coreConfig, envVars);
|
|
563
605
|
configureRightsManagement(coreConfig, envVars);
|
|
564
|
-
|
|
606
|
+
configureSynchronisedStorage(coreConfig, envVars);
|
|
607
|
+
configureFederatedCatalogue(coreConfig, envVars);
|
|
608
|
+
configureDataSpaceConnector(coreConfig, envVars);
|
|
565
609
|
return coreConfig;
|
|
566
610
|
}
|
|
567
611
|
/**
|
|
@@ -581,14 +625,13 @@ function getIotaConfig(coreConfig) {
|
|
|
581
625
|
function configureEntityStorage(coreConfig, envVars) {
|
|
582
626
|
coreConfig.types ??= {};
|
|
583
627
|
coreConfig.types.entityStorageConnector ??= [];
|
|
584
|
-
|
|
585
|
-
|
|
628
|
+
const entityStorageConnectorTypes = envVars.entityStorageConnectorType?.split(",") ?? [];
|
|
629
|
+
if (entityStorageConnectorTypes.includes(engineTypes.EntityStorageConnectorType.Memory)) {
|
|
586
630
|
coreConfig.types.entityStorageConnector.push({
|
|
587
631
|
type: engineTypes.EntityStorageConnectorType.Memory
|
|
588
632
|
});
|
|
589
633
|
}
|
|
590
|
-
if ((
|
|
591
|
-
envVars.entityStorageConnectorType === engineTypes.EntityStorageConnectorType.File) {
|
|
634
|
+
if (entityStorageConnectorTypes.includes(engineTypes.EntityStorageConnectorType.File)) {
|
|
592
635
|
coreConfig.types.entityStorageConnector.push({
|
|
593
636
|
type: engineTypes.EntityStorageConnectorType.File,
|
|
594
637
|
options: {
|
|
@@ -597,7 +640,7 @@ function configureEntityStorage(coreConfig, envVars) {
|
|
|
597
640
|
}
|
|
598
641
|
});
|
|
599
642
|
}
|
|
600
|
-
if (
|
|
643
|
+
if (entityStorageConnectorTypes.includes(engineTypes.EntityStorageConnectorType.AwsDynamoDb)) {
|
|
601
644
|
coreConfig.types.entityStorageConnector.push({
|
|
602
645
|
type: engineTypes.EntityStorageConnectorType.AwsDynamoDb,
|
|
603
646
|
options: {
|
|
@@ -611,7 +654,7 @@ function configureEntityStorage(coreConfig, envVars) {
|
|
|
611
654
|
}
|
|
612
655
|
});
|
|
613
656
|
}
|
|
614
|
-
if (
|
|
657
|
+
if (entityStorageConnectorTypes.includes(engineTypes.EntityStorageConnectorType.AzureCosmosDb)) {
|
|
615
658
|
coreConfig.types.entityStorageConnector.push({
|
|
616
659
|
type: engineTypes.EntityStorageConnectorType.AzureCosmosDb,
|
|
617
660
|
options: {
|
|
@@ -625,7 +668,7 @@ function configureEntityStorage(coreConfig, envVars) {
|
|
|
625
668
|
}
|
|
626
669
|
});
|
|
627
670
|
}
|
|
628
|
-
if (
|
|
671
|
+
if (entityStorageConnectorTypes.includes(engineTypes.EntityStorageConnectorType.GcpFirestoreDb)) {
|
|
629
672
|
coreConfig.types.entityStorageConnector.push({
|
|
630
673
|
type: engineTypes.EntityStorageConnectorType.GcpFirestoreDb,
|
|
631
674
|
options: {
|
|
@@ -640,41 +683,27 @@ function configureEntityStorage(coreConfig, envVars) {
|
|
|
640
683
|
}
|
|
641
684
|
});
|
|
642
685
|
}
|
|
643
|
-
if (
|
|
686
|
+
if (entityStorageConnectorTypes.includes(engineTypes.EntityStorageConnectorType.ScyllaDb)) {
|
|
644
687
|
coreConfig.types.entityStorageConnector.push({
|
|
645
688
|
type: engineTypes.EntityStorageConnectorType.ScyllaDb,
|
|
646
689
|
options: {
|
|
647
690
|
config: {
|
|
648
|
-
hosts: envVars.scylladbHosts
|
|
691
|
+
hosts: envVars.scylladbHosts?.split(",") ?? [],
|
|
649
692
|
localDataCenter: envVars.scylladbLocalDataCenter ?? "",
|
|
650
|
-
keyspace: envVars.scylladbKeyspace ?? ""
|
|
651
|
-
|
|
652
|
-
tablePrefix: envVars.entityStorageTablePrefix
|
|
653
|
-
}
|
|
654
|
-
});
|
|
655
|
-
}
|
|
656
|
-
if (core.Is.stringValue(envVars.mySqlHost)) {
|
|
657
|
-
coreConfig.types.entityStorageConnector.push({
|
|
658
|
-
type: engineTypes.EntityStorageConnectorType.MySqlDb,
|
|
659
|
-
options: {
|
|
660
|
-
config: {
|
|
661
|
-
host: envVars.mySqlHost,
|
|
662
|
-
port: envVars.mySqlPort ?? 3306,
|
|
663
|
-
user: envVars.mySqlUser ?? "",
|
|
664
|
-
password: envVars.mySqlPassword ?? "",
|
|
665
|
-
database: envVars.mySqlDatabase ?? ""
|
|
693
|
+
keyspace: envVars.scylladbKeyspace ?? "",
|
|
694
|
+
port: core.Coerce.integer(envVars.scylladbPort)
|
|
666
695
|
},
|
|
667
696
|
tablePrefix: envVars.entityStorageTablePrefix
|
|
668
697
|
}
|
|
669
698
|
});
|
|
670
699
|
}
|
|
671
|
-
if (
|
|
700
|
+
if (entityStorageConnectorTypes.includes(engineTypes.EntityStorageConnectorType.MySqlDb)) {
|
|
672
701
|
coreConfig.types.entityStorageConnector.push({
|
|
673
702
|
type: engineTypes.EntityStorageConnectorType.MySqlDb,
|
|
674
703
|
options: {
|
|
675
704
|
config: {
|
|
676
|
-
host: envVars.mySqlHost,
|
|
677
|
-
port: envVars.mySqlPort
|
|
705
|
+
host: envVars.mySqlHost ?? "",
|
|
706
|
+
port: core.Coerce.integer(envVars.mySqlPort),
|
|
678
707
|
user: envVars.mySqlUser ?? "",
|
|
679
708
|
password: envVars.mySqlPassword ?? "",
|
|
680
709
|
database: envVars.mySqlDatabase ?? ""
|
|
@@ -683,13 +712,13 @@ function configureEntityStorage(coreConfig, envVars) {
|
|
|
683
712
|
}
|
|
684
713
|
});
|
|
685
714
|
}
|
|
686
|
-
if (
|
|
715
|
+
if (entityStorageConnectorTypes.includes(engineTypes.EntityStorageConnectorType.MongoDb)) {
|
|
687
716
|
coreConfig.types.entityStorageConnector.push({
|
|
688
717
|
type: engineTypes.EntityStorageConnectorType.MongoDb,
|
|
689
718
|
options: {
|
|
690
719
|
config: {
|
|
691
|
-
host: envVars.mongoDbHost,
|
|
692
|
-
port: envVars.mongoDbPort,
|
|
720
|
+
host: envVars.mongoDbHost ?? "",
|
|
721
|
+
port: core.Coerce.integer(envVars.mongoDbPort),
|
|
693
722
|
user: envVars.mongoDbUser ?? "",
|
|
694
723
|
password: envVars.mongoDbPassword ?? "",
|
|
695
724
|
database: envVars.mongoDbDatabase ?? ""
|
|
@@ -698,13 +727,13 @@ function configureEntityStorage(coreConfig, envVars) {
|
|
|
698
727
|
}
|
|
699
728
|
});
|
|
700
729
|
}
|
|
701
|
-
if (
|
|
730
|
+
if (entityStorageConnectorTypes.includes(engineTypes.EntityStorageConnectorType.PostgreSql)) {
|
|
702
731
|
coreConfig.types.entityStorageConnector.push({
|
|
703
732
|
type: engineTypes.EntityStorageConnectorType.PostgreSql,
|
|
704
733
|
options: {
|
|
705
734
|
config: {
|
|
706
|
-
host: envVars.postgreSqlHost,
|
|
707
|
-
port: envVars.postgreSqlPort,
|
|
735
|
+
host: envVars.postgreSqlHost ?? "",
|
|
736
|
+
port: core.Coerce.integer(envVars.postgreSqlPort),
|
|
708
737
|
user: envVars.postgreSqlUser ?? "",
|
|
709
738
|
password: envVars.postgreSqlPassword ?? "",
|
|
710
739
|
database: envVars.postgreSqlDatabase ?? ""
|
|
@@ -713,11 +742,21 @@ function configureEntityStorage(coreConfig, envVars) {
|
|
|
713
742
|
}
|
|
714
743
|
});
|
|
715
744
|
}
|
|
716
|
-
const
|
|
717
|
-
if (
|
|
745
|
+
const defaultEntityStorageConnectorType = envVars.entityStorageConnectorDefault ?? entityStorageConnectorTypes[0];
|
|
746
|
+
if (entityStorageConnectorTypes.includes(engineTypes.EntityStorageConnectorType.Synchronised)) {
|
|
747
|
+
// For synchronised storage we use the default connector as the one we wrap for real DB operations
|
|
748
|
+
coreConfig.types.entityStorageConnector.push({
|
|
749
|
+
type: engineTypes.EntityStorageConnectorType.Synchronised,
|
|
750
|
+
options: {
|
|
751
|
+
entityStorageConnectorType: defaultEntityStorageConnectorType
|
|
752
|
+
}
|
|
753
|
+
});
|
|
754
|
+
}
|
|
755
|
+
if (core.Is.arrayValue(entityStorageConnectorTypes)) {
|
|
718
756
|
for (const config of coreConfig.types.entityStorageConnector) {
|
|
719
|
-
if (config.type ===
|
|
757
|
+
if (config.type === defaultEntityStorageConnectorType) {
|
|
720
758
|
config.isDefault = true;
|
|
759
|
+
break;
|
|
721
760
|
}
|
|
722
761
|
}
|
|
723
762
|
}
|
|
@@ -729,14 +768,13 @@ function configureEntityStorage(coreConfig, envVars) {
|
|
|
729
768
|
*/
|
|
730
769
|
function configureBlobStorage(coreConfig, envVars) {
|
|
731
770
|
coreConfig.types.blobStorageConnector ??= [];
|
|
732
|
-
|
|
733
|
-
|
|
771
|
+
const blobStorageConnectorTypes = envVars.blobStorageConnectorType?.split(",") ?? [];
|
|
772
|
+
if (blobStorageConnectorTypes.includes(engineTypes.BlobStorageConnectorType.Memory)) {
|
|
734
773
|
coreConfig.types.blobStorageConnector.push({
|
|
735
774
|
type: engineTypes.BlobStorageConnectorType.Memory
|
|
736
775
|
});
|
|
737
776
|
}
|
|
738
|
-
if ((
|
|
739
|
-
envVars.blobStorageConnectorType === engineTypes.BlobStorageConnectorType.File) {
|
|
777
|
+
if (blobStorageConnectorTypes.includes(engineTypes.BlobStorageConnectorType.File)) {
|
|
740
778
|
coreConfig.types.blobStorageConnector.push({
|
|
741
779
|
type: engineTypes.BlobStorageConnectorType.File,
|
|
742
780
|
options: {
|
|
@@ -749,18 +787,18 @@ function configureBlobStorage(coreConfig, envVars) {
|
|
|
749
787
|
}
|
|
750
788
|
});
|
|
751
789
|
}
|
|
752
|
-
if (
|
|
790
|
+
if (blobStorageConnectorTypes.includes(engineTypes.BlobStorageConnectorType.Ipfs)) {
|
|
753
791
|
coreConfig.types.blobStorageConnector.push({
|
|
754
792
|
type: engineTypes.BlobStorageConnectorType.Ipfs,
|
|
755
793
|
options: {
|
|
756
794
|
config: {
|
|
757
|
-
apiUrl: envVars.ipfsApiUrl,
|
|
795
|
+
apiUrl: envVars.ipfsApiUrl ?? "",
|
|
758
796
|
bearerToken: envVars.ipfsBearerToken
|
|
759
797
|
}
|
|
760
798
|
}
|
|
761
799
|
});
|
|
762
800
|
}
|
|
763
|
-
if (
|
|
801
|
+
if (blobStorageConnectorTypes.includes(engineTypes.BlobStorageConnectorType.AwsS3)) {
|
|
764
802
|
coreConfig.types.blobStorageConnector.push({
|
|
765
803
|
type: engineTypes.BlobStorageConnectorType.AwsS3,
|
|
766
804
|
options: {
|
|
@@ -775,7 +813,7 @@ function configureBlobStorage(coreConfig, envVars) {
|
|
|
775
813
|
}
|
|
776
814
|
});
|
|
777
815
|
}
|
|
778
|
-
if (
|
|
816
|
+
if (blobStorageConnectorTypes.includes(engineTypes.BlobStorageConnectorType.AzureStorage)) {
|
|
779
817
|
coreConfig.types.blobStorageConnector.push({
|
|
780
818
|
type: engineTypes.BlobStorageConnectorType.AzureStorage,
|
|
781
819
|
options: {
|
|
@@ -789,7 +827,7 @@ function configureBlobStorage(coreConfig, envVars) {
|
|
|
789
827
|
}
|
|
790
828
|
});
|
|
791
829
|
}
|
|
792
|
-
if (
|
|
830
|
+
if (blobStorageConnectorTypes.includes(engineTypes.BlobStorageConnectorType.GcpStorage)) {
|
|
793
831
|
coreConfig.types.blobStorageConnector.push({
|
|
794
832
|
type: engineTypes.BlobStorageConnectorType.GcpStorage,
|
|
795
833
|
options: {
|
|
@@ -803,12 +841,20 @@ function configureBlobStorage(coreConfig, envVars) {
|
|
|
803
841
|
}
|
|
804
842
|
});
|
|
805
843
|
}
|
|
806
|
-
|
|
807
|
-
|
|
844
|
+
if (core.Is.arrayValue(blobStorageConnectorTypes)) {
|
|
845
|
+
const defaultStorageConnectorType = envVars.blobStorageConnectorDefault ?? blobStorageConnectorTypes[0];
|
|
808
846
|
for (const config of coreConfig.types.blobStorageConnector) {
|
|
809
847
|
if (config.type === defaultStorageConnectorType) {
|
|
810
848
|
config.isDefault = true;
|
|
811
849
|
}
|
|
850
|
+
// If this blob storage connector is the one to use for public access
|
|
851
|
+
// then add it as a feature
|
|
852
|
+
if (core.Is.stringValue(envVars.blobStorageConnectorPublic) &&
|
|
853
|
+
config.type === envVars.blobStorageConnectorPublic) {
|
|
854
|
+
config.features ??= [];
|
|
855
|
+
config.features.push("public");
|
|
856
|
+
break;
|
|
857
|
+
}
|
|
812
858
|
}
|
|
813
859
|
}
|
|
814
860
|
if (coreConfig.types.blobStorageConnector.length > 0) {
|
|
@@ -818,7 +864,7 @@ function configureBlobStorage(coreConfig, envVars) {
|
|
|
818
864
|
options: {
|
|
819
865
|
config: {
|
|
820
866
|
vaultKeyId: (envVars.blobStorageEnableEncryption ?? false)
|
|
821
|
-
? envVars.
|
|
867
|
+
? envVars.blobStorageEncryptionKeyId
|
|
822
868
|
: undefined
|
|
823
869
|
}
|
|
824
870
|
}
|
|
@@ -1313,51 +1359,168 @@ function configureDocumentManagement(coreConfig, envVars) {
|
|
|
1313
1359
|
}
|
|
1314
1360
|
}
|
|
1315
1361
|
/**
|
|
1316
|
-
* Configures the
|
|
1362
|
+
* Configures the rights management.
|
|
1317
1363
|
* @param coreConfig The core config.
|
|
1318
1364
|
* @param envVars The environment variables.
|
|
1319
1365
|
*/
|
|
1320
|
-
function
|
|
1321
|
-
if (core.
|
|
1322
|
-
coreConfig.types.
|
|
1323
|
-
coreConfig.types.
|
|
1324
|
-
type: engineTypes.
|
|
1366
|
+
function configureRightsManagement(coreConfig, envVars) {
|
|
1367
|
+
if (core.Coerce.boolean(envVars.rightsManagementEnabled) ?? false) {
|
|
1368
|
+
coreConfig.types.rightsManagementPapComponent ??= [];
|
|
1369
|
+
coreConfig.types.rightsManagementPapComponent.push({
|
|
1370
|
+
type: engineTypes.RightsManagementPapComponentType.Service
|
|
1371
|
+
});
|
|
1372
|
+
coreConfig.types.rightsManagementPmpComponent ??= [];
|
|
1373
|
+
coreConfig.types.rightsManagementPmpComponent.push({
|
|
1374
|
+
type: engineTypes.RightsManagementPmpComponentType.Service
|
|
1375
|
+
});
|
|
1376
|
+
coreConfig.types.rightsManagementPipComponent ??= [];
|
|
1377
|
+
coreConfig.types.rightsManagementPipComponent.push({
|
|
1378
|
+
type: engineTypes.RightsManagementPipComponentType.Service,
|
|
1379
|
+
options: {
|
|
1380
|
+
informationModulesConfig: core.Is.arrayValue(envVars.rightsManagementInformationSources)
|
|
1381
|
+
? envVars.rightsManagementInformationSources
|
|
1382
|
+
: undefined
|
|
1383
|
+
}
|
|
1384
|
+
});
|
|
1385
|
+
coreConfig.types.rightsManagementPxpComponent ??= [];
|
|
1386
|
+
coreConfig.types.rightsManagementPxpComponent.push({
|
|
1387
|
+
type: engineTypes.RightsManagementPxpComponentType.Service,
|
|
1388
|
+
options: {
|
|
1389
|
+
actionModulesConfig: core.Is.arrayValue(envVars.rightsManagementExecutionActions)
|
|
1390
|
+
? envVars.rightsManagementExecutionActions
|
|
1391
|
+
: undefined
|
|
1392
|
+
}
|
|
1393
|
+
});
|
|
1394
|
+
coreConfig.types.rightsManagementPdpComponent ??= [];
|
|
1395
|
+
coreConfig.types.rightsManagementPdpComponent.push({
|
|
1396
|
+
type: engineTypes.RightsManagementPdpComponentType.Service
|
|
1397
|
+
});
|
|
1398
|
+
coreConfig.types.rightsManagementPepComponent ??= [];
|
|
1399
|
+
coreConfig.types.rightsManagementPepComponent.push({
|
|
1400
|
+
type: engineTypes.RightsManagementPepComponentType.Service,
|
|
1401
|
+
options: {
|
|
1402
|
+
processorModulesConfig: core.Is.arrayValue(envVars.rightsManagementEnforcementProcessors)
|
|
1403
|
+
? envVars.rightsManagementEnforcementProcessors
|
|
1404
|
+
: undefined
|
|
1405
|
+
}
|
|
1406
|
+
});
|
|
1407
|
+
coreConfig.types.rightsManagementPnpComponent ??= [];
|
|
1408
|
+
coreConfig.types.rightsManagementPnpComponent.push({
|
|
1409
|
+
type: engineTypes.RightsManagementPnpComponentType.Service,
|
|
1410
|
+
options: {
|
|
1411
|
+
negotiatorModulesConfig: core.Is.arrayValue(envVars.rightsManagementNegotiators)
|
|
1412
|
+
? envVars.rightsManagementNegotiators
|
|
1413
|
+
: undefined
|
|
1414
|
+
}
|
|
1415
|
+
});
|
|
1416
|
+
coreConfig.types.rightsManagementPnapComponent ??= [];
|
|
1417
|
+
coreConfig.types.rightsManagementPnapComponent.push({
|
|
1418
|
+
type: engineTypes.RightsManagementPnapComponentType.Service
|
|
1419
|
+
});
|
|
1420
|
+
coreConfig.types.rightsManagementPnrpComponent ??= [];
|
|
1421
|
+
coreConfig.types.rightsManagementPnrpComponent.push({
|
|
1422
|
+
type: engineTypes.RightsManagementPnrpComponentType.Service,
|
|
1325
1423
|
options: {
|
|
1326
1424
|
config: {
|
|
1327
|
-
|
|
1328
|
-
|
|
1425
|
+
negotiationMethodId: envVars.rightsManagementNegotiationMethodId ?? "",
|
|
1426
|
+
negotiationComponentCreator: async (url) => new rightsManagementRestClient.PolicyNegotiationPointClient({ endpoint: url })
|
|
1329
1427
|
}
|
|
1330
1428
|
}
|
|
1331
1429
|
});
|
|
1332
1430
|
}
|
|
1333
1431
|
}
|
|
1334
1432
|
/**
|
|
1335
|
-
* Configures the
|
|
1433
|
+
* Configures the task scheduler.
|
|
1336
1434
|
* @param coreConfig The core config.
|
|
1337
1435
|
* @param envVars The environment variables.
|
|
1338
1436
|
*/
|
|
1339
|
-
function
|
|
1340
|
-
if (core.Coerce.boolean(envVars.
|
|
1341
|
-
coreConfig.types.
|
|
1342
|
-
coreConfig.types.
|
|
1343
|
-
type: engineTypes.
|
|
1437
|
+
function configureTaskScheduler(coreConfig, envVars) {
|
|
1438
|
+
if (core.Coerce.boolean(envVars.taskSchedulerEnabled) ?? false) {
|
|
1439
|
+
coreConfig.types.taskSchedulerComponent ??= [];
|
|
1440
|
+
coreConfig.types.taskSchedulerComponent.push({
|
|
1441
|
+
type: engineTypes.TaskSchedulerComponentType.Service
|
|
1344
1442
|
});
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
/**
|
|
1446
|
+
* Configures the synchronised storage.
|
|
1447
|
+
* @param coreConfig The core config.
|
|
1448
|
+
* @param envVars The environment variables.
|
|
1449
|
+
*/
|
|
1450
|
+
function configureSynchronisedStorage(coreConfig, envVars) {
|
|
1451
|
+
if (core.Is.arrayValue(coreConfig.types.identityResolverComponent) &&
|
|
1452
|
+
(core.Coerce.boolean(envVars.synchronisedStorageEnabled) ?? false)) {
|
|
1453
|
+
// Check if the config provides a custom verifiable storage key id
|
|
1454
|
+
let verifiableStorageKeyId = core.Coerce.string(envVars.synchronisedStorageVerifiableStorageKeyId);
|
|
1455
|
+
if (!core.Is.stringValue(verifiableStorageKeyId)) {
|
|
1456
|
+
// No custom key so default to the network setting
|
|
1457
|
+
verifiableStorageKeyId = envVars.iotaNetwork;
|
|
1458
|
+
}
|
|
1459
|
+
coreConfig.types.synchronisedStorageComponent ??= [];
|
|
1460
|
+
coreConfig.types.synchronisedStorageComponent.push({
|
|
1461
|
+
type: engineTypes.SynchronisedStorageComponentType.Service,
|
|
1462
|
+
options: {
|
|
1463
|
+
config: {
|
|
1464
|
+
verifiableStorageKeyId: verifiableStorageKeyId ?? "",
|
|
1465
|
+
synchronisedStorageMethodId: envVars.synchronisedStorageVerificationMethodId,
|
|
1466
|
+
blobStorageEncryptionKeyId: envVars.synchronisedStorageBlobStorageEncryptionKeyId,
|
|
1467
|
+
entityUpdateIntervalMinutes: core.Coerce.number(envVars.synchronisedStorageEntityUpdateIntervalMinutes),
|
|
1468
|
+
consolidationIntervalMinutes: core.Coerce.number(envVars.synchronisedStorageConsolidationIntervalMinutes),
|
|
1469
|
+
consolidationBatchSize: core.Coerce.number(envVars.synchronisedStorageConsolidationBatchSize),
|
|
1470
|
+
maxConsolidations: core.Coerce.number(envVars.synchronisedStorageMaxConsolidations)
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1348
1473
|
});
|
|
1474
|
+
// If there is a trusted url set, we need to add a client
|
|
1475
|
+
// and give it a feature of trusted so that when the synchronised
|
|
1476
|
+
// storage is created it can pickup the correct component
|
|
1477
|
+
if (core.Is.stringValue(envVars.synchronisedStorageTrustedUrl)) {
|
|
1478
|
+
coreConfig.types.synchronisedStorageComponent.push({
|
|
1479
|
+
type: engineTypes.SynchronisedStorageComponentType.RestClient,
|
|
1480
|
+
options: {
|
|
1481
|
+
endpoint: envVars.synchronisedStorageTrustedUrl
|
|
1482
|
+
},
|
|
1483
|
+
features: ["trusted"]
|
|
1484
|
+
});
|
|
1485
|
+
}
|
|
1349
1486
|
}
|
|
1350
1487
|
}
|
|
1351
1488
|
/**
|
|
1352
|
-
* Configures the
|
|
1489
|
+
* Configures the federated catalogue.
|
|
1353
1490
|
* @param coreConfig The core config.
|
|
1354
1491
|
* @param envVars The environment variables.
|
|
1355
1492
|
*/
|
|
1356
|
-
function
|
|
1357
|
-
if (core.Coerce.boolean(envVars.
|
|
1358
|
-
coreConfig.types.
|
|
1359
|
-
coreConfig.types.
|
|
1360
|
-
type: engineTypes.
|
|
1493
|
+
function configureFederatedCatalogue(coreConfig, envVars) {
|
|
1494
|
+
if (core.Coerce.boolean(envVars.federatedCatalogueEnabled) ?? false) {
|
|
1495
|
+
coreConfig.types.federatedCatalogueComponent ??= [];
|
|
1496
|
+
coreConfig.types.federatedCatalogueComponent.push({
|
|
1497
|
+
type: engineTypes.FederatedCatalogueComponentType.Service,
|
|
1498
|
+
options: {
|
|
1499
|
+
config: {
|
|
1500
|
+
subResourceCacheTtlMs: core.Coerce.number(envVars.federatedCatalogueCacheTtlMs),
|
|
1501
|
+
clearingHouseApproverList: core.Coerce.object(envVars.federatedCatalogueClearingHouseApproverList) ?? []
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1504
|
+
});
|
|
1505
|
+
}
|
|
1506
|
+
}
|
|
1507
|
+
/**
|
|
1508
|
+
* Configures the data space connector.
|
|
1509
|
+
* @param coreConfig The core config.
|
|
1510
|
+
* @param envVars The environment variables.
|
|
1511
|
+
*/
|
|
1512
|
+
function configureDataSpaceConnector(coreConfig, envVars) {
|
|
1513
|
+
if (core.Coerce.boolean(envVars.dataSpaceConnectorEnabled) ?? false) {
|
|
1514
|
+
coreConfig.types.dataSpaceConnectorComponent ??= [];
|
|
1515
|
+
coreConfig.types.dataSpaceConnectorComponent.push({
|
|
1516
|
+
type: engineTypes.DataSpaceConnectorComponentType.Service,
|
|
1517
|
+
options: {
|
|
1518
|
+
config: {
|
|
1519
|
+
dataSpaceConnectorAppDescriptors: core.Is.arrayValue(envVars.dataSpaceConnectorApps)
|
|
1520
|
+
? envVars.dataSpaceConnectorApps
|
|
1521
|
+
: undefined
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1361
1524
|
});
|
|
1362
1525
|
}
|
|
1363
1526
|
}
|
|
@@ -1400,9 +1563,10 @@ function configureDlt(coreConfig, envVars) {
|
|
|
1400
1563
|
* @param coreEngineConfig The core engine config.
|
|
1401
1564
|
* @param serverInfo The server information.
|
|
1402
1565
|
* @param openApiSpecPath The path to the open api spec.
|
|
1566
|
+
* @param favIconPath The path to the favicon.
|
|
1403
1567
|
* @returns The the config for the core and the server.
|
|
1404
1568
|
*/
|
|
1405
|
-
function buildEngineServerConfiguration(envVars, coreEngineConfig, serverInfo, openApiSpecPath) {
|
|
1569
|
+
function buildEngineServerConfiguration(envVars, coreEngineConfig, serverInfo, openApiSpecPath, favIconPath) {
|
|
1406
1570
|
envVars.authSigningKeyId ??= "auth-signing";
|
|
1407
1571
|
const webServerOptions = {
|
|
1408
1572
|
port: core.Coerce.number(envVars.port),
|
|
@@ -1429,7 +1593,8 @@ function buildEngineServerConfiguration(envVars, coreEngineConfig, serverInfo, o
|
|
|
1429
1593
|
options: {
|
|
1430
1594
|
config: {
|
|
1431
1595
|
serverInfo,
|
|
1432
|
-
openApiSpecPath
|
|
1596
|
+
openApiSpecPath,
|
|
1597
|
+
favIconPath
|
|
1433
1598
|
}
|
|
1434
1599
|
}
|
|
1435
1600
|
}
|
|
@@ -1546,16 +1711,19 @@ function buildEngineServerConfiguration(envVars, coreEngineConfig, serverInfo, o
|
|
|
1546
1711
|
* @returns The engine server.
|
|
1547
1712
|
*/
|
|
1548
1713
|
async function start(nodeOptions, engineServerConfig, envVars) {
|
|
1549
|
-
envVars.
|
|
1550
|
-
|
|
1551
|
-
|
|
1714
|
+
const entityStorageConnectorType = envVars.entityStorageConnectorType?.split(",") ?? [];
|
|
1715
|
+
const blobStorageConnectorType = envVars.blobStorageConnectorType?.split(",") ?? [];
|
|
1716
|
+
// If the blob storage or entity storage is configured with file connectors
|
|
1717
|
+
// then we need to make sure the storageFileRoot is set
|
|
1718
|
+
if ((entityStorageConnectorType.includes(engineTypes.EntityStorageConnectorType.File) ||
|
|
1719
|
+
blobStorageConnectorType.includes(engineTypes.BlobStorageConnectorType.File) ||
|
|
1552
1720
|
core.Is.empty(nodeOptions?.stateStorage)) &&
|
|
1553
1721
|
!core.Is.stringValue(envVars.storageFileRoot)) {
|
|
1554
1722
|
throw new core.GeneralError("node", "storageFileRootNotSet", {
|
|
1555
1723
|
storageFileRoot: `${nodeOptions?.envPrefix ?? ""}_STORAGE_FILE_ROOT`
|
|
1556
1724
|
});
|
|
1557
1725
|
}
|
|
1558
|
-
// Create the engine instance using file state storage
|
|
1726
|
+
// Create the engine instance using file state storage unless one is configured in options
|
|
1559
1727
|
const engine$1 = new engine.Engine({
|
|
1560
1728
|
config: engineServerConfig,
|
|
1561
1729
|
stateStorage: nodeOptions?.stateStorage ?? new engineCore.FileStateStorage(envVars.stateFilename ?? ""),
|
|
@@ -1599,7 +1767,7 @@ async function run(nodeOptions) {
|
|
|
1599
1767
|
nodeOptions ??= {};
|
|
1600
1768
|
const serverInfo = {
|
|
1601
1769
|
name: nodeOptions?.serverName ?? "TWIN Node Server",
|
|
1602
|
-
version: nodeOptions?.serverVersion ?? "0.0.2-next.
|
|
1770
|
+
version: nodeOptions?.serverVersion ?? "0.0.2-next.10" // x-release-please-version
|
|
1603
1771
|
};
|
|
1604
1772
|
console.log(`\u001B[4mđŠī¸ ${serverInfo.name} v${serverInfo.version}\u001B[24m\n`);
|
|
1605
1773
|
if (!core.Is.stringValue(nodeOptions?.executionDirectory)) {
|
|
@@ -1612,15 +1780,24 @@ async function run(nodeOptions) {
|
|
|
1612
1780
|
console.info("Locales Directory:", nodeOptions.localesDirectory);
|
|
1613
1781
|
await initialiseLocales(nodeOptions.localesDirectory);
|
|
1614
1782
|
if (core.Is.empty(nodeOptions?.openApiSpecFile)) {
|
|
1615
|
-
const specFile = path.resolve(path.join(nodeOptions.executionDirectory, "docs", "open-api", "spec.json"));
|
|
1783
|
+
const specFile = path.resolve(path.join(nodeOptions.executionDirectory ?? "", "docs", "open-api", "spec.json"));
|
|
1616
1784
|
console.info("Default OpenAPI Spec File:", specFile);
|
|
1617
1785
|
if (await fileExists(specFile)) {
|
|
1618
1786
|
nodeOptions ??= {};
|
|
1619
1787
|
nodeOptions.openApiSpecFile = specFile;
|
|
1620
1788
|
}
|
|
1621
1789
|
}
|
|
1790
|
+
if (core.Is.empty(nodeOptions?.favIconFile)) {
|
|
1791
|
+
const favIconFile = path.resolve(path.join(nodeOptions.executionDirectory ?? "", "static", "favicon.png"));
|
|
1792
|
+
console.info("Default Favicon File:", favIconFile);
|
|
1793
|
+
if (await fileExists(favIconFile)) {
|
|
1794
|
+
nodeOptions ??= {};
|
|
1795
|
+
nodeOptions.favIconFile = favIconFile;
|
|
1796
|
+
}
|
|
1797
|
+
}
|
|
1622
1798
|
nodeOptions.envPrefix ??= "TWIN_NODE_";
|
|
1623
1799
|
console.info("Environment Prefix:", nodeOptions.envPrefix);
|
|
1800
|
+
overrideModuleImport(nodeOptions.executionDirectory ?? "");
|
|
1624
1801
|
const { engineServerConfig, nodeEnvVars: envVars } = await buildConfiguration(process.env, nodeOptions, serverInfo);
|
|
1625
1802
|
console.info();
|
|
1626
1803
|
const startResult = await start(nodeOptions, engineServerConfig, envVars);
|
|
@@ -1657,7 +1834,8 @@ async function buildConfiguration(processEnv, options, serverInfo) {
|
|
|
1657
1834
|
}
|
|
1658
1835
|
if (core.Is.arrayValue(options?.envFilenames)) {
|
|
1659
1836
|
const output = dotenv__namespace.config({
|
|
1660
|
-
path: options?.envFilenames
|
|
1837
|
+
path: options?.envFilenames,
|
|
1838
|
+
quiet: true
|
|
1661
1839
|
});
|
|
1662
1840
|
// We don't want to throw an error if the default environment file is not found.
|
|
1663
1841
|
// Only if we have custom environment files.
|
|
@@ -1672,12 +1850,18 @@ async function buildConfiguration(processEnv, options, serverInfo) {
|
|
|
1672
1850
|
// Expand any environment variables that use the @file: syntax
|
|
1673
1851
|
const keys = Object.keys(envVars);
|
|
1674
1852
|
for (const key of keys) {
|
|
1675
|
-
if (core.Is.stringValue(envVars[key]) &&
|
|
1853
|
+
if (core.Is.stringValue(envVars[key]) &&
|
|
1854
|
+
(envVars[key].startsWith("@text:") || envVars[key].startsWith("@json:"))) {
|
|
1676
1855
|
const filePath = envVars[key].slice(6).trim();
|
|
1677
1856
|
const embeddedFile = path.resolve(path.join(options.executionDirectory ?? "", filePath));
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1857
|
+
if (envVars[key].startsWith("@text:")) {
|
|
1858
|
+
console.info(`Expanding Environment Variable: ${key} from text file: ${embeddedFile}`);
|
|
1859
|
+
envVars[key] = await loadTextFile(embeddedFile);
|
|
1860
|
+
}
|
|
1861
|
+
else if (envVars[key].startsWith("@json:")) {
|
|
1862
|
+
console.info(`Expanding Environment Variable: ${key} from JSON file: ${embeddedFile}`);
|
|
1863
|
+
envVars[key] = await loadJsonFile(embeddedFile);
|
|
1864
|
+
}
|
|
1681
1865
|
}
|
|
1682
1866
|
}
|
|
1683
1867
|
// Extend the environment variables with any additional custom configuration.
|
|
@@ -1687,7 +1871,7 @@ async function buildConfiguration(processEnv, options, serverInfo) {
|
|
|
1687
1871
|
}
|
|
1688
1872
|
// Build the engine configuration from the environment variables.
|
|
1689
1873
|
const coreConfig = buildEngineConfiguration(envVars);
|
|
1690
|
-
const engineServerConfig = buildEngineServerConfiguration(envVars, coreConfig, serverInfo, options?.openApiSpecFile);
|
|
1874
|
+
const engineServerConfig = buildEngineServerConfiguration(envVars, coreConfig, serverInfo, options?.openApiSpecFile, options?.favIconFile);
|
|
1691
1875
|
// Merge any custom configuration provided in the options.
|
|
1692
1876
|
if (core.Is.arrayValue(options?.configFilenames)) {
|
|
1693
1877
|
for (const configFile of options.configFilenames) {
|
|
@@ -1708,15 +1892,49 @@ async function buildConfiguration(processEnv, options, serverInfo) {
|
|
|
1708
1892
|
}
|
|
1709
1893
|
return { engineServerConfig, nodeEnvVars: envVars };
|
|
1710
1894
|
}
|
|
1895
|
+
/**
|
|
1896
|
+
* Override module imports to use local files where possible.
|
|
1897
|
+
* @param executionDirectory The execution directory for resolving local module paths.
|
|
1898
|
+
*/
|
|
1899
|
+
function overrideModuleImport(executionDirectory) {
|
|
1900
|
+
modules.ModuleHelper.overrideImport(async (moduleName) => {
|
|
1901
|
+
// If the module path for example when dynamically loading
|
|
1902
|
+
// modules looks like a local file then we try to resolve
|
|
1903
|
+
// using the local file system
|
|
1904
|
+
const isLocal = modules.ModuleHelper.isLocalModule(moduleName);
|
|
1905
|
+
if (isLocal) {
|
|
1906
|
+
// See if we can just resolve the filename locally
|
|
1907
|
+
let localFilename = path.resolve(moduleName);
|
|
1908
|
+
let exists = await fileExists(localFilename);
|
|
1909
|
+
if (!exists) {
|
|
1910
|
+
// Doesn't exist in the current directory, try the execution directory
|
|
1911
|
+
localFilename = path.resolve(executionDirectory, moduleName);
|
|
1912
|
+
exists = await fileExists(localFilename);
|
|
1913
|
+
}
|
|
1914
|
+
if (exists) {
|
|
1915
|
+
// If the module exists then we can load it, otherwise
|
|
1916
|
+
// we fallback to regular handling to see if that can import it
|
|
1917
|
+
return {
|
|
1918
|
+
module: await import(process.platform === "win32" ? `file://${localFilename}` : localFilename),
|
|
1919
|
+
useDefault: false
|
|
1920
|
+
};
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
// The filename doesn't look like a local module, so just use default handling
|
|
1924
|
+
return {
|
|
1925
|
+
useDefault: true
|
|
1926
|
+
};
|
|
1927
|
+
});
|
|
1928
|
+
}
|
|
1711
1929
|
|
|
1712
1930
|
exports.NodeFeatures = NodeFeatures;
|
|
1713
1931
|
exports.bootstrap = bootstrap;
|
|
1714
|
-
exports.bootstrapAttestationMethod = bootstrapAttestationMethod;
|
|
1715
1932
|
exports.bootstrapAuth = bootstrapAuth;
|
|
1716
1933
|
exports.bootstrapBlobEncryption = bootstrapBlobEncryption;
|
|
1717
1934
|
exports.bootstrapImmutableProofMethod = bootstrapImmutableProofMethod;
|
|
1718
1935
|
exports.bootstrapNodeIdentity = bootstrapNodeIdentity;
|
|
1719
1936
|
exports.bootstrapNodeUser = bootstrapNodeUser;
|
|
1937
|
+
exports.bootstrapSynchronisedStorage = bootstrapSynchronisedStorage;
|
|
1720
1938
|
exports.buildConfiguration = buildConfiguration;
|
|
1721
1939
|
exports.buildEngineConfiguration = buildEngineConfiguration;
|
|
1722
1940
|
exports.buildEngineServerConfiguration = buildEngineServerConfiguration;
|
|
@@ -1725,5 +1943,7 @@ exports.getExecutionDirectory = getExecutionDirectory;
|
|
|
1725
1943
|
exports.getFeatures = getFeatures;
|
|
1726
1944
|
exports.initialiseLocales = initialiseLocales;
|
|
1727
1945
|
exports.loadJsonFile = loadJsonFile;
|
|
1946
|
+
exports.loadTextFile = loadTextFile;
|
|
1947
|
+
exports.overrideModuleImport = overrideModuleImport;
|
|
1728
1948
|
exports.run = run;
|
|
1729
1949
|
exports.start = start;
|