mindoodb 0.0.86 → 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 +5 -2
- 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/core/BaseMindooDB.d.ts +67 -9
- package/dist/core/BaseMindooDB.d.ts.map +1 -1
- package/dist/core/BaseMindooDB.js +261 -12
- package/dist/core/BaseMindooDB.js.map +1 -1
- package/dist/core/BaseMindooTenant.d.ts +29 -0
- package/dist/core/BaseMindooTenant.d.ts.map +1 -1
- package/dist/core/BaseMindooTenant.js +111 -6
- package/dist/core/BaseMindooTenant.js.map +1 -1
- package/dist/core/BaseMindooTenantDirectory.d.ts +93 -0
- package/dist/core/BaseMindooTenantDirectory.d.ts.map +1 -1
- package/dist/core/BaseMindooTenantDirectory.js +292 -0
- 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/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 +6 -0
- package/dist/core/appendonlystores/network/AuthenticationService.d.ts.map +1 -1
- package/dist/core/appendonlystores/network/AuthenticationService.js +8 -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/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/sqlite/SqliteContentAddressedStore.d.ts +7 -0
- package/dist/core/appendonlystores/sqlite/SqliteContentAddressedStore.d.ts.map +1 -1
- package/dist/core/appendonlystores/sqlite/SqliteContentAddressedStore.js +18 -0
- package/dist/core/appendonlystores/sqlite/SqliteContentAddressedStore.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/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 +143 -8
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.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/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/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/server/DeviceDiscoveryService.d.ts.map +1 -1
- package/dist/server/DeviceDiscoveryService.js +3 -4
- package/dist/server/DeviceDiscoveryService.js.map +1 -1
- package/dist/server/SystemAdminAuth.d.ts.map +1 -1
- package/dist/server/SystemAdminAuth.js +3 -4
- package/dist/server/SystemAdminAuth.js.map +1 -1
- package/dist/server/TenantManager.d.ts +36 -0
- package/dist/server/TenantManager.d.ts.map +1 -1
- package/dist/server/TenantManager.js +234 -4
- package/dist/server/TenantManager.js.map +1 -1
- package/dist/server/http/MindooDBServerCore.d.ts.map +1 -1
- package/dist/server/http/MindooDBServerCore.js +7 -0
- package/dist/server/http/MindooDBServerCore.js.map +1 -1
- package/dist/server/peer/PeerAuthService.d.ts.map +1 -1
- package/dist/server/peer/PeerAuthService.js +3 -4
- package/dist/server/peer/PeerAuthService.js.map +1 -1
- package/dist/server/peer/PeerReplicator.d.ts.map +1 -1
- package/dist/server/peer/PeerReplicator.js +2 -1
- package/dist/server/peer/PeerReplicator.js.map +1 -1
- package/package.json +1 -1
|
@@ -8,6 +8,7 @@ const singleFlight_1 = require("./utils/singleFlight");
|
|
|
8
8
|
const logging_1 = require("./logging");
|
|
9
9
|
const grantKeys_1 = require("./accesscontrol/grantKeys");
|
|
10
10
|
const types_2 = require("./accesscontrol/types");
|
|
11
|
+
const builtinDbInvariants_1 = require("./builtinDbInvariants");
|
|
11
12
|
const DirectoryStateNode_1 = require("./accesscontrol/DirectoryStateNode");
|
|
12
13
|
const DirectoryTimeTravelIndex_1 = require("./accesscontrol/DirectoryTimeTravelIndex");
|
|
13
14
|
const directoryProjection_1 = require("./accesscontrol/directoryProjection");
|
|
@@ -1321,6 +1322,10 @@ class BaseMindooTenantDirectory {
|
|
|
1321
1322
|
* server to gate all sync operations for a database id.
|
|
1322
1323
|
*/
|
|
1323
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) {
|
|
1324
1329
|
// Gate 1 — tenant-wide database-id allowlist (`databaseCreationPolicy`).
|
|
1325
1330
|
// Orthogonal to read access: a database must clear BOTH gates.
|
|
1326
1331
|
if (!(await this.isDatabaseAllowed(input.dbid, { signingKey: input.signingKey }))) {
|
|
@@ -1348,6 +1353,261 @@ class BaseMindooTenantDirectory {
|
|
|
1348
1353
|
});
|
|
1349
1354
|
return decision.allowed;
|
|
1350
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
|
+
}
|
|
1351
1611
|
/**
|
|
1352
1612
|
* Whether the current user may open and sync `dbid` under the database read
|
|
1353
1613
|
* gate (`doc_read`, §6.6). The client-side counterpart of
|
|
@@ -1364,6 +1624,10 @@ class BaseMindooTenantDirectory {
|
|
|
1364
1624
|
const adminKey = this.tenant.getAdministrationPublicKey();
|
|
1365
1625
|
if (currentUser.userSigningPublicKey === adminKey)
|
|
1366
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;
|
|
1367
1631
|
const decision = await this.canDo("doc_read", dbid);
|
|
1368
1632
|
return decision.allowed;
|
|
1369
1633
|
}
|
|
@@ -1382,12 +1646,30 @@ class BaseMindooTenantDirectory {
|
|
|
1382
1646
|
(0, types_3.validateAccessPolicy)(policy);
|
|
1383
1647
|
await this.writePolicyDoc(types_3.ACL_DEFAULT_POLICY_DOC_ID, policy, administrationPrivateKey, administrationPrivateKeyPassword);
|
|
1384
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
|
+
}
|
|
1385
1664
|
/**
|
|
1386
1665
|
* Create or update a per-database policy override (§6.2). Fields set here
|
|
1387
1666
|
* override the tenant default for `dbid`; unset fields inherit it.
|
|
1388
1667
|
*/
|
|
1389
1668
|
async setDatabaseAccessPolicy(dbid, policy, administrationPrivateKey, administrationPrivateKeyPassword) {
|
|
1390
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
|
+
}
|
|
1391
1673
|
if (policy.llm !== undefined) {
|
|
1392
1674
|
throw new Error("llm is a tenant-level setting; set it on the default policy");
|
|
1393
1675
|
}
|
|
@@ -1439,6 +1721,8 @@ class BaseMindooTenantDirectory {
|
|
|
1439
1721
|
groups: rule.groups ?? [],
|
|
1440
1722
|
}),
|
|
1441
1723
|
action: rule.action ?? "allow",
|
|
1724
|
+
...(rule.maxRecipients !== undefined ? { maxRecipients: rule.maxRecipients } : {}),
|
|
1725
|
+
...(rule.docIdPrefixes !== undefined ? { docIdPrefixes: [...new Set(rule.docIdPrefixes)] } : {}),
|
|
1442
1726
|
};
|
|
1443
1727
|
// Fail fast on malformed clauses / operators (§6.3 closed set).
|
|
1444
1728
|
(0, types_3.validateAclRule)(ruleDoc);
|
|
@@ -1462,6 +1746,14 @@ class BaseMindooTenantDirectory {
|
|
|
1462
1746
|
data.description = ruleDoc.description;
|
|
1463
1747
|
if (ruleDoc.withfields !== undefined)
|
|
1464
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;
|
|
1465
1757
|
}, {
|
|
1466
1758
|
signingKeyPair: adminSigningKeyPair,
|
|
1467
1759
|
signingKeyPassword: administrationPrivateKeyPassword,
|