@filelayer/core 0.3.0

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.
Files changed (69) hide show
  1. package/CHANGELOG.md +338 -0
  2. package/LICENSE +202 -0
  3. package/MIGRATIONS.md +328 -0
  4. package/NOTICE +37 -0
  5. package/README.md +343 -0
  6. package/SEMANTICS.md +729 -0
  7. package/dist/authz.d.ts +524 -0
  8. package/dist/authz.d.ts.map +1 -0
  9. package/dist/authz.js +889 -0
  10. package/dist/authz.js.map +1 -0
  11. package/dist/db.d.ts +145 -0
  12. package/dist/db.d.ts.map +1 -0
  13. package/dist/db.js +217 -0
  14. package/dist/db.js.map +1 -0
  15. package/dist/delivery.d.ts +293 -0
  16. package/dist/delivery.d.ts.map +1 -0
  17. package/dist/delivery.js +519 -0
  18. package/dist/delivery.js.map +1 -0
  19. package/dist/errors.d.ts +16 -0
  20. package/dist/errors.d.ts.map +1 -0
  21. package/dist/errors.js +21 -0
  22. package/dist/errors.js.map +1 -0
  23. package/dist/filelayer.d.ts +542 -0
  24. package/dist/filelayer.d.ts.map +1 -0
  25. package/dist/filelayer.js +1360 -0
  26. package/dist/filelayer.js.map +1 -0
  27. package/dist/index.d.ts +8 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +8 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/simple.d.ts +297 -0
  32. package/dist/simple.d.ts.map +1 -0
  33. package/dist/simple.js +492 -0
  34. package/dist/simple.js.map +1 -0
  35. package/dist/storage.d.ts +269 -0
  36. package/dist/storage.d.ts.map +1 -0
  37. package/dist/storage.js +700 -0
  38. package/dist/storage.js.map +1 -0
  39. package/dist/store.d.ts +432 -0
  40. package/dist/store.d.ts.map +1 -0
  41. package/dist/store.js +862 -0
  42. package/dist/store.js.map +1 -0
  43. package/package.json +77 -0
  44. package/schema.sql +1190 -0
  45. package/src/authz.ts +1398 -0
  46. package/src/db.ts +271 -0
  47. package/src/delivery.ts +737 -0
  48. package/src/errors.ts +24 -0
  49. package/src/filelayer.ts +1836 -0
  50. package/src/index.ts +7 -0
  51. package/src/simple.ts +666 -0
  52. package/src/storage.ts +917 -0
  53. package/src/store.ts +1072 -0
  54. package/test/delivery.test.ts +0 -0
  55. package/test/group-subjects.test.ts +1072 -0
  56. package/test/helpers.ts +65 -0
  57. package/test/listing.test.ts +689 -0
  58. package/test/local-s3.d.mts +33 -0
  59. package/test/local-s3.mjs +400 -0
  60. package/test/persistence.test.ts +953 -0
  61. package/test/regression.test.ts +619 -0
  62. package/test/s3-live.test.ts +322 -0
  63. package/test/security.test.ts +1652 -0
  64. package/test/semantics.test.ts +888 -0
  65. package/test/storage.test.ts +437 -0
  66. package/test/tiers.test.ts +432 -0
  67. package/test/vault-example.test.ts +302 -0
  68. package/tsconfig.build.json +29 -0
  69. package/tsconfig.json +19 -0
package/dist/simple.js ADDED
@@ -0,0 +1,492 @@
1
+ /**
2
+ * PROGRESSIVE DISCLOSURE -- the tiered surface.
3
+ *
4
+ * This file exists to answer one question:
5
+ *
6
+ * "A developer who only needs a public avatar should be able to use an
7
+ * extremely simple version. A developer who needs a multi-tenant document
8
+ * system should be able to turn on the advanced capabilities."
9
+ *
10
+ * WHAT THIS FILE IS
11
+ * -----------------
12
+ * A thin, additive facade over `Filelayer`. It provisions the concepts the
13
+ * developer did not ask for (a workspace, a service identity, a membership) so
14
+ * that the trivial case costs one line, and it names the concepts the developer
15
+ * DID ask for (an owner, an org, a role) so that the advanced case is reachable
16
+ * by adding an option rather than by rewriting.
17
+ *
18
+ * WHAT THIS FILE IS NOT
19
+ * ---------------------
20
+ * It contains **no authorization logic**. Every call below goes through the same
21
+ * `authz.ts` engine as the full API, with the same audit guarantee. Grep it: the
22
+ * only `if` statements are about which identifier to resolve, never about
23
+ * whether access is permitted.
24
+ *
25
+ * THE FOUR SECURITY PROPERTIES, AND HOW EACH SURVIVES THE ERGONOMICS
26
+ * -----------------------------------------------------------------
27
+ * P1 (deny by default, no public boolean).
28
+ * `{ public: true }` does NOT set a flag. It creates an `anonymous` grant
29
+ * row -- the same row `share({subject:{type:'anonymous'}})` creates -- which
30
+ * is explicit, listable, revocable and audited. `unpublish()` revokes it.
31
+ * There is still no `file.public` column and there never will be.
32
+ *
33
+ * P2 (no ambient authority).
34
+ * Auto-provisioning creates IDENTITIES, never PERMISSIONS beyond the file
35
+ * the caller is creating. A user auto-registered by `put({owner})` gets the
36
+ * `member` role, and `member` + the default `private` visibility means they
37
+ * can read exactly their own files and nothing else. Reads never
38
+ * auto-provision: an unknown `as:` is a 404, not a silent downgrade to
39
+ * anonymous. That last rule is the one that would have been easy to get
40
+ * wrong and is tested explicitly.
41
+ *
42
+ * P3 (tenant isolation is structural).
43
+ * Untouched. Every file still has a real `org_id`. The "no org" experience
44
+ * is a DEFAULT org, not a NULL one -- see ARCHITECTURE-PROGRESSIVE.md for
45
+ * the measurement showing that a nullable `org_id` silently disables the
46
+ * composite foreign key that P3 rests on.
47
+ *
48
+ * P4 (a URL never outlives its permission).
49
+ * The public URL is `/f/:id` served by our own delivery path, which
50
+ * re-authorizes on every request. Revoke the anonymous grant and the URL is
51
+ * dead on the next request. This is the property a public S3 bucket and
52
+ * Supabase's `getPublicUrl` cannot offer at any price. It is ALSO the
53
+ * property that CDN caching would weaken -- see `delivery.ts`, which
54
+ * defaults to `no-store` for that reason.
55
+ */
56
+ import { FilelayerError, } from "./filelayer.js";
57
+ import { DEFAULT_PROJECT_ID } from "./store.js";
58
+ /**
59
+ * The org every file lands in when the developer never mentioned an org.
60
+ *
61
+ * A reserved `external_id`, not a magic NULL. Tier 3 is reached by naming a
62
+ * different org, not by turning a boundary on.
63
+ */
64
+ export const DEFAULT_WORKSPACE = '__filelayer_workspace__';
65
+ /**
66
+ * The identity that owns files uploaded with no `owner:`.
67
+ *
68
+ * It is the `owner` of the default workspace (so tier 1 works with no user
69
+ * model at all) and a plain `member` of any org the developer names (so it does
70
+ * NOT acquire admin read over a real tenant's documents as a side effect of
71
+ * being used for an unowned upload).
72
+ */
73
+ export const SYSTEM_ACTOR = '__filelayer_system__';
74
+ // -----------------------------------------------------------------------------
75
+ // Content sniffing
76
+ // -----------------------------------------------------------------------------
77
+ //
78
+ // Deliberately a short, conservative list of formats with unambiguous magic
79
+ // bytes. Everything else is `application/octet-stream`, which -- combined with
80
+ // the `X-Content-Type-Options: nosniff` and sandbox CSP headers in delivery.ts
81
+ // -- means an unrecognised upload is downloaded rather than executed. SVG is
82
+ // absent on purpose: it has no reliable magic number and it is a script
83
+ // execution context. Pass `contentType` explicitly if you need one.
84
+ const MAGIC = [
85
+ [[0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a], 'image/png'],
86
+ [[0xff, 0xd8, 0xff], 'image/jpeg'],
87
+ [[0x47, 0x49, 0x46, 0x38], 'image/gif'],
88
+ [[0x25, 0x50, 0x44, 0x46], 'application/pdf'],
89
+ ];
90
+ export function sniffContentType(body) {
91
+ for (const [sig, type] of MAGIC) {
92
+ if (sig.every((b, i) => body[i] === b))
93
+ return type;
94
+ }
95
+ // RIFF....WEBP
96
+ if (body[0] === 0x52 && body[1] === 0x49 && body[2] === 0x46 && body[3] === 0x46 &&
97
+ body[8] === 0x57 && body[9] === 0x45 && body[10] === 0x42 && body[11] === 0x50) {
98
+ return 'image/webp';
99
+ }
100
+ return 'application/octet-stream';
101
+ }
102
+ class Identities {
103
+ fl;
104
+ constructor(fl) {
105
+ this.fl = fl;
106
+ }
107
+ /** The project this facade resolves identities in. See P8 in schema.sql. */
108
+ get project() {
109
+ return this.fl.projectId ?? DEFAULT_PROJECT_ID;
110
+ }
111
+ /**
112
+ * Get-or-create an org by the customer's own id.
113
+ *
114
+ * `ON CONFLICT (project_id, external_id)` rather than a read-then-write, so
115
+ * two cold requests racing to publish the first avatar cannot produce a
116
+ * duplicate-key error in the developer's face.
117
+ *
118
+ * P8 (THE CUSTOMER'S ID SPACE IS THE CUSTOMER'S), AND THIS IS THE SHARPEST
119
+ * EDGE OF IT. When `external_id` was globally
120
+ * unique, this exact statement was a cross-tenant compromise: customer B
121
+ * calling `put({ org: 'acme' })` did not get an error, it got customer A's
122
+ * org id -- and `membership()` below then added B's user to A's tenant. The
123
+ * conflict target is now the (project, external_id) pair, so "acme" in one
124
+ * customer's application and "acme" in another are different rows that can
125
+ * never resolve to each other.
126
+ */
127
+ async org(externalId, opts = {}) {
128
+ const { rows } = await this.fl.store.db.query(`INSERT INTO org (project_id, external_id, name) VALUES ($3, $1, $2)
129
+ ON CONFLICT (project_id, external_id) DO UPDATE SET external_id = EXCLUDED.external_id
130
+ RETURNING id`, [externalId, opts.name ?? externalId, this.project]);
131
+ const id = rows[0].id;
132
+ if (opts.ownerActorId)
133
+ await this.membership(id, opts.ownerActorId, 'owner', true);
134
+ return id;
135
+ }
136
+ /**
137
+ * Get-or-create an identity by the customer's own id, within this project.
138
+ *
139
+ * FAILS CLOSED ON A DELETED IDENTITY. Auto-provisioning must never resurrect
140
+ * someone who has been deleted: `ON CONFLICT DO UPDATE` would otherwise hand
141
+ * back a soft-deleted actor's id, and the caller would go on to create a file
142
+ * owned by a person the system has been told no longer exists. Access would
143
+ * still be denied downstream (P7 kills their role and their grants), so the
144
+ * failure is fail-closed either way -- but silently owning rows to a deleted
145
+ * identity is a data-integrity mess, not a security decision we should be
146
+ * making by accident.
147
+ */
148
+ async actor(externalId) {
149
+ const { rows } = await this.fl.store.db.query(`INSERT INTO actor (project_id, external_id) VALUES ($2, $1)
150
+ ON CONFLICT (project_id, external_id) DO UPDATE SET external_id = EXCLUDED.external_id
151
+ RETURNING id, deleted_at`, [externalId, this.project]);
152
+ const row = rows[0];
153
+ if (row.deleted_at !== null) {
154
+ throw new FilelayerError(404, 'not_found', 'deleted_actor');
155
+ }
156
+ return row.id;
157
+ }
158
+ /** Read-only lookup. Returns null for an unknown id -- callers must fail closed. */
159
+ async findActor(externalId) {
160
+ const { rows } = await this.fl.store.db.query(`SELECT id FROM actor
161
+ WHERE external_id = $1 AND project_id = $2 AND deleted_at IS NULL`, [externalId, this.project]);
162
+ return rows[0]?.id ?? null;
163
+ }
164
+ /**
165
+ * Ensure a membership exists, WITHOUT ever downgrading an existing one.
166
+ *
167
+ * `DO NOTHING` and not `DO UPDATE`: auto-provisioning must never be able to
168
+ * demote an admin a developer deliberately promoted, nor promote a member.
169
+ * Deliberate role changes go through `orgs.setRole`, which is authorized.
170
+ */
171
+ async membership(orgId, actorId, role, bootstrap = false) {
172
+ const { rows } = await this.fl.store.db.query(`INSERT INTO membership (org_id, actor_id, role) VALUES ($1,$2,$3)
173
+ ON CONFLICT (org_id, actor_id) DO NOTHING
174
+ RETURNING true AS inserted`, [orgId, actorId, role]);
175
+ // P5: an identity acquiring standing in an org is a privilege change and is
176
+ // audited like any other, even though it was implicit.
177
+ if (rows.length > 0) {
178
+ await this.fl.store.audit({
179
+ orgId,
180
+ action: bootstrap ? 'member.bootstrap' : 'member.add',
181
+ decision: 'allow',
182
+ actorId,
183
+ fileId: null,
184
+ context: { targetActorId: actorId, toRole: role, via: 'auto_provision' },
185
+ });
186
+ }
187
+ }
188
+ }
189
+ // -----------------------------------------------------------------------------
190
+ // files.*
191
+ // -----------------------------------------------------------------------------
192
+ export class FilesApi {
193
+ fl;
194
+ ids;
195
+ constructor(fl) {
196
+ this.fl = fl;
197
+ this.ids = new Identities(fl);
198
+ }
199
+ /**
200
+ * TIER 1: `put(bytes, { public: true })` -> a URL, no other concepts.
201
+ * TIER 2: `put(bytes, { owner: userId })` -> adds an owner.
202
+ * TIER 3: `put(bytes, { org, owner })` -> adds a tenant.
203
+ * TIER 4: `+ expiresIn / retainFor / metadata`
204
+ */
205
+ async put(body, opts = {}) {
206
+ const contentType = opts.contentType ?? sniffContentType(body);
207
+ const name = opts.name ?? 'file';
208
+ // Resolve the tenant. Naming an org is what promotes you from tier 2 to
209
+ // tier 3; not naming one puts you in the default workspace, which is a real
210
+ // org with a real id, not a hole in the model.
211
+ const isDefaultWorkspace = opts.org === undefined;
212
+ const system = await this.ids.actor(SYSTEM_ACTOR);
213
+ const orgId = isDefaultWorkspace
214
+ ? await this.ids.org(DEFAULT_WORKSPACE, { name: 'workspace', ownerActorId: system })
215
+ : await this.ids.org(opts.org);
216
+ // Resolve the owner. `owner:` auto-registers, because the developer is
217
+ // asserting the identity exists in their system; we are not inventing it.
218
+ let ownerId;
219
+ if (opts.owner !== undefined) {
220
+ ownerId = await this.ids.actor(opts.owner);
221
+ await this.ids.membership(orgId, ownerId, 'member');
222
+ }
223
+ else {
224
+ ownerId = system;
225
+ // In a NAMED org the service identity is only a `member`, so an unowned
226
+ // upload into a customer tenant does not hand us admin read over that
227
+ // tenant's documents. In the default workspace it is the owner, because
228
+ // somebody has to be.
229
+ if (!isDefaultWorkspace)
230
+ await this.ids.membership(orgId, system, 'member');
231
+ }
232
+ // The uploader is a Principal, not a bare id: ownership is derived from the
233
+ // authorized identity, never supplied beside it. See the note on the
234
+ // signature of `upload()` in filelayer.ts.
235
+ const file = await this.fl.upload({ actorId: ownerId }, orgId, {
236
+ name,
237
+ contentType,
238
+ body,
239
+ ...(opts.visibility ? { visibility: opts.visibility } : {}),
240
+ ...(opts.expiresIn !== undefined ? { expiresIn: opts.expiresIn } : {}),
241
+ ...(opts.retainFor !== undefined ? { retainFor: opts.retainFor } : {}),
242
+ ...(opts.metadata ? { metadata: opts.metadata } : {}),
243
+ });
244
+ let url;
245
+ if (opts.public) {
246
+ // P1 intact: this is a grant row, not a flag. The uploader mints it, so
247
+ // it goes through `authorizeShare` and is audited as `grant.create`.
248
+ await this.fl.share({ actorId: ownerId }, file.id, { subject: { type: 'anonymous' } });
249
+ url = this.publicUrl(file.id);
250
+ }
251
+ return {
252
+ id: file.id,
253
+ name: file.name,
254
+ contentType: file.contentType,
255
+ size: file.sizeBytes ?? body.byteLength,
256
+ ...(url ? { url } : {}),
257
+ };
258
+ }
259
+ /**
260
+ * Read a file as a user (`{ as }`), or anonymously (no options).
261
+ *
262
+ * `headers` is carried through from the delivery layer so that the simple
263
+ * tier is exactly as safe as the full one. A tier-2 app that writes
264
+ * `res.writeHead(200, { 'content-type': f.contentType })` re-opens the
265
+ * stored-XSS path; `res.writeHead(200, f.headers)` does not.
266
+ */
267
+ async get(fileId, opts = {}) {
268
+ const principal = await this.principal(opts);
269
+ const { file, body, headers } = await this.fl.read(principal, fileId);
270
+ return { id: file.id, name: file.name, contentType: file.contentType, body, headers };
271
+ }
272
+ async delete(fileId, opts = {}) {
273
+ await this.fl.delete(await this.principal(opts), fileId);
274
+ }
275
+ /** Make an existing file publicly readable. Same grant row as `put({public:true})`. */
276
+ async publish(fileId, opts = {}) {
277
+ const principal = await this.principal(opts, { orSystem: true });
278
+ const g = await this.fl.share(principal, fileId, { subject: { type: 'anonymous' } });
279
+ return { url: this.publicUrl(fileId), grantId: g.grantId };
280
+ }
281
+ /**
282
+ * Withdraw public access. Revokes every live anonymous grant on the file.
283
+ *
284
+ * This is the operation a public bucket cannot perform: after it returns, a
285
+ * URL that has been printed, indexed and shared stops working on the very
286
+ * next request, because delivery re-authorizes every time.
287
+ */
288
+ async unpublish(fileId, opts = {}) {
289
+ const principal = await this.principal(opts, { orSystem: true });
290
+ const grants = await this.fl.listGrants(principal, fileId);
291
+ let revoked = 0;
292
+ for (const g of grants) {
293
+ if (g.subjectType !== 'anonymous' || !g.live)
294
+ continue;
295
+ await this.fl.revoke(principal, g.id);
296
+ revoked++;
297
+ }
298
+ return { revoked };
299
+ }
300
+ /**
301
+ * The delivery URL for a public file. Meaningless without an anonymous grant.
302
+ *
303
+ * IT ALWAYS BUILDS ON `/f`, so whatever serves it has to be mounted there.
304
+ * `deliveryHandler(fl)` is -- its `filePrefix` defaults to `/f`. But
305
+ * `fileDownloadRoute(fl, { principal })` mounted on its own defaults to a
306
+ * `prefix` of `/files`, and a URL from here would 404 against it. Either
307
+ * mount `deliveryHandler`, or pass `prefix: '/f'` to `fileDownloadRoute`.
308
+ */
309
+ publicUrl(fileId) {
310
+ return `${this.fl.baseUrl ?? ''}/f/${fileId}`;
311
+ }
312
+ /**
313
+ * Metadata only, no bytes, authorized identically to `get`.
314
+ *
315
+ * This used to call `read()` (fetching the whole object) and then
316
+ * `getFileRecord()` (unauthorized, and now private). It calls the authorized
317
+ * `stat()` instead, which means a metadata lookup no longer moves the bytes
318
+ * and -- since the cap counts bytes leaving (P6) -- no longer spends a
319
+ * download from a capped grant.
320
+ */
321
+ async stat(fileId, opts = {}) {
322
+ return this.fl.stat(await this.principal(opts), fileId);
323
+ }
324
+ /**
325
+ * External user id -> Principal.
326
+ *
327
+ * FAILS CLOSED. An `as:` we cannot resolve is 404, never a silent demotion to
328
+ * an anonymous principal -- which would turn a typo in a user id into a read
329
+ * of every public file, and, worse, would make the audit log attribute it to
330
+ * nobody.
331
+ */
332
+ async principal(opts, cfg = {}) {
333
+ if (opts.as === undefined) {
334
+ if (!cfg.orSystem)
335
+ return { actorId: null };
336
+ // publish/unpublish with no `as` is a server-side administrative action in
337
+ // the default workspace, performed by the service identity that owns it.
338
+ return { actorId: await this.ids.actor(SYSTEM_ACTOR) };
339
+ }
340
+ const actorId = await this.ids.findActor(opts.as);
341
+ if (!actorId)
342
+ throw new FilelayerError(404, 'not_found', 'unknown_actor');
343
+ return { actorId };
344
+ }
345
+ }
346
+ // -----------------------------------------------------------------------------
347
+ // orgs.* -- tier 3
348
+ // -----------------------------------------------------------------------------
349
+ export class OrgsApi {
350
+ fl;
351
+ ids;
352
+ constructor(fl) {
353
+ this.fl = fl;
354
+ this.ids = new Identities(fl);
355
+ }
356
+ /**
357
+ * Create a tenant with its first owner. Idempotent.
358
+ *
359
+ * The owner is created WITH the org for the same reason `createOrg` does it:
360
+ * there is never a memberless org for someone to walk into.
361
+ */
362
+ async create(externalId, opts) {
363
+ const ownerActorId = await this.ids.actor(opts.owner);
364
+ const id = await this.ids.org(externalId, {
365
+ ...(opts.name ? { name: opts.name } : {}),
366
+ ownerActorId,
367
+ });
368
+ return { id };
369
+ }
370
+ /**
371
+ * Add or change a member's role.
372
+ *
373
+ * `as` is REQUIRED and is not defaulted. Membership is the privilege that
374
+ * confers every other privilege; there is no convenience worth an unauthorized
375
+ * path to it. This is the one place where the tiered API is deliberately no
376
+ * shorter than the full API.
377
+ */
378
+ async setRole(org, user, role, opts) {
379
+ const { orgId, actorId, principal } = await this.trio(org, user, opts.as);
380
+ await this.fl.addMember(principal, orgId, actorId, role);
381
+ }
382
+ async removeMember(org, user, opts) {
383
+ const { orgId, actorId, principal } = await this.trio(org, user, opts.as);
384
+ await this.fl.removeMember(principal, orgId, actorId);
385
+ }
386
+ /** Audit trail for a tenant. Requires `read_audit`, i.e. admin or owner. */
387
+ async audit(org, opts) {
388
+ const orgId = await this.requireOrg(org);
389
+ const principal = await this.requirePrincipal(opts.as);
390
+ return this.fl.auditLog(principal, orgId, {
391
+ ...(opts.decision ? { decision: opts.decision } : {}),
392
+ ...(opts.limit ? { limit: opts.limit } : {}),
393
+ });
394
+ }
395
+ async verifyAudit(org, opts) {
396
+ const orgId = await this.requireOrg(org);
397
+ return this.fl.verifyAuditChain(await this.requirePrincipal(opts.as), orgId);
398
+ }
399
+ async trio(org, user, as) {
400
+ const orgId = await this.requireOrg(org);
401
+ const actorId = await this.ids.actor(user); // the TARGET may be new
402
+ const principal = await this.requirePrincipal(as); // the CALLER may not
403
+ return { orgId, actorId, principal };
404
+ }
405
+ async requireOrg(externalId) {
406
+ const { rows } = await this.fl.store.db.query(`SELECT o.id FROM org o
407
+ JOIN project p ON p.id = o.project_id
408
+ WHERE o.external_id = $1
409
+ AND o.project_id = $2
410
+ AND o.deleted_at IS NULL
411
+ AND p.deleted_at IS NULL`, [externalId, this.fl.projectId ?? DEFAULT_PROJECT_ID]);
412
+ if (!rows[0])
413
+ throw new FilelayerError(404, 'not_found', 'unknown_org');
414
+ return rows[0].id;
415
+ }
416
+ async requirePrincipal(as) {
417
+ const actorId = await this.ids.findActor(as);
418
+ if (!actorId)
419
+ throw new FilelayerError(404, 'not_found', 'unknown_actor');
420
+ return { actorId };
421
+ }
422
+ }
423
+ export class SharesApi {
424
+ fl;
425
+ ids;
426
+ constructor(fl) {
427
+ this.fl = fl;
428
+ this.ids = new Identities(fl);
429
+ }
430
+ async create(fileId, opts) {
431
+ const actorId = await this.ids.findActor(opts.as);
432
+ if (!actorId)
433
+ throw new FilelayerError(404, 'not_found', 'unknown_actor');
434
+ if (opts.withUser && opts.withOrg) {
435
+ throw new FilelayerError(400, 'ambiguous_subject', 'withUser_and_withOrg');
436
+ }
437
+ if (opts.minRole && !opts.withOrg) {
438
+ throw new FilelayerError(400, 'ambiguous_subject', 'minRole_without_withOrg');
439
+ }
440
+ const subject = opts.withUser
441
+ ? { type: 'actor', actorId: await this.ids.actor(opts.withUser) }
442
+ : opts.withOrg
443
+ ? // The subject org must ALREADY exist. `withOrg` is not a
444
+ // get-or-create: auto-provisioning a tenant here would mean a typo in
445
+ // an org name silently mints a grant to an empty organization that
446
+ // anybody could later be added to, which is a permission granted to a
447
+ // population nobody has audited. Unknown org is 404, like `as:`.
448
+ opts.minRole
449
+ ? { type: 'role', orgId: await this.requireOrgId(opts.withOrg), minRole: opts.minRole }
450
+ : { type: 'org', orgId: await this.requireOrgId(opts.withOrg) }
451
+ : { type: 'link' };
452
+ return this.fl.share({ actorId }, fileId, {
453
+ subject,
454
+ ...(opts.capabilities ? { capabilities: opts.capabilities } : {}),
455
+ ...(opts.expiresIn !== undefined ? { expiresIn: opts.expiresIn } : {}),
456
+ ...(opts.maxDownloads !== undefined ? { maxDownloads: opts.maxDownloads } : {}),
457
+ ...(opts.password !== undefined ? { password: opts.password } : {}),
458
+ });
459
+ }
460
+ async revoke(grantId, opts) {
461
+ const actorId = await this.ids.findActor(opts.as);
462
+ if (!actorId)
463
+ throw new FilelayerError(404, 'not_found', 'unknown_actor');
464
+ return this.fl.revoke({ actorId }, grantId);
465
+ }
466
+ async list(fileId, opts) {
467
+ const actorId = await this.ids.findActor(opts.as);
468
+ if (!actorId)
469
+ throw new FilelayerError(404, 'not_found', 'unknown_actor');
470
+ return this.fl.listGrants({ actorId }, fileId);
471
+ }
472
+ /** Redeem a share link. No identity required -- the secret is the credential. */
473
+ async redeem(secret, opts = {}) {
474
+ return this.fl.redeem(secret, opts);
475
+ }
476
+ /**
477
+ * External org id -> internal id, within this project. Read-only and
478
+ * fail-closed, exactly like `Identities.findActor`.
479
+ */
480
+ async requireOrgId(externalId) {
481
+ const { rows } = await this.fl.store.db.query(`SELECT o.id FROM org o
482
+ JOIN project p ON p.id = o.project_id
483
+ WHERE o.external_id = $1
484
+ AND o.project_id = $2
485
+ AND o.deleted_at IS NULL
486
+ AND p.deleted_at IS NULL`, [externalId, this.fl.projectId ?? DEFAULT_PROJECT_ID]);
487
+ if (!rows[0])
488
+ throw new FilelayerError(404, 'not_found', 'unknown_org');
489
+ return rows[0].id;
490
+ }
491
+ }
492
+ //# sourceMappingURL=simple.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"simple.js","sourceRoot":"","sources":["../src/simple.ts"],"names":[],"mappings":"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"}