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
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.BaseMindooTenantDirectory = void 0;
|
|
4
4
|
const types_1 = require("./types");
|
|
5
|
+
const errors_1 = require("./errors");
|
|
5
6
|
const timeSource_1 = require("./utils/timeSource");
|
|
6
7
|
const singleFlight_1 = require("./utils/singleFlight");
|
|
7
8
|
const logging_1 = require("./logging");
|
|
8
9
|
const grantKeys_1 = require("./accesscontrol/grantKeys");
|
|
9
10
|
const types_2 = require("./accesscontrol/types");
|
|
11
|
+
const builtinDbInvariants_1 = require("./builtinDbInvariants");
|
|
10
12
|
const DirectoryStateNode_1 = require("./accesscontrol/DirectoryStateNode");
|
|
11
13
|
const DirectoryTimeTravelIndex_1 = require("./accesscontrol/DirectoryTimeTravelIndex");
|
|
12
14
|
const directoryProjection_1 = require("./accesscontrol/directoryProjection");
|
|
@@ -20,6 +22,48 @@ const MAX_UNKNOWN_KEYS_TRACKED = 1000;
|
|
|
20
22
|
function unionStrings(existing, added) {
|
|
21
23
|
return Array.from(new Set([...existing, ...added]));
|
|
22
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* Grant documents by `username_hash`, maintained by the unified cache pass.
|
|
27
|
+
* Keeps ids only, never documents, so its size is independent of the
|
|
28
|
+
* document cache.
|
|
29
|
+
*/
|
|
30
|
+
class GrantDocumentIndex {
|
|
31
|
+
constructor() {
|
|
32
|
+
this.idsByHash = new Map();
|
|
33
|
+
/** id -> hash and position in modification order (grows on every re-seen change). */
|
|
34
|
+
this.entries = new Map();
|
|
35
|
+
this.nextOrder = 0;
|
|
36
|
+
}
|
|
37
|
+
/** Latest state of a document: its `username_hash` if it is a grant, else undefined. */
|
|
38
|
+
record(docId, usernameHash) {
|
|
39
|
+
const previous = this.entries.get(docId);
|
|
40
|
+
if (previous) {
|
|
41
|
+
this.entries.delete(docId);
|
|
42
|
+
const ids = this.idsByHash.get(previous.hash);
|
|
43
|
+
ids?.delete(docId);
|
|
44
|
+
if (ids && ids.size === 0)
|
|
45
|
+
this.idsByHash.delete(previous.hash);
|
|
46
|
+
}
|
|
47
|
+
if (typeof usernameHash !== "string")
|
|
48
|
+
return;
|
|
49
|
+
this.entries.set(docId, { hash: usernameHash, order: this.nextOrder++ });
|
|
50
|
+
let ids = this.idsByHash.get(usernameHash);
|
|
51
|
+
if (!ids) {
|
|
52
|
+
ids = new Set();
|
|
53
|
+
this.idsByHash.set(usernameHash, ids);
|
|
54
|
+
}
|
|
55
|
+
ids.add(docId);
|
|
56
|
+
}
|
|
57
|
+
/** Ids of the grants carrying any of `hashes`, oldest change first. */
|
|
58
|
+
docIdsFor(hashes) {
|
|
59
|
+
const ids = [];
|
|
60
|
+
for (const hash of hashes) {
|
|
61
|
+
for (const id of this.idsByHash.get(hash) ?? [])
|
|
62
|
+
ids.push(id);
|
|
63
|
+
}
|
|
64
|
+
return ids.sort((a, b) => this.entries.get(a).order - this.entries.get(b).order);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
23
67
|
class BaseMindooTenantDirectory {
|
|
24
68
|
constructor(tenant, logger) {
|
|
25
69
|
this.directoryDB = null;
|
|
@@ -33,6 +77,13 @@ class BaseMindooTenantDirectory {
|
|
|
33
77
|
// (docs/accesscontrol.md §6.5), so the set of trusted keys is recomputed as
|
|
34
78
|
// the union of all grant documents' current key arrays after each cache pass.
|
|
35
79
|
this.grantDocIdToSigningKeys = new Map();
|
|
80
|
+
// Index of grant documents: `username_hash` -> ids of the (non-deleted,
|
|
81
|
+
// readable) grant documents carrying it, plus each id's position in
|
|
82
|
+
// modification order. findGrantAccessDocuments loads only the documents
|
|
83
|
+
// it returns instead of re-scanning the whole directory: the token check
|
|
84
|
+
// runs it on every sync request, and with more grants than the document
|
|
85
|
+
// cache holds the scan rebuilt every grant from its changes each time.
|
|
86
|
+
this.grantIndex = new GrantDocumentIndex();
|
|
36
87
|
// Best-effort reverse lookup cache for public signing key -> user details.
|
|
37
88
|
// Entries remain available even after revocation so historical UIs can still show identity labels.
|
|
38
89
|
this.userLookupCache = new Map();
|
|
@@ -141,7 +192,19 @@ class BaseMindooTenantDirectory {
|
|
|
141
192
|
}
|
|
142
193
|
return this.directoryDB;
|
|
143
194
|
}
|
|
195
|
+
async registerUsers(users, administrationPrivateKey, administrationPrivateKeyPassword) {
|
|
196
|
+
await this.tenant.withDecryptedKeyReuse(async () => {
|
|
197
|
+
for (const user of users) {
|
|
198
|
+
await this.registerUserWithKeyReuse(user.userId, administrationPrivateKey, administrationPrivateKeyPassword, user.userDetails, user.label, user.options);
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
}
|
|
144
202
|
async registerUser(userId, administrationPrivateKey, administrationPrivateKeyPassword, userDetails, label, options) {
|
|
203
|
+
// One registration signs several directory changes and key distributions
|
|
204
|
+
// with the administration key; decrypt it once instead of for each.
|
|
205
|
+
await this.tenant.withDecryptedKeyReuse(() => this.registerUserWithKeyReuse(userId, administrationPrivateKey, administrationPrivateKeyPassword, userDetails, label, options));
|
|
206
|
+
}
|
|
207
|
+
async registerUserWithKeyReuse(userId, administrationPrivateKey, administrationPrivateKeyPassword, userDetails, label, options) {
|
|
145
208
|
this.logger.info(`Registering user: ${userId.username}`);
|
|
146
209
|
// Check if user with same username (case-insensitive) already has ACTIVE
|
|
147
210
|
// access. A grant whose keys were removed (revocation, §6.5) is ignored
|
|
@@ -350,22 +413,26 @@ class BaseMindooTenantDirectory {
|
|
|
350
413
|
// identity round-trips: a cleartext name never collides with a 64-char hex
|
|
351
414
|
// hash, so this is safe (docs/accesscontrol.md §6.5).
|
|
352
415
|
targetHashes.add(username);
|
|
353
|
-
const matchesTarget = (value) => typeof value === "string" && targetHashes.has(value);
|
|
354
416
|
const matchingDocs = [];
|
|
355
|
-
//
|
|
417
|
+
// The unified cache pass above indexed the grant documents by
|
|
418
|
+
// username_hash; only the matches are loaded, oldest first as the former
|
|
419
|
+
// full scan returned them.
|
|
356
420
|
// No signature verification needed - DB already enforces admin-only.
|
|
357
421
|
// Revocation no longer uses a separate document: it removes keys from the
|
|
358
422
|
// grant document in place (docs/accesscontrol.md §6.5). A fully-revoked user
|
|
359
423
|
// therefore still has a grant document here, but with empty key arrays.
|
|
360
424
|
// Callers that need "active access" should inspect the key arrays (see
|
|
361
425
|
// {@link isUserRevoked} / {@link getUserPublicKeys}).
|
|
362
|
-
for
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
data.type === "grantaccess" &&
|
|
366
|
-
matchesTarget(data.username_hash)) {
|
|
426
|
+
for (const docId of this.grantIndex.docIdsFor(targetHashes)) {
|
|
427
|
+
try {
|
|
428
|
+
matchingDocs.push(await directoryDB.getDocument(docId));
|
|
367
429
|
this.logger.debug(`Found grant access document for username: ${username}`);
|
|
368
|
-
|
|
430
|
+
}
|
|
431
|
+
catch (error) {
|
|
432
|
+
// Deleted since the cache pass: no longer a grant, as for the scan.
|
|
433
|
+
if (!(error instanceof errors_1.DocumentDeletedError) && !(error instanceof errors_1.DocumentNotFoundError)) {
|
|
434
|
+
throw error;
|
|
435
|
+
}
|
|
369
436
|
}
|
|
370
437
|
}
|
|
371
438
|
this.logger.debug(`Found ${matchingDocs.length} grant access document(s) for username: ${username}`);
|
|
@@ -594,6 +661,7 @@ class BaseMindooTenantDirectory {
|
|
|
594
661
|
// are idempotent) instead of leaving its documents half-applied.
|
|
595
662
|
const fullRebuild = startCursor === null;
|
|
596
663
|
const grantDocIdToSigningKeys = fullRebuild ? new Map() : this.grantDocIdToSigningKeys;
|
|
664
|
+
const grantIndex = fullRebuild ? new GrantDocumentIndex() : this.grantIndex;
|
|
597
665
|
const userLookupCache = fullRebuild ? new Map() : this.userLookupCache;
|
|
598
666
|
const wipeKeyToGrantDocId = fullRebuild ? new Map() : this.wipeKeyToGrantDocId;
|
|
599
667
|
let tenantSettingsCache = fullRebuild ? null : this.tenantSettingsCache;
|
|
@@ -616,6 +684,9 @@ class BaseMindooTenantDirectory {
|
|
|
616
684
|
// Process changes (documents are returned in order by lastModified, oldest to newest)
|
|
617
685
|
for await (const { doc, cursor } of directoryDB.iterateChangesSince(startCursor)) {
|
|
618
686
|
const data = doc.getData();
|
|
687
|
+
// Deleted and unreadable documents arrive as tombstones with empty
|
|
688
|
+
// data and drop out, as they never matched the former full scan.
|
|
689
|
+
grantIndex.record(doc.getId(), data.form === "useroperation" && data.type === "grantaccess" ? data.username_hash : undefined);
|
|
619
690
|
// Process user operation documents (grant/revoke access)
|
|
620
691
|
if (data.form === "useroperation") {
|
|
621
692
|
// Check if this is a grant access document. A grant may carry MULTIPLE
|
|
@@ -813,6 +884,7 @@ class BaseMindooTenantDirectory {
|
|
|
813
884
|
}
|
|
814
885
|
this.trustedKeysCache = trustedKeysCache;
|
|
815
886
|
this.grantDocIdToSigningKeys = grantDocIdToSigningKeys;
|
|
887
|
+
this.grantIndex = grantIndex;
|
|
816
888
|
this.userLookupCache = userLookupCache;
|
|
817
889
|
this.wipeKeyToGrantDocId = wipeKeyToGrantDocId;
|
|
818
890
|
this.tenantSettingsCache = tenantSettingsCache;
|
|
@@ -1250,6 +1322,10 @@ class BaseMindooTenantDirectory {
|
|
|
1250
1322
|
* server to gate all sync operations for a database id.
|
|
1251
1323
|
*/
|
|
1252
1324
|
async evaluateDbAccessForSigningKey(input) {
|
|
1325
|
+
return (await this.evaluateDbAccessModeForSigningKey(input)).mode === "read";
|
|
1326
|
+
}
|
|
1327
|
+
/** The configured gates (§6.7 id allowlist, §6.8 read gate), without the personal-database invariant. */
|
|
1328
|
+
async evaluateConfiguredReadGate(input) {
|
|
1253
1329
|
// Gate 1 — tenant-wide database-id allowlist (`databaseCreationPolicy`).
|
|
1254
1330
|
// Orthogonal to read access: a database must clear BOTH gates.
|
|
1255
1331
|
if (!(await this.isDatabaseAllowed(input.dbid, { signingKey: input.signingKey }))) {
|
|
@@ -1277,6 +1353,261 @@ class BaseMindooTenantDirectory {
|
|
|
1277
1353
|
});
|
|
1278
1354
|
return decision.allowed;
|
|
1279
1355
|
}
|
|
1356
|
+
/**
|
|
1357
|
+
* The read scope of a principal in `dbid` (docs/accesscontrol.md §6.12):
|
|
1358
|
+
* `"all"` when it reads every document, `"own"` when the database's
|
|
1359
|
+
* `docReadScope` is `"own"` and the principal is neither the tenant admin nor
|
|
1360
|
+
* matched by a `doc_read_all` allow rule. The directory databases are never
|
|
1361
|
+
* scoped. Evaluated at the directory head, like the read gate.
|
|
1362
|
+
*/
|
|
1363
|
+
async evaluateReadScopeForSigningKey(input) {
|
|
1364
|
+
if (input.dbid === types_1.DIRECTORY_DB_ID || input.dbid === types_1.USER_DIRECTORY_DB_ID)
|
|
1365
|
+
return "all";
|
|
1366
|
+
const adminKey = this.tenant.getAdministrationPublicKey();
|
|
1367
|
+
if (input.signingKey === adminKey)
|
|
1368
|
+
return "all";
|
|
1369
|
+
const decision = await this.evaluateAccessForSigningKey({
|
|
1370
|
+
op: "doc_read_all",
|
|
1371
|
+
dbid: input.dbid,
|
|
1372
|
+
signingKey: input.signingKey,
|
|
1373
|
+
trustedTime: Number.MAX_SAFE_INTEGER,
|
|
1374
|
+
});
|
|
1375
|
+
return decision.allowed ? "all" : "own";
|
|
1376
|
+
}
|
|
1377
|
+
/**
|
|
1378
|
+
* The current user's read scope in `dbid` (§6.12), the client-side
|
|
1379
|
+
* counterpart of {@link evaluateReadScopeForSigningKey}, e.g. to tell the
|
|
1380
|
+
* user that a list only shows their own documents.
|
|
1381
|
+
*/
|
|
1382
|
+
async getReadScope(dbid) {
|
|
1383
|
+
const currentUser = await this.tenant.getCurrentUserId();
|
|
1384
|
+
return this.evaluateReadScopeForSigningKey({ dbid, signingKey: currentUser.userSigningPublicKey });
|
|
1385
|
+
}
|
|
1386
|
+
/**
|
|
1387
|
+
* Like {@link evaluateDbAccessForSigningKey}, but distinguishes the deposit
|
|
1388
|
+
* right (docs/accesscontrol.md §6.10): `"read"` passes the read gate (all
|
|
1389
|
+
* sync), `"deposit"` fails it but holds a `doc_deposit` allow (push of
|
|
1390
|
+
* sealed creates only), `"none"` neither. `maxRecipients` is the bound from
|
|
1391
|
+
* the matching deposit rule.
|
|
1392
|
+
*/
|
|
1393
|
+
async evaluateDbAccessModeForSigningKey(input) {
|
|
1394
|
+
// Built-in personal-database invariant (§6.11): decides first, and a
|
|
1395
|
+
// configured policy can only take away what it grants.
|
|
1396
|
+
const personal = await this.evaluatePersonalDatabaseAccess(input);
|
|
1397
|
+
if (personal) {
|
|
1398
|
+
if (personal.mode === "none")
|
|
1399
|
+
return personal;
|
|
1400
|
+
if ((0, DirectoryStateNode_1.signingKeysEqual)(input.signingKey, this.tenant.getAdministrationPublicKey())) {
|
|
1401
|
+
return personal;
|
|
1402
|
+
}
|
|
1403
|
+
if (personal.mode === "read") {
|
|
1404
|
+
return (await this.evaluateConfiguredReadGate(input)) ? personal : { mode: "none", reason: "denied by the configured policy" };
|
|
1405
|
+
}
|
|
1406
|
+
if (!(await this.isDatabaseAllowed(input.dbid, { signingKey: input.signingKey })))
|
|
1407
|
+
return { mode: "none" };
|
|
1408
|
+
const configured = await this.evaluateAccessForSigningKey({
|
|
1409
|
+
op: "doc_deposit",
|
|
1410
|
+
dbid: input.dbid,
|
|
1411
|
+
signingKey: input.signingKey,
|
|
1412
|
+
trustedTime: Number.MAX_SAFE_INTEGER,
|
|
1413
|
+
});
|
|
1414
|
+
// Only a matching deny rule counts here; the built-in grant replaces the allow rule.
|
|
1415
|
+
return !configured.allowed && configured.matchedRuleId
|
|
1416
|
+
? { mode: "none", reason: `deposit denied by rule ${configured.matchedRuleId}` }
|
|
1417
|
+
: personal;
|
|
1418
|
+
}
|
|
1419
|
+
if (await this.evaluateConfiguredReadGate(input)) {
|
|
1420
|
+
return { mode: "read" };
|
|
1421
|
+
}
|
|
1422
|
+
// The id allowlist (gate 1) applies to depositors as well.
|
|
1423
|
+
if (!(await this.isDatabaseAllowed(input.dbid, { signingKey: input.signingKey }))) {
|
|
1424
|
+
return { mode: "none" };
|
|
1425
|
+
}
|
|
1426
|
+
const decision = await this.evaluateAccessForSigningKey({
|
|
1427
|
+
op: "doc_deposit",
|
|
1428
|
+
dbid: input.dbid,
|
|
1429
|
+
signingKey: input.signingKey,
|
|
1430
|
+
trustedTime: Number.MAX_SAFE_INTEGER,
|
|
1431
|
+
});
|
|
1432
|
+
if (!decision.allowed)
|
|
1433
|
+
return { mode: "none" };
|
|
1434
|
+
const rule = await this.depositRule(decision.matchedRuleId);
|
|
1435
|
+
return {
|
|
1436
|
+
mode: "deposit",
|
|
1437
|
+
maxRecipients: rule?.maxRecipients ?? types_2.DEFAULT_DEPOSIT_MAX_RECIPIENTS,
|
|
1438
|
+
...(rule?.docIdPrefixes?.length ? { docIdPrefixes: rule.docIdPrefixes } : {}),
|
|
1439
|
+
};
|
|
1440
|
+
}
|
|
1441
|
+
/**
|
|
1442
|
+
* The built-in rule for personal databases (§6.11), or `null` when `dbid` is
|
|
1443
|
+
* not one. `<prefix><grantDocId>` may be read and synced by the devices on
|
|
1444
|
+
* that grant and by the tenant admin; it may be deposited into by
|
|
1445
|
+
* identities whose grant lists the prefix in `depositPrefixes`; nobody
|
|
1446
|
+
* else gets anything. An unknown grant id leaves everyone but the admin out.
|
|
1447
|
+
*/
|
|
1448
|
+
async evaluatePersonalDatabaseAccess(input) {
|
|
1449
|
+
const head = await this.getDirectoryStateHead();
|
|
1450
|
+
const owner = (0, builtinDbInvariants_1.personalDatabaseOwner)(input.dbid, head.defaultPolicy?.personalDatabasePrefixes ?? []);
|
|
1451
|
+
if (!owner)
|
|
1452
|
+
return null;
|
|
1453
|
+
if ((0, DirectoryStateNode_1.signingKeysEqual)(input.signingKey, this.tenant.getAdministrationPublicKey())) {
|
|
1454
|
+
return { mode: "read", reason: "tenant admin" };
|
|
1455
|
+
}
|
|
1456
|
+
const grant = await this.loadGrantDocument(owner.grantDocId);
|
|
1457
|
+
if (grant && (0, grantKeys_1.extractActiveKeyPairs)(grant).some((pair) => (0, DirectoryStateNode_1.signingKeysEqual)(pair.signingPublicKey, input.signingKey))) {
|
|
1458
|
+
return { mode: "read", reason: "a device of the owner" };
|
|
1459
|
+
}
|
|
1460
|
+
const signerGrants = await this.grantsForSigningKey(input.signingKey);
|
|
1461
|
+
const listed = (grant, field) => Array.isArray(grant[field]) && grant[field].includes(owner.prefix);
|
|
1462
|
+
if (signerGrants.some((grant) => listed(grant, "accessPrefixes"))) {
|
|
1463
|
+
return { mode: "read", reason: `service access to ${owner.prefix}* granted` };
|
|
1464
|
+
}
|
|
1465
|
+
const depositors = signerGrants.filter((grant) => listed(grant, "depositPrefixes"));
|
|
1466
|
+
if (depositors.length > 0) {
|
|
1467
|
+
// Document ids a deposit may use: the grant's `depositDocIdPrefixes`
|
|
1468
|
+
// (per database prefix, e.g. {"mmail_": ["memo_"]}), else the built-in
|
|
1469
|
+
// default for the prefix (mmail_: "memo_", MindooMail messages).
|
|
1470
|
+
const configured = depositors
|
|
1471
|
+
.map((grant) => grant.depositDocIdPrefixes)
|
|
1472
|
+
.map((byPrefix) => (byPrefix && Array.isArray(byPrefix[owner.prefix]) ? byPrefix[owner.prefix] : null))
|
|
1473
|
+
.find((list) => list !== null);
|
|
1474
|
+
const docIdPrefixes = configured
|
|
1475
|
+
? configured.map(String).filter((p) => p.length > 0)
|
|
1476
|
+
: [...(builtinDbInvariants_1.BUILTIN_PERSONAL_DEPOSIT_DOC_ID_PREFIXES[owner.prefix] ?? [])];
|
|
1477
|
+
return {
|
|
1478
|
+
mode: "deposit",
|
|
1479
|
+
maxRecipients: 1,
|
|
1480
|
+
...(docIdPrefixes.length ? { docIdPrefixes } : {}),
|
|
1481
|
+
reason: `deposit into ${owner.prefix}* granted`,
|
|
1482
|
+
};
|
|
1483
|
+
}
|
|
1484
|
+
// Internal mail (§6.11): members deposit MindooMail messages into each
|
|
1485
|
+
// other's mailbox, under the tenant's `internalMail` setting.
|
|
1486
|
+
if (owner.prefix === "mmail_" && grant && signerGrants.length > 0) {
|
|
1487
|
+
const mode = head.defaultPolicy?.internalMail ?? "members";
|
|
1488
|
+
let allowed = mode === "members";
|
|
1489
|
+
if (mode === "rules") {
|
|
1490
|
+
const decision = await this.evaluateAccessForSigningKey({
|
|
1491
|
+
op: "doc_deposit",
|
|
1492
|
+
dbid: input.dbid,
|
|
1493
|
+
signingKey: input.signingKey,
|
|
1494
|
+
trustedTime: Number.MAX_SAFE_INTEGER,
|
|
1495
|
+
});
|
|
1496
|
+
allowed = decision.allowed && decision.matchedRuleId !== undefined;
|
|
1497
|
+
}
|
|
1498
|
+
if (allowed) {
|
|
1499
|
+
const limits = head.defaultPolicy?.internalMailLimits ?? {};
|
|
1500
|
+
return {
|
|
1501
|
+
mode: "deposit",
|
|
1502
|
+
maxRecipients: 1,
|
|
1503
|
+
docIdPrefixes: [...(builtinDbInvariants_1.BUILTIN_PERSONAL_DEPOSIT_DOC_ID_PREFIXES.mmail_ ?? [])],
|
|
1504
|
+
internalMail: {
|
|
1505
|
+
maxBytesPerMail: limits.maxBytesPerMail ?? types_2.DEFAULT_INTERNAL_MAIL_LIMITS.maxBytesPerMail,
|
|
1506
|
+
maxMailsPerHour: limits.maxMailsPerHour ?? types_2.DEFAULT_INTERNAL_MAIL_LIMITS.maxMailsPerHour,
|
|
1507
|
+
},
|
|
1508
|
+
reason: `internal mail (${mode})`,
|
|
1509
|
+
};
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
return { mode: "none", reason: grant ? "personal database of another person" : "personal database without a matching grant" };
|
|
1513
|
+
}
|
|
1514
|
+
/** A `grantaccess` document by id, or null (missing, deleted, or another kind of document). */
|
|
1515
|
+
async loadGrantDocument(grantDocId) {
|
|
1516
|
+
const directoryDB = await this.getDirectoryDB();
|
|
1517
|
+
try {
|
|
1518
|
+
const doc = await directoryDB.getDocument(grantDocId);
|
|
1519
|
+
if (doc.isDeleted())
|
|
1520
|
+
return null;
|
|
1521
|
+
const data = doc.getData();
|
|
1522
|
+
return data.form === "useroperation" && data.type === "grantaccess" ? data : null;
|
|
1523
|
+
}
|
|
1524
|
+
catch {
|
|
1525
|
+
return null;
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
/** The active grants that list `signingKey` as a device. */
|
|
1529
|
+
async grantsForSigningKey(signingKey) {
|
|
1530
|
+
const lookup = await this.getUserBySigningPublicKey(signingKey);
|
|
1531
|
+
if (!lookup)
|
|
1532
|
+
return [];
|
|
1533
|
+
const grants = await this.findGrantAccessDocuments(lookup.username);
|
|
1534
|
+
return grants
|
|
1535
|
+
.filter((doc) => !doc.isDeleted())
|
|
1536
|
+
.map((doc) => doc.getData())
|
|
1537
|
+
.filter((data) => (0, grantKeys_1.extractActiveKeyPairs)(data).some((pair) => (0, DirectoryStateNode_1.signingKeysEqual)(pair.signingPublicKey, signingKey)));
|
|
1538
|
+
}
|
|
1539
|
+
/**
|
|
1540
|
+
* Let an identity deposit into personal databases with these prefixes
|
|
1541
|
+
* (§6.11), e.g. `["mmail_"]` for a mail gateway. Written on the person's
|
|
1542
|
+
* grant, admin-signed; an empty list takes the right away again.
|
|
1543
|
+
*/
|
|
1544
|
+
async setGrantDepositPrefixes(username, prefixes, administrationPrivateKey, administrationPrivateKeyPassword) {
|
|
1545
|
+
await this.setGrantPersonalDatabaseRights(username, { depositPrefixes: prefixes }, administrationPrivateKey, administrationPrivateKeyPassword);
|
|
1546
|
+
}
|
|
1547
|
+
/**
|
|
1548
|
+
* The rights an identity has in other people's personal databases (§6.11):
|
|
1549
|
+
* `depositPrefixes` (push sealed creates only) and `accessPrefixes` (read
|
|
1550
|
+
* and sync, for a service such as the mail gateway serving every outbox).
|
|
1551
|
+
* Written on the identity's grant, admin-signed; a field left out stays as
|
|
1552
|
+
* it is, an empty list removes the right. `depositDocIdPrefixes` sets the
|
|
1553
|
+
* document ids a deposit may use per database prefix (e.g.
|
|
1554
|
+
* `{ mmail_: ["memo_"] }`, the built-in default); an empty list allows any id.
|
|
1555
|
+
*/
|
|
1556
|
+
async setGrantPersonalDatabaseRights(username, rights, administrationPrivateKey, administrationPrivateKeyPassword) {
|
|
1557
|
+
for (const prefix of [...(rights.depositPrefixes ?? []), ...(rights.accessPrefixes ?? [])]) {
|
|
1558
|
+
if (!builtinDbInvariants_1.PERSONAL_DATABASE_PREFIX_REGEX.test(prefix)) {
|
|
1559
|
+
throw new Error(`depositPrefixes: "${prefix}" is not a personal-database prefix like "mmail_"`);
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1562
|
+
for (const [prefix, ids] of Object.entries(rights.depositDocIdPrefixes ?? {})) {
|
|
1563
|
+
if (!builtinDbInvariants_1.PERSONAL_DATABASE_PREFIX_REGEX.test(prefix)) {
|
|
1564
|
+
throw new Error(`depositDocIdPrefixes: "${prefix}" is not a personal-database prefix like "mmail_"`);
|
|
1565
|
+
}
|
|
1566
|
+
(0, types_2.validateDocIdPrefixes)(ids, `depositDocIdPrefixes.${prefix}`);
|
|
1567
|
+
}
|
|
1568
|
+
const grants = (await this.findGrantAccessDocuments(username)).filter((doc) => !doc.isDeleted());
|
|
1569
|
+
if (grants.length === 0)
|
|
1570
|
+
throw new Error(`No grant for ${username}`);
|
|
1571
|
+
const directoryDB = await this.getDirectoryDB();
|
|
1572
|
+
const signingKeyPair = {
|
|
1573
|
+
publicKey: this.tenant.getAdministrationPublicKey(),
|
|
1574
|
+
privateKey: administrationPrivateKey,
|
|
1575
|
+
};
|
|
1576
|
+
for (const grant of grants) {
|
|
1577
|
+
await directoryDB.changeDoc(grant, (d) => {
|
|
1578
|
+
const data = d.getData();
|
|
1579
|
+
if (rights.depositPrefixes)
|
|
1580
|
+
data.depositPrefixes = [...new Set(rights.depositPrefixes)];
|
|
1581
|
+
if (rights.accessPrefixes)
|
|
1582
|
+
data.accessPrefixes = [...new Set(rights.accessPrefixes)];
|
|
1583
|
+
if (rights.depositDocIdPrefixes) {
|
|
1584
|
+
const merged = { ...(data.depositDocIdPrefixes ?? {}) };
|
|
1585
|
+
for (const [prefix, ids] of Object.entries(rights.depositDocIdPrefixes))
|
|
1586
|
+
merged[prefix] = [...new Set(ids)];
|
|
1587
|
+
data.depositDocIdPrefixes = merged;
|
|
1588
|
+
}
|
|
1589
|
+
}, { signingKeyPair, signingKeyPassword: administrationPrivateKeyPassword });
|
|
1590
|
+
}
|
|
1591
|
+
await this.updateUnifiedCache();
|
|
1592
|
+
}
|
|
1593
|
+
/**
|
|
1594
|
+
* Whether the current user holds the deposit right on `dbid` (§6.10): a
|
|
1595
|
+
* `doc_deposit` allow rule matches and no deny does. Says nothing about the
|
|
1596
|
+
* read gate; callers check {@link canReadDatabase} first.
|
|
1597
|
+
*/
|
|
1598
|
+
async canDepositDatabase(dbid) {
|
|
1599
|
+
if (dbid === types_1.DIRECTORY_DB_ID || dbid === types_1.USER_DIRECTORY_DB_ID)
|
|
1600
|
+
return false;
|
|
1601
|
+
const currentUser = await this.tenant.getCurrentUserId();
|
|
1602
|
+
const access = await this.evaluateDbAccessModeForSigningKey({ dbid, signingKey: currentUser.userSigningPublicKey });
|
|
1603
|
+
return access.mode === "deposit";
|
|
1604
|
+
}
|
|
1605
|
+
async depositRule(ruleId) {
|
|
1606
|
+
if (!ruleId)
|
|
1607
|
+
return null;
|
|
1608
|
+
const head = await this.getDirectoryStateHead();
|
|
1609
|
+
return (head.rulesByType.get("doc_deposit") ?? []).find((r) => r.ruleId === ruleId) ?? null;
|
|
1610
|
+
}
|
|
1280
1611
|
/**
|
|
1281
1612
|
* Whether the current user may open and sync `dbid` under the database read
|
|
1282
1613
|
* gate (`doc_read`, §6.6). The client-side counterpart of
|
|
@@ -1293,6 +1624,10 @@ class BaseMindooTenantDirectory {
|
|
|
1293
1624
|
const adminKey = this.tenant.getAdministrationPublicKey();
|
|
1294
1625
|
if (currentUser.userSigningPublicKey === adminKey)
|
|
1295
1626
|
return true;
|
|
1627
|
+
// The personal-database invariant (§6.11) decides first, policy on top.
|
|
1628
|
+
const personal = await this.evaluatePersonalDatabaseAccess({ dbid, signingKey: currentUser.userSigningPublicKey });
|
|
1629
|
+
if (personal && personal.mode !== "read")
|
|
1630
|
+
return false;
|
|
1296
1631
|
const decision = await this.canDo("doc_read", dbid);
|
|
1297
1632
|
return decision.allowed;
|
|
1298
1633
|
}
|
|
@@ -1311,12 +1646,30 @@ class BaseMindooTenantDirectory {
|
|
|
1311
1646
|
(0, types_3.validateAccessPolicy)(policy);
|
|
1312
1647
|
await this.writePolicyDoc(types_3.ACL_DEFAULT_POLICY_DOC_ID, policy, administrationPrivateKey, administrationPrivateKeyPassword);
|
|
1313
1648
|
}
|
|
1649
|
+
async requireV2EntrySignatures(administrationPrivateKey, administrationPrivateKeyPassword) {
|
|
1650
|
+
const head = await this.getDirectoryStateHead();
|
|
1651
|
+
const existing = head.defaultPolicy?.requireMetadataSignatureSince;
|
|
1652
|
+
if (typeof existing === "number" && Number.isFinite(existing)) {
|
|
1653
|
+
return { since: existing, changed: false };
|
|
1654
|
+
}
|
|
1655
|
+
const since = (0, timeSource_1.semanticNow)();
|
|
1656
|
+
await this.setDefaultAccessPolicy(head.defaultPolicy
|
|
1657
|
+
? { requireMetadataSignatureSince: since }
|
|
1658
|
+
: // No policy document yet: writing one is what activates access
|
|
1659
|
+
// control, so it carries the master switch, exactly as a new tenant
|
|
1660
|
+
// gets it from the factory.
|
|
1661
|
+
{ disableAllAccessChecksAndPolicies: true, requireMetadataSignatureSince: since }, administrationPrivateKey, administrationPrivateKeyPassword);
|
|
1662
|
+
return { since, changed: true };
|
|
1663
|
+
}
|
|
1314
1664
|
/**
|
|
1315
1665
|
* Create or update a per-database policy override (§6.2). Fields set here
|
|
1316
1666
|
* override the tenant default for `dbid`; unset fields inherit it.
|
|
1317
1667
|
*/
|
|
1318
1668
|
async setDatabaseAccessPolicy(dbid, policy, administrationPrivateKey, administrationPrivateKeyPassword) {
|
|
1319
1669
|
(0, types_3.validateAccessPolicy)(policy);
|
|
1670
|
+
if (policy.personalDatabasePrefixes !== undefined) {
|
|
1671
|
+
throw new Error("personalDatabasePrefixes is a tenant-level setting; set it on the default policy");
|
|
1672
|
+
}
|
|
1320
1673
|
if (policy.llm !== undefined) {
|
|
1321
1674
|
throw new Error("llm is a tenant-level setting; set it on the default policy");
|
|
1322
1675
|
}
|
|
@@ -1368,6 +1721,8 @@ class BaseMindooTenantDirectory {
|
|
|
1368
1721
|
groups: rule.groups ?? [],
|
|
1369
1722
|
}),
|
|
1370
1723
|
action: rule.action ?? "allow",
|
|
1724
|
+
...(rule.maxRecipients !== undefined ? { maxRecipients: rule.maxRecipients } : {}),
|
|
1725
|
+
...(rule.docIdPrefixes !== undefined ? { docIdPrefixes: [...new Set(rule.docIdPrefixes)] } : {}),
|
|
1371
1726
|
};
|
|
1372
1727
|
// Fail fast on malformed clauses / operators (§6.3 closed set).
|
|
1373
1728
|
(0, types_3.validateAclRule)(ruleDoc);
|
|
@@ -1391,6 +1746,14 @@ class BaseMindooTenantDirectory {
|
|
|
1391
1746
|
data.description = ruleDoc.description;
|
|
1392
1747
|
if (ruleDoc.withfields !== undefined)
|
|
1393
1748
|
data.withfields = ruleDoc.withfields;
|
|
1749
|
+
if (ruleDoc.maxRecipients !== undefined)
|
|
1750
|
+
data.maxRecipients = ruleDoc.maxRecipients;
|
|
1751
|
+
else if (data.maxRecipients !== undefined)
|
|
1752
|
+
delete data.maxRecipients;
|
|
1753
|
+
if (ruleDoc.docIdPrefixes?.length)
|
|
1754
|
+
data.docIdPrefixes = ruleDoc.docIdPrefixes;
|
|
1755
|
+
else if (data.docIdPrefixes !== undefined)
|
|
1756
|
+
delete data.docIdPrefixes;
|
|
1394
1757
|
}, {
|
|
1395
1758
|
signingKeyPair: adminSigningKeyPair,
|
|
1396
1759
|
signingKeyPassword: administrationPrivateKeyPassword,
|