@nacre.work/api 0.6.0 → 0.8.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.
- package/dist/adapters.d.ts +29 -1
- package/dist/adapters.d.ts.map +1 -1
- package/dist/adapters.js +154 -13
- package/dist/adapters.js.map +1 -1
- package/dist/auth.d.ts +84 -4
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +74 -3
- package/dist/auth.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +2 -4
- package/dist/init.js.map +1 -1
- package/dist/main.js +4 -5
- package/dist/main.js.map +1 -1
- package/dist/oauth-store.d.ts +30 -5
- package/dist/oauth-store.d.ts.map +1 -1
- package/dist/oauth-store.js +64 -18
- package/dist/oauth-store.js.map +1 -1
- package/dist/rebuild-collection.js +2 -4
- package/dist/rebuild-collection.js.map +1 -1
- package/dist/server.d.ts +114 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +220 -14
- package/dist/server.js.map +1 -1
- package/package.json +2 -2
package/dist/adapters.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { buildFilter, VectorStore, type CacheStore, type Metadata } from '@nacre.work/core';
|
|
2
2
|
import type { Pool } from 'pg';
|
|
3
3
|
import { type Reranker } from './rerank.js';
|
|
4
|
-
import type { Reindex, ReindexOutcome, ReindexStatus, ReferenceQueries, ReferenceQuery, AuditEvent, AuditQuery, AuditReader, AuditRecord, AuditWriter, Documents, GrantInput, GrantRecord, Grants, Ingest, IngestOutcome, IngestRefused, IngestRequest, Job, Jobs, Layer, LayerOutcome, DocumentView, Layers, Page, PageResult, Workspace, WorkspaceOutcome, Workspaces, SearchHit, SearchOptions, SearchService } from './server.js';
|
|
4
|
+
import type { Reindex, ReindexOutcome, ReindexStatus, ReferenceQueries, ReferenceQuery, AuditEvent, AuditQuery, AuditReader, AuditRecord, AuditWriter, Documents, GrantInput, GrantRecord, Grants, Ingest, IngestOutcome, IngestRefused, IngestRequest, Job, Jobs, Layer, LayerOutcome, DocumentView, Layers, Page, PageResult, EmbeddingProvider as EmbeddingProviderView, EmbeddingProviderOutcome, EmbeddingProviders, Workspace, WorkspaceOutcome, Workspaces, SearchHit, SearchOptions, SearchService } from './server.js';
|
|
5
5
|
import { type AuthContext } from './auth.js';
|
|
6
6
|
/**
|
|
7
7
|
* The adapters that put the permission model on the request path.
|
|
@@ -407,6 +407,34 @@ export declare class PostgresLayers implements Layers {
|
|
|
407
407
|
* who closed that terminal had no way forward that did not involve reading the
|
|
408
408
|
* database directly.
|
|
409
409
|
*/
|
|
410
|
+
/**
|
|
411
|
+
* Embedding providers, which had no API at all.
|
|
412
|
+
*
|
|
413
|
+
* `init` wrote one row and nothing else ever did, so a deployment had exactly
|
|
414
|
+
* one model and the documented way to add a second was `psql` — the shape this
|
|
415
|
+
* repository has closed twice before, at the workspace listing and at
|
|
416
|
+
* `/v1/users`. The capability was already built everywhere else: the schema has
|
|
417
|
+
* carried `embedding_providers.org_id` since migration 0001, and the search
|
|
418
|
+
* path, the worker and the reindex machinery were all taught to honour it.
|
|
419
|
+
*
|
|
420
|
+
* The **database** narrows both operations and this class does not repeat it.
|
|
421
|
+
* `org_isolation` (0002) shows a caller their own organization's rows and the
|
|
422
|
+
* installation default; the three restrictive policies from 0019 require a
|
|
423
|
+
* write to name the caller's own organization, so a tenant cannot create,
|
|
424
|
+
* change or remove the global row whatever this code passes.
|
|
425
|
+
*/
|
|
426
|
+
export declare class PostgresEmbeddingProviders implements EmbeddingProviders {
|
|
427
|
+
private readonly pool;
|
|
428
|
+
private readonly role?;
|
|
429
|
+
constructor(pool: Pool, role?: string | undefined);
|
|
430
|
+
list(auth: AuthContext): Promise<readonly EmbeddingProviderView[]>;
|
|
431
|
+
create(auth: AuthContext, input: {
|
|
432
|
+
name: string;
|
|
433
|
+
endpoint: string;
|
|
434
|
+
model: string;
|
|
435
|
+
dimensions: number;
|
|
436
|
+
}): Promise<EmbeddingProviderOutcome>;
|
|
437
|
+
}
|
|
410
438
|
export declare class PostgresWorkspaces implements Workspaces {
|
|
411
439
|
private readonly pool;
|
|
412
440
|
private readonly role?;
|
package/dist/adapters.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapters.d.ts","sourceRoot":"","sources":["../src/adapters.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EAcX,WAAW,EAIX,KAAK,UAAU,EAEf,KAAK,QAAQ,
|
|
1
|
+
{"version":3,"file":"adapters.d.ts","sourceRoot":"","sources":["../src/adapters.ts"],"names":[],"mappings":"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"}
|
package/dist/adapters.js
CHANGED
|
@@ -2,7 +2,7 @@ import { buildFilter, cachedEffectivePrincipals, documentKey, effectivePrincipal
|
|
|
2
2
|
import { createHash } from 'node:crypto';
|
|
3
3
|
import { encodeCursor, pageOf } from './pagination.js';
|
|
4
4
|
import { applyRanking } from './rerank.js';
|
|
5
|
-
import { withinDelegation } from './auth.js';
|
|
5
|
+
import { administers, delegatedLayers, withinDelegation } from './auth.js';
|
|
6
6
|
export class PostgresDocuments {
|
|
7
7
|
pool;
|
|
8
8
|
payload;
|
|
@@ -58,7 +58,7 @@ export class PostgresDocuments {
|
|
|
58
58
|
if (plan.kind === 'none')
|
|
59
59
|
return undefined;
|
|
60
60
|
const { rows } = await client.query(`SELECT d.id, d.title, d.layer_id, l.slug AS layer, d.status, d.chunk_count,
|
|
61
|
-
d.updated_at, d.metadata, d.source_type, d.source_ref
|
|
61
|
+
d.updated_at, d.metadata, d.source_type, d.source_ref, d.error
|
|
62
62
|
FROM documents d
|
|
63
63
|
JOIN layers l ON l.id = d.layer_id AND l.org_id = d.org_id
|
|
64
64
|
WHERE d.org_id = $1 AND d.id = $2 AND d.deleted_at IS NULL`, [orgId, documentId]);
|
|
@@ -71,7 +71,7 @@ export class PostgresDocuments {
|
|
|
71
71
|
// the layer and this would hand over the same document a request later.
|
|
72
72
|
// `undefined` here becomes the 404 an unreachable document gets, which
|
|
73
73
|
// is invariant 6 — the caller learns nothing about why.
|
|
74
|
-
if (!withinDelegation(auth, row.layer_id))
|
|
74
|
+
if (!withinDelegation(auth, row.layer_id, 'read'))
|
|
75
75
|
return undefined;
|
|
76
76
|
// `all` is org_admin, which reaches everything by rule 3. Otherwise the
|
|
77
77
|
// document has to sit in a layer the plan reached, or be named
|
|
@@ -94,6 +94,15 @@ export class PostgresDocuments {
|
|
|
94
94
|
// tag has no way to tell a successful write from a dropped one — and
|
|
95
95
|
// this field was dropped by the handler for as long as it existed.
|
|
96
96
|
metadata: (row.metadata ?? {}),
|
|
97
|
+
// Why it failed, which the worker has always written and nothing has
|
|
98
|
+
// ever read back. An operator with five failed documents and this
|
|
99
|
+
// field empty has one route left, and it is `docker logs` — so the
|
|
100
|
+
// answer was reachable only by whoever holds the host.
|
|
101
|
+
//
|
|
102
|
+
// Only on `failed`. The column keeps the last error across a retry
|
|
103
|
+
// that succeeded, and reporting that beside `indexed` would describe
|
|
104
|
+
// a document that is fine as a document with a problem.
|
|
105
|
+
error: row.status === 'failed' ? row.error : null,
|
|
97
106
|
// Minted here and nowhere earlier: everything above this line is the
|
|
98
107
|
// permission check, so a link exists only for a caller who has just
|
|
99
108
|
// been found to hold `read` on this document. `write` alone does not
|
|
@@ -145,7 +154,7 @@ export class PostgresDocuments {
|
|
|
145
154
|
if (layerId === undefined)
|
|
146
155
|
return false;
|
|
147
156
|
// The narrowing, same as everywhere a layer id and a document meet.
|
|
148
|
-
if (!withinDelegation(auth, layerId))
|
|
157
|
+
if (!withinDelegation(auth, layerId, 'write'))
|
|
149
158
|
return false;
|
|
150
159
|
if (plan.kind === 'scoped') {
|
|
151
160
|
// deniedDocs first, as on the delete path: a deny beats an allow at
|
|
@@ -186,7 +195,7 @@ export class NacreSearchService {
|
|
|
186
195
|
const principals = await principalsFor(client, auth, this.deps.principalsCache);
|
|
187
196
|
const grants = await loadGrants(client, auth.orgId, principals);
|
|
188
197
|
const tree = await loadScopeTree(client, auth.orgId, grants.filter((g) => g.scope.type === 'document').map((g) => g.scope.id));
|
|
189
|
-
const plan = activeResolver().resolve({ orgId: auth.orgId, role: auth.role, principals, grants, tree }, 'read');
|
|
198
|
+
const plan = activeResolver().resolve({ orgId: auth.orgId, role: auth.role, principals, grants, tree, ...ceilingOf(auth) }, 'read');
|
|
190
199
|
// Every live layer with the model it is on. Small — an installation
|
|
191
200
|
// runs tens of layers per organization, not thousands — and it is what
|
|
192
201
|
// decides how many branches the query has.
|
|
@@ -262,7 +271,12 @@ export class NacreSearchService {
|
|
|
262
271
|
// beside the permission constraint the index answers that exactly, which is
|
|
263
272
|
// also why this is not a post-filter — invariant 2 holds because the clause
|
|
264
273
|
// is inside the traversal and `top_k` still returns k permitted results.
|
|
265
|
-
|
|
274
|
+
// `read`, because this is the search path and there is no other verb it
|
|
275
|
+
// could be asking about. A layer the person gave the application `write`
|
|
276
|
+
// and not `read` is absent from this set, which is exactly rule 6: an
|
|
277
|
+
// ingest-only layer is not searchable, and it never was for a principal
|
|
278
|
+
// holding `write` alone either.
|
|
279
|
+
const delegated = delegatedLayers(auth, 'read');
|
|
266
280
|
if (delegated !== undefined) {
|
|
267
281
|
const both = narrow?.layers === undefined ? [...delegated] : narrow.layers.filter((id) => delegated.includes(id));
|
|
268
282
|
// The caller named layers and none of them survive the narrowing. Empty,
|
|
@@ -645,7 +659,7 @@ export class NacreIngest {
|
|
|
645
659
|
const principals = await principalsFor(client, auth, this.deps.principalsCache);
|
|
646
660
|
const grants = await loadGrants(client, auth.orgId, principals);
|
|
647
661
|
const tree = await loadScopeTree(client, auth.orgId, grants.filter((g) => g.scope.type === 'document').map((g) => g.scope.id));
|
|
648
|
-
const plan = activeResolver().resolve({ orgId: auth.orgId, role: auth.role, principals, grants, tree }, 'write');
|
|
662
|
+
const plan = activeResolver().resolve({ orgId: auth.orgId, role: auth.role, principals, grants, tree, ...ceilingOf(auth) }, 'write');
|
|
649
663
|
if (plan.kind === 'none')
|
|
650
664
|
return undefined;
|
|
651
665
|
const { rows } = await client.query(`SELECT id FROM layers WHERE org_id = $1 AND slug = $2 AND deleted_at IS NULL`, [auth.orgId, layerSlug]);
|
|
@@ -657,7 +671,7 @@ export class NacreIngest {
|
|
|
657
671
|
// restricted an application to one layer restricted what it can *put*
|
|
658
672
|
// there too, and an ingest is the one verb where the layer arrives named
|
|
659
673
|
// rather than being discovered by a query.
|
|
660
|
-
if (!withinDelegation(auth, id))
|
|
674
|
+
if (!withinDelegation(auth, id, 'write'))
|
|
661
675
|
return undefined;
|
|
662
676
|
// A layer that exists but is not writable and a layer that does not exist
|
|
663
677
|
// return the same thing, so the caller cannot tell them apart.
|
|
@@ -802,7 +816,7 @@ export class NacreIngest {
|
|
|
802
816
|
const principals = await principalsFor(client, auth, this.deps.principalsCache);
|
|
803
817
|
const grants = await loadGrants(client, auth.orgId, principals);
|
|
804
818
|
const tree = await loadScopeTree(client, auth.orgId, [documentId]);
|
|
805
|
-
const plan = activeResolver().resolve({ orgId: auth.orgId, role: auth.role, principals, grants, tree }, 'write');
|
|
819
|
+
const plan = activeResolver().resolve({ orgId: auth.orgId, role: auth.role, principals, grants, tree, ...ceilingOf(auth) }, 'write');
|
|
806
820
|
if (plan.kind === 'none')
|
|
807
821
|
return false;
|
|
808
822
|
const { rows } = await client.query(`SELECT layer_id FROM documents
|
|
@@ -813,7 +827,7 @@ export class NacreIngest {
|
|
|
813
827
|
// The narrowing. A delegation restricted to one layer must not be able
|
|
814
828
|
// to delete out of another, and this is a `write` path so rule 6 does
|
|
815
829
|
// not help: the person's own `read` never came into it.
|
|
816
|
-
if (!withinDelegation(auth, layerId))
|
|
830
|
+
if (!withinDelegation(auth, layerId, 'write'))
|
|
817
831
|
return false;
|
|
818
832
|
if (plan.kind === 'scoped') {
|
|
819
833
|
// deniedDocs first. `resolve` populates it precisely so a deny beats
|
|
@@ -933,7 +947,21 @@ async function contextFor(client, auth, cache) {
|
|
|
933
947
|
const principals = await principalsFor(client, auth, cache);
|
|
934
948
|
const grants = await loadGrants(client, auth.orgId, principals);
|
|
935
949
|
const tree = await loadScopeTree(client, auth.orgId, grants.filter((g) => g.scope.type === 'document').map((g) => g.scope.id));
|
|
936
|
-
return { orgId: auth.orgId, role: auth.role, principals, grants, tree };
|
|
950
|
+
return { orgId: auth.orgId, role: auth.role, principals, grants, tree, ...ceilingOf(auth) };
|
|
951
|
+
}
|
|
952
|
+
/**
|
|
953
|
+
* The delegation's permission ceiling, in the shape `ResolveInput` takes it.
|
|
954
|
+
*
|
|
955
|
+
* One function so the three places that build a resolve input cannot disagree,
|
|
956
|
+
* and so adding a fourth is a compile error rather than a silently unbounded
|
|
957
|
+
* token. `resolve` applies it before rule 3, which is the part that matters:
|
|
958
|
+
* an `org_admin` reaches everything by role and by no grant at all, so a
|
|
959
|
+
* ceiling consulted after that line would not bound the one principal it
|
|
960
|
+
* exists for.
|
|
961
|
+
*/
|
|
962
|
+
function ceilingOf(auth) {
|
|
963
|
+
const ceiling = auth.delegation?.permissions;
|
|
964
|
+
return ceiling === undefined ? {} : { ceiling };
|
|
937
965
|
}
|
|
938
966
|
export class PostgresLayers {
|
|
939
967
|
pool;
|
|
@@ -994,7 +1022,10 @@ export class PostgresLayers {
|
|
|
994
1022
|
const projection = `l.id, l.slug, l.name, l.workspace_id, l.description, l.created_at,
|
|
995
1023
|
l.created_at::text AS created_at_text,
|
|
996
1024
|
(SELECT count(*) FROM documents d
|
|
997
|
-
WHERE d.layer_id = l.id AND d.deleted_at IS NULL) AS document_count
|
|
1025
|
+
WHERE d.layer_id = l.id AND d.deleted_at IS NULL) AS document_count,
|
|
1026
|
+
(SELECT count(*) FROM documents d
|
|
1027
|
+
WHERE d.layer_id = l.id AND d.deleted_at IS NULL
|
|
1028
|
+
AND d.status = 'failed') AS failed_count`;
|
|
998
1029
|
// Ordered by (created_at, id) rather than by slug, because that is the
|
|
999
1030
|
// cursor's sort key and a page has to be stable under inserts. Sorting
|
|
1000
1031
|
// by a mutable column would move rows between pages when one is renamed.
|
|
@@ -1018,6 +1049,7 @@ export class PostgresLayers {
|
|
|
1018
1049
|
workspaceId: r.workspace_id,
|
|
1019
1050
|
description: r.description ?? '',
|
|
1020
1051
|
documentCount: Number(r.document_count),
|
|
1052
|
+
failedCount: Number(r.failed_count),
|
|
1021
1053
|
createdAt: r.created_at.toISOString(),
|
|
1022
1054
|
}));
|
|
1023
1055
|
return pageOf(layers, page, (l, i) => ({
|
|
@@ -1186,6 +1218,8 @@ export class PostgresLayers {
|
|
|
1186
1218
|
description: row.description ?? '',
|
|
1187
1219
|
// Just created, so this is a fact rather than a query.
|
|
1188
1220
|
documentCount: 0,
|
|
1221
|
+
// A layer created a moment ago has nothing in it to have failed.
|
|
1222
|
+
failedCount: 0,
|
|
1189
1223
|
createdAt: row.created_at.toISOString(),
|
|
1190
1224
|
},
|
|
1191
1225
|
};
|
|
@@ -1202,6 +1236,77 @@ export class PostgresLayers {
|
|
|
1202
1236
|
* who closed that terminal had no way forward that did not involve reading the
|
|
1203
1237
|
* database directly.
|
|
1204
1238
|
*/
|
|
1239
|
+
/**
|
|
1240
|
+
* Embedding providers, which had no API at all.
|
|
1241
|
+
*
|
|
1242
|
+
* `init` wrote one row and nothing else ever did, so a deployment had exactly
|
|
1243
|
+
* one model and the documented way to add a second was `psql` — the shape this
|
|
1244
|
+
* repository has closed twice before, at the workspace listing and at
|
|
1245
|
+
* `/v1/users`. The capability was already built everywhere else: the schema has
|
|
1246
|
+
* carried `embedding_providers.org_id` since migration 0001, and the search
|
|
1247
|
+
* path, the worker and the reindex machinery were all taught to honour it.
|
|
1248
|
+
*
|
|
1249
|
+
* The **database** narrows both operations and this class does not repeat it.
|
|
1250
|
+
* `org_isolation` (0002) shows a caller their own organization's rows and the
|
|
1251
|
+
* installation default; the three restrictive policies from 0019 require a
|
|
1252
|
+
* write to name the caller's own organization, so a tenant cannot create,
|
|
1253
|
+
* change or remove the global row whatever this code passes.
|
|
1254
|
+
*/
|
|
1255
|
+
export class PostgresEmbeddingProviders {
|
|
1256
|
+
pool;
|
|
1257
|
+
role;
|
|
1258
|
+
constructor(pool, role) {
|
|
1259
|
+
this.pool = pool;
|
|
1260
|
+
this.role = role;
|
|
1261
|
+
}
|
|
1262
|
+
async list(auth) {
|
|
1263
|
+
return withOrg(this.pool, auth.orgId, async (client) => {
|
|
1264
|
+
// The installation default last. It is the one every layer starts on,
|
|
1265
|
+
// so it is the least interesting entry in a list somebody opened to
|
|
1266
|
+
// find the model they added.
|
|
1267
|
+
const { rows } = await client.query(`SELECT id, name, model, dimensions, org_id
|
|
1268
|
+
FROM embedding_providers
|
|
1269
|
+
ORDER BY org_id IS NULL, name`);
|
|
1270
|
+
return rows.map((r) => ({
|
|
1271
|
+
id: r.id,
|
|
1272
|
+
name: r.name,
|
|
1273
|
+
model: r.model,
|
|
1274
|
+
dimensions: Number(r.dimensions),
|
|
1275
|
+
isDefault: r.org_id === null,
|
|
1276
|
+
}));
|
|
1277
|
+
}, this.role === undefined ? {} : { role: this.role });
|
|
1278
|
+
}
|
|
1279
|
+
async create(auth, input) {
|
|
1280
|
+
if (!administers(auth))
|
|
1281
|
+
return { kind: 'denied' };
|
|
1282
|
+
return withOrg(this.pool, auth.orgId, async (client) => {
|
|
1283
|
+
// `auth.orgId` and never NULL, so a tenant cannot write the global
|
|
1284
|
+
// default. The restrictive policy refuses it anyway — this is the
|
|
1285
|
+
// application saying the same thing, because a statement that relies
|
|
1286
|
+
// only on a policy is one nobody reading it can check.
|
|
1287
|
+
const { rows } = await client.query(`INSERT INTO embedding_providers (org_id, name, endpoint, model, dimensions)
|
|
1288
|
+
VALUES ($1,$2,$3,$4,$5)
|
|
1289
|
+
ON CONFLICT DO NOTHING
|
|
1290
|
+
RETURNING id, name, model, dimensions`, [auth.orgId, input.name, input.endpoint, input.model, input.dimensions]);
|
|
1291
|
+
const row = rows[0];
|
|
1292
|
+
// By here the caller has proved they administer the organization, so
|
|
1293
|
+
// there is nothing about visibility left to protect: a name already in
|
|
1294
|
+
// use is a fact about the resource, and 409 says so.
|
|
1295
|
+
if (row === undefined)
|
|
1296
|
+
return { kind: 'conflict' };
|
|
1297
|
+
return {
|
|
1298
|
+
kind: 'created',
|
|
1299
|
+
provider: {
|
|
1300
|
+
id: row.id,
|
|
1301
|
+
name: row.name,
|
|
1302
|
+
model: row.model,
|
|
1303
|
+
dimensions: Number(row.dimensions),
|
|
1304
|
+
isDefault: false,
|
|
1305
|
+
},
|
|
1306
|
+
};
|
|
1307
|
+
}, this.role === undefined ? {} : { role: this.role });
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1205
1310
|
export class PostgresWorkspaces {
|
|
1206
1311
|
pool;
|
|
1207
1312
|
role;
|
|
@@ -1282,6 +1387,37 @@ export class PostgresWorkspaces {
|
|
|
1282
1387
|
// for a workspace with nothing in it yet.
|
|
1283
1388
|
return referenceAllows(context, { type: 'workspace', id: w.id }, 'read');
|
|
1284
1389
|
});
|
|
1390
|
+
/**
|
|
1391
|
+
* What this caller holds on **this workspace**, asked per request and
|
|
1392
|
+
* never derived from `users.role`.
|
|
1393
|
+
*
|
|
1394
|
+
* Authority over a workspace comes from a grant placed on the workspace
|
|
1395
|
+
* scope, or from the role, and `referenceAllows` is the one thing that
|
|
1396
|
+
* answers that — the same function the filter above uses for its third
|
|
1397
|
+
* branch.
|
|
1398
|
+
*
|
|
1399
|
+
* The plan is deliberately *not* consulted. `resolve` flattens a grant
|
|
1400
|
+
* set to the layers it reaches, so a workspace grant and a grant on a
|
|
1401
|
+
* layer inside that workspace produce the same scoped plan: asking the
|
|
1402
|
+
* plan cannot tell "admin on the workspace" from "admin on one layer in
|
|
1403
|
+
* it", and reporting the second as the first would put "New layer" in
|
|
1404
|
+
* front of a principal the server refuses. That was the first shape of
|
|
1405
|
+
* this function, and the T-case is what caught it.
|
|
1406
|
+
*
|
|
1407
|
+
* `read` is added rather than asked, and that is the one thing here
|
|
1408
|
+
* taken on trust from the filter above: a row only reaches this point
|
|
1409
|
+
* because the caller can see the workspace — through a grant on it, or
|
|
1410
|
+
* through a layer inside it, or by role. Either way `read` is true.
|
|
1411
|
+
*/
|
|
1412
|
+
const permissionsOn = (w) => {
|
|
1413
|
+
const held = new Set(['read']);
|
|
1414
|
+
for (const permission of ['write', 'admin']) {
|
|
1415
|
+
if (referenceAllows(context, { type: 'workspace', id: w.id }, permission)) {
|
|
1416
|
+
held.add(permission);
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
return ['read', 'write', 'admin'].filter((p) => held.has(p));
|
|
1420
|
+
};
|
|
1285
1421
|
const fetched = page === undefined ? rows : rows.slice(0, page.limit);
|
|
1286
1422
|
const items = reachable
|
|
1287
1423
|
.filter((w) => fetched.some((f) => f.id === w.id))
|
|
@@ -1291,6 +1427,7 @@ export class PostgresWorkspaces {
|
|
|
1291
1427
|
name: w.name,
|
|
1292
1428
|
layerCount: (w.layer_ids ?? []).length,
|
|
1293
1429
|
createdAt: w.created_at.toISOString(),
|
|
1430
|
+
permissions: permissionsOn(w),
|
|
1294
1431
|
}));
|
|
1295
1432
|
// From the last row **fetched**, not the last returned — the same rule
|
|
1296
1433
|
// the grant listing follows and for the same reason: the filter above
|
|
@@ -1320,7 +1457,7 @@ export class PostgresWorkspaces {
|
|
|
1320
1457
|
* documents live.
|
|
1321
1458
|
*/
|
|
1322
1459
|
async create(auth, input) {
|
|
1323
|
-
if (auth
|
|
1460
|
+
if (!administers(auth))
|
|
1324
1461
|
return { kind: 'denied' };
|
|
1325
1462
|
return withOrg(this.pool, auth.orgId, async (client) => {
|
|
1326
1463
|
const { rows } = await client.query(`INSERT INTO workspaces (org_id, slug, name) VALUES ($1,$2,$3)
|
|
@@ -1342,6 +1479,10 @@ export class PostgresWorkspaces {
|
|
|
1342
1479
|
name: row.name,
|
|
1343
1480
|
layerCount: 0,
|
|
1344
1481
|
createdAt: row.created_at.toISOString(),
|
|
1482
|
+
// Creating one is `org_admin`, and an `org_admin` reaches
|
|
1483
|
+
// everything by role — so this is the whole set rather than a
|
|
1484
|
+
// resolve call that could only agree with the check just made.
|
|
1485
|
+
permissions: ['read', 'write', 'admin'],
|
|
1345
1486
|
},
|
|
1346
1487
|
};
|
|
1347
1488
|
}, this.scope);
|