@3sln/trove 0.0.5 → 0.0.8
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.md +6 -0
- package/package.json +1 -1
- package/packages/core/src/collections/index.js +146 -1
- package/packages/core/src/encryption/envelope.js +444 -0
- package/packages/core/src/encryption/exposure.js +101 -0
- package/packages/core/src/encryption/keys.js +88 -0
- package/packages/core/src/encryption/policy.js +112 -0
- package/packages/core/src/encryption/rotation.js +313 -0
- package/packages/core/src/index.js +17 -1
- package/packages/core/src/links.js +85 -0
- package/packages/core/src/metadata/memory.js +3 -1
- package/packages/core/src/metadata/sqlite.js +27 -7
- package/packages/core/src/scan.js +35 -1
- package/packages/core/src/storage/cost.js +228 -0
- package/packages/core/src/storage/drivers.js +13 -4
- package/packages/core/src/storage/registry.js +37 -4
- package/packages/core/src/uploads.js +197 -15
- package/packages/core/src/vfs.js +148 -9
- package/packages/server/src/engine/providers/core.js +38 -3
- package/packages/server/src/index.js +8 -0
- package/packages/server/src/routes.js +29 -1
- package/packages/web/dist/assets/main-y778bpte.js +356 -0
- package/packages/web/dist/assets/{main-f0f2tfhp.js.map → main-y778bpte.js.map} +10 -8
- package/packages/web/dist/assets/styles-nfy8t3n1.css +1 -0
- package/packages/web/dist/index.html +9 -3
- package/packages/web/dist/sw.js +1 -1
- package/packages/web/src/bl/actions.js +20 -4
- package/packages/web/src/bl/services.js +64 -4
- package/packages/web/src/platform/api.js +134 -11
- package/packages/web/src/styles.css +3 -0
- package/packages/web/src/ui/components/overlays.js +13 -0
- package/packages/web/dist/assets/main-f0f2tfhp.js +0 -356
- package/packages/web/dist/assets/styles-d3cyysgp.css +0 -1
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import { Router, json, parseRange } from './router.js';
|
|
6
6
|
import {
|
|
7
7
|
TroveError, assertSafePluginSql, concatBytes, metadataUrl, publicOrigin,
|
|
8
|
+
shouldEncrypt,
|
|
8
9
|
} from '@3sln/trove/core';
|
|
9
10
|
import { parseContribUri, CORE_DOMAIN } from '@3sln/trove/core/plugins/identity.js';
|
|
10
11
|
|
|
@@ -444,10 +445,20 @@ export function createRouter() {
|
|
|
444
445
|
|
|
445
446
|
// --- uploads ---------------------------------------------------------------
|
|
446
447
|
|
|
447
|
-
r.post('/api/collections/:collection/uploads', [], async (ctx) => {
|
|
448
|
+
r.post('/api/collections/:collection/uploads', ['collections', 'vfs'], async (ctx) => {
|
|
448
449
|
const b = await body(ctx.req);
|
|
449
450
|
if (!b.name) throw TroveError.invalid('name is required');
|
|
450
451
|
const collection = await ctx.access.collection(scopedCollection(ctx), 'write');
|
|
452
|
+
// An upload onto an encrypted collection is handed the collection's key, so the client
|
|
453
|
+
// can seal the bytes before they reach the bucket. That key decrypts EVERYTHING in the
|
|
454
|
+
// collection, which makes it a read capability however it arrives — and `write` does
|
|
455
|
+
// not imply `read` here (only `admin` expands). Without this, a write-only API key,
|
|
456
|
+
// which the key model explicitly supports, could ask for a plan for a one-byte file and
|
|
457
|
+
// receive the means to decrypt the whole collection.
|
|
458
|
+
//
|
|
459
|
+
// Refused rather than quietly narrowed to a plaintext upload: silently storing in the
|
|
460
|
+
// clear on a collection someone set up to be encrypted is the worse failure.
|
|
461
|
+
await assertReadIfKeyed(ctx, b);
|
|
451
462
|
return uploadDescriptor(await collection.createUpload({
|
|
452
463
|
name: b.name, size: Number(b.size ?? 0), contentType: b.contentType,
|
|
453
464
|
overwrite: b.overwrite === true,
|
|
@@ -1150,6 +1161,23 @@ async function assertTaskAccess(ctx, task, what) {
|
|
|
1150
1161
|
* manage API keys, however broadly it was scoped, because a key that can mint keys can
|
|
1151
1162
|
* outlive its own revocation. Then the ordinary admin check on the principal.
|
|
1152
1163
|
*/
|
|
1164
|
+
|
|
1165
|
+
/**
|
|
1166
|
+
* An upload plan that will carry the collection key needs `read`, not merely `write`.
|
|
1167
|
+
*
|
|
1168
|
+
* Checked before the session is created, so a refusal costs nothing and leaves no orphan.
|
|
1169
|
+
*/
|
|
1170
|
+
async function assertReadIfKeyed(ctx, body) {
|
|
1171
|
+
if (!ctx.collections?.encryptionFor) return;
|
|
1172
|
+
const collectionId = scopedCollection(ctx);
|
|
1173
|
+
const encryption = await ctx.collections.encryptionFor(collectionId);
|
|
1174
|
+
if (!encryption?.enabled) return;
|
|
1175
|
+
const contentType = body.contentType || ctx.vfs?.guessContentType?.(body.name) || '';
|
|
1176
|
+
if (!shouldEncrypt(encryption, { name: body.name, contentType })) return;
|
|
1177
|
+
// Throws if the caller does not hold read on this collection.
|
|
1178
|
+
await ctx.access.collection(collectionId, 'read');
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1153
1181
|
function requireHumanAdmin(ctx, action) {
|
|
1154
1182
|
if (ctx.grant) {
|
|
1155
1183
|
throw TroveError.forbidden(`An API key cannot ${action} — sign in as an administrator`);
|