mindoodb 0.0.85 → 0.0.88
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/README-server.md +6 -2
- package/README.md +6 -4
- package/dist/appendonlystores/network/ClientNetworkContentAddressedStore.d.ts.map +1 -1
- package/dist/appendonlystores/network/ClientNetworkContentAddressedStore.js +2 -1
- package/dist/appendonlystores/network/ClientNetworkContentAddressedStore.js.map +1 -1
- package/dist/appendonlystores/network/ServerNetworkContentAddressedStore.d.ts +151 -12
- package/dist/appendonlystores/network/ServerNetworkContentAddressedStore.d.ts.map +1 -1
- package/dist/appendonlystores/network/ServerNetworkContentAddressedStore.js +340 -24
- package/dist/appendonlystores/network/ServerNetworkContentAddressedStore.js.map +1 -1
- package/dist/browser/index.mjs +14 -0
- package/dist/cloudflare/DurableObjectSql.d.ts +56 -0
- package/dist/cloudflare/DurableObjectSql.d.ts.map +1 -0
- package/dist/cloudflare/DurableObjectSql.js +180 -0
- package/dist/cloudflare/DurableObjectSql.js.map +1 -0
- package/dist/cloudflare/MindooDatabaseObject.d.ts +86 -0
- package/dist/cloudflare/MindooDatabaseObject.d.ts.map +1 -0
- package/dist/cloudflare/MindooDatabaseObject.js +614 -0
- package/dist/cloudflare/MindooDatabaseObject.js.map +1 -0
- package/dist/cloudflare/MindooServerObject.d.ts +63 -0
- package/dist/cloudflare/MindooServerObject.d.ts.map +1 -0
- package/dist/cloudflare/MindooServerObject.js +521 -0
- package/dist/cloudflare/MindooServerObject.js.map +1 -0
- package/dist/cloudflare/WorkersCryptoAdapter.d.ts +7 -0
- package/dist/cloudflare/WorkersCryptoAdapter.d.ts.map +1 -0
- package/dist/cloudflare/WorkersCryptoAdapter.js +14 -0
- package/dist/cloudflare/WorkersCryptoAdapter.js.map +1 -0
- package/dist/cloudflare/host.d.ts +49 -0
- package/dist/cloudflare/host.d.ts.map +1 -0
- package/dist/cloudflare/host.js +104 -0
- package/dist/cloudflare/host.js.map +1 -0
- package/dist/cloudflare/http.d.ts +91 -0
- package/dist/cloudflare/http.d.ts.map +1 -0
- package/dist/cloudflare/http.js +266 -0
- package/dist/cloudflare/http.js.map +1 -0
- package/dist/cloudflare/index.d.ts +12 -0
- package/dist/cloudflare/index.d.ts.map +1 -0
- package/dist/cloudflare/index.js +20 -0
- package/dist/cloudflare/index.js.map +1 -0
- package/dist/cloudflare/storeRpc.d.ts +22 -0
- package/dist/cloudflare/storeRpc.d.ts.map +1 -0
- package/dist/cloudflare/storeRpc.js +85 -0
- package/dist/cloudflare/storeRpc.js.map +1 -0
- package/dist/cloudflare/types.d.ts +101 -0
- package/dist/cloudflare/types.d.ts.map +1 -0
- package/dist/cloudflare/types.js +10 -0
- package/dist/cloudflare/types.js.map +1 -0
- package/dist/cloudflare/worker.d.ts +25 -0
- package/dist/cloudflare/worker.d.ts.map +1 -0
- package/dist/cloudflare/worker.js +170 -0
- package/dist/cloudflare/worker.js.map +1 -0
- package/dist/core/BaseMindooDB.d.ts +95 -10
- package/dist/core/BaseMindooDB.d.ts.map +1 -1
- package/dist/core/BaseMindooDB.js +441 -98
- package/dist/core/BaseMindooDB.js.map +1 -1
- package/dist/core/BaseMindooTenant.d.ts +43 -0
- package/dist/core/BaseMindooTenant.d.ts.map +1 -1
- package/dist/core/BaseMindooTenant.js +157 -6
- package/dist/core/BaseMindooTenant.js.map +1 -1
- package/dist/core/BaseMindooTenantDirectory.d.ts +103 -0
- package/dist/core/BaseMindooTenantDirectory.d.ts.map +1 -1
- package/dist/core/BaseMindooTenantDirectory.js +371 -8
- package/dist/core/BaseMindooTenantDirectory.js.map +1 -1
- package/dist/core/BaseMindooTenantFactory.d.ts.map +1 -1
- package/dist/core/BaseMindooTenantFactory.js +2 -1
- package/dist/core/BaseMindooTenantFactory.js.map +1 -1
- package/dist/core/ChangeDocDraft.d.ts +133 -0
- package/dist/core/ChangeDocDraft.d.ts.map +1 -0
- package/dist/core/ChangeDocDraft.js +492 -0
- package/dist/core/ChangeDocDraft.js.map +1 -0
- package/dist/core/MindooDBServerAdmin.d.ts.map +1 -1
- package/dist/core/MindooDBServerAdmin.js +2 -1
- package/dist/core/MindooDBServerAdmin.js.map +1 -1
- package/dist/core/accesscontrol/directoryProjection.d.ts.map +1 -1
- package/dist/core/accesscontrol/directoryProjection.js +19 -0
- package/dist/core/accesscontrol/directoryProjection.js.map +1 -1
- package/dist/core/accesscontrol/evaluate.d.ts +14 -0
- package/dist/core/accesscontrol/evaluate.d.ts.map +1 -1
- package/dist/core/accesscontrol/evaluate.js +42 -1
- package/dist/core/accesscontrol/evaluate.js.map +1 -1
- package/dist/core/accesscontrol/types.d.ts +88 -4
- package/dist/core/accesscontrol/types.d.ts.map +1 -1
- package/dist/core/accesscontrol/types.js +114 -1
- package/dist/core/accesscontrol/types.js.map +1 -1
- package/dist/core/appendonlystores/network/AuthenticationService.d.ts +14 -0
- package/dist/core/appendonlystores/network/AuthenticationService.d.ts.map +1 -1
- package/dist/core/appendonlystores/network/AuthenticationService.js +27 -7
- package/dist/core/appendonlystores/network/AuthenticationService.js.map +1 -1
- package/dist/core/appendonlystores/network/IrohNetworkTransport.d.ts.map +1 -1
- package/dist/core/appendonlystores/network/IrohNetworkTransport.js +12 -0
- package/dist/core/appendonlystores/network/IrohNetworkTransport.js.map +1 -1
- package/dist/core/appendonlystores/network/IrohStreamIO.d.ts.map +1 -1
- package/dist/core/appendonlystores/network/IrohStreamIO.js +3 -23
- package/dist/core/appendonlystores/network/IrohStreamIO.js.map +1 -1
- package/dist/core/appendonlystores/rpc/RpcContentAddressedStore.d.ts +6 -0
- package/dist/core/appendonlystores/rpc/RpcContentAddressedStore.d.ts.map +1 -1
- package/dist/core/appendonlystores/rpc/RpcContentAddressedStore.js +20 -1
- package/dist/core/appendonlystores/rpc/RpcContentAddressedStore.js.map +1 -1
- package/dist/core/appendonlystores/rpc/storeRpcCodec.d.ts.map +1 -1
- package/dist/core/appendonlystores/rpc/storeRpcCodec.js +3 -23
- package/dist/core/appendonlystores/rpc/storeRpcCodec.js.map +1 -1
- package/dist/core/appendonlystores/sqlite/SqliteContentAddressedStore.d.ts +7 -0
- package/dist/core/appendonlystores/sqlite/SqliteContentAddressedStore.d.ts.map +1 -1
- package/dist/core/appendonlystores/sqlite/SqliteContentAddressedStore.js +39 -30
- package/dist/core/appendonlystores/sqlite/SqliteContentAddressedStore.js.map +1 -1
- package/dist/core/appendonlystores/sqlite/SqliteDriver.d.ts +37 -1
- package/dist/core/appendonlystores/sqlite/SqliteDriver.d.ts.map +1 -1
- package/dist/core/appendonlystores/sqlite/SqliteDriver.js +99 -40
- package/dist/core/appendonlystores/sqlite/SqliteDriver.js.map +1 -1
- package/dist/core/appendonlystores/syncStores.d.ts.map +1 -1
- package/dist/core/appendonlystores/syncStores.js +25 -5
- package/dist/core/appendonlystores/syncStores.js.map +1 -1
- package/dist/core/appendonlystores/types.d.ts +7 -1
- package/dist/core/appendonlystores/types.d.ts.map +1 -1
- package/dist/core/appendonlystores/types.js +1 -0
- package/dist/core/appendonlystores/types.js.map +1 -1
- package/dist/core/builtinDbInvariants.d.ts +49 -0
- package/dist/core/builtinDbInvariants.d.ts.map +1 -1
- package/dist/core/builtinDbInvariants.js +86 -1
- package/dist/core/builtinDbInvariants.js.map +1 -1
- package/dist/core/crypto/AuthChallenge.d.ts +68 -0
- package/dist/core/crypto/AuthChallenge.d.ts.map +1 -0
- package/dist/core/crypto/AuthChallenge.js +118 -0
- package/dist/core/crypto/AuthChallenge.js.map +1 -0
- package/dist/core/crypto/DomainStatement.d.ts +5 -3
- package/dist/core/crypto/DomainStatement.d.ts.map +1 -1
- package/dist/core/crypto/DomainStatement.js +5 -3
- package/dist/core/crypto/DomainStatement.js.map +1 -1
- package/dist/core/crypto/encryptedFields.d.ts +11 -0
- package/dist/core/crypto/encryptedFields.d.ts.map +1 -1
- package/dist/core/crypto/encryptedFields.js +18 -0
- package/dist/core/crypto/encryptedFields.js.map +1 -1
- package/dist/core/expressions/evaluateExpression.d.ts +8 -0
- package/dist/core/expressions/evaluateExpression.d.ts.map +1 -1
- package/dist/core/expressions/evaluateExpression.js +29 -4
- package/dist/core/expressions/evaluateExpression.js.map +1 -1
- package/dist/core/expressions/metadata.js +4 -4
- package/dist/core/expressions/metadata.js.map +1 -1
- package/dist/core/index.d.ts +3 -3
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +17 -6
- package/dist/core/index.js.map +1 -1
- package/dist/core/query/executeQuery.d.ts.map +1 -1
- package/dist/core/query/executeQuery.js +5 -1
- package/dist/core/query/executeQuery.js.map +1 -1
- package/dist/core/query/executeQueryInclude.d.ts.map +1 -1
- package/dist/core/query/executeQueryInclude.js +2 -0
- package/dist/core/query/executeQueryInclude.js.map +1 -1
- package/dist/core/query/queryView.d.ts.map +1 -1
- package/dist/core/query/queryView.js +5 -1
- package/dist/core/query/queryView.js.map +1 -1
- package/dist/core/types.d.ts +204 -10
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/core/uri/MindooURI.d.ts.map +1 -1
- package/dist/core/uri/MindooURI.js +3 -24
- package/dist/core/uri/MindooURI.js.map +1 -1
- package/dist/core/userkeys/index.d.ts +1 -0
- package/dist/core/userkeys/index.d.ts.map +1 -1
- package/dist/core/userkeys/index.js +5 -1
- package/dist/core/userkeys/index.js.map +1 -1
- package/dist/core/userkeys/sealedValues.d.ts +54 -0
- package/dist/core/userkeys/sealedValues.d.ts.map +1 -0
- package/dist/core/userkeys/sealedValues.js +67 -0
- package/dist/core/userkeys/sealedValues.js.map +1 -0
- package/dist/core/utils/base64.d.ts +17 -0
- package/dist/core/utils/base64.d.ts.map +1 -0
- package/dist/core/utils/base64.js +54 -0
- package/dist/core/utils/base64.js.map +1 -0
- package/dist/core/utils/dynamicImportInterop.d.ts +10 -0
- package/dist/core/utils/dynamicImportInterop.d.ts.map +1 -0
- package/dist/core/utils/dynamicImportInterop.js +22 -0
- package/dist/core/utils/dynamicImportInterop.js.map +1 -0
- package/dist/core/utils/idGeneration.d.ts.map +1 -1
- package/dist/core/utils/idGeneration.js +14 -3
- package/dist/core/utils/idGeneration.js.map +1 -1
- package/dist/node/appendonlystores/BasicOnDiskContentAddressedStore.d.ts.map +1 -1
- package/dist/node/appendonlystores/BasicOnDiskContentAddressedStore.js +7 -0
- package/dist/node/appendonlystores/BasicOnDiskContentAddressedStore.js.map +1 -1
- package/dist/node/server/FsServerDataStore.d.ts +23 -0
- package/dist/node/server/FsServerDataStore.d.ts.map +1 -0
- package/dist/node/server/FsServerDataStore.js +59 -0
- package/dist/node/server/FsServerDataStore.js.map +1 -0
- package/dist/node/server/IrohServerEndpoint.d.ts +1 -1
- package/dist/node/server/IrohServerEndpoint.d.ts.map +1 -1
- package/dist/node/server/IrohServerRpc.d.ts +5 -5
- package/dist/node/server/IrohServerRpc.d.ts.map +1 -1
- package/dist/node/server/IrohServerRpc.js +1 -1
- package/dist/node/server/IrohServerRpc.js.map +1 -1
- package/dist/node/server/MindooDBServer.d.ts +29 -150
- package/dist/node/server/MindooDBServer.d.ts.map +1 -1
- package/dist/node/server/MindooDBServer.js +160 -1573
- package/dist/node/server/MindooDBServer.js.map +1 -1
- package/dist/node/server/PurgedDocRegistry.d.ts +4 -27
- package/dist/node/server/PurgedDocRegistry.d.ts.map +1 -1
- package/dist/node/server/PurgedDocRegistry.js +6 -83
- package/dist/node/server/PurgedDocRegistry.js.map +1 -1
- package/dist/node/server/StoreFactory.d.ts +1 -1
- package/dist/node/server/StoreFactory.d.ts.map +1 -1
- package/dist/node/server/TenantManager.d.ts +16 -284
- package/dist/node/server/TenantManager.d.ts.map +1 -1
- package/dist/node/server/TenantManager.js +35 -1168
- package/dist/node/server/TenantManager.js.map +1 -1
- package/dist/node/server/add-to-network.js +1 -1
- package/dist/node/server/add-to-network.js.map +1 -1
- package/dist/node/server/config.d.ts +11 -15
- package/dist/node/server/config.d.ts.map +1 -1
- package/dist/node/server/config.js +30 -245
- package/dist/node/server/config.js.map +1 -1
- package/dist/node/server/expressAdapter.d.ts +23 -0
- package/dist/node/server/expressAdapter.d.ts.map +1 -0
- package/dist/node/server/expressAdapter.js +108 -0
- package/dist/node/server/expressAdapter.js.map +1 -0
- package/dist/node/server/nativeIrohStreamIO.d.ts +1 -1
- package/dist/node/server/nativeIrohStreamIO.d.ts.map +1 -1
- package/dist/node/server/peer/FileClusterAuditStorage.d.ts +16 -0
- package/dist/node/server/peer/FileClusterAuditStorage.d.ts.map +1 -0
- package/dist/node/server/peer/FileClusterAuditStorage.js +39 -0
- package/dist/node/server/peer/FileClusterAuditStorage.js.map +1 -0
- package/dist/node/server/peer/IrohPeerLink.d.ts +1 -1
- package/dist/node/server/peer/IrohPeerLink.d.ts.map +1 -1
- package/dist/node/server/peer/routes.d.ts +10 -44
- package/dist/node/server/peer/routes.d.ts.map +1 -1
- package/dist/node/server/peer/routes.js +33 -275
- package/dist/node/server/peer/routes.js.map +1 -1
- package/dist/node/server/resolveServerPassword.js +1 -1
- package/dist/node/server/resolveServerPassword.js.map +1 -1
- package/dist/node/server/server.js +1 -1
- package/dist/node/server/server.js.map +1 -1
- package/dist/node/server/serverinit.js +1 -1
- package/dist/node/server/serverinit.js.map +1 -1
- package/dist/server/CapabilityMatcher.d.ts +60 -0
- package/dist/server/CapabilityMatcher.d.ts.map +1 -0
- package/dist/server/CapabilityMatcher.js +138 -0
- package/dist/server/CapabilityMatcher.js.map +1 -0
- package/dist/server/DeviceDiscoveryService.d.ts +36 -0
- package/dist/server/DeviceDiscoveryService.d.ts.map +1 -0
- package/dist/server/DeviceDiscoveryService.js +164 -0
- package/dist/server/DeviceDiscoveryService.js.map +1 -0
- package/dist/server/PurgedDocRegistry.d.ts +35 -0
- package/dist/server/PurgedDocRegistry.d.ts.map +1 -0
- package/dist/server/PurgedDocRegistry.js +90 -0
- package/dist/server/PurgedDocRegistry.js.map +1 -0
- package/dist/server/ServerDataStore.d.ts +67 -0
- package/dist/server/ServerDataStore.d.ts.map +1 -0
- package/dist/server/ServerDataStore.js +88 -0
- package/dist/server/ServerDataStore.js.map +1 -0
- package/dist/server/SyncEventBus.d.ts +39 -0
- package/dist/server/SyncEventBus.d.ts.map +1 -0
- package/dist/server/SyncEventBus.js +41 -0
- package/dist/server/SyncEventBus.js.map +1 -0
- package/dist/server/SystemAdminAuth.d.ts +82 -0
- package/dist/server/SystemAdminAuth.d.ts.map +1 -0
- package/dist/server/SystemAdminAuth.js +227 -0
- package/dist/server/SystemAdminAuth.js.map +1 -0
- package/dist/server/TenantManager.d.ts +366 -0
- package/dist/server/TenantManager.d.ts.map +1 -0
- package/dist/server/TenantManager.js +1416 -0
- package/dist/server/TenantManager.js.map +1 -0
- package/dist/server/http/MindooDBServerCore.d.ts +313 -0
- package/dist/server/http/MindooDBServerCore.d.ts.map +1 -0
- package/dist/server/http/MindooDBServerCore.js +1410 -0
- package/dist/server/http/MindooDBServerCore.js.map +1 -0
- package/dist/server/http/types.d.ts +89 -0
- package/dist/server/http/types.d.ts.map +1 -0
- package/dist/server/http/types.js +29 -0
- package/dist/server/http/types.js.map +1 -0
- package/dist/server/index.d.ts +32 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +76 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/peer/ClusterAuditLog.d.ts +71 -0
- package/dist/server/peer/ClusterAuditLog.d.ts.map +1 -0
- package/dist/server/peer/ClusterAuditLog.js +81 -0
- package/dist/server/peer/ClusterAuditLog.js.map +1 -0
- package/dist/server/peer/ClusterJobRunner.d.ts +43 -0
- package/dist/server/peer/ClusterJobRunner.d.ts.map +1 -0
- package/dist/server/peer/ClusterJobRunner.js +119 -0
- package/dist/server/peer/ClusterJobRunner.js.map +1 -0
- package/dist/server/peer/ClusterManager.d.ts +149 -0
- package/dist/server/peer/ClusterManager.d.ts.map +1 -0
- package/dist/server/peer/ClusterManager.js +292 -0
- package/dist/server/peer/ClusterManager.js.map +1 -0
- package/dist/server/peer/PeerAuthService.d.ts +69 -0
- package/dist/server/peer/PeerAuthService.d.ts.map +1 -0
- package/dist/server/peer/PeerAuthService.js +207 -0
- package/dist/server/peer/PeerAuthService.js.map +1 -0
- package/dist/server/peer/PeerReplicator.d.ts +303 -0
- package/dist/server/peer/PeerReplicator.d.ts.map +1 -0
- package/dist/server/peer/PeerReplicator.js +1072 -0
- package/dist/server/peer/PeerReplicator.js.map +1 -0
- package/dist/server/peer/clusterHttp.d.ts +113 -0
- package/dist/server/peer/clusterHttp.d.ts.map +1 -0
- package/dist/server/peer/clusterHttp.js +290 -0
- package/dist/server/peer/clusterHttp.js.map +1 -0
- package/dist/server/peer/types.d.ts +244 -0
- package/dist/server/peer/types.d.ts.map +1 -0
- package/dist/server/peer/types.js +189 -0
- package/dist/server/peer/types.js.map +1 -0
- package/dist/server/rateLimits.d.ts +119 -0
- package/dist/server/rateLimits.d.ts.map +1 -0
- package/dist/server/rateLimits.js +159 -0
- package/dist/server/rateLimits.js.map +1 -0
- package/dist/server/serverConfig.d.ts +37 -0
- package/dist/server/serverConfig.d.ts.map +1 -0
- package/dist/server/serverConfig.js +251 -0
- package/dist/server/serverConfig.js.map +1 -0
- package/dist/server/timestampProxy.d.ts +129 -0
- package/dist/server/timestampProxy.d.ts.map +1 -0
- package/dist/server/timestampProxy.js +361 -0
- package/dist/server/timestampProxy.js.map +1 -0
- package/dist/server/types.d.ts +267 -0
- package/dist/server/types.d.ts.map +1 -0
- package/dist/server/types.js +56 -0
- package/dist/server/types.js.map +1 -0
- package/dist/server/validation.d.ts +43 -0
- package/dist/server/validation.d.ts.map +1 -0
- package/dist/server/validation.js +104 -0
- package/dist/server/validation.js.map +1 -0
- package/package.json +9 -1
|
@@ -1,420 +1,30 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* authenticating with remote servers and opening tenant directories.
|
|
7
|
-
*
|
|
8
|
-
* When a tenant has a $publicinfos key (sent during publishToServer), the
|
|
9
|
-
* manager creates a real BaseMindooTenant for directory reading. User
|
|
10
|
-
* authentication is then validated against the admin-signed directory DB
|
|
11
|
-
* rather than a static JSON config.
|
|
12
|
-
*
|
|
13
|
-
* When no $publicinfos key is available, a SimpleMindooDirectory backed
|
|
14
|
-
* by config.json users[] is used as a fallback (useful for tests).
|
|
15
|
-
*
|
|
16
|
-
* A CompositeMindooDirectory wraps whichever directory source is used and
|
|
17
|
-
* also checks the global trusted-servers list, enabling server-to-server
|
|
18
|
-
* auth without servers being in each tenant's user directory.
|
|
3
|
+
* Node host of the {@link CoreTenantManager}: tenant state lives in files
|
|
4
|
+
* below the data directory and every tenant database is an on-disk store in
|
|
5
|
+
* `<dataDir>/<tenantId>/stores` (see {@link StoreFactory}).
|
|
19
6
|
*/
|
|
20
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
-
exports.TenantManager = exports.SimpleMindooDirectory = void 0;
|
|
8
|
+
exports.TenantManager = exports.NodeServerStoreProvider = exports.SimpleMindooDirectory = void 0;
|
|
22
9
|
const fs_1 = require("fs");
|
|
23
10
|
const path_1 = require("path");
|
|
24
11
|
const NodeCryptoAdapter_1 = require("../crypto/NodeCryptoAdapter");
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
const PurgedDocRegistry_1 = require("./PurgedDocRegistry");
|
|
28
|
-
const purgeDocumentHistory_1 = require("../../core/appendonlystores/purgeDocumentHistory");
|
|
29
|
-
const Ed25519WitnessProvider_1 = require("../../core/accesscontrol/timestamp/Ed25519WitnessProvider");
|
|
30
|
-
const BaseMindooTenantFactory_1 = require("../../core/BaseMindooTenantFactory");
|
|
31
|
-
const RSAEncryption_1 = require("../../core/crypto/RSAEncryption");
|
|
32
|
-
const privateKeyEncryption_1 = require("../../core/crypto/privateKeyEncryption");
|
|
33
|
-
const KeyBag_1 = require("../../core/keys/KeyBag");
|
|
34
|
-
const types_1 = require("../../core/types");
|
|
35
|
-
const builtinDbInvariants_1 = require("../../core/builtinDbInvariants");
|
|
36
|
-
const DirectoryStateNode_1 = require("../../core/accesscontrol/DirectoryStateNode");
|
|
37
|
-
const tier1Evaluator_1 = require("../../core/accesscontrol/tier1Evaluator");
|
|
12
|
+
const TenantManager_1 = require("../../server/TenantManager");
|
|
13
|
+
const FsServerDataStore_1 = require("./FsServerDataStore");
|
|
38
14
|
const StoreFactory_1 = require("./StoreFactory");
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
class
|
|
43
|
-
constructor(
|
|
44
|
-
this.storeFactory = storeFactory;
|
|
45
|
-
}
|
|
46
|
-
createStore(dbId, _options) {
|
|
47
|
-
return {
|
|
48
|
-
docStore: this.storeFactory.getStore(dbId, "docs"),
|
|
49
|
-
attachmentStore: this.storeFactory.getStore(dbId, "attachments"),
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
// ---------------------------------------------------------------------------
|
|
54
|
-
// SimpleMindooDirectory — config-based fallback
|
|
55
|
-
// ---------------------------------------------------------------------------
|
|
56
|
-
class SimpleMindooDirectory {
|
|
57
|
-
constructor(config) {
|
|
58
|
-
/** Canonical index: entries are identified by their signing public key. */
|
|
59
|
-
this.usersByKey = new Map();
|
|
60
|
-
/**
|
|
61
|
-
* Backward-compat index by documentation-only username, populated only for
|
|
62
|
-
* config entries that carry a `username`. Lets legacy username-based
|
|
63
|
-
* challenges keep resolving; `username` is never required.
|
|
64
|
-
*/
|
|
65
|
-
this.usersByUsername = new Map();
|
|
66
|
-
this.revokedUsers = new Set();
|
|
67
|
-
this.adminSigningPublicKey = config.adminSigningPublicKey;
|
|
68
|
-
if (config.users) {
|
|
69
|
-
for (const user of config.users) {
|
|
70
|
-
// Identity is the signing key. `username` is documentation-only and
|
|
71
|
-
// ignored for matching; index it only as a legacy convenience.
|
|
72
|
-
this.usersByKey.set(user.signingPublicKey, user);
|
|
73
|
-
if (typeof user.username === "string" && user.username.trim()) {
|
|
74
|
-
this.usersByUsername.set(user.username.toLowerCase(), user);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
async getUserPublicKeys(username) {
|
|
80
|
-
const normalizedUsername = username.toLowerCase();
|
|
81
|
-
if (this.revokedUsers.has(normalizedUsername)) {
|
|
82
|
-
return null;
|
|
83
|
-
}
|
|
84
|
-
const user = this.usersByUsername.get(normalizedUsername);
|
|
85
|
-
if (!user) {
|
|
86
|
-
return null;
|
|
87
|
-
}
|
|
88
|
-
return {
|
|
89
|
-
signingPublicKey: user.signingPublicKey,
|
|
90
|
-
encryptionPublicKey: user.encryptionPublicKey,
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
async getUserBySigningPublicKey(publicKey) {
|
|
94
|
-
const exact = this.usersByKey.get(publicKey);
|
|
95
|
-
if (exact) {
|
|
96
|
-
return this.toLookup(exact);
|
|
97
|
-
}
|
|
98
|
-
const needle = publicKey.replace(/\s+/g, "");
|
|
99
|
-
if (!needle)
|
|
100
|
-
return null;
|
|
101
|
-
for (const [key, user] of this.usersByKey) {
|
|
102
|
-
if (key.replace(/\s+/g, "") === needle) {
|
|
103
|
-
return this.toLookup(user);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
return null;
|
|
107
|
-
}
|
|
108
|
-
toLookup(user) {
|
|
109
|
-
if (typeof user.username === "string" &&
|
|
110
|
-
this.revokedUsers.has(user.username.toLowerCase())) {
|
|
111
|
-
return null;
|
|
112
|
-
}
|
|
113
|
-
return {
|
|
114
|
-
username: typeof user.username === "string" ? user.username : "",
|
|
115
|
-
signingPublicKey: user.signingPublicKey,
|
|
116
|
-
encryptionPublicKey: user.encryptionPublicKey,
|
|
117
|
-
details: null,
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
async isUserRevoked(username) {
|
|
121
|
-
return this.revokedUsers.has(username.toLowerCase());
|
|
122
|
-
}
|
|
123
|
-
async validatePublicSigningKey(publicKey, _opts) {
|
|
124
|
-
if (publicKey === this.adminSigningPublicKey) {
|
|
125
|
-
return true;
|
|
126
|
-
}
|
|
127
|
-
const user = this.usersByKey.get(publicKey);
|
|
128
|
-
if (!user) {
|
|
129
|
-
return false;
|
|
130
|
-
}
|
|
131
|
-
if (typeof user.username === "string" &&
|
|
132
|
-
this.revokedUsers.has(user.username.toLowerCase())) {
|
|
133
|
-
return false;
|
|
134
|
-
}
|
|
135
|
-
return true;
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
exports.SimpleMindooDirectory = SimpleMindooDirectory;
|
|
139
|
-
// ---------------------------------------------------------------------------
|
|
140
|
-
// CompositeMindooDirectory — wraps tenant directory + trusted servers
|
|
141
|
-
// ---------------------------------------------------------------------------
|
|
142
|
-
/**
|
|
143
|
-
* Wraps a per-tenant directory (real or config-based) and also checks
|
|
144
|
-
* the global trusted-servers list. Holds a reference to the array so
|
|
145
|
-
* runtime changes via the admin API are immediately visible.
|
|
146
|
-
*/
|
|
147
|
-
class CompositeMindooDirectory {
|
|
148
|
-
constructor(inner, trustedServers, adminBootstrapIdentity) {
|
|
149
|
-
this.inner = inner;
|
|
150
|
-
this.trustedServers = trustedServers;
|
|
151
|
-
this.adminBootstrapIdentity = adminBootstrapIdentity;
|
|
152
|
-
this.adminUsernameNormalized =
|
|
153
|
-
adminBootstrapIdentity?.username.toLowerCase() ?? null;
|
|
154
|
-
}
|
|
155
|
-
async getUserPublicKeys(username) {
|
|
156
|
-
const normalizedUsername = username.toLowerCase();
|
|
157
|
-
// Bootstrap special-case: allow configured admin identity to authenticate
|
|
158
|
-
// even before directory grantaccess docs are present on the server.
|
|
159
|
-
if (this.adminBootstrapIdentity &&
|
|
160
|
-
this.adminUsernameNormalized &&
|
|
161
|
-
normalizedUsername === this.adminUsernameNormalized) {
|
|
162
|
-
return {
|
|
163
|
-
signingPublicKey: this.adminBootstrapIdentity.signingPublicKey,
|
|
164
|
-
encryptionPublicKey: this.adminBootstrapIdentity.encryptionPublicKey,
|
|
165
|
-
};
|
|
166
|
-
}
|
|
167
|
-
const result = await this.inner.getUserPublicKeys(username);
|
|
168
|
-
if (result)
|
|
169
|
-
return result;
|
|
170
|
-
for (const server of this.trustedServers) {
|
|
171
|
-
if (server.name.toLowerCase() === normalizedUsername) {
|
|
172
|
-
return {
|
|
173
|
-
signingPublicKey: server.signingPublicKey,
|
|
174
|
-
encryptionPublicKey: server.encryptionPublicKey,
|
|
175
|
-
};
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
return null;
|
|
179
|
-
}
|
|
180
|
-
async isUserRevoked(username) {
|
|
181
|
-
const normalizedUsername = username.toLowerCase();
|
|
182
|
-
if (this.adminUsernameNormalized &&
|
|
183
|
-
normalizedUsername === this.adminUsernameNormalized) {
|
|
184
|
-
return false;
|
|
185
|
-
}
|
|
186
|
-
for (const server of this.trustedServers) {
|
|
187
|
-
if (server.name.toLowerCase() === normalizedUsername) {
|
|
188
|
-
return false;
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
return this.inner.isUserRevoked(username);
|
|
192
|
-
}
|
|
193
|
-
async validatePublicSigningKey(publicKey, opts) {
|
|
194
|
-
if (this.adminBootstrapIdentity &&
|
|
195
|
-
(0, DirectoryStateNode_1.signingKeysEqual)(this.adminBootstrapIdentity.signingPublicKey, publicKey)) {
|
|
196
|
-
return true;
|
|
197
|
-
}
|
|
198
|
-
const innerResult = await this.inner.validatePublicSigningKey(publicKey, opts);
|
|
199
|
-
if (innerResult)
|
|
200
|
-
return true;
|
|
201
|
-
for (const server of this.trustedServers) {
|
|
202
|
-
if (server.signingPublicKey === publicKey) {
|
|
203
|
-
return true;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
return false;
|
|
207
|
-
}
|
|
208
|
-
// -----------------------------------------------------------------------
|
|
209
|
-
// Advanced directory features forwarded to the inner directory. Config-based
|
|
210
|
-
// inner directories (SimpleMindooDirectory) do not implement these, so the
|
|
211
|
-
// composite degrades gracefully (no key-based identity, no read policy, no
|
|
212
|
-
// wipe directives) — preserving legacy config-only behavior — while a real
|
|
213
|
-
// BaseMindooTenantDirectory inner enables the key-based auth, read gate, and
|
|
214
|
-
// remote-wipe paths (docs/accesscontrol.md §6.5).
|
|
215
|
-
// -----------------------------------------------------------------------
|
|
216
|
-
async getUserBySigningPublicKey(publicKey) {
|
|
217
|
-
if (typeof this.inner.getUserBySigningPublicKey === "function") {
|
|
218
|
-
const innerLookup = await this.inner.getUserBySigningPublicKey(publicKey);
|
|
219
|
-
if (innerLookup)
|
|
220
|
-
return innerLookup;
|
|
221
|
-
}
|
|
222
|
-
// Bootstrap/config fallback: the admin and trusted-server identities can be
|
|
223
|
-
// resolved by their signing key even without a grantaccess document on this
|
|
224
|
-
// server, mirroring getUserPublicKeys. Without this, key-based identity
|
|
225
|
-
// resolution (and the validateToken active-key check below) would treat the
|
|
226
|
-
// admin's own key as unknown the moment the directory has no admin grant.
|
|
227
|
-
if (this.adminBootstrapIdentity &&
|
|
228
|
-
(0, DirectoryStateNode_1.signingKeysEqual)(this.adminBootstrapIdentity.signingPublicKey, publicKey)) {
|
|
229
|
-
return {
|
|
230
|
-
username: this.adminBootstrapIdentity.username,
|
|
231
|
-
signingPublicKey: this.adminBootstrapIdentity.signingPublicKey,
|
|
232
|
-
encryptionPublicKey: this.adminBootstrapIdentity.encryptionPublicKey,
|
|
233
|
-
details: null,
|
|
234
|
-
};
|
|
235
|
-
}
|
|
236
|
-
for (const server of this.trustedServers) {
|
|
237
|
-
if (server.signingPublicKey === publicKey) {
|
|
238
|
-
return {
|
|
239
|
-
username: server.name,
|
|
240
|
-
signingPublicKey: server.signingPublicKey,
|
|
241
|
-
encryptionPublicKey: server.encryptionPublicKey,
|
|
242
|
-
details: null,
|
|
243
|
-
};
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
return null;
|
|
247
|
-
}
|
|
248
|
-
async getUserSigningKeyUniverse(username) {
|
|
249
|
-
const base = typeof this.inner.getUserSigningKeyUniverse === "function"
|
|
250
|
-
? await this.inner.getUserSigningKeyUniverse(username)
|
|
251
|
-
: { active: [], wipeRequested: [] };
|
|
252
|
-
// The admin bootstrap identity, trusted servers, and config-based users are
|
|
253
|
-
// valid principals even without a grantaccess document on this server (the
|
|
254
|
-
// server config / bootstrap identity is the root of trust). Their signing
|
|
255
|
-
// key must therefore count as ACTIVE so the token-validation gate in
|
|
256
|
-
// AuthenticationService.validateToken (which requires the device key to be
|
|
257
|
-
// in this active set) does not reject a freshly-issued admin/config token.
|
|
258
|
-
// We reuse getUserPublicKeys, which already applies exactly those fallbacks.
|
|
259
|
-
const active = new Set(base.active);
|
|
260
|
-
const fallback = await this.getUserPublicKeys(username);
|
|
261
|
-
if (fallback)
|
|
262
|
-
active.add(fallback.signingPublicKey);
|
|
263
|
-
return { active: Array.from(active), wipeRequested: base.wipeRequested };
|
|
264
|
-
}
|
|
265
|
-
async getUserKeyPairs(username) {
|
|
266
|
-
if (typeof this.inner.getUserKeyPairs === "function") {
|
|
267
|
-
return this.inner.getUserKeyPairs(username);
|
|
268
|
-
}
|
|
269
|
-
return [];
|
|
270
|
-
}
|
|
271
|
-
async getWipeGrantDocId(signingKey) {
|
|
272
|
-
if (typeof this.inner.getWipeGrantDocId === "function") {
|
|
273
|
-
return this.inner.getWipeGrantDocId(signingKey);
|
|
274
|
-
}
|
|
275
|
-
return null;
|
|
276
|
-
}
|
|
277
|
-
async getRevokedDecryptionKeyIdsForSigningKey(signingKey) {
|
|
278
|
-
if (typeof this.inner.getRevokedDecryptionKeyIdsForSigningKey === "function") {
|
|
279
|
-
return this.inner.getRevokedDecryptionKeyIdsForSigningKey(signingKey);
|
|
280
|
-
}
|
|
281
|
-
return [];
|
|
282
|
-
}
|
|
283
|
-
async evaluateDbAccessForSigningKey(input) {
|
|
284
|
-
if (typeof this.inner.evaluateDbAccessForSigningKey === "function") {
|
|
285
|
-
return this.inner.evaluateDbAccessForSigningKey(input);
|
|
286
|
-
}
|
|
287
|
-
// Config-based directories carry no database-open policy -> unrestricted.
|
|
288
|
-
return true;
|
|
289
|
-
}
|
|
290
|
-
/**
|
|
291
|
-
* Forwarded so the witness enforces Tier 1 rules on push
|
|
292
|
-
* (docs/accesscontrol.md §7). `buildTier1Evaluator` only wires the check when
|
|
293
|
-
* the directory it is given has this method; without the forward every push
|
|
294
|
-
* was accepted, whatever the tenant's rules said. Config-based directories
|
|
295
|
-
* have no rules and therefore no method, which keeps their legacy behavior.
|
|
296
|
-
*/
|
|
297
|
-
get evaluateAccessForSigningKey() {
|
|
298
|
-
const inner = this.inner;
|
|
299
|
-
return typeof inner.evaluateAccessForSigningKey === "function"
|
|
300
|
-
? (input) => inner.evaluateAccessForSigningKey(input)
|
|
301
|
-
: undefined;
|
|
302
|
-
}
|
|
303
|
-
/** Forwarded with {@link evaluateAccessForSigningKey}: `$author` at grant level. */
|
|
304
|
-
get isSamePerson() {
|
|
305
|
-
const inner = this.inner;
|
|
306
|
-
return typeof inner.isSamePerson === "function" ? (input) => inner.isSamePerson(input) : undefined;
|
|
307
|
-
}
|
|
308
|
-
/**
|
|
309
|
-
* Forwarded so server-side `userdirectory` writes can resolve the signer's
|
|
310
|
-
* grant. Without this, `evaluateUserdirectoryInvariant` sees a missing
|
|
311
|
-
* method, leaves `signerUsernameHash` null, and denies every non-admin
|
|
312
|
-
* change as "signer is not a granted device" — including the founder
|
|
313
|
-
* wrapping a second device of their own User-Key.
|
|
314
|
-
*/
|
|
315
|
-
async resolveUsernameHashForSigningKey(signingKey, trustedTime) {
|
|
316
|
-
if (typeof this.inner.resolveUsernameHashForSigningKey === "function") {
|
|
317
|
-
return this.inner.resolveUsernameHashForSigningKey(signingKey, trustedTime);
|
|
318
|
-
}
|
|
319
|
-
return null;
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
// ---------------------------------------------------------------------------
|
|
323
|
-
// TenantManager
|
|
324
|
-
// ---------------------------------------------------------------------------
|
|
325
|
-
/**
|
|
326
|
-
* TenantManager is responsible for:
|
|
327
|
-
* - Loading the global server identity and trusted servers
|
|
328
|
-
* - Loading tenant configurations from disk
|
|
329
|
-
* - Caching loaded tenants
|
|
330
|
-
* - Registering new tenants
|
|
331
|
-
* - Managing tenant stores and authentication services
|
|
332
|
-
* - Creating a real MindooDB tenant for directory reading (when keys available)
|
|
333
|
-
* - Runtime management of trusted servers
|
|
334
|
-
*/
|
|
335
|
-
class TenantManager {
|
|
336
|
-
constructor(dataDir, serverPassword) {
|
|
337
|
-
this.loadedTenants = new Map();
|
|
338
|
-
this.serverIdentity = null;
|
|
339
|
-
this.trustedServers = [];
|
|
340
|
-
/** Peer wiring handed down to every server store; see setPeerAccessControl. */
|
|
341
|
-
this.peerAccessControl = {};
|
|
342
|
-
/** Per-tenant persistent registry of executed purges (docs/accesscontrol.md §13). */
|
|
343
|
-
this.purgedDocRegistries = new Map();
|
|
344
|
-
/** In-flight purge executions, keyed by normalized tenant id (serializes runs). */
|
|
345
|
-
this.purgeInFlight = new Map();
|
|
15
|
+
var TenantManager_2 = require("../../server/TenantManager");
|
|
16
|
+
Object.defineProperty(exports, "SimpleMindooDirectory", { enumerable: true, get: function () { return TenantManager_2.SimpleMindooDirectory; } });
|
|
17
|
+
/** The on-disk stores of every tenant below `<dataDir>/<tenantId>/stores`. */
|
|
18
|
+
class NodeServerStoreProvider {
|
|
19
|
+
constructor(dataDir, dataStore) {
|
|
346
20
|
this.dataDir = dataDir;
|
|
347
|
-
this.
|
|
348
|
-
this.cryptoAdapter = new NodeCryptoAdapter_1.NodeCryptoAdapter();
|
|
349
|
-
if (!(0, fs_1.existsSync)(dataDir)) {
|
|
350
|
-
(0, fs_1.mkdirSync)(dataDir, { recursive: true });
|
|
351
|
-
console.log(`[TenantManager] Created data directory: ${dataDir}`);
|
|
352
|
-
}
|
|
353
|
-
// Load global server identity
|
|
354
|
-
const identityPath = (0, path_1.join)(dataDir, "server.identity.json");
|
|
355
|
-
if ((0, fs_1.existsSync)(identityPath)) {
|
|
356
|
-
this.serverIdentity = JSON.parse((0, fs_1.readFileSync)(identityPath, "utf-8"));
|
|
357
|
-
console.log(`[TenantManager] Loaded server identity: ${this.serverIdentity.username}`);
|
|
358
|
-
}
|
|
359
|
-
else {
|
|
360
|
-
console.log(`[TenantManager] No server.identity.json found (run "npm run init" to create one)`);
|
|
361
|
-
}
|
|
362
|
-
// Load trusted servers
|
|
363
|
-
const trustedPath = (0, path_1.join)(dataDir, "trusted-servers.json");
|
|
364
|
-
if ((0, fs_1.existsSync)(trustedPath)) {
|
|
365
|
-
this.trustedServers = JSON.parse((0, fs_1.readFileSync)(trustedPath, "utf-8"));
|
|
366
|
-
console.log(`[TenantManager] Loaded ${this.trustedServers.length} trusted server(s)`);
|
|
367
|
-
}
|
|
21
|
+
this.dataStore = dataStore;
|
|
368
22
|
}
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
// =======================================================================
|
|
372
|
-
getServerIdentity() {
|
|
373
|
-
return this.serverIdentity;
|
|
23
|
+
createTenantStores(tenantId, config) {
|
|
24
|
+
return new StoreFactory_1.StoreFactory(tenantId, config, this.dataDir);
|
|
374
25
|
}
|
|
375
|
-
getServerPublicInfo() {
|
|
376
|
-
if (!this.serverIdentity)
|
|
377
|
-
return null;
|
|
378
|
-
return {
|
|
379
|
-
name: this.serverIdentity.username,
|
|
380
|
-
signingPublicKey: this.serverIdentity.userSigningKeyPair
|
|
381
|
-
.publicKey,
|
|
382
|
-
encryptionPublicKey: this.serverIdentity.userEncryptionKeyPair
|
|
383
|
-
.publicKey,
|
|
384
|
-
};
|
|
385
|
-
}
|
|
386
|
-
getDataDir() {
|
|
387
|
-
return this.dataDir;
|
|
388
|
-
}
|
|
389
|
-
/**
|
|
390
|
-
* Teach every server store how to recognise a trusted peer.
|
|
391
|
-
*
|
|
392
|
-
* `MindooDBServer` calls this once at startup. Two hooks, both required for
|
|
393
|
-
* peer replication to be correct:
|
|
394
|
-
*
|
|
395
|
-
* - `peerTokenResolver` lets a peer's cluster-level JWT through the tenant
|
|
396
|
-
* sync routes. A peer holds no per-tenant grant, so without it the mirror
|
|
397
|
-
* would need a second replication protocol; with it the peer reuses the
|
|
398
|
-
* client one and the store skips the per-user gates that do not apply to a
|
|
399
|
-
* full replica.
|
|
400
|
-
* - `trustedWitnessResolver` decides whose witness receipts may be preserved
|
|
401
|
-
* instead of re-stamped (convergence invariant 3). Without it a replicated
|
|
402
|
-
* entry would get a fresh `receivedAt` on every hop and "trusted time"
|
|
403
|
-
* would mean "time it reached the last server".
|
|
404
|
-
*
|
|
405
|
-
* Must be called before the first {@link getServerStore}, since stores are
|
|
406
|
-
* cached per tenant and database.
|
|
407
|
-
*/
|
|
408
|
-
setPeerAccessControl(options) {
|
|
409
|
-
this.peerAccessControl = options;
|
|
410
|
-
}
|
|
411
|
-
/**
|
|
412
|
-
* Database ids this server holds for a tenant, read from
|
|
413
|
-
* `{dataDir}/{tenantId}/stores`. Used by peer replication to mirror every
|
|
414
|
-
* database rather than a configured subset.
|
|
415
|
-
*/
|
|
416
26
|
listDatabases(tenantId) {
|
|
417
|
-
const storesDir = (0, path_1.join)(
|
|
27
|
+
const storesDir = this.dataStore.resolvePath((0, path_1.join)(tenantId, "stores"));
|
|
418
28
|
if (!(0, fs_1.existsSync)(storesDir)) {
|
|
419
29
|
return [];
|
|
420
30
|
}
|
|
@@ -426,774 +36,31 @@ class TenantManager {
|
|
|
426
36
|
return [];
|
|
427
37
|
}
|
|
428
38
|
}
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
*/
|
|
436
|
-
async getServerEncryptionPrivateKey() {
|
|
437
|
-
if (this.serverEncryptionPrivateKey)
|
|
438
|
-
return this.serverEncryptionPrivateKey;
|
|
439
|
-
if (!this.serverIdentity || !this.serverPassword)
|
|
440
|
-
return undefined;
|
|
441
|
-
const subtle = this.cryptoAdapter.getSubtle();
|
|
442
|
-
const keyBuffer = await (0, privateKeyEncryption_1.decryptPrivateKey)(this.cryptoAdapter, this.serverIdentity.userEncryptionKeyPair
|
|
443
|
-
.privateKey, this.serverPassword, "encryption");
|
|
444
|
-
this.serverEncryptionPrivateKey = await subtle.importKey("pkcs8", keyBuffer, { name: "RSA-OAEP", hash: "SHA-256" }, false, ["decrypt"]);
|
|
445
|
-
return this.serverEncryptionPrivateKey;
|
|
446
|
-
}
|
|
447
|
-
/**
|
|
448
|
-
* Build (and cache) the server's witness identity used to stamp receipts on
|
|
449
|
-
* accepted entries (docs/accesscontrol.md §5.3). Returns undefined when the
|
|
450
|
-
* server identity or its unlock password is unavailable, in which case the
|
|
451
|
-
* server runs in pre-access-control mode (no stamping, no Tier 1 advertised).
|
|
452
|
-
*/
|
|
453
|
-
async getWitnessSigner() {
|
|
454
|
-
if (this.witnessSigner)
|
|
455
|
-
return this.witnessSigner;
|
|
456
|
-
if (!this.serverIdentity || !this.serverPassword)
|
|
457
|
-
return undefined;
|
|
458
|
-
const subtle = this.cryptoAdapter.getSubtle();
|
|
459
|
-
const signingKeyBuffer = await (0, privateKeyEncryption_1.decryptPrivateKey)(this.cryptoAdapter, this.serverIdentity.userSigningKeyPair.privateKey, this.serverPassword, "signing");
|
|
460
|
-
const signingPrivateKey = await subtle.importKey("pkcs8", signingKeyBuffer, { name: "Ed25519" }, false, ["sign"]);
|
|
461
|
-
this.witnessSigner = {
|
|
462
|
-
publicKeyPem: this.serverIdentity.userSigningKeyPair.publicKey,
|
|
463
|
-
signingPrivateKey,
|
|
464
|
-
subtle,
|
|
465
|
-
};
|
|
466
|
-
return this.witnessSigner;
|
|
467
|
-
}
|
|
468
|
-
/**
|
|
469
|
-
* Build the trusted-time provider that stamps receipts on accepted entries
|
|
470
|
-
* (docs/accesscontrol.md §5.3, §13). v1 wraps the server's Ed25519 witness
|
|
471
|
-
* identity; returns undefined when no witness signer is available, leaving the
|
|
472
|
-
* server in pre-access-control mode.
|
|
473
|
-
*/
|
|
474
|
-
async getTimestampProvider() {
|
|
475
|
-
const signer = await this.getWitnessSigner();
|
|
476
|
-
if (!signer)
|
|
477
|
-
return undefined;
|
|
478
|
-
return new Ed25519WitnessProvider_1.Ed25519WitnessProvider({ signer, subtle: signer.subtle });
|
|
479
|
-
}
|
|
480
|
-
// =======================================================================
|
|
481
|
-
// Trusted server management
|
|
482
|
-
// =======================================================================
|
|
483
|
-
listTrustedServers() {
|
|
484
|
-
return [...this.trustedServers];
|
|
485
|
-
}
|
|
486
|
-
addTrustedServer(server) {
|
|
487
|
-
const existing = this.trustedServers.find((s) => s.name.toLowerCase() === server.name.toLowerCase());
|
|
488
|
-
if (existing) {
|
|
489
|
-
throw new Error(`Trusted server "${server.name}" already exists`);
|
|
490
|
-
}
|
|
491
|
-
this.trustedServers.push(server);
|
|
492
|
-
this.persistTrustedServers();
|
|
493
|
-
console.log(`[TenantManager] Added trusted server: ${server.name}`);
|
|
494
|
-
}
|
|
495
|
-
/**
|
|
496
|
-
* Add or replace a trusted server by name.
|
|
497
|
-
*
|
|
498
|
-
* Peer CRUD needs an upsert: `addTrustedServer` throws on an existing name,
|
|
499
|
-
* which is right for `add-to-network` (a silent overwrite of a peer's keys
|
|
500
|
-
* would be a trust change disguised as a config edit) but wrong for the admin
|
|
501
|
-
* API, where changing a peer's url or role is the common case.
|
|
502
|
-
*/
|
|
503
|
-
saveTrustedServer(server) {
|
|
504
|
-
const idx = this.trustedServers.findIndex((s) => s.name.toLowerCase() === server.name.toLowerCase());
|
|
505
|
-
if (idx === -1) {
|
|
506
|
-
this.trustedServers.push(server);
|
|
507
|
-
}
|
|
508
|
-
else {
|
|
509
|
-
this.trustedServers[idx] = server;
|
|
510
|
-
}
|
|
511
|
-
this.persistTrustedServers();
|
|
512
|
-
console.log(`[TenantManager] Saved trusted server: ${server.name}`);
|
|
513
|
-
}
|
|
514
|
-
removeTrustedServer(name) {
|
|
515
|
-
const idx = this.trustedServers.findIndex((s) => s.name.toLowerCase() === name.toLowerCase());
|
|
516
|
-
if (idx === -1)
|
|
517
|
-
return false;
|
|
518
|
-
this.trustedServers.splice(idx, 1);
|
|
519
|
-
this.persistTrustedServers();
|
|
520
|
-
console.log(`[TenantManager] Removed trusted server: ${name}`);
|
|
521
|
-
return true;
|
|
522
|
-
}
|
|
523
|
-
persistTrustedServers() {
|
|
524
|
-
const filePath = (0, path_1.join)(this.dataDir, "trusted-servers.json");
|
|
525
|
-
(0, fs_1.writeFileSync)(filePath, JSON.stringify(this.trustedServers, null, 2), "utf-8");
|
|
526
|
-
}
|
|
527
|
-
getServerKeyBagPath() {
|
|
528
|
-
return (0, path_1.join)(this.dataDir, "server.keybag");
|
|
529
|
-
}
|
|
530
|
-
async loadServerKeyBag() {
|
|
531
|
-
if (!this.serverIdentity || !this.serverPassword) {
|
|
532
|
-
throw new Error("Server identity and server password are required to manage tenant $publicinfos keys");
|
|
533
|
-
}
|
|
534
|
-
const keyBag = await this.createServerKeyBag(this.serverIdentity, this.serverPassword);
|
|
535
|
-
const keyBagPath = this.getServerKeyBagPath();
|
|
536
|
-
if ((0, fs_1.existsSync)(keyBagPath)) {
|
|
537
|
-
const data = (0, fs_1.readFileSync)(keyBagPath);
|
|
538
|
-
await keyBag.load(new Uint8Array(data));
|
|
539
|
-
}
|
|
540
|
-
return keyBag;
|
|
541
|
-
}
|
|
542
|
-
async createServerKeyBag(serverUser, serverPassword) {
|
|
543
|
-
const wrappingKey = await KeyBag_1.KeyBag.deriveWrappingKey(serverUser.userEncryptionKeyPair.privateKey, serverPassword, this.cryptoAdapter);
|
|
544
|
-
return new KeyBag_1.KeyBag({
|
|
545
|
-
wrappingKey,
|
|
546
|
-
cryptoAdapter: this.cryptoAdapter,
|
|
547
|
-
});
|
|
548
|
-
}
|
|
549
|
-
async saveServerKeyBag(keyBag) {
|
|
550
|
-
const data = await keyBag.save();
|
|
551
|
-
(0, fs_1.writeFileSync)(this.getServerKeyBagPath(), new DataView(data.buffer, data.byteOffset, data.byteLength));
|
|
552
|
-
}
|
|
553
|
-
bytesToBase64(bytes) {
|
|
554
|
-
return Buffer.from(bytes).toString("base64");
|
|
555
|
-
}
|
|
556
|
-
mergeUniqueKeys(keys) {
|
|
557
|
-
const seen = new Set();
|
|
558
|
-
const unique = [];
|
|
559
|
-
for (const key of keys) {
|
|
560
|
-
const signature = this.bytesToBase64(key);
|
|
561
|
-
if (seen.has(signature)) {
|
|
562
|
-
continue;
|
|
563
|
-
}
|
|
564
|
-
seen.add(signature);
|
|
565
|
-
unique.push(key);
|
|
566
|
-
}
|
|
567
|
-
return unique;
|
|
568
|
-
}
|
|
569
|
-
async computePublicInfosFingerprint(key) {
|
|
570
|
-
const digest = await this.cryptoAdapter
|
|
571
|
-
.getSubtle()
|
|
572
|
-
.digest("SHA-256", key);
|
|
573
|
-
return Array.from(new Uint8Array(digest).slice(0, 8))
|
|
574
|
-
.map((byte) => byte.toString(16).padStart(2, "0"))
|
|
575
|
-
.join(":");
|
|
576
|
-
}
|
|
577
|
-
resolveTenantDir(tenantId) {
|
|
578
|
-
const baseDir = (0, path_1.resolve)(this.dataDir);
|
|
579
|
-
const tenantDir = (0, path_1.resolve)(baseDir, tenantId);
|
|
580
|
-
if (tenantDir !== baseDir && !tenantDir.startsWith(`${baseDir}${path_1.sep}`)) {
|
|
581
|
-
throw new Error(`Resolved tenant path escapes data directory for tenantId "${tenantId}"`);
|
|
582
|
-
}
|
|
583
|
-
return tenantDir;
|
|
584
|
-
}
|
|
585
|
-
resolveTenantConfigPath(tenantId) {
|
|
586
|
-
return (0, path_1.join)(this.resolveTenantDir(tenantId), "config.json");
|
|
587
|
-
}
|
|
588
|
-
async decryptEncryptedPublicInfosKey(base64Payload) {
|
|
589
|
-
if (!this.serverIdentity) {
|
|
590
|
-
throw new Error("Server identity not initialized; cannot decrypt encrypted $publicinfos payload");
|
|
591
|
-
}
|
|
592
|
-
if (!this.serverPassword) {
|
|
593
|
-
throw new Error("Server password not configured; cannot decrypt encrypted $publicinfos payload");
|
|
594
|
-
}
|
|
595
|
-
const privateKeyBuffer = await (0, privateKeyEncryption_1.decryptPrivateKey)(this.cryptoAdapter, this.serverIdentity.userEncryptionKeyPair
|
|
596
|
-
.privateKey, this.serverPassword, "encryption");
|
|
597
|
-
const privateKey = await this.cryptoAdapter.getSubtle().importKey("pkcs8", privateKeyBuffer, {
|
|
598
|
-
name: "RSA-OAEP",
|
|
599
|
-
hash: "SHA-256",
|
|
600
|
-
}, false, ["decrypt"]);
|
|
601
|
-
const decrypted = await new RSAEncryption_1.RSAEncryption(this.cryptoAdapter).decrypt(new Uint8Array(Buffer.from(base64Payload, "base64")), privateKey);
|
|
602
|
-
return new Uint8Array(decrypted);
|
|
603
|
-
}
|
|
604
|
-
async resolveIncomingPublicInfosKey(request) {
|
|
605
|
-
if (request.encryptedPublicInfosKey) {
|
|
606
|
-
return this.decryptEncryptedPublicInfosKey(request.encryptedPublicInfosKey);
|
|
607
|
-
}
|
|
608
|
-
if (request.publicInfosKey) {
|
|
609
|
-
return new Uint8Array(Buffer.from(request.publicInfosKey, "base64"));
|
|
610
|
-
}
|
|
611
|
-
throw new Error("Tenant registration requires encryptedPublicInfosKey or publicInfosKey");
|
|
612
|
-
}
|
|
613
|
-
async getStoredPublicInfosKeys(tenantId) {
|
|
614
|
-
const keys = [];
|
|
615
|
-
if (this.serverIdentity &&
|
|
616
|
-
this.serverPassword &&
|
|
617
|
-
(0, fs_1.existsSync)(this.getServerKeyBagPath())) {
|
|
618
|
-
const keyBag = await this.loadServerKeyBag();
|
|
619
|
-
keys.push(...(await keyBag.getAllKeys("doc", tenantId, types_1.PUBLIC_INFOS_KEY_ID)));
|
|
620
|
-
}
|
|
621
|
-
return this.mergeUniqueKeys(keys);
|
|
622
|
-
}
|
|
623
|
-
async persistTenantPublicInfosKey(tenantId, key) {
|
|
624
|
-
const keyBag = await this.loadServerKeyBag();
|
|
625
|
-
const existingKeys = await keyBag.getAllKeys("doc", tenantId, types_1.PUBLIC_INFOS_KEY_ID);
|
|
626
|
-
const incomingSignature = this.bytesToBase64(key);
|
|
627
|
-
const alreadyStored = existingKeys.some((entry) => this.bytesToBase64(entry) === incomingSignature);
|
|
628
|
-
if (!alreadyStored) {
|
|
629
|
-
await keyBag.set("doc", tenantId, types_1.PUBLIC_INFOS_KEY_ID, key, Date.now());
|
|
630
|
-
await this.saveServerKeyBag(keyBag);
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
|
-
/**
|
|
634
|
-
* All stored `$publicinfos` AES key versions for a tenant from `server.keybag`.
|
|
635
|
-
* Empty when the tenant was never published or the server keybag is locked.
|
|
636
|
-
*/
|
|
637
|
-
async getPublicInfosKeysForTenant(tenantId) {
|
|
638
|
-
return this.getStoredPublicInfosKeys(tenantId.toLowerCase());
|
|
639
|
-
}
|
|
640
|
-
async listTenantPublicInfosFingerprints(tenantId) {
|
|
641
|
-
const normalizedId = tenantId.toLowerCase();
|
|
642
|
-
const configPath = this.resolveTenantConfigPath(normalizedId);
|
|
643
|
-
if (!(0, fs_1.existsSync)(configPath)) {
|
|
644
|
-
throw new Error(`Tenant ${normalizedId} not found`);
|
|
645
|
-
}
|
|
646
|
-
const fingerprints = await Promise.all((await this.getStoredPublicInfosKeys(normalizedId)).map((key) => this.computePublicInfosFingerprint(key)));
|
|
647
|
-
return [...new Set(fingerprints)].sort();
|
|
648
|
-
}
|
|
649
|
-
// =======================================================================
|
|
650
|
-
// Tenant management
|
|
651
|
-
// =======================================================================
|
|
652
|
-
async registerTenant(request) {
|
|
653
|
-
const tenantId = request.tenantId.toLowerCase();
|
|
654
|
-
const tenantDir = this.resolveTenantDir(tenantId);
|
|
655
|
-
const configPath = this.resolveTenantConfigPath(tenantId);
|
|
656
|
-
if (!this.serverIdentity || !this.serverPassword) {
|
|
657
|
-
throw new Error("Tenant registration requires an unlocked server identity to store $publicinfos keys");
|
|
658
|
-
}
|
|
659
|
-
const publicInfosKey = await this.resolveIncomingPublicInfosKey(request);
|
|
660
|
-
if ((0, fs_1.existsSync)(configPath)) {
|
|
661
|
-
const existingConfig = JSON.parse((0, fs_1.readFileSync)(configPath, "utf-8"));
|
|
662
|
-
const incomingSignature = this.bytesToBase64(publicInfosKey);
|
|
663
|
-
const existingKeys = await this.getStoredPublicInfosKeys(tenantId);
|
|
664
|
-
const hasMatch = existingKeys.some((key) => this.bytesToBase64(key) === incomingSignature);
|
|
665
|
-
if (hasMatch) {
|
|
666
|
-
console.log(`[TenantManager] Tenant ${tenantId} already registered with matching $publicinfos key`);
|
|
667
|
-
return {
|
|
668
|
-
context: { tenantId, config: existingConfig },
|
|
669
|
-
created: false,
|
|
670
|
-
};
|
|
671
|
-
}
|
|
672
|
-
throw new Error(`Tenant ${tenantId} already exists with different $publicinfos key`);
|
|
673
|
-
}
|
|
674
|
-
(0, fs_1.mkdirSync)(tenantDir, { recursive: true });
|
|
675
|
-
const config = {
|
|
676
|
-
adminUsername: request.adminUsername,
|
|
677
|
-
adminSigningPublicKey: request.adminSigningPublicKey,
|
|
678
|
-
adminEncryptionPublicKey: request.adminEncryptionPublicKey,
|
|
679
|
-
defaultStoreType: request.defaultStoreType || "file",
|
|
680
|
-
users: [],
|
|
681
|
-
};
|
|
682
|
-
try {
|
|
683
|
-
(0, fs_1.writeFileSync)(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
684
|
-
await this.persistTenantPublicInfosKey(tenantId, publicInfosKey);
|
|
685
|
-
}
|
|
686
|
-
catch (error) {
|
|
687
|
-
(0, fs_1.rmSync)(tenantDir, { recursive: true, force: true });
|
|
688
|
-
throw error;
|
|
689
|
-
}
|
|
690
|
-
console.log(`[TenantManager] Registered tenant: ${tenantId} ($publicinfos stored in server keybag)`);
|
|
691
|
-
return {
|
|
692
|
-
context: { tenantId, config },
|
|
693
|
-
created: true,
|
|
694
|
-
};
|
|
695
|
-
}
|
|
696
|
-
async getTenant(tenantId) {
|
|
697
|
-
const normalizedId = tenantId.toLowerCase();
|
|
698
|
-
const cached = this.loadedTenants.get(normalizedId);
|
|
699
|
-
if (cached) {
|
|
700
|
-
return cached;
|
|
701
|
-
}
|
|
702
|
-
const loaded = await this.loadTenant(normalizedId);
|
|
703
|
-
this.loadedTenants.set(normalizedId, loaded);
|
|
704
|
-
return loaded;
|
|
705
|
-
}
|
|
706
|
-
tenantExists(tenantId) {
|
|
707
|
-
const normalizedId = tenantId.toLowerCase();
|
|
708
|
-
const configPath = this.resolveTenantConfigPath(normalizedId);
|
|
709
|
-
return (0, fs_1.existsSync)(configPath);
|
|
710
|
-
}
|
|
711
|
-
/**
|
|
712
|
-
* Read the tenant's registered admin signing key without fully loading the
|
|
713
|
-
* tenant. Returns `null` if the tenant is missing or the config is unreadable.
|
|
714
|
-
*/
|
|
715
|
-
getTenantAdminSigningPublicKey(tenantId) {
|
|
716
|
-
try {
|
|
717
|
-
const normalizedId = tenantId.toLowerCase();
|
|
718
|
-
const cached = this.loadedTenants.get(normalizedId);
|
|
719
|
-
if (cached?.context.config.adminSigningPublicKey) {
|
|
720
|
-
return cached.context.config.adminSigningPublicKey;
|
|
721
|
-
}
|
|
722
|
-
const configPath = this.resolveTenantConfigPath(normalizedId);
|
|
723
|
-
if (!(0, fs_1.existsSync)(configPath)) {
|
|
724
|
-
return null;
|
|
725
|
-
}
|
|
726
|
-
const config = JSON.parse((0, fs_1.readFileSync)(configPath, "utf-8"));
|
|
727
|
-
return typeof config.adminSigningPublicKey === "string" &&
|
|
728
|
-
config.adminSigningPublicKey.length > 0
|
|
729
|
-
? config.adminSigningPublicKey
|
|
730
|
-
: null;
|
|
731
|
-
}
|
|
732
|
-
catch {
|
|
733
|
-
return null;
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
|
-
/**
|
|
737
|
-
* True when `publicsignkey` is the registered tenant-admin signing key for
|
|
738
|
-
* `tenantId` (PEM whitespace-insensitive).
|
|
739
|
-
*/
|
|
740
|
-
isTenantAdminSigningKeyFor(tenantId, publicsignkey) {
|
|
741
|
-
const adminKey = this.getTenantAdminSigningPublicKey(tenantId);
|
|
742
|
-
return adminKey !== null && (0, DirectoryStateNode_1.signingKeysEqual)(adminKey, publicsignkey);
|
|
743
|
-
}
|
|
744
|
-
/**
|
|
745
|
-
* True when `publicsignkey` is the registered admin signing key of any
|
|
746
|
-
* tenant on this server. Used to issue a system JWT so that admin can
|
|
747
|
-
* DELETE their own tenant; other `/system/*` routes stay capability-gated.
|
|
748
|
-
*/
|
|
749
|
-
isRegisteredTenantAdminSigningKey(publicsignkey) {
|
|
750
|
-
for (const tenantId of this.listTenants()) {
|
|
751
|
-
if (this.isTenantAdminSigningKeyFor(tenantId, publicsignkey)) {
|
|
752
|
-
return true;
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
return false;
|
|
756
|
-
}
|
|
757
|
-
listTenants() {
|
|
758
|
-
if (!(0, fs_1.existsSync)(this.dataDir)) {
|
|
759
|
-
return [];
|
|
760
|
-
}
|
|
761
|
-
const entries = (0, fs_1.readdirSync)(this.dataDir);
|
|
762
|
-
const tenants = [];
|
|
763
|
-
for (const entry of entries) {
|
|
764
|
-
const entryPath = (0, path_1.join)(this.dataDir, entry);
|
|
765
|
-
const configPath = (0, path_1.join)(entryPath, "config.json");
|
|
766
|
-
if ((0, fs_1.statSync)(entryPath).isDirectory() && (0, fs_1.existsSync)(configPath)) {
|
|
767
|
-
tenants.push(entry);
|
|
768
|
-
}
|
|
769
|
-
}
|
|
770
|
-
return tenants.sort();
|
|
771
|
-
}
|
|
772
|
-
/**
|
|
773
|
-
* Update operator-owned fields of an existing tenant configuration.
|
|
774
|
-
* Only the fields present in `updates` are overwritten.
|
|
775
|
-
*/
|
|
776
|
-
updateTenantConfig(tenantId, updates) {
|
|
777
|
-
const normalizedId = tenantId.toLowerCase();
|
|
778
|
-
const configPath = this.resolveTenantConfigPath(normalizedId);
|
|
779
|
-
if (!(0, fs_1.existsSync)(configPath)) {
|
|
780
|
-
throw new Error(`Tenant ${normalizedId} not found`);
|
|
781
|
-
}
|
|
782
|
-
const config = JSON.parse((0, fs_1.readFileSync)(configPath, "utf-8"));
|
|
783
|
-
if (updates.defaultStoreType !== undefined) {
|
|
784
|
-
config.defaultStoreType = updates.defaultStoreType;
|
|
785
|
-
}
|
|
786
|
-
(0, fs_1.writeFileSync)(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
787
|
-
this.loadedTenants.delete(normalizedId);
|
|
788
|
-
console.log(`[TenantManager] Updated tenant config: ${normalizedId}`);
|
|
789
|
-
}
|
|
790
|
-
async removeTenant(tenantId) {
|
|
791
|
-
const normalizedId = tenantId.toLowerCase();
|
|
792
|
-
const tenantDir = this.resolveTenantDir(normalizedId);
|
|
793
|
-
if (!(0, fs_1.existsSync)(tenantDir)) {
|
|
794
|
-
throw new Error(`Tenant ${normalizedId} does not exist`);
|
|
795
|
-
}
|
|
796
|
-
this.loadedTenants.delete(normalizedId);
|
|
797
|
-
if (this.serverIdentity &&
|
|
798
|
-
this.serverPassword &&
|
|
799
|
-
(0, fs_1.existsSync)(this.getServerKeyBagPath())) {
|
|
800
|
-
const keyBag = await this.loadServerKeyBag();
|
|
801
|
-
await keyBag.deleteKey("doc", normalizedId, types_1.PUBLIC_INFOS_KEY_ID);
|
|
802
|
-
await this.saveServerKeyBag(keyBag);
|
|
803
|
-
}
|
|
804
|
-
(0, fs_1.rmSync)(tenantDir, { recursive: true, force: true });
|
|
805
|
-
console.log(`[TenantManager] Removed tenant: ${normalizedId}`);
|
|
806
|
-
}
|
|
807
|
-
// =======================================================================
|
|
808
|
-
// Server stores
|
|
809
|
-
// =======================================================================
|
|
810
|
-
async getServerStore(tenantId, dbId, storeKind) {
|
|
811
|
-
const tenant = await this.getTenant(tenantId);
|
|
812
|
-
const cacheKey = `${dbId}:${storeKind}`;
|
|
813
|
-
const cached = tenant.serverStores.get(cacheKey);
|
|
814
|
-
if (cached) {
|
|
815
|
-
return cached;
|
|
816
|
-
}
|
|
817
|
-
const localStore = tenant.storeFactory.getStore(dbId, storeKind);
|
|
818
|
-
// Access-control v1 wiring (docs/accesscontrol.md §5–§7). The witness signer
|
|
819
|
-
// lets the server stamp receipts on accepted entries; the Tier 1 evaluator
|
|
820
|
-
// enforces identity-tier rules at push time. Both are only active when the
|
|
821
|
-
// server identity is unlocked and the directory supports evaluation, so
|
|
822
|
-
// pre-access-control deployments keep their existing behavior.
|
|
823
|
-
const timestampProvider = await this.getTimestampProvider();
|
|
824
|
-
const directory = tenant.directory;
|
|
825
|
-
const tier1Evaluator = this.buildTier1Evaluator(directory, localStore);
|
|
826
|
-
const wipeGrantDocIdResolver = this.buildWipeGrantDocIdResolver(directory);
|
|
827
|
-
// Database-open gate (directory-restricted policy). Never applied to the
|
|
828
|
-
// directory store itself, which must always sync so the policy can be read.
|
|
829
|
-
const dbAccessEvaluator = dbId === "directory" ? undefined : this.buildDbAccessEvaluator(directory);
|
|
830
|
-
// Per-user revoked-key blacklist (§13). Never applied to the directory store
|
|
831
|
-
// itself (its policy, which defines the blacklist, must always sync).
|
|
832
|
-
const revokedKeyResolver = dbId === "directory"
|
|
833
|
-
? undefined
|
|
834
|
-
: this.buildRevokedKeyResolver(directory);
|
|
835
|
-
// Purged-document denylist (§13). Never applied to the directory store (purge
|
|
836
|
-
// requests themselves live there and must always sync).
|
|
837
|
-
const purgedDocResolver = dbId === "directory"
|
|
838
|
-
? undefined
|
|
839
|
-
: () => this.getPurgedDocRegistry(tenantId).getPurgedDocIds(dbId);
|
|
840
|
-
const builtinWriteContext = dbId === types_1.USER_DIRECTORY_DB_ID
|
|
841
|
-
? this.buildUserdirectoryWriteContext(tenant, localStore)
|
|
842
|
-
: undefined;
|
|
843
|
-
const serverStore = new ServerNetworkContentAddressedStore_1.ServerNetworkContentAddressedStore(localStore, directory, tenant.authService, this.cryptoAdapter, undefined, {
|
|
844
|
-
timestampProvider,
|
|
845
|
-
witnessDbid: dbId,
|
|
846
|
-
tier1Evaluator,
|
|
847
|
-
wipeGrantDocIdResolver,
|
|
848
|
-
dbAccessEvaluator,
|
|
849
|
-
revokedKeyResolver,
|
|
850
|
-
purgedDocResolver,
|
|
851
|
-
builtinWriteContext,
|
|
852
|
-
...this.peerAccessControl,
|
|
853
|
-
});
|
|
854
|
-
tenant.serverStores.set(cacheKey, serverStore);
|
|
855
|
-
console.log(`[TenantManager] Created server store for ${tenantId}/${dbId}/${storeKind}`);
|
|
856
|
-
return serverStore;
|
|
857
|
-
}
|
|
858
|
-
/**
|
|
859
|
-
* Build a Tier 1 evaluator closure for a server store, or undefined when the
|
|
860
|
-
* directory cannot evaluate access (e.g. config-based directory without the
|
|
861
|
-
* access-control state chain). The closure resolves `$author` for non-create
|
|
862
|
-
* ops by reading the document's `doc_create` entry creator key from the local
|
|
863
|
-
* store (metadata only, no decryption) and comparing it to the change author.
|
|
864
|
-
*/
|
|
865
|
-
/**
|
|
866
|
-
* The witness's Tier 1 gate. The evaluation itself is shared with the peer
|
|
867
|
-
* listener, which has to run it for entries that never reach a witness at
|
|
868
|
-
* all — see `core/accesscontrol/tier1Evaluator.ts`.
|
|
869
|
-
*/
|
|
870
|
-
buildTier1Evaluator(directory, localStore) {
|
|
871
|
-
return (0, tier1Evaluator_1.buildTier1Evaluator)(directory, localStore);
|
|
872
|
-
}
|
|
873
|
-
/**
|
|
874
|
-
* Builtin `userdirectory` write invariant: admin may create/delete, only the
|
|
875
|
-
* owning person may change. `username_hash` is read from the decrypted
|
|
876
|
-
* `doc_create` payload (`$publicinfos`). Personal documents are sealed to
|
|
877
|
-
* their owner and therefore undecryptable here, so they are owned by whoever
|
|
878
|
-
* signed their `doc_create` — `resolveCreatorSigningKey` answers that from
|
|
879
|
-
* the entry chain alone.
|
|
880
|
-
*/
|
|
881
|
-
buildUserdirectoryWriteContext(tenant, localStore) {
|
|
882
|
-
const adminPublicKey = tenant.context.config.adminSigningPublicKey;
|
|
883
|
-
const mindooTenant = tenant.mindooTenant;
|
|
884
|
-
return {
|
|
885
|
-
adminPublicKey,
|
|
886
|
-
resolveCreatorSigningKey: async (entry) => {
|
|
887
|
-
const createEntry = await this.findCreateEntry(localStore, entry);
|
|
888
|
-
return createEntry?.createdByPublicKey ?? null;
|
|
889
|
-
},
|
|
890
|
-
resolveDocumentUsernameHash: mindooTenant
|
|
891
|
-
? async (entry) => {
|
|
892
|
-
const createEntry = await this.findCreateEntry(localStore, entry);
|
|
893
|
-
if (!createEntry)
|
|
894
|
-
return null;
|
|
895
|
-
try {
|
|
896
|
-
const plaintext = await mindooTenant.decryptPayload(createEntry.encryptedData, createEntry.decryptionKeyId);
|
|
897
|
-
return (0, builtinDbInvariants_1.usernameHashFromCreateChangeBytes)(plaintext);
|
|
898
|
-
}
|
|
899
|
-
catch {
|
|
900
|
-
return null;
|
|
901
|
-
}
|
|
902
|
-
}
|
|
903
|
-
: undefined,
|
|
904
|
-
};
|
|
905
|
-
}
|
|
906
|
-
async findCreateEntry(localStore, entry) {
|
|
907
|
-
if (entry.entryType === "doc_create") {
|
|
908
|
-
return entry;
|
|
909
|
-
}
|
|
910
|
-
try {
|
|
911
|
-
const metas = await localStore.findNewEntriesForDoc([], entry.docId);
|
|
912
|
-
const createMeta = metas.find((m) => m.entryType === "doc_create");
|
|
913
|
-
if (!createMeta)
|
|
914
|
-
return null;
|
|
915
|
-
const loaded = await localStore.getEntries([createMeta.id]);
|
|
916
|
-
return loaded[0] ?? null;
|
|
917
|
-
}
|
|
918
|
-
catch {
|
|
919
|
-
return null;
|
|
920
|
-
}
|
|
921
|
-
}
|
|
922
|
-
/**
|
|
923
|
-
* Build a database-open evaluator closure for a server store, or undefined
|
|
924
|
-
* when the directory cannot evaluate the database-open policy (e.g. a
|
|
925
|
-
* config-based directory without the access-control state chain). When the
|
|
926
|
-
* tenant policy is `"directory-restricted"`, this rejects sync for any
|
|
927
|
-
* database id that is not in the allowlist; `"directory"` is always allowed
|
|
928
|
-
* and the tenant admin is exempt (resolved from the principal signing key).
|
|
929
|
-
*/
|
|
930
|
-
buildDbAccessEvaluator(directory) {
|
|
931
|
-
const evaluable = directory;
|
|
932
|
-
if (typeof evaluable.evaluateDbAccessForSigningKey !== "function") {
|
|
933
|
-
return undefined;
|
|
934
|
-
}
|
|
935
|
-
return async (principal, dbid) => {
|
|
936
|
-
return evaluable.evaluateDbAccessForSigningKey({
|
|
937
|
-
dbid,
|
|
938
|
-
signingKey: principal.signingKey ?? "",
|
|
939
|
-
});
|
|
940
|
-
};
|
|
941
|
-
}
|
|
942
|
-
/**
|
|
943
|
-
* Build a remote-wipe resolver closure for a server store, or undefined when
|
|
944
|
-
* the directory does not support wipe directives (docs/accesscontrol.md §6.5).
|
|
945
|
-
* Maps a wipe-targeted signing key to the admin-signed grant document id so
|
|
946
|
-
* the server can serve only that document to the targeted device.
|
|
947
|
-
*/
|
|
948
|
-
buildWipeGrantDocIdResolver(directory) {
|
|
949
|
-
if (typeof directory.getWipeGrantDocId !== "function") {
|
|
950
|
-
return undefined;
|
|
951
|
-
}
|
|
952
|
-
return (signingKey) => directory.getWipeGrantDocId(signingKey);
|
|
953
|
-
}
|
|
954
|
-
/**
|
|
955
|
-
* Build a per-user revoked-key blacklist resolver for a server store, or
|
|
956
|
-
* undefined when the directory cannot resolve revoked keys (e.g. a
|
|
957
|
-
* config-based directory). Maps the authenticated principal's signing key to
|
|
958
|
-
* the set of decryption key ids revoked for them at the directory head
|
|
959
|
-
* (docs/accesscontrol.md §13).
|
|
960
|
-
*/
|
|
961
|
-
buildRevokedKeyResolver(directory) {
|
|
962
|
-
if (typeof directory.getRevokedDecryptionKeyIdsForSigningKey !== "function") {
|
|
963
|
-
return undefined;
|
|
964
|
-
}
|
|
965
|
-
return async (principal) => {
|
|
966
|
-
const ids = await directory.getRevokedDecryptionKeyIdsForSigningKey(principal.signingKey ?? "");
|
|
967
|
-
return new Set(ids);
|
|
968
|
-
};
|
|
969
|
-
}
|
|
970
|
-
async getStore(tenantId, dbId, storeKind) {
|
|
971
|
-
const tenant = await this.getTenant(tenantId);
|
|
972
|
-
return tenant.storeFactory.getStore(dbId, storeKind);
|
|
973
|
-
}
|
|
974
|
-
/**
|
|
975
|
-
* Documents purged from `dbId` on this server (the persistent purge
|
|
976
|
-
* registry). Peer pull replication refuses their entries.
|
|
977
|
-
*/
|
|
978
|
-
getPurgedDocIds(tenantId, dbId) {
|
|
979
|
-
return this.getPurgedDocRegistry(tenantId).getPurgedDocIds(dbId);
|
|
980
|
-
}
|
|
981
|
-
/** The (lazily-loaded, cached) purge registry for a tenant. */
|
|
982
|
-
getPurgedDocRegistry(tenantId) {
|
|
983
|
-
const normalizedId = tenantId.toLowerCase();
|
|
984
|
-
let registry = this.purgedDocRegistries.get(normalizedId);
|
|
985
|
-
if (!registry) {
|
|
986
|
-
registry = new PurgedDocRegistry_1.PurgedDocRegistry((0, path_1.join)(this.resolveTenantDir(normalizedId), "purged-docs.json"));
|
|
987
|
-
this.purgedDocRegistries.set(normalizedId, registry);
|
|
988
|
-
}
|
|
989
|
-
return registry;
|
|
39
|
+
}
|
|
40
|
+
exports.NodeServerStoreProvider = NodeServerStoreProvider;
|
|
41
|
+
function ensureDataDir(dataDir) {
|
|
42
|
+
if (!(0, fs_1.existsSync)(dataDir)) {
|
|
43
|
+
(0, fs_1.mkdirSync)(dataDir, { recursive: true });
|
|
44
|
+
console.log(`[TenantManager] Created data directory: ${dataDir}`);
|
|
990
45
|
}
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
* the directory does not expose the purge reader). Runs are serialized per
|
|
1002
|
-
* tenant; concurrent callers share the in-flight promise.
|
|
1003
|
-
*/
|
|
1004
|
-
async executePendingPurges(tenantId) {
|
|
1005
|
-
const normalizedId = tenantId.toLowerCase();
|
|
1006
|
-
const existing = this.purgeInFlight.get(normalizedId);
|
|
1007
|
-
if (existing)
|
|
1008
|
-
return existing;
|
|
1009
|
-
const run = this.executePendingPurgesInternal(normalizedId).finally(() => {
|
|
1010
|
-
this.purgeInFlight.delete(normalizedId);
|
|
46
|
+
return dataDir;
|
|
47
|
+
}
|
|
48
|
+
class TenantManager extends TenantManager_1.TenantManager {
|
|
49
|
+
constructor(dataDir, serverPassword) {
|
|
50
|
+
const dataStore = new FsServerDataStore_1.FsServerDataStore(ensureDataDir(dataDir));
|
|
51
|
+
super({
|
|
52
|
+
dataStore,
|
|
53
|
+
storeProvider: new NodeServerStoreProvider(dataDir, dataStore),
|
|
54
|
+
cryptoAdapter: new NodeCryptoAdapter_1.NodeCryptoAdapter(),
|
|
55
|
+
serverPassword,
|
|
1011
56
|
});
|
|
1012
|
-
this.
|
|
1013
|
-
return run;
|
|
1014
|
-
}
|
|
1015
|
-
async executePendingPurgesInternal(normalizedId) {
|
|
1016
|
-
const tenant = await this.getTenant(normalizedId);
|
|
1017
|
-
if (!tenant.mindooTenant) {
|
|
1018
|
-
// Config-based fallback: the server holds no $publicinfos directory and
|
|
1019
|
-
// cannot read purge requests. Clients still purge locally on reconcile.
|
|
1020
|
-
return;
|
|
1021
|
-
}
|
|
1022
|
-
let directory;
|
|
1023
|
-
try {
|
|
1024
|
-
directory = await tenant.mindooTenant.openDirectory();
|
|
1025
|
-
}
|
|
1026
|
-
catch (error) {
|
|
1027
|
-
console.error(`[TenantManager] executePendingPurges: cannot open directory for ${normalizedId}:`, error);
|
|
1028
|
-
return;
|
|
1029
|
-
}
|
|
1030
|
-
if (typeof directory.getRequestedDocHistoryPurges !== "function") {
|
|
1031
|
-
return;
|
|
1032
|
-
}
|
|
1033
|
-
let requests;
|
|
1034
|
-
try {
|
|
1035
|
-
requests = await directory.getRequestedDocHistoryPurges();
|
|
1036
|
-
}
|
|
1037
|
-
catch (error) {
|
|
1038
|
-
console.error(`[TenantManager] executePendingPurges: cannot read purge requests for ${normalizedId}:`, error);
|
|
1039
|
-
return;
|
|
1040
|
-
}
|
|
1041
|
-
if (requests.length === 0)
|
|
1042
|
-
return;
|
|
1043
|
-
const registry = this.getPurgedDocRegistry(normalizedId);
|
|
1044
|
-
const purgedNow = new Set();
|
|
1045
|
-
const touchedDbIds = new Set();
|
|
1046
|
-
for (const request of requests) {
|
|
1047
|
-
if (registry.isRequestProcessed(request.purgeRequestDocId)) {
|
|
1048
|
-
continue;
|
|
1049
|
-
}
|
|
1050
|
-
if (typeof request.dbId !== "string" || request.dbId.length === 0) {
|
|
1051
|
-
continue;
|
|
1052
|
-
}
|
|
1053
|
-
for (const docId of request.docIds) {
|
|
1054
|
-
if (typeof docId !== "string" || docId.length === 0)
|
|
1055
|
-
continue;
|
|
1056
|
-
// Denylist BEFORE purging: the sync push path consults the registry
|
|
1057
|
-
// live, so from this moment re-pushes of the doc are rejected. Doing it
|
|
1058
|
-
// after the store purges would leave a window in which the entries are
|
|
1059
|
-
// already gone but a stale client could still re-push (and the server
|
|
1060
|
-
// would re-accept) the very history that is being purged. The record is
|
|
1061
|
-
// kept regardless of per-store outcome so re-pushes are rejected even
|
|
1062
|
-
// if one store had nothing to purge.
|
|
1063
|
-
registry.recordPurgedDoc(request.dbId, docId);
|
|
1064
|
-
await this.purgeDocumentFromDatabase(normalizedId, request.dbId, docId);
|
|
1065
|
-
purgedNow.add(`${request.dbId}\u0000${docId}`);
|
|
1066
|
-
touchedDbIds.add(request.dbId);
|
|
1067
|
-
}
|
|
1068
|
-
registry.markRequestProcessed(request.purgeRequestDocId);
|
|
1069
|
-
console.log(`[TenantManager] Executed purge request ${request.purgeRequestDocId} (${request.docIds.length} doc(s)) for ${normalizedId}/${request.dbId}`);
|
|
1070
|
-
}
|
|
1071
|
-
// A purge keeps attachment chunks that another document's history still
|
|
1072
|
-
// references (an in-place copy). Once that document is purged as well,
|
|
1073
|
-
// the earlier purge's leftovers are unreferenced: sweep them now, since a
|
|
1074
|
-
// processed request is never executed again.
|
|
1075
|
-
for (const dbId of touchedDbIds) {
|
|
1076
|
-
for (const docId of registry.getPurgedDocIds(dbId)) {
|
|
1077
|
-
if (!purgedNow.has(`${dbId}\u0000${docId}`)) {
|
|
1078
|
-
await this.purgeDocumentFromDatabase(normalizedId, dbId, docId);
|
|
1079
|
-
}
|
|
1080
|
-
}
|
|
1081
|
-
}
|
|
1082
|
-
}
|
|
1083
|
-
/**
|
|
1084
|
-
* Purge one document from a database's docs and attachment stores. Attachment
|
|
1085
|
-
* chunks still referenced by another document are kept (see
|
|
1086
|
-
* `purgeDocumentHistory.ts`). Failures are logged, never thrown.
|
|
1087
|
-
*/
|
|
1088
|
-
async purgeDocumentFromDatabase(tenantId, dbId, docId) {
|
|
1089
|
-
try {
|
|
1090
|
-
await (0, purgeDocumentHistory_1.purgeDocumentHistoryFromStores)({
|
|
1091
|
-
docsStore: await this.getStore(tenantId, dbId, types_1.StoreKind.docs),
|
|
1092
|
-
attachmentStore: await this.getStore(tenantId, dbId, types_1.StoreKind.attachments),
|
|
1093
|
-
}, docId);
|
|
1094
|
-
}
|
|
1095
|
-
catch (error) {
|
|
1096
|
-
console.error(`[TenantManager] executePendingPurges: failed to purge ${docId} in ${tenantId}/${dbId}:`, error);
|
|
1097
|
-
}
|
|
57
|
+
this.dataDir = dataDir;
|
|
1098
58
|
}
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
return tenant.authService;
|
|
59
|
+
getDataDir() {
|
|
60
|
+
return this.dataDir;
|
|
1102
61
|
}
|
|
1103
62
|
getCryptoAdapter() {
|
|
1104
|
-
return
|
|
1105
|
-
}
|
|
1106
|
-
async reloadTenant(tenantId) {
|
|
1107
|
-
const normalizedId = tenantId.toLowerCase();
|
|
1108
|
-
this.loadedTenants.delete(normalizedId);
|
|
1109
|
-
return this.getTenant(normalizedId);
|
|
1110
|
-
}
|
|
1111
|
-
clearCache() {
|
|
1112
|
-
this.loadedTenants.clear();
|
|
1113
|
-
console.log(`[TenantManager] Cleared tenant cache`);
|
|
1114
|
-
}
|
|
1115
|
-
// -----------------------------------------------------------------------
|
|
1116
|
-
// Private helpers
|
|
1117
|
-
// -----------------------------------------------------------------------
|
|
1118
|
-
async loadTenant(tenantId) {
|
|
1119
|
-
const configPath = this.resolveTenantConfigPath(tenantId);
|
|
1120
|
-
if (!(0, fs_1.existsSync)(configPath)) {
|
|
1121
|
-
throw new Error(`Tenant ${tenantId} not found: ${configPath}`);
|
|
1122
|
-
}
|
|
1123
|
-
const configJson = (0, fs_1.readFileSync)(configPath, "utf-8");
|
|
1124
|
-
const config = JSON.parse(configJson);
|
|
1125
|
-
const context = { tenantId, config };
|
|
1126
|
-
const storeFactory = new StoreFactory_1.StoreFactory(tenantId, config, this.dataDir);
|
|
1127
|
-
// Build the inner directory (real or config-based fallback)
|
|
1128
|
-
let innerDirectory;
|
|
1129
|
-
let mindooTenant;
|
|
1130
|
-
const publicInfosKeys = await this.getStoredPublicInfosKeys(tenantId);
|
|
1131
|
-
if (publicInfosKeys.length > 0 &&
|
|
1132
|
-
this.serverIdentity &&
|
|
1133
|
-
this.serverPassword) {
|
|
1134
|
-
try {
|
|
1135
|
-
const result = await this.createDirectoryTenant(tenantId, config, storeFactory, publicInfosKeys);
|
|
1136
|
-
mindooTenant = result.tenant;
|
|
1137
|
-
innerDirectory = await mindooTenant.openDirectory();
|
|
1138
|
-
console.log(`[TenantManager] Loaded tenant ${tenantId} with real directory`);
|
|
1139
|
-
}
|
|
1140
|
-
catch (error) {
|
|
1141
|
-
console.error(`[TenantManager] Failed to create real directory for ${tenantId}, falling back to config:`, error);
|
|
1142
|
-
innerDirectory = new SimpleMindooDirectory(config);
|
|
1143
|
-
}
|
|
1144
|
-
}
|
|
1145
|
-
else {
|
|
1146
|
-
if (publicInfosKeys.length > 0 && !this.serverIdentity) {
|
|
1147
|
-
console.log(`[TenantManager] Tenant ${tenantId} has $publicinfos keys but no server identity; using config-based directory`);
|
|
1148
|
-
}
|
|
1149
|
-
else {
|
|
1150
|
-
console.log(`[TenantManager] No $publicinfos keys for ${tenantId}, using config-based directory`);
|
|
1151
|
-
}
|
|
1152
|
-
innerDirectory = new SimpleMindooDirectory(config);
|
|
1153
|
-
}
|
|
1154
|
-
// Wrap with CompositeMindooDirectory so trusted servers are also recognized
|
|
1155
|
-
const directory = new CompositeMindooDirectory(innerDirectory, this.trustedServers, config.adminUsername
|
|
1156
|
-
? {
|
|
1157
|
-
username: config.adminUsername,
|
|
1158
|
-
signingPublicKey: config.adminSigningPublicKey,
|
|
1159
|
-
encryptionPublicKey: config.adminEncryptionPublicKey,
|
|
1160
|
-
}
|
|
1161
|
-
: undefined);
|
|
1162
|
-
const authService = new AuthenticationService_1.AuthenticationService(this.cryptoAdapter, directory, tenantId);
|
|
1163
|
-
console.log(`[TenantManager] Loaded tenant: ${tenantId}`);
|
|
1164
|
-
return {
|
|
1165
|
-
context,
|
|
1166
|
-
storeFactory,
|
|
1167
|
-
authService,
|
|
1168
|
-
directory,
|
|
1169
|
-
mindooTenant,
|
|
1170
|
-
serverStores: new Map(),
|
|
1171
|
-
};
|
|
1172
|
-
}
|
|
1173
|
-
/**
|
|
1174
|
-
* Create a real BaseMindooTenant for directory reading using the global
|
|
1175
|
-
* server identity.
|
|
1176
|
-
*/
|
|
1177
|
-
async createDirectoryTenant(tenantId, config, storeFactory, publicInfosKeys) {
|
|
1178
|
-
const storeFactoryAdapter = new StoreFactoryAdapter(storeFactory);
|
|
1179
|
-
const factory = new BaseMindooTenantFactory_1.BaseMindooTenantFactory(storeFactoryAdapter, this.cryptoAdapter);
|
|
1180
|
-
const serverUser = this.serverIdentity;
|
|
1181
|
-
// Create a KeyBag using the server user's encryption key
|
|
1182
|
-
const keyBag = await this.createServerKeyBag(serverUser, this.serverPassword);
|
|
1183
|
-
for (const publicInfosKey of this.mergeUniqueKeys(publicInfosKeys)) {
|
|
1184
|
-
await keyBag.set("doc", tenantId, types_1.PUBLIC_INFOS_KEY_ID, publicInfosKey);
|
|
1185
|
-
}
|
|
1186
|
-
// Collect trusted server signing keys for additionalTrustedKeys
|
|
1187
|
-
const openOptions = {};
|
|
1188
|
-
if (this.trustedServers.length > 0) {
|
|
1189
|
-
const trustedKeys = new Map();
|
|
1190
|
-
for (const server of this.trustedServers) {
|
|
1191
|
-
trustedKeys.set(server.signingPublicKey, true);
|
|
1192
|
-
}
|
|
1193
|
-
openOptions.additionalTrustedKeys = trustedKeys;
|
|
1194
|
-
}
|
|
1195
|
-
const tenant = await factory.openTenant(tenantId, config.adminSigningPublicKey, config.adminEncryptionPublicKey, serverUser, this.serverPassword, keyBag, openOptions);
|
|
1196
|
-
return { tenant };
|
|
63
|
+
return super.getCryptoAdapter();
|
|
1197
64
|
}
|
|
1198
65
|
}
|
|
1199
66
|
exports.TenantManager = TenantManager;
|