@powerhousedao/reactor-api 6.2.0-dev.8 → 6.2.0-rc.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/{adapter-gateway-apollo-IZuGzFaY.mjs → adapter-gateway-apollo-CxV_hMTv.mjs} +27 -5
- package/dist/adapter-gateway-apollo-CxV_hMTv.mjs.map +1 -0
- package/dist/index.d.mts +134 -269
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +619 -687
- package/dist/index.mjs.map +1 -1
- package/dist/src/packages/vite-loader.mjs +3 -3
- package/dist/src/packages/vite-loader.mjs.map +1 -1
- package/dist/{utils-CtC8sjRo.mjs → utils-iibOQ50e.mjs} +216 -50
- package/dist/utils-iibOQ50e.mjs.map +1 -0
- package/package.json +28 -16
- package/dist/adapter-gateway-apollo-IZuGzFaY.mjs.map +0 -1
- package/dist/utils-CtC8sjRo.mjs.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
|
|
2
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
3
|
-
import { a as isSubgraphClass, c as loadDocumentModels, d as BaseSubgraph, f as AuthorizationPolicy, i as buildGraphqlOperations, l as loadProcessors, n as buildGraphQlDriveDocument, o as debounce, p as
|
|
2
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="88c66104-0cca-57b8-b3ad-93532b51c2b3")}catch(e){}}();
|
|
3
|
+
import { a as isSubgraphClass, c as loadDocumentModels, d as BaseSubgraph, f as AuthorizationPolicy, i as buildGraphqlOperations, l as loadProcessors, m as createAuthorizationService, n as buildGraphQlDriveDocument, o as debounce, p as AuthorizedDocumentHandle, r as buildGraphqlOperation, t as buildGraphQlDocument, u as loadSubgraphs } from "./utils-iibOQ50e.mjs";
|
|
4
4
|
import { AnalyticsQueryEngine } from "@powerhousedao/analytics-engine-core";
|
|
5
5
|
import { AnalyticsModel, AnalyticsResolvers, typedefs } from "@powerhousedao/analytics-engine-graphql";
|
|
6
6
|
import { gql } from "graphql-tag";
|
|
7
7
|
import { GraphQLError, Kind, parse, print } from "graphql";
|
|
8
|
+
import { DEFAULT_DRIVE_CONTAINER_TYPES, DriveCollectionId, PropagationMode as PropagationMode$1, consolidateSyncOperations, driveIdFromUrl, envelopesToSyncOperations, parseDriveUrl } from "@powerhousedao/reactor";
|
|
8
9
|
import { ConsoleLogger, childLogger, documentModelDocumentModelModule } from "document-model";
|
|
9
10
|
import path from "node:path";
|
|
10
11
|
import { match } from "path-to-regexp";
|
|
11
|
-
import { verifyAuthBearerToken } from "@renown/sdk";
|
|
12
12
|
import { buildSubgraphSchema } from "@apollo/subgraph";
|
|
13
13
|
import { typeDefs } from "@powerhousedao/document-engineering/graphql";
|
|
14
14
|
import { camelCase, kebabCase, pascalCase } from "change-case";
|
|
15
15
|
import { GraphQLJSONObject } from "graphql-type-json";
|
|
16
16
|
import { setName } from "@powerhousedao/shared/document-model";
|
|
17
|
-
import { DEFAULT_DRIVE_CONTAINER_TYPES, PropagationMode as PropagationMode$1, consolidateSyncOperations, driveIdFromUrl, envelopesToSyncOperations, parseDriveUrl } from "@powerhousedao/reactor";
|
|
18
17
|
import * as z$1 from "zod";
|
|
19
18
|
import { z } from "zod";
|
|
20
19
|
import { createHandler } from "graphql-sse/lib/use/fetch";
|
|
@@ -35,6 +34,7 @@ import { tmpdir } from "node:os";
|
|
|
35
34
|
import { WebSocketServer } from "ws";
|
|
36
35
|
import { createRelationalDb } from "@powerhousedao/shared/processors";
|
|
37
36
|
import { Kysely, Migrator, sql } from "kysely";
|
|
37
|
+
import { verifyAuthBearerToken } from "@renown/sdk";
|
|
38
38
|
import { PGlite } from "@electric-sql/pglite";
|
|
39
39
|
import { AtomicNodeFs } from "@powerhousedao/pglite-fs";
|
|
40
40
|
import knex from "knex";
|
|
@@ -70,16 +70,16 @@ var AnalyticsSubgraph = class extends BaseSubgraph {
|
|
|
70
70
|
};
|
|
71
71
|
//#endregion
|
|
72
72
|
//#region src/graphql/auth/schema.graphql
|
|
73
|
-
var schema_default$2 = "# Auth Subgraph Schema\n# Contains all document permission and authorization related types\n\nscalar DateTime\n\n# Permission levels for documents\nenum DocumentPermissionLevel {\n # Can fetch and read the document\n READ\n # Can push updates and modify the document\n WRITE\n # Can manage document permissions and settings\n ADMIN\n}\n\n# Document permission entry\ntype DocumentPermissionEntry {\n documentId: String!\n userAddress: String!\n permission: DocumentPermissionLevel!\n grantedBy: String!\n createdAt: DateTime!\n updatedAt: DateTime!\n}\n\n#
|
|
73
|
+
var schema_default$2 = "# Auth Subgraph Schema\n# Contains all document permission and authorization related types\n\nscalar DateTime\n\n# Permission levels for documents\nenum DocumentPermissionLevel {\n # Can fetch and read the document\n READ\n # Can push updates and modify the document\n WRITE\n # Can manage document permissions and settings\n ADMIN\n}\n\n# Document permission entry\ntype DocumentPermissionEntry {\n documentId: String!\n userAddress: String!\n permission: DocumentPermissionLevel!\n grantedBy: String!\n createdAt: DateTime!\n updatedAt: DateTime!\n}\n\n# Operation user permission entry\ntype OperationUserPermission {\n documentId: String!\n operationType: String!\n userAddress: String!\n grantedBy: String!\n createdAt: DateTime!\n}\n\n# Combined document access info\ntype DocumentAccessInfo {\n documentId: String!\n permissions: [DocumentPermissionEntry!]!\n}\n\n# Operation permissions info\ntype OperationPermissionsInfo {\n documentId: String!\n operationType: String!\n userPermissions: [OperationUserPermission!]!\n}\n\n# Document protection info\ntype DocumentProtectionInfo {\n documentId: String!\n protected: Boolean!\n ownerAddress: String\n}\n\ntype Query {\n # Get permissions for a document (requires ADMIN permission or global admin)\n documentAccess(documentId: String!): DocumentAccessInfo!\n\n # Get protection status for a document (requires ADMIN permission or global admin)\n documentProtection(documentId: String!): DocumentProtectionInfo!\n\n # Get all documents the current user has explicit access to\n userDocumentPermissions: [DocumentPermissionEntry!]!\n\n # Get operation permissions for a document and operation type\n # (requires ADMIN permission or global admin)\n operationPermissions(\n documentId: String!\n operationType: String!\n ): OperationPermissionsInfo!\n\n # Check if the current user can execute a specific operation on a document\n canExecuteOperation(documentId: String!, operationType: String!): Boolean!\n}\n\ntype Mutation {\n # Set document protection status (requires ADMIN permission or global admin)\n setDocumentProtection(\n documentId: String!\n protected: Boolean!\n ): DocumentProtectionInfo!\n\n # Transfer document ownership (requires ADMIN permission or global admin)\n transferDocumentOwnership(\n documentId: String!\n newOwnerAddress: String!\n ): DocumentProtectionInfo!\n\n # Grant a user permission on a document (requires ADMIN permission or global admin)\n grantDocumentPermission(\n documentId: String!\n userAddress: String!\n permission: DocumentPermissionLevel!\n ): DocumentPermissionEntry!\n\n # Revoke a user's permission on a document (requires ADMIN permission or global admin)\n revokeDocumentPermission(documentId: String!, userAddress: String!): Boolean!\n\n # --- Operation Permissions ---\n\n # Grant a user permission to execute an operation on a document\n grantOperationPermission(\n documentId: String!\n operationType: String!\n userAddress: String!\n ): OperationUserPermission!\n\n # Revoke a user's permission to execute an operation\n revokeOperationPermission(\n documentId: String!\n operationType: String!\n userAddress: String!\n ): Boolean!\n}\n";
|
|
74
74
|
//#endregion
|
|
75
75
|
//#region src/graphql/auth/resolvers.ts
|
|
76
|
-
async function documentAccess(service, args) {
|
|
76
|
+
async function documentAccess(service, authorizationService, args, userAddress) {
|
|
77
|
+
if (!userAddress) throw new GraphQLError("Authentication required");
|
|
78
|
+
if (!await authorizationService.canManage(args.documentId, userAddress)) throw new GraphQLError("Forbidden: You must be an admin of this document to view its permissions");
|
|
77
79
|
const permissions = await service.getDocumentPermissions(args.documentId);
|
|
78
|
-
const groupPermissions = await service.getDocumentGroupPermissions(args.documentId);
|
|
79
80
|
return {
|
|
80
81
|
documentId: args.documentId,
|
|
81
|
-
permissions
|
|
82
|
-
groupPermissions
|
|
82
|
+
permissions
|
|
83
83
|
};
|
|
84
84
|
}
|
|
85
85
|
async function userDocumentPermissions(service, userAddress) {
|
|
@@ -96,56 +96,18 @@ async function revokeDocumentPermission(service, authorizationService, args, rev
|
|
|
96
96
|
await service.revokePermission(args.documentId, args.userAddress);
|
|
97
97
|
return true;
|
|
98
98
|
}
|
|
99
|
-
async function
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
async function group(service, args) {
|
|
103
|
-
return service.getGroup(args.id);
|
|
104
|
-
}
|
|
105
|
-
async function userGroups(service, args) {
|
|
106
|
-
return service.getUserGroups(args.userAddress);
|
|
107
|
-
}
|
|
108
|
-
async function createGroup(service, args) {
|
|
109
|
-
return service.createGroup(args.name, args.description ?? void 0);
|
|
110
|
-
}
|
|
111
|
-
async function deleteGroup(service, args) {
|
|
112
|
-
await service.deleteGroup(args.id);
|
|
113
|
-
return true;
|
|
114
|
-
}
|
|
115
|
-
async function addUserToGroup(service, args) {
|
|
116
|
-
await service.addUserToGroup(args.userAddress, args.groupId);
|
|
117
|
-
return true;
|
|
118
|
-
}
|
|
119
|
-
async function removeUserFromGroup(service, args) {
|
|
120
|
-
await service.removeUserFromGroup(args.userAddress, args.groupId);
|
|
121
|
-
return true;
|
|
122
|
-
}
|
|
123
|
-
async function getGroupMembers(service, groupId) {
|
|
124
|
-
return service.getGroupMembers(groupId);
|
|
125
|
-
}
|
|
126
|
-
async function grantGroupPermission(service, authorizationService, args, grantedByAddress) {
|
|
127
|
-
if (!grantedByAddress) throw new GraphQLError("Authentication required");
|
|
128
|
-
if (!await authorizationService.canManage(args.documentId, grantedByAddress)) throw new GraphQLError("Forbidden: You must be an admin of this document to grant permissions");
|
|
129
|
-
return service.grantGroupPermission(args.documentId, args.groupId, args.permission, grantedByAddress);
|
|
130
|
-
}
|
|
131
|
-
async function revokeGroupPermission(service, authorizationService, args, revokedByAddress) {
|
|
132
|
-
if (!revokedByAddress) throw new GraphQLError("Authentication required");
|
|
133
|
-
if (!await authorizationService.canManage(args.documentId, revokedByAddress)) throw new GraphQLError("Forbidden: You must be an admin of this document to revoke permissions");
|
|
134
|
-
await service.revokeGroupPermission(args.documentId, args.groupId);
|
|
135
|
-
return true;
|
|
136
|
-
}
|
|
137
|
-
async function operationPermissions(service, args) {
|
|
99
|
+
async function operationPermissions(service, authorizationService, args, userAddress) {
|
|
100
|
+
if (!userAddress) throw new GraphQLError("Authentication required");
|
|
101
|
+
if (!await authorizationService.canManage(args.documentId, userAddress)) throw new GraphQLError("Forbidden: You must be an admin of this document to view its operation permissions");
|
|
138
102
|
const userPermissions = await service.getOperationUserPermissions(args.documentId, args.operationType);
|
|
139
|
-
const groupPermissions = await service.getOperationGroupPermissions(args.documentId, args.operationType);
|
|
140
103
|
return {
|
|
141
104
|
documentId: args.documentId,
|
|
142
105
|
operationType: args.operationType,
|
|
143
|
-
userPermissions
|
|
144
|
-
groupPermissions
|
|
106
|
+
userPermissions
|
|
145
107
|
};
|
|
146
108
|
}
|
|
147
|
-
async function canExecuteOperation(
|
|
148
|
-
return
|
|
109
|
+
async function canExecuteOperation(authorizationService, args, userAddress) {
|
|
110
|
+
return authorizationService.canMutate(args.documentId, args.operationType, userAddress);
|
|
149
111
|
}
|
|
150
112
|
async function grantOperationPermission(service, authorizationService, args, grantedByAddress) {
|
|
151
113
|
if (!grantedByAddress) throw new GraphQLError("Authentication required");
|
|
@@ -158,18 +120,9 @@ async function revokeOperationPermission(service, authorizationService, args, re
|
|
|
158
120
|
await service.revokeOperationPermission(args.documentId, args.operationType, args.userAddress);
|
|
159
121
|
return true;
|
|
160
122
|
}
|
|
161
|
-
async function
|
|
162
|
-
if (!
|
|
163
|
-
if (!await authorizationService.canManage(args.documentId,
|
|
164
|
-
return service.grantGroupOperationPermission(args.documentId, args.operationType, args.groupId, grantedByAddress);
|
|
165
|
-
}
|
|
166
|
-
async function revokeGroupOperationPermission(service, authorizationService, args, revokedByAddress) {
|
|
167
|
-
if (!revokedByAddress) throw new GraphQLError("Authentication required");
|
|
168
|
-
if (!await authorizationService.canManage(args.documentId, revokedByAddress)) throw new GraphQLError("Forbidden: You must be an admin of this document to revoke operation permissions");
|
|
169
|
-
await service.revokeGroupOperationPermission(args.documentId, args.operationType, args.groupId);
|
|
170
|
-
return true;
|
|
171
|
-
}
|
|
172
|
-
async function documentProtection(service, args) {
|
|
123
|
+
async function documentProtection(service, authorizationService, args, userAddress) {
|
|
124
|
+
if (!userAddress) throw new GraphQLError("Authentication required");
|
|
125
|
+
if (!await authorizationService.canManage(args.documentId, userAddress)) throw new GraphQLError("Forbidden: You must be an admin of this document to view its protection info");
|
|
173
126
|
return service.getDocumentProtection(args.documentId);
|
|
174
127
|
}
|
|
175
128
|
async function setDocumentProtection(service, authorizationService, args, userAddress) {
|
|
@@ -195,8 +148,7 @@ async function transferDocumentOwnership(service, authorizationService, args, us
|
|
|
195
148
|
* This subgraph is conditionally registered based on the DOCUMENT_PERMISSIONS_ENABLED
|
|
196
149
|
* feature flag. When enabled, it provides GraphQL operations for:
|
|
197
150
|
* - Document permissions (grant/revoke user access)
|
|
198
|
-
* -
|
|
199
|
-
* - Group document permissions (grant/revoke group access)
|
|
151
|
+
* - Document protection and ownership
|
|
200
152
|
* - Operation-level permissions (fine-grained operation control)
|
|
201
153
|
*/
|
|
202
154
|
var AuthSubgraph = class extends BaseSubgraph {
|
|
@@ -210,11 +162,11 @@ var AuthSubgraph = class extends BaseSubgraph {
|
|
|
210
162
|
typeDefs = gql(schema_default$2);
|
|
211
163
|
resolvers = {
|
|
212
164
|
Query: {
|
|
213
|
-
documentAccess: async (_parent, args) => {
|
|
165
|
+
documentAccess: async (_parent, args, ctx) => {
|
|
214
166
|
this.logger.debug("documentAccess(@args)", args);
|
|
215
167
|
if (!this.documentPermissionService) throw new GraphQLError("DocumentPermissionService not available");
|
|
216
168
|
try {
|
|
217
|
-
return await documentAccess(this.documentPermissionService, args);
|
|
169
|
+
return await documentAccess(this.documentPermissionService, this.authorizationService, await this.withCanonicalDocumentId(args, ctx), ctx.user?.address);
|
|
218
170
|
} catch (error) {
|
|
219
171
|
this.logger.error("Error in documentAccess: @error", error);
|
|
220
172
|
throw error;
|
|
@@ -231,41 +183,11 @@ var AuthSubgraph = class extends BaseSubgraph {
|
|
|
231
183
|
throw error;
|
|
232
184
|
}
|
|
233
185
|
},
|
|
234
|
-
|
|
235
|
-
this.logger.debug("groups");
|
|
236
|
-
if (!this.documentPermissionService) throw new GraphQLError("DocumentPermissionService not available");
|
|
237
|
-
try {
|
|
238
|
-
return await groups(this.documentPermissionService);
|
|
239
|
-
} catch (error) {
|
|
240
|
-
this.logger.error("Error in groups: @error", error);
|
|
241
|
-
throw error;
|
|
242
|
-
}
|
|
243
|
-
},
|
|
244
|
-
group: async (_parent, args) => {
|
|
245
|
-
this.logger.debug("group(@args)", args);
|
|
246
|
-
if (!this.documentPermissionService) throw new GraphQLError("DocumentPermissionService not available");
|
|
247
|
-
try {
|
|
248
|
-
return await group(this.documentPermissionService, args);
|
|
249
|
-
} catch (error) {
|
|
250
|
-
this.logger.error("Error in group: @error", error);
|
|
251
|
-
throw error;
|
|
252
|
-
}
|
|
253
|
-
},
|
|
254
|
-
userGroups: async (_parent, args) => {
|
|
255
|
-
this.logger.debug("userGroups(@args)", args);
|
|
256
|
-
if (!this.documentPermissionService) throw new GraphQLError("DocumentPermissionService not available");
|
|
257
|
-
try {
|
|
258
|
-
return await userGroups(this.documentPermissionService, args);
|
|
259
|
-
} catch (error) {
|
|
260
|
-
this.logger.error("Error in userGroups: @error", error);
|
|
261
|
-
throw error;
|
|
262
|
-
}
|
|
263
|
-
},
|
|
264
|
-
operationPermissions: async (_parent, args) => {
|
|
186
|
+
operationPermissions: async (_parent, args, ctx) => {
|
|
265
187
|
this.logger.debug("operationPermissions(@args)", args);
|
|
266
188
|
if (!this.documentPermissionService) throw new GraphQLError("DocumentPermissionService not available");
|
|
267
189
|
try {
|
|
268
|
-
return await operationPermissions(this.documentPermissionService, args);
|
|
190
|
+
return await operationPermissions(this.documentPermissionService, this.authorizationService, await this.withCanonicalDocumentId(args, ctx), ctx.user?.address);
|
|
269
191
|
} catch (error) {
|
|
270
192
|
this.logger.error("Error in operationPermissions: @error", error);
|
|
271
193
|
throw error;
|
|
@@ -273,9 +195,8 @@ var AuthSubgraph = class extends BaseSubgraph {
|
|
|
273
195
|
},
|
|
274
196
|
canExecuteOperation: async (_parent, args, ctx) => {
|
|
275
197
|
this.logger.debug("canExecuteOperation(@args)", args);
|
|
276
|
-
if (!this.documentPermissionService) throw new GraphQLError("DocumentPermissionService not available");
|
|
277
198
|
try {
|
|
278
|
-
return await canExecuteOperation(this.
|
|
199
|
+
return await canExecuteOperation(this.authorizationService, await this.withCanonicalDocumentId(args, ctx), ctx.user?.address);
|
|
279
200
|
} catch (error) {
|
|
280
201
|
this.logger.error("Error in canExecuteOperation: @error", error);
|
|
281
202
|
throw error;
|
|
@@ -284,9 +205,8 @@ var AuthSubgraph = class extends BaseSubgraph {
|
|
|
284
205
|
documentProtection: async (_parent, args, ctx) => {
|
|
285
206
|
this.logger.debug("documentProtection(@args)", args);
|
|
286
207
|
if (!this.documentPermissionService) throw new GraphQLError("DocumentPermissionService not available");
|
|
287
|
-
if (!ctx.user?.address) throw new GraphQLError("Authentication required to view document protection info");
|
|
288
208
|
try {
|
|
289
|
-
return await documentProtection(this.documentPermissionService, args);
|
|
209
|
+
return await documentProtection(this.documentPermissionService, this.authorizationService, await this.withCanonicalDocumentId(args, ctx), ctx.user?.address);
|
|
290
210
|
} catch (error) {
|
|
291
211
|
this.logger.error("Error in documentProtection: @error", error);
|
|
292
212
|
throw error;
|
|
@@ -298,7 +218,7 @@ var AuthSubgraph = class extends BaseSubgraph {
|
|
|
298
218
|
this.logger.debug("setDocumentProtection(@args)", args);
|
|
299
219
|
if (!this.documentPermissionService) throw new GraphQLError("DocumentPermissionService not available");
|
|
300
220
|
try {
|
|
301
|
-
return await setDocumentProtection(this.documentPermissionService, this.authorizationService, args, ctx.user?.address);
|
|
221
|
+
return await setDocumentProtection(this.documentPermissionService, this.authorizationService, await this.withCanonicalDocumentId(args, ctx), ctx.user?.address);
|
|
302
222
|
} catch (error) {
|
|
303
223
|
this.logger.error("Error in setDocumentProtection: @error", error);
|
|
304
224
|
throw error;
|
|
@@ -308,7 +228,7 @@ var AuthSubgraph = class extends BaseSubgraph {
|
|
|
308
228
|
this.logger.debug("transferDocumentOwnership(@args)", args);
|
|
309
229
|
if (!this.documentPermissionService) throw new GraphQLError("DocumentPermissionService not available");
|
|
310
230
|
try {
|
|
311
|
-
return await transferDocumentOwnership(this.documentPermissionService, this.authorizationService, args, ctx.user?.address);
|
|
231
|
+
return await transferDocumentOwnership(this.documentPermissionService, this.authorizationService, await this.withCanonicalDocumentId(args, ctx), ctx.user?.address);
|
|
312
232
|
} catch (error) {
|
|
313
233
|
this.logger.error("Error in transferDocumentOwnership: @error", error);
|
|
314
234
|
throw error;
|
|
@@ -318,7 +238,11 @@ var AuthSubgraph = class extends BaseSubgraph {
|
|
|
318
238
|
this.logger.debug("grantDocumentPermission(@args)", args);
|
|
319
239
|
if (!this.documentPermissionService) throw new GraphQLError("DocumentPermissionService not available");
|
|
320
240
|
try {
|
|
321
|
-
|
|
241
|
+
const resolved = await this.withCanonicalDocumentId(args, ctx);
|
|
242
|
+
return await grantDocumentPermission(this.documentPermissionService, this.authorizationService, {
|
|
243
|
+
...resolved,
|
|
244
|
+
permission: resolved.permission
|
|
245
|
+
}, ctx.user?.address);
|
|
322
246
|
} catch (error) {
|
|
323
247
|
this.logger.error("Error in grantDocumentPermission: @error", error);
|
|
324
248
|
throw error;
|
|
@@ -328,77 +252,17 @@ var AuthSubgraph = class extends BaseSubgraph {
|
|
|
328
252
|
this.logger.debug("revokeDocumentPermission(@args)", args);
|
|
329
253
|
if (!this.documentPermissionService) throw new GraphQLError("DocumentPermissionService not available");
|
|
330
254
|
try {
|
|
331
|
-
return await revokeDocumentPermission(this.documentPermissionService, this.authorizationService, args, ctx.user?.address);
|
|
255
|
+
return await revokeDocumentPermission(this.documentPermissionService, this.authorizationService, await this.withCanonicalDocumentId(args, ctx), ctx.user?.address);
|
|
332
256
|
} catch (error) {
|
|
333
257
|
this.logger.error("Error in revokeDocumentPermission: @error", error);
|
|
334
258
|
throw error;
|
|
335
259
|
}
|
|
336
260
|
},
|
|
337
|
-
createGroup: async (_parent, args) => {
|
|
338
|
-
this.logger.debug("createGroup(@args)", args);
|
|
339
|
-
if (!this.documentPermissionService) throw new GraphQLError("DocumentPermissionService not available");
|
|
340
|
-
try {
|
|
341
|
-
return await createGroup(this.documentPermissionService, args);
|
|
342
|
-
} catch (error) {
|
|
343
|
-
this.logger.error("Error in createGroup: @error", error);
|
|
344
|
-
throw error;
|
|
345
|
-
}
|
|
346
|
-
},
|
|
347
|
-
deleteGroup: async (_parent, args) => {
|
|
348
|
-
this.logger.debug("deleteGroup(@args)", args);
|
|
349
|
-
if (!this.documentPermissionService) throw new GraphQLError("DocumentPermissionService not available");
|
|
350
|
-
try {
|
|
351
|
-
return await deleteGroup(this.documentPermissionService, args);
|
|
352
|
-
} catch (error) {
|
|
353
|
-
this.logger.error("Error in deleteGroup: @error", error);
|
|
354
|
-
throw error;
|
|
355
|
-
}
|
|
356
|
-
},
|
|
357
|
-
addUserToGroup: async (_parent, args) => {
|
|
358
|
-
this.logger.debug("addUserToGroup(@args)", args);
|
|
359
|
-
if (!this.documentPermissionService) throw new GraphQLError("DocumentPermissionService not available");
|
|
360
|
-
try {
|
|
361
|
-
return await addUserToGroup(this.documentPermissionService, args);
|
|
362
|
-
} catch (error) {
|
|
363
|
-
this.logger.error("Error in addUserToGroup: @error", error);
|
|
364
|
-
throw error;
|
|
365
|
-
}
|
|
366
|
-
},
|
|
367
|
-
removeUserFromGroup: async (_parent, args) => {
|
|
368
|
-
this.logger.debug("removeUserFromGroup(@args)", args);
|
|
369
|
-
if (!this.documentPermissionService) throw new GraphQLError("DocumentPermissionService not available");
|
|
370
|
-
try {
|
|
371
|
-
return await removeUserFromGroup(this.documentPermissionService, args);
|
|
372
|
-
} catch (error) {
|
|
373
|
-
this.logger.error("Error in removeUserFromGroup: @error", error);
|
|
374
|
-
throw error;
|
|
375
|
-
}
|
|
376
|
-
},
|
|
377
|
-
grantGroupPermission: async (_parent, args, ctx) => {
|
|
378
|
-
this.logger.debug("grantGroupPermission(@args)", args);
|
|
379
|
-
if (!this.documentPermissionService) throw new GraphQLError("DocumentPermissionService not available");
|
|
380
|
-
try {
|
|
381
|
-
return await grantGroupPermission(this.documentPermissionService, this.authorizationService, args, ctx.user?.address);
|
|
382
|
-
} catch (error) {
|
|
383
|
-
this.logger.error("Error in grantGroupPermission: @error", error);
|
|
384
|
-
throw error;
|
|
385
|
-
}
|
|
386
|
-
},
|
|
387
|
-
revokeGroupPermission: async (_parent, args, ctx) => {
|
|
388
|
-
this.logger.debug("revokeGroupPermission(@args)", args);
|
|
389
|
-
if (!this.documentPermissionService) throw new GraphQLError("DocumentPermissionService not available");
|
|
390
|
-
try {
|
|
391
|
-
return await revokeGroupPermission(this.documentPermissionService, this.authorizationService, args, ctx.user?.address);
|
|
392
|
-
} catch (error) {
|
|
393
|
-
this.logger.error("Error in revokeGroupPermission: @error", error);
|
|
394
|
-
throw error;
|
|
395
|
-
}
|
|
396
|
-
},
|
|
397
261
|
grantOperationPermission: async (_parent, args, ctx) => {
|
|
398
262
|
this.logger.debug("grantOperationPermission(@args)", args);
|
|
399
263
|
if (!this.documentPermissionService) throw new GraphQLError("DocumentPermissionService not available");
|
|
400
264
|
try {
|
|
401
|
-
return await grantOperationPermission(this.documentPermissionService, this.authorizationService, args, ctx.user?.address);
|
|
265
|
+
return await grantOperationPermission(this.documentPermissionService, this.authorizationService, await this.withCanonicalDocumentId(args, ctx), ctx.user?.address);
|
|
402
266
|
} catch (error) {
|
|
403
267
|
this.logger.error("Error in grantOperationPermission: @error", error);
|
|
404
268
|
throw error;
|
|
@@ -408,49 +272,13 @@ var AuthSubgraph = class extends BaseSubgraph {
|
|
|
408
272
|
this.logger.debug("revokeOperationPermission(@args)", args);
|
|
409
273
|
if (!this.documentPermissionService) throw new GraphQLError("DocumentPermissionService not available");
|
|
410
274
|
try {
|
|
411
|
-
return await revokeOperationPermission(this.documentPermissionService, this.authorizationService, args, ctx.user?.address);
|
|
275
|
+
return await revokeOperationPermission(this.documentPermissionService, this.authorizationService, await this.withCanonicalDocumentId(args, ctx), ctx.user?.address);
|
|
412
276
|
} catch (error) {
|
|
413
277
|
this.logger.error("Error in revokeOperationPermission: @error", error);
|
|
414
278
|
throw error;
|
|
415
279
|
}
|
|
416
|
-
},
|
|
417
|
-
grantGroupOperationPermission: async (_parent, args, ctx) => {
|
|
418
|
-
this.logger.debug("grantGroupOperationPermission(@args)", args);
|
|
419
|
-
if (!this.documentPermissionService) throw new GraphQLError("DocumentPermissionService not available");
|
|
420
|
-
try {
|
|
421
|
-
return await grantGroupOperationPermission(this.documentPermissionService, this.authorizationService, args, ctx.user?.address);
|
|
422
|
-
} catch (error) {
|
|
423
|
-
this.logger.error("Error in grantGroupOperationPermission: @error", error);
|
|
424
|
-
throw error;
|
|
425
|
-
}
|
|
426
|
-
},
|
|
427
|
-
revokeGroupOperationPermission: async (_parent, args, ctx) => {
|
|
428
|
-
this.logger.debug("revokeGroupOperationPermission(@args)", args);
|
|
429
|
-
if (!this.documentPermissionService) throw new GraphQLError("DocumentPermissionService not available");
|
|
430
|
-
try {
|
|
431
|
-
return await revokeGroupOperationPermission(this.documentPermissionService, this.authorizationService, args, ctx.user?.address);
|
|
432
|
-
} catch (error) {
|
|
433
|
-
this.logger.error("Error in revokeGroupOperationPermission: @error", error);
|
|
434
|
-
throw error;
|
|
435
|
-
}
|
|
436
280
|
}
|
|
437
|
-
}
|
|
438
|
-
Group: { members: async (parent) => {
|
|
439
|
-
if (!this.documentPermissionService) throw new GraphQLError("DocumentPermissionService not available");
|
|
440
|
-
return getGroupMembers(this.documentPermissionService, parent.id);
|
|
441
|
-
} },
|
|
442
|
-
DocumentGroupPermission: { group: async (parent) => {
|
|
443
|
-
if (!this.documentPermissionService) throw new GraphQLError("DocumentPermissionService not available");
|
|
444
|
-
const grp = await group(this.documentPermissionService, { id: parent.groupId });
|
|
445
|
-
if (!grp) throw new GraphQLError(`Group not found: ${parent.groupId}`);
|
|
446
|
-
return grp;
|
|
447
|
-
} },
|
|
448
|
-
OperationGroupPermission: { group: async (parent) => {
|
|
449
|
-
if (!this.documentPermissionService) throw new GraphQLError("DocumentPermissionService not available");
|
|
450
|
-
const grp = await group(this.documentPermissionService, { id: parent.groupId });
|
|
451
|
-
if (!grp) throw new GraphQLError(`Group not found: ${parent.groupId}`);
|
|
452
|
-
return grp;
|
|
453
|
-
} }
|
|
281
|
+
}
|
|
454
282
|
};
|
|
455
283
|
onSetup() {
|
|
456
284
|
this.logger.debug("Setting up AuthSubgraph");
|
|
@@ -477,7 +305,7 @@ function createAuthFetchMiddleware(authService) {
|
|
|
477
305
|
async function createGatewayAdapter(type, logger) {
|
|
478
306
|
switch (type) {
|
|
479
307
|
case "apollo": {
|
|
480
|
-
const { ApolloGatewayAdapter } = await import("./adapter-gateway-apollo-
|
|
308
|
+
const { ApolloGatewayAdapter } = await import("./adapter-gateway-apollo-CxV_hMTv.mjs");
|
|
481
309
|
return new ApolloGatewayAdapter(logger);
|
|
482
310
|
}
|
|
483
311
|
case "mercurius": {
|
|
@@ -499,115 +327,6 @@ async function createHttpAdapter(type) {
|
|
|
499
327
|
}
|
|
500
328
|
}
|
|
501
329
|
//#endregion
|
|
502
|
-
//#region src/services/auth.service.ts
|
|
503
|
-
var AuthService = class {
|
|
504
|
-
config;
|
|
505
|
-
constructor(config) {
|
|
506
|
-
this.config = config;
|
|
507
|
-
}
|
|
508
|
-
async authenticateRequest(request) {
|
|
509
|
-
if (!this.config.enabled) return {
|
|
510
|
-
user: void 0,
|
|
511
|
-
admins: [],
|
|
512
|
-
auth_enabled: false
|
|
513
|
-
};
|
|
514
|
-
const method = request.method;
|
|
515
|
-
if (method === "OPTIONS" || method === "GET") return {
|
|
516
|
-
user: void 0,
|
|
517
|
-
admins: this.config.admins,
|
|
518
|
-
auth_enabled: true
|
|
519
|
-
};
|
|
520
|
-
return this.verifyBearer(request.headers.get("authorization") ?? void 0);
|
|
521
|
-
}
|
|
522
|
-
/**
|
|
523
|
-
* Verify a Bearer token regardless of HTTP method. Use this from non-GraphQL
|
|
524
|
-
* middleware that must enforce authentication on every request.
|
|
525
|
-
*/
|
|
526
|
-
async verifyBearer(authorization) {
|
|
527
|
-
if (!this.config.enabled) return {
|
|
528
|
-
user: void 0,
|
|
529
|
-
admins: [],
|
|
530
|
-
auth_enabled: false
|
|
531
|
-
};
|
|
532
|
-
const token = authorization?.split(" ")[1];
|
|
533
|
-
if (!token) return {
|
|
534
|
-
user: void 0,
|
|
535
|
-
admins: this.config.admins,
|
|
536
|
-
auth_enabled: true
|
|
537
|
-
};
|
|
538
|
-
try {
|
|
539
|
-
const verified = await this.verifyToken(token);
|
|
540
|
-
if (!verified) return new Response(JSON.stringify({ error: "Verification failed" }), { status: 401 });
|
|
541
|
-
const user = this.extractUserFromVerification(verified);
|
|
542
|
-
if (!user) return new Response(JSON.stringify({ error: "Missing credentials" }), { status: 401 });
|
|
543
|
-
if (!this.config.skipCredentialVerification) {
|
|
544
|
-
if (!await this.verifyCredentialExists(user.address, user.chainId, verified.issuer)) return new Response(JSON.stringify({ error: "Credentials no longer valid" }), { status: 401 });
|
|
545
|
-
}
|
|
546
|
-
return {
|
|
547
|
-
user,
|
|
548
|
-
admins: this.config.admins,
|
|
549
|
-
auth_enabled: true
|
|
550
|
-
};
|
|
551
|
-
} catch {
|
|
552
|
-
return new Response(JSON.stringify({ error: "Authentication failed" }), { status: 401 });
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
async authenticateWebSocketConnection(connectionParams) {
|
|
556
|
-
if (!this.config.enabled) return null;
|
|
557
|
-
const authHeader = connectionParams.authorization;
|
|
558
|
-
if (!authHeader) throw new Error("Missing authorization in connection parameters");
|
|
559
|
-
const token = authHeader.split(" ")[1];
|
|
560
|
-
if (!token) throw new Error("Invalid authorization format");
|
|
561
|
-
const verified = await this.verifyToken(token);
|
|
562
|
-
if (!verified) throw new Error("Token verification failed");
|
|
563
|
-
const user = this.extractUserFromVerification(verified);
|
|
564
|
-
if (!user) throw new Error("Invalid credentials");
|
|
565
|
-
if (!this.config.skipCredentialVerification) {
|
|
566
|
-
if (!await this.verifyCredentialExists(user.address, user.chainId, verified.issuer)) throw new Error("Credentials no longer valid");
|
|
567
|
-
}
|
|
568
|
-
return user;
|
|
569
|
-
}
|
|
570
|
-
/**
|
|
571
|
-
* Verify the auth bearer token
|
|
572
|
-
*/
|
|
573
|
-
async verifyToken(token) {
|
|
574
|
-
return await verifyAuthBearerToken(token);
|
|
575
|
-
}
|
|
576
|
-
/**
|
|
577
|
-
* Extract user information from verification result
|
|
578
|
-
*/
|
|
579
|
-
extractUserFromVerification(verified) {
|
|
580
|
-
try {
|
|
581
|
-
const { address, chainId, networkId } = verified.verifiableCredential.credentialSubject;
|
|
582
|
-
if (!address || !chainId || !networkId) return null;
|
|
583
|
-
return {
|
|
584
|
-
address,
|
|
585
|
-
chainId,
|
|
586
|
-
networkId
|
|
587
|
-
};
|
|
588
|
-
} catch {
|
|
589
|
-
return null;
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
/**
|
|
593
|
-
* Verify that the credential still exists on the Renown API
|
|
594
|
-
*/
|
|
595
|
-
async verifyCredentialExists(address, chainId, appId) {
|
|
596
|
-
const url = `https://www.renown.id/api/auth/credential?address=${address}&chainId=${chainId}&connectId=${appId}&appId=${appId}`;
|
|
597
|
-
try {
|
|
598
|
-
const response = await fetch(url, { method: "GET" });
|
|
599
|
-
const credential = (await response.json()).credential;
|
|
600
|
-
const appIdVerfied = credential.credentialSubject.id;
|
|
601
|
-
const addressVerfied = credential.issuer.id.split(":")[4];
|
|
602
|
-
const chainIdVerfied = credential.issuer.id.split(":")[3];
|
|
603
|
-
if (response.status !== 200) return false;
|
|
604
|
-
return appIdVerfied === appId && addressVerfied.toLocaleLowerCase() === address.toLocaleLowerCase() && chainIdVerfied === chainId.toString();
|
|
605
|
-
} catch {
|
|
606
|
-
return false;
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
};
|
|
610
|
-
//#endregion
|
|
611
330
|
//#region src/utils/create-schema.ts
|
|
612
331
|
const logger = childLogger(["reactor-api", "create-schema"]);
|
|
613
332
|
/**
|
|
@@ -631,6 +350,49 @@ const stripScalarDefinitions = (doc) => {
|
|
|
631
350
|
definitions: filteredDefinitions
|
|
632
351
|
});
|
|
633
352
|
};
|
|
353
|
+
/**
|
|
354
|
+
* Type-system definition kinds that GraphQL requires to be uniquely named.
|
|
355
|
+
* A subgraph SDL with two definitions sharing a name fails federation
|
|
356
|
+
* composition ("There can be only one type named X"), which is fatal to the
|
|
357
|
+
* whole gateway. We dedupe these by name; field/operation/extension nodes are
|
|
358
|
+
* left untouched.
|
|
359
|
+
*/
|
|
360
|
+
const TYPE_DEFINITION_KINDS = new Set([
|
|
361
|
+
Kind.OBJECT_TYPE_DEFINITION,
|
|
362
|
+
Kind.ENUM_TYPE_DEFINITION,
|
|
363
|
+
Kind.INPUT_OBJECT_TYPE_DEFINITION,
|
|
364
|
+
Kind.INTERFACE_TYPE_DEFINITION,
|
|
365
|
+
Kind.UNION_TYPE_DEFINITION,
|
|
366
|
+
Kind.SCALAR_TYPE_DEFINITION
|
|
367
|
+
]);
|
|
368
|
+
/**
|
|
369
|
+
* Drop duplicate type-system definitions by name, keeping the first occurrence.
|
|
370
|
+
* Deduping is by name across ALL kinds (a `type` and an `enum` sharing a name
|
|
371
|
+
* collide in GraphQL too), so the first definition of a given name wins
|
|
372
|
+
* regardless of kind. State types are emitted before operation types, so
|
|
373
|
+
* keep-first preserves the authoritative state definition. This heals a document
|
|
374
|
+
* model that defines the same name twice (global+local, state+operation, or
|
|
375
|
+
* twice in one scope) so the assembled subgraph SDL composes instead of crashing
|
|
376
|
+
* the gateway (Sentry #917).
|
|
377
|
+
*/
|
|
378
|
+
const dedupeTypeDefinitions = (doc) => {
|
|
379
|
+
const seen = /* @__PURE__ */ new Set();
|
|
380
|
+
const definitions = doc.definitions.filter((def) => {
|
|
381
|
+
if (!TYPE_DEFINITION_KINDS.has(def.kind)) return true;
|
|
382
|
+
const name = def.name?.value;
|
|
383
|
+
if (!name) return true;
|
|
384
|
+
if (seen.has(name)) {
|
|
385
|
+
logger.debug(`Dropping duplicate type definition: ${name}`);
|
|
386
|
+
return false;
|
|
387
|
+
}
|
|
388
|
+
seen.add(name);
|
|
389
|
+
return true;
|
|
390
|
+
});
|
|
391
|
+
return {
|
|
392
|
+
kind: Kind.DOCUMENT,
|
|
393
|
+
definitions
|
|
394
|
+
};
|
|
395
|
+
};
|
|
634
396
|
const buildSubgraphSchemaModule = (documentModels, resolvers, typeDefs) => {
|
|
635
397
|
const newResolvers = {
|
|
636
398
|
...resolvers,
|
|
@@ -696,7 +458,7 @@ const getDocumentModelTypeDefs = (documentModels, typeDefs$1) => {
|
|
|
696
458
|
stateJSON: JSONObject
|
|
697
459
|
}\n`;
|
|
698
460
|
});
|
|
699
|
-
return gql`
|
|
461
|
+
return dedupeTypeDefinitions(gql`
|
|
700
462
|
scalar JSONObject
|
|
701
463
|
${typeDefs.join("\n").replaceAll(";", "")}
|
|
702
464
|
|
|
@@ -768,7 +530,7 @@ const getDocumentModelTypeDefs = (documentModels, typeDefs$1) => {
|
|
|
768
530
|
}
|
|
769
531
|
|
|
770
532
|
${stripScalarDefinitions(typeDefs$1)}
|
|
771
|
-
|
|
533
|
+
`);
|
|
772
534
|
};
|
|
773
535
|
/**
|
|
774
536
|
* Extract type names from a GraphQL schema.
|
|
@@ -2070,6 +1832,17 @@ function toGqlDocumentChangeEvent(event) {
|
|
|
2070
1832
|
} : null
|
|
2071
1833
|
};
|
|
2072
1834
|
}
|
|
1835
|
+
/**
|
|
1836
|
+
* Distinct document ids a change event references: affected documents plus the
|
|
1837
|
+
* parent/child ids in `context` (delete/relationship events carry only context).
|
|
1838
|
+
*/
|
|
1839
|
+
function collectChangedDocumentIds(event) {
|
|
1840
|
+
const ids = /* @__PURE__ */ new Set();
|
|
1841
|
+
for (const doc of event.documents) if (doc.header.id) ids.add(doc.header.id);
|
|
1842
|
+
if (event.context?.parentId) ids.add(event.context.parentId);
|
|
1843
|
+
if (event.context?.childId) ids.add(event.context.childId);
|
|
1844
|
+
return [...ids];
|
|
1845
|
+
}
|
|
2073
1846
|
function matchesSearchFilter(event, search) {
|
|
2074
1847
|
if (search.type) {
|
|
2075
1848
|
if (!event.documents.some((doc) => doc.header.documentType === search.type)) return false;
|
|
@@ -2541,7 +2314,7 @@ async function touchChannel(syncManager, args) {
|
|
|
2541
2314
|
};
|
|
2542
2315
|
const options = { sinceTimestampUtcMs: args.input.sinceTimestampUtcMs };
|
|
2543
2316
|
try {
|
|
2544
|
-
await syncManager.add(args.input.name, args.input.collectionId, {
|
|
2317
|
+
await syncManager.add(args.input.name, DriveCollectionId.fromKey(args.input.collectionId), {
|
|
2545
2318
|
type: "polling",
|
|
2546
2319
|
parameters: {}
|
|
2547
2320
|
}, filter, options, args.input.id);
|
|
@@ -2564,14 +2337,14 @@ async function touchChannel(syncManager, args) {
|
|
|
2564
2337
|
* client ordinal the switchboard has successfully applied, so the client
|
|
2565
2338
|
* can trim its own outbox)
|
|
2566
2339
|
*/
|
|
2567
|
-
function pollSyncEnvelopes(syncManager, args) {
|
|
2340
|
+
function pollSyncEnvelopes(syncManager, args, forbiddenIds = /* @__PURE__ */ new Set()) {
|
|
2568
2341
|
let remote;
|
|
2569
2342
|
try {
|
|
2570
2343
|
remote = syncManager.getById(args.channelId);
|
|
2571
2344
|
} catch (error) {
|
|
2572
2345
|
throw new GraphQLError(`Channel not found: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
2573
2346
|
}
|
|
2574
|
-
const deadLetters = remote.channel.deadLetter.items.map((syncOp) => ({
|
|
2347
|
+
const deadLetters = remote.channel.deadLetter.items.filter((syncOp) => !forbiddenIds.has(syncOp.documentId)).map((syncOp) => ({
|
|
2575
2348
|
documentId: syncOp.documentId,
|
|
2576
2349
|
error: syncOp.error?.message ?? "Unknown error",
|
|
2577
2350
|
jobId: syncOp.jobId,
|
|
@@ -2595,6 +2368,12 @@ function pollSyncEnvelopes(syncManager, args) {
|
|
|
2595
2368
|
}
|
|
2596
2369
|
}
|
|
2597
2370
|
const operations = remote.channel.outbox.items;
|
|
2371
|
+
if (forbiddenIds.size > 0) {
|
|
2372
|
+
for (const syncOp of operations) if (forbiddenIds.has(syncOp.documentId)) {
|
|
2373
|
+
syncOp.deliveredCount = syncOp.operations.length;
|
|
2374
|
+
syncOp.emittedCount = syncOp.operations.length;
|
|
2375
|
+
}
|
|
2376
|
+
}
|
|
2598
2377
|
if (operations.length === 0) return {
|
|
2599
2378
|
envelopes: [],
|
|
2600
2379
|
ackOrdinal: remote.channel.inbox.ackOrdinal,
|
|
@@ -2786,7 +2565,7 @@ var DocumentModelSubgraph = class extends BaseSubgraph {
|
|
|
2786
2565
|
view
|
|
2787
2566
|
});
|
|
2788
2567
|
if (result.document.documentType !== documentType) throw new GraphQLError(`Document with id ${identifier} is not of type ${documentType}`);
|
|
2789
|
-
await this.
|
|
2568
|
+
await this.assertCanReadCanonical(result.document.id, ctx);
|
|
2790
2569
|
return result;
|
|
2791
2570
|
},
|
|
2792
2571
|
documents: async (_, args, ctx) => {
|
|
@@ -2828,10 +2607,10 @@ var DocumentModelSubgraph = class extends BaseSubgraph {
|
|
|
2828
2607
|
return result;
|
|
2829
2608
|
},
|
|
2830
2609
|
documentOutgoingRelationships: async (_, args, ctx) => {
|
|
2831
|
-
const {
|
|
2832
|
-
await this.assertCanRead(sourceIdentifier, ctx);
|
|
2610
|
+
const { relationshipType, view, paging } = args;
|
|
2611
|
+
const handle = await this.assertCanRead(args.sourceIdentifier, ctx);
|
|
2833
2612
|
const result = await documentOutgoingRelationships(this.reactorClient, {
|
|
2834
|
-
sourceIdentifier,
|
|
2613
|
+
sourceIdentifier: handle.fetchIdentifier,
|
|
2835
2614
|
relationshipType,
|
|
2836
2615
|
view,
|
|
2837
2616
|
paging
|
|
@@ -2844,10 +2623,10 @@ var DocumentModelSubgraph = class extends BaseSubgraph {
|
|
|
2844
2623
|
};
|
|
2845
2624
|
},
|
|
2846
2625
|
documentIncomingRelationships: async (_, args, ctx) => {
|
|
2847
|
-
const {
|
|
2848
|
-
await this.assertCanRead(targetIdentifier, ctx);
|
|
2626
|
+
const { relationshipType, view, paging } = args;
|
|
2627
|
+
const handle = await this.assertCanRead(args.targetIdentifier, ctx);
|
|
2849
2628
|
return documentIncomingRelationships(this.reactorClient, {
|
|
2850
|
-
targetIdentifier,
|
|
2629
|
+
targetIdentifier: handle.fetchIdentifier,
|
|
2851
2630
|
relationshipType,
|
|
2852
2631
|
view,
|
|
2853
2632
|
paging
|
|
@@ -2857,8 +2636,9 @@ var DocumentModelSubgraph = class extends BaseSubgraph {
|
|
|
2857
2636
|
Mutation: { [documentName]: () => ({}) },
|
|
2858
2637
|
[`${documentName}Mutations`]: {
|
|
2859
2638
|
createDocument: async (_, args, ctx) => {
|
|
2860
|
-
const {
|
|
2861
|
-
|
|
2639
|
+
const { name, slug, preferredEditor, initialState } = args;
|
|
2640
|
+
let parentIdentifier = args.parentIdentifier;
|
|
2641
|
+
if (parentIdentifier) parentIdentifier = (await this.assertCanWrite(parentIdentifier, ctx)).fetchIdentifier;
|
|
2862
2642
|
else this.assertCanCreate(ctx);
|
|
2863
2643
|
let createdDoc;
|
|
2864
2644
|
if (initialState || preferredEditor) createdDoc = await createDocumentWithInitialState(this.reactorClient, {
|
|
@@ -2879,8 +2659,8 @@ var DocumentModelSubgraph = class extends BaseSubgraph {
|
|
|
2879
2659
|
return createdDoc;
|
|
2880
2660
|
},
|
|
2881
2661
|
createEmptyDocument: async (_, args, ctx) => {
|
|
2882
|
-
|
|
2883
|
-
if (parentIdentifier) await this.assertCanWrite(parentIdentifier, ctx);
|
|
2662
|
+
let parentIdentifier = args.parentIdentifier;
|
|
2663
|
+
if (parentIdentifier) parentIdentifier = (await this.assertCanWrite(parentIdentifier, ctx)).fetchIdentifier;
|
|
2884
2664
|
else this.assertCanCreate(ctx);
|
|
2885
2665
|
const result = await createEmptyDocument(this.reactorClient, {
|
|
2886
2666
|
documentType,
|
|
@@ -2892,24 +2672,24 @@ var DocumentModelSubgraph = class extends BaseSubgraph {
|
|
|
2892
2672
|
...operations.reduce((mutations, op) => {
|
|
2893
2673
|
mutations[camelCase(op.name)] = async (_, args, ctx) => {
|
|
2894
2674
|
const { docId, input } = args;
|
|
2895
|
-
await this.assertCanExecuteOperation(docId, op.name, ctx);
|
|
2896
|
-
if ((await this.reactorClient.get(
|
|
2675
|
+
const effectiveDocId = (await this.assertCanExecuteOperation(docId, op.name, ctx)).fetchIdentifier;
|
|
2676
|
+
if ((await this.reactorClient.get(effectiveDocId)).header.documentType !== documentType) throw new GraphQLError(`Document with id ${docId} is not of type ${documentType}`);
|
|
2897
2677
|
const action = this.documentModel.actions[camelCase(op.name)];
|
|
2898
2678
|
if (!action) throw new GraphQLError(`Action ${op.name} not found`);
|
|
2899
2679
|
try {
|
|
2900
|
-
return toGqlPhDocument(await this.reactorClient.execute(
|
|
2680
|
+
return toGqlPhDocument(await this.reactorClient.execute(effectiveDocId, "main", [action(input)]));
|
|
2901
2681
|
} catch (error) {
|
|
2902
2682
|
throw new GraphQLError(error instanceof Error ? error.message : `Failed to ${op.name}`);
|
|
2903
2683
|
}
|
|
2904
2684
|
};
|
|
2905
2685
|
mutations[`${camelCase(op.name)}Async`] = async (_, args, ctx) => {
|
|
2906
2686
|
const { docId, input } = args;
|
|
2907
|
-
await this.assertCanExecuteOperation(docId, op.name, ctx);
|
|
2908
|
-
if ((await this.reactorClient.get(
|
|
2687
|
+
const effectiveDocId = (await this.assertCanExecuteOperation(docId, op.name, ctx)).fetchIdentifier;
|
|
2688
|
+
if ((await this.reactorClient.get(effectiveDocId)).header.documentType !== documentType) throw new GraphQLError(`Document with id ${docId} is not of type ${documentType}`);
|
|
2909
2689
|
const action = this.documentModel.actions[camelCase(op.name)];
|
|
2910
2690
|
if (!action) throw new GraphQLError(`Action ${op.name} not found`);
|
|
2911
2691
|
try {
|
|
2912
|
-
return (await this.reactorClient.executeAsync(
|
|
2692
|
+
return (await this.reactorClient.executeAsync(effectiveDocId, "main", [action(input)])).id;
|
|
2913
2693
|
} catch (error) {
|
|
2914
2694
|
throw new GraphQLError(error instanceof Error ? error.message : `Failed to ${op.name}`);
|
|
2915
2695
|
}
|
|
@@ -3088,7 +2868,6 @@ var GraphQLManager = class {
|
|
|
3088
2868
|
coreSubgraphsMap = /* @__PURE__ */ new Map();
|
|
3089
2869
|
contextFields = {};
|
|
3090
2870
|
subgraphs = /* @__PURE__ */ new Map();
|
|
3091
|
-
authService = null;
|
|
3092
2871
|
subgraphWsDisposers = /* @__PURE__ */ new Map();
|
|
3093
2872
|
#authMiddleware;
|
|
3094
2873
|
#driveMiddleware;
|
|
@@ -3103,7 +2882,7 @@ var GraphQLManager = class {
|
|
|
3103
2882
|
*/
|
|
3104
2883
|
reactorDriveClient;
|
|
3105
2884
|
authorizationService;
|
|
3106
|
-
constructor(path, httpServer, wsServer, reactorClient, relationalDb, analyticsStore, syncManager, logger, httpAdapter, gatewayAdapter,
|
|
2885
|
+
constructor(path, httpServer, wsServer, reactorClient, relationalDb, analyticsStore, syncManager, logger, httpAdapter, gatewayAdapter, authService, documentPermissionService, featureFlags = DefaultFeatureFlags, port = 4001, authorizationService, reactorDriveClient) {
|
|
3107
2886
|
this.path = path;
|
|
3108
2887
|
this.httpServer = httpServer;
|
|
3109
2888
|
this.wsServer = wsServer;
|
|
@@ -3114,14 +2893,13 @@ var GraphQLManager = class {
|
|
|
3114
2893
|
this.logger = logger;
|
|
3115
2894
|
this.httpAdapter = httpAdapter;
|
|
3116
2895
|
this.gatewayAdapter = gatewayAdapter;
|
|
3117
|
-
this.
|
|
2896
|
+
this.authService = authService;
|
|
3118
2897
|
this.documentPermissionService = documentPermissionService;
|
|
3119
2898
|
this.featureFlags = featureFlags;
|
|
3120
2899
|
this.port = port;
|
|
3121
2900
|
if (!authorizationService) throw new Error("GraphQLManager requires an authorizationService");
|
|
3122
2901
|
this.authorizationService = authorizationService;
|
|
3123
2902
|
this.reactorDriveClient = reactorDriveClient;
|
|
3124
|
-
if (this.authConfig) this.authService = new AuthService(this.authConfig);
|
|
3125
2903
|
this.driveOwnershipCache = new DriveOwnershipCache(this.reactorClient);
|
|
3126
2904
|
this.wsServer.setMaxListeners(0);
|
|
3127
2905
|
}
|
|
@@ -3902,7 +3680,8 @@ function ensureJobSubscription(reactorClient, jobId) {
|
|
|
3902
3680
|
error: jobInfo.error?.message ?? null,
|
|
3903
3681
|
result: jobInfo.result ?? {}
|
|
3904
3682
|
},
|
|
3905
|
-
jobId
|
|
3683
|
+
jobId,
|
|
3684
|
+
documentId: jobInfo.documentId
|
|
3906
3685
|
};
|
|
3907
3686
|
pubSub.publish(SUBSCRIPTION_TRIGGERS.JOB_CHANGES, payload);
|
|
3908
3687
|
const isTerminal = String(jobInfo.status) === "FAILED" || String(jobInfo.status) === "READ_MODELS_READY" || jobInfo.completedAtUtcIso !== void 0;
|
|
@@ -3952,12 +3731,14 @@ var ReactorSubgraph = class extends BaseSubgraph {
|
|
|
3952
3731
|
* Delegates to base assertCanExecuteOperation for each action.
|
|
3953
3732
|
*/
|
|
3954
3733
|
async assertCanExecuteOperations(documentId, actions, ctx) {
|
|
3734
|
+
let handle = AuthorizedDocumentHandle.skipped(documentId);
|
|
3955
3735
|
for (const action of actions) {
|
|
3956
3736
|
if (!action || typeof action !== "object") continue;
|
|
3957
3737
|
const operationType = action.type;
|
|
3958
3738
|
if (typeof operationType !== "string") continue;
|
|
3959
|
-
await this.assertCanExecuteOperation(documentId, operationType, ctx);
|
|
3739
|
+
handle = await this.assertCanExecuteOperation(documentId, operationType, ctx);
|
|
3960
3740
|
}
|
|
3741
|
+
return handle;
|
|
3961
3742
|
}
|
|
3962
3743
|
/**
|
|
3963
3744
|
* Returns the drive id when the given identifier (id or slug) refers
|
|
@@ -3974,11 +3755,25 @@ var ReactorSubgraph = class extends BaseSubgraph {
|
|
|
3974
3755
|
return;
|
|
3975
3756
|
}
|
|
3976
3757
|
}
|
|
3758
|
+
/**
|
|
3759
|
+
* Adds to `forbidden` the canonical document ids in `syncOps` that the caller
|
|
3760
|
+
* cannot read, checking each distinct id once. Sync operation document ids are
|
|
3761
|
+
* canonical (never slugs), so no resolution is needed.
|
|
3762
|
+
*/
|
|
3763
|
+
async #collectForbiddenDocuments(syncOps, forbidden, ctx) {
|
|
3764
|
+
const checked = /* @__PURE__ */ new Set();
|
|
3765
|
+
for (const syncOp of syncOps) {
|
|
3766
|
+
const documentId = syncOp.documentId;
|
|
3767
|
+
if (checked.has(documentId) || forbidden.has(documentId)) continue;
|
|
3768
|
+
checked.add(documentId);
|
|
3769
|
+
if (!await this.canReadDocument(documentId, ctx)) forbidden.add(documentId);
|
|
3770
|
+
}
|
|
3771
|
+
}
|
|
3977
3772
|
typeDefs = gql(schema_default);
|
|
3978
3773
|
resolvers = {
|
|
3979
3774
|
PHDocument: { operations: async (parent, args, ctx) => {
|
|
3980
3775
|
this.logger.debug("PHDocument.operations(@parent.id, @args)", parent.id, args);
|
|
3981
|
-
await this.
|
|
3776
|
+
await this.assertCanReadCanonical(parent.id, ctx);
|
|
3982
3777
|
try {
|
|
3983
3778
|
const filter = {
|
|
3984
3779
|
documentId: parent.id,
|
|
@@ -4011,8 +3806,11 @@ var ReactorSubgraph = class extends BaseSubgraph {
|
|
|
4011
3806
|
document: async (_parent, args, ctx) => {
|
|
4012
3807
|
this.logger.debug("document(@args)", args);
|
|
4013
3808
|
try {
|
|
4014
|
-
await this.assertCanRead(args.identifier, ctx);
|
|
4015
|
-
return await document(this.reactorClient,
|
|
3809
|
+
const handle = await this.assertCanRead(args.identifier, ctx);
|
|
3810
|
+
return await document(this.reactorClient, {
|
|
3811
|
+
...args,
|
|
3812
|
+
identifier: handle.fetchIdentifier
|
|
3813
|
+
});
|
|
4016
3814
|
} catch (error) {
|
|
4017
3815
|
this.logger.error("Error in document: @Error", error);
|
|
4018
3816
|
throw error;
|
|
@@ -4021,8 +3819,11 @@ var ReactorSubgraph = class extends BaseSubgraph {
|
|
|
4021
3819
|
documentOutgoingRelationships: async (_parent, args, ctx) => {
|
|
4022
3820
|
this.logger.debug("documentOutgoingRelationships(@args)", args);
|
|
4023
3821
|
try {
|
|
4024
|
-
await this.assertCanRead(args.sourceIdentifier, ctx);
|
|
4025
|
-
return await documentOutgoingRelationships(this.reactorClient,
|
|
3822
|
+
const handle = await this.assertCanRead(args.sourceIdentifier, ctx);
|
|
3823
|
+
return await documentOutgoingRelationships(this.reactorClient, {
|
|
3824
|
+
...args,
|
|
3825
|
+
sourceIdentifier: handle.fetchIdentifier
|
|
3826
|
+
});
|
|
4026
3827
|
} catch (error) {
|
|
4027
3828
|
this.logger.error("Error in documentOutgoingRelationships: @Error", error);
|
|
4028
3829
|
throw error;
|
|
@@ -4031,8 +3832,11 @@ var ReactorSubgraph = class extends BaseSubgraph {
|
|
|
4031
3832
|
documentIncomingRelationships: async (_parent, args, ctx) => {
|
|
4032
3833
|
this.logger.debug("documentIncomingRelationships(@args)", args);
|
|
4033
3834
|
try {
|
|
4034
|
-
await this.assertCanRead(args.targetIdentifier, ctx);
|
|
4035
|
-
const result = await documentIncomingRelationships(this.reactorClient,
|
|
3835
|
+
const handle = await this.assertCanRead(args.targetIdentifier, ctx);
|
|
3836
|
+
const result = await documentIncomingRelationships(this.reactorClient, {
|
|
3837
|
+
...args,
|
|
3838
|
+
targetIdentifier: handle.fetchIdentifier
|
|
3839
|
+
});
|
|
4036
3840
|
if (!this.authorizationService.isSupremeAdmin(ctx.user?.address)) {
|
|
4037
3841
|
const filteredItems = [];
|
|
4038
3842
|
for (const item of result.items) if (await this.canReadDocument(item.id, ctx)) filteredItems.push(item);
|
|
@@ -4080,17 +3884,37 @@ var ReactorSubgraph = class extends BaseSubgraph {
|
|
|
4080
3884
|
documentOperations: async (_parent, args, ctx) => {
|
|
4081
3885
|
this.logger.debug("documentOperations(@args)", args);
|
|
4082
3886
|
try {
|
|
4083
|
-
await this.assertCanRead(args.filter.documentId, ctx);
|
|
4084
|
-
return await documentOperations(this.reactorClient,
|
|
3887
|
+
const handle = await this.assertCanRead(args.filter.documentId, ctx);
|
|
3888
|
+
return await documentOperations(this.reactorClient, {
|
|
3889
|
+
...args,
|
|
3890
|
+
filter: {
|
|
3891
|
+
...args.filter,
|
|
3892
|
+
documentId: handle.fetchIdentifier
|
|
3893
|
+
}
|
|
3894
|
+
});
|
|
4085
3895
|
} catch (error) {
|
|
4086
3896
|
this.logger.error("Error in documentOperations: @Error", error);
|
|
4087
3897
|
throw error;
|
|
4088
3898
|
}
|
|
4089
3899
|
},
|
|
4090
|
-
pollSyncEnvelopes: (_parent, args) => {
|
|
3900
|
+
pollSyncEnvelopes: async (_parent, args, ctx) => {
|
|
4091
3901
|
this.logger.debug("pollSyncEnvelopes(@args)", args);
|
|
4092
3902
|
try {
|
|
4093
|
-
|
|
3903
|
+
let remote;
|
|
3904
|
+
try {
|
|
3905
|
+
remote = this.syncManager.getById(args.channelId);
|
|
3906
|
+
} catch (error) {
|
|
3907
|
+
throw new GraphQLError(`Channel not found: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
3908
|
+
}
|
|
3909
|
+
const driveId = remote.collectionId.driveId;
|
|
3910
|
+
const isAdmin = this.authorizationService.isSupremeAdmin(ctx.user?.address);
|
|
3911
|
+
if (!isAdmin) await this.assertCanReadCanonical(driveId, ctx);
|
|
3912
|
+
const forbiddenIds = /* @__PURE__ */ new Set();
|
|
3913
|
+
if (!isAdmin) {
|
|
3914
|
+
if (await this.authorizationService.canRead(driveId, void 0)) await this.#collectForbiddenDocuments(remote.channel.outbox.items, forbiddenIds, ctx);
|
|
3915
|
+
await this.#collectForbiddenDocuments(remote.channel.deadLetter.items, forbiddenIds, ctx);
|
|
3916
|
+
}
|
|
3917
|
+
const { envelopes, ackOrdinal, deadLetters, hasMore } = pollSyncEnvelopes(this.syncManager, args, forbiddenIds);
|
|
4094
3918
|
return {
|
|
4095
3919
|
envelopes,
|
|
4096
3920
|
ackOrdinal,
|
|
@@ -4109,7 +3933,7 @@ var ReactorSubgraph = class extends BaseSubgraph {
|
|
|
4109
3933
|
try {
|
|
4110
3934
|
if (args.parentIdentifier) {
|
|
4111
3935
|
const parent = await document(this.reactorClient, { identifier: args.parentIdentifier });
|
|
4112
|
-
await this.
|
|
3936
|
+
await this.assertCanWriteCanonical(parent.document.id, ctx);
|
|
4113
3937
|
} else this.assertCanCreate(ctx);
|
|
4114
3938
|
const result = await createDocument(this.reactorClient, args, this.graphqlManager.reactorDriveClient);
|
|
4115
3939
|
if (result?.id && isDriveContainerType(result.documentType)) this.graphqlManager.driveOwnershipCache.add(result.id);
|
|
@@ -4125,7 +3949,7 @@ var ReactorSubgraph = class extends BaseSubgraph {
|
|
|
4125
3949
|
try {
|
|
4126
3950
|
if (args.parentIdentifier) {
|
|
4127
3951
|
const parent = await document(this.reactorClient, { identifier: args.parentIdentifier });
|
|
4128
|
-
await this.
|
|
3952
|
+
await this.assertCanWriteCanonical(parent.document.id, ctx);
|
|
4129
3953
|
} else this.assertCanCreate(ctx);
|
|
4130
3954
|
const result = await createEmptyDocument(this.reactorClient, args, this.graphqlManager.reactorDriveClient);
|
|
4131
3955
|
if (result?.id && isDriveContainerType(result.documentType)) this.graphqlManager.driveOwnershipCache.add(result.id);
|
|
@@ -4139,8 +3963,11 @@ var ReactorSubgraph = class extends BaseSubgraph {
|
|
|
4139
3963
|
mutateDocument: async (_parent, args, ctx) => {
|
|
4140
3964
|
this.logger.debug("mutateDocument(@args)", args);
|
|
4141
3965
|
try {
|
|
4142
|
-
await this.assertCanExecuteOperations(args.documentIdentifier, args.actions, ctx);
|
|
4143
|
-
return await mutateDocument(this.reactorClient,
|
|
3966
|
+
const handle = await this.assertCanExecuteOperations(args.documentIdentifier, args.actions, ctx);
|
|
3967
|
+
return await mutateDocument(this.reactorClient, {
|
|
3968
|
+
...args,
|
|
3969
|
+
documentIdentifier: handle.fetchIdentifier
|
|
3970
|
+
});
|
|
4144
3971
|
} catch (error) {
|
|
4145
3972
|
this.logger.error("Error in mutateDocument(@args): @Error", args, error);
|
|
4146
3973
|
throw error;
|
|
@@ -4149,8 +3976,11 @@ var ReactorSubgraph = class extends BaseSubgraph {
|
|
|
4149
3976
|
mutateDocumentAsync: async (_parent, args, ctx) => {
|
|
4150
3977
|
this.logger.debug("mutateDocumentAsync(@args)", args);
|
|
4151
3978
|
try {
|
|
4152
|
-
await this.assertCanExecuteOperations(args.documentIdentifier, args.actions, ctx);
|
|
4153
|
-
return await mutateDocumentAsync(this.reactorClient,
|
|
3979
|
+
const handle = await this.assertCanExecuteOperations(args.documentIdentifier, args.actions, ctx);
|
|
3980
|
+
return await mutateDocumentAsync(this.reactorClient, {
|
|
3981
|
+
...args,
|
|
3982
|
+
documentIdentifier: handle.fetchIdentifier
|
|
3983
|
+
});
|
|
4154
3984
|
} catch (error) {
|
|
4155
3985
|
this.logger.error("Error in mutateDocumentAsync(@args): @Error", args, error);
|
|
4156
3986
|
throw error;
|
|
@@ -4159,8 +3989,11 @@ var ReactorSubgraph = class extends BaseSubgraph {
|
|
|
4159
3989
|
renameDocument: async (_parent, args, ctx) => {
|
|
4160
3990
|
this.logger.debug("renameDocument(@args)", args);
|
|
4161
3991
|
try {
|
|
4162
|
-
await this.assertCanWrite(args.documentIdentifier, ctx);
|
|
4163
|
-
return await renameDocument(this.reactorClient,
|
|
3992
|
+
const handle = await this.assertCanWrite(args.documentIdentifier, ctx);
|
|
3993
|
+
return await renameDocument(this.reactorClient, {
|
|
3994
|
+
...args,
|
|
3995
|
+
documentIdentifier: handle.fetchIdentifier
|
|
3996
|
+
});
|
|
4164
3997
|
} catch (error) {
|
|
4165
3998
|
this.logger.error("Error in renameDocument(@args): @Error", args, error);
|
|
4166
3999
|
throw error;
|
|
@@ -4169,8 +4002,11 @@ var ReactorSubgraph = class extends BaseSubgraph {
|
|
|
4169
4002
|
setPreferredEditor: async (_parent, args, ctx) => {
|
|
4170
4003
|
this.logger.debug("setPreferredEditor(@args)", args);
|
|
4171
4004
|
try {
|
|
4172
|
-
await this.assertCanWrite(args.documentIdentifier, ctx);
|
|
4173
|
-
return await setPreferredEditor(this.reactorClient,
|
|
4005
|
+
const handle = await this.assertCanWrite(args.documentIdentifier, ctx);
|
|
4006
|
+
return await setPreferredEditor(this.reactorClient, {
|
|
4007
|
+
...args,
|
|
4008
|
+
documentIdentifier: handle.fetchIdentifier
|
|
4009
|
+
});
|
|
4174
4010
|
} catch (error) {
|
|
4175
4011
|
this.logger.error("Error in setPreferredEditor(@args): @Error", args, error);
|
|
4176
4012
|
throw error;
|
|
@@ -4179,8 +4015,11 @@ var ReactorSubgraph = class extends BaseSubgraph {
|
|
|
4179
4015
|
addRelationship: async (_parent, args, ctx) => {
|
|
4180
4016
|
this.logger.debug("addRelationship(@args)", args);
|
|
4181
4017
|
try {
|
|
4182
|
-
await this.assertCanWrite(args.sourceIdentifier, ctx);
|
|
4183
|
-
return await addRelationship(this.reactorClient,
|
|
4018
|
+
const handle = await this.assertCanWrite(args.sourceIdentifier, ctx);
|
|
4019
|
+
return await addRelationship(this.reactorClient, {
|
|
4020
|
+
...args,
|
|
4021
|
+
sourceIdentifier: handle.fetchIdentifier
|
|
4022
|
+
});
|
|
4184
4023
|
} catch (error) {
|
|
4185
4024
|
this.logger.error("Error in addRelationship(@args): @Error", args, error);
|
|
4186
4025
|
throw error;
|
|
@@ -4189,8 +4028,11 @@ var ReactorSubgraph = class extends BaseSubgraph {
|
|
|
4189
4028
|
removeRelationship: async (_parent, args, ctx) => {
|
|
4190
4029
|
this.logger.debug("removeRelationship(@args)", args);
|
|
4191
4030
|
try {
|
|
4192
|
-
await this.assertCanWrite(args.sourceIdentifier, ctx);
|
|
4193
|
-
return await removeRelationship(this.reactorClient,
|
|
4031
|
+
const handle = await this.assertCanWrite(args.sourceIdentifier, ctx);
|
|
4032
|
+
return await removeRelationship(this.reactorClient, {
|
|
4033
|
+
...args,
|
|
4034
|
+
sourceIdentifier: handle.fetchIdentifier
|
|
4035
|
+
});
|
|
4194
4036
|
} catch (error) {
|
|
4195
4037
|
this.logger.error("Error in removeRelationship(@args): @Error", args, error);
|
|
4196
4038
|
throw error;
|
|
@@ -4199,9 +4041,13 @@ var ReactorSubgraph = class extends BaseSubgraph {
|
|
|
4199
4041
|
moveRelationship: async (_parent, args, ctx) => {
|
|
4200
4042
|
this.logger.debug("moveRelationship(@args)", args);
|
|
4201
4043
|
try {
|
|
4202
|
-
await this.assertCanWrite(args.sourceParentIdentifier, ctx);
|
|
4203
|
-
await this.assertCanWrite(args.targetParentIdentifier, ctx);
|
|
4204
|
-
return await moveRelationship(this.reactorClient,
|
|
4044
|
+
const sourceHandle = await this.assertCanWrite(args.sourceParentIdentifier, ctx);
|
|
4045
|
+
const targetHandle = await this.assertCanWrite(args.targetParentIdentifier, ctx);
|
|
4046
|
+
return await moveRelationship(this.reactorClient, {
|
|
4047
|
+
...args,
|
|
4048
|
+
sourceParentIdentifier: sourceHandle.fetchIdentifier,
|
|
4049
|
+
targetParentIdentifier: targetHandle.fetchIdentifier
|
|
4050
|
+
});
|
|
4205
4051
|
} catch (error) {
|
|
4206
4052
|
this.logger.error("Error in moveRelationship(@args): @Error @args", error, args);
|
|
4207
4053
|
throw error;
|
|
@@ -4210,9 +4056,12 @@ var ReactorSubgraph = class extends BaseSubgraph {
|
|
|
4210
4056
|
deleteDocument: async (_parent, args, ctx) => {
|
|
4211
4057
|
this.logger.debug("deleteDocument(@args)", args);
|
|
4212
4058
|
try {
|
|
4213
|
-
await this.assertCanWrite(args.identifier, ctx);
|
|
4214
|
-
const driveIdToInvalidate = await this.#resolveDriveId(
|
|
4215
|
-
const result = await deleteDocument(this.reactorClient,
|
|
4059
|
+
const identifier = (await this.assertCanWrite(args.identifier, ctx)).fetchIdentifier;
|
|
4060
|
+
const driveIdToInvalidate = await this.#resolveDriveId(identifier);
|
|
4061
|
+
const result = await deleteDocument(this.reactorClient, {
|
|
4062
|
+
...args,
|
|
4063
|
+
identifier
|
|
4064
|
+
}, this.graphqlManager.reactorDriveClient);
|
|
4216
4065
|
if (result && driveIdToInvalidate) this.graphqlManager.driveOwnershipCache.remove(driveIdToInvalidate);
|
|
4217
4066
|
return result;
|
|
4218
4067
|
} catch (error) {
|
|
@@ -4223,25 +4072,49 @@ var ReactorSubgraph = class extends BaseSubgraph {
|
|
|
4223
4072
|
deleteDocuments: async (_parent, args, ctx) => {
|
|
4224
4073
|
this.logger.debug("deleteDocuments(@args)", args);
|
|
4225
4074
|
try {
|
|
4226
|
-
|
|
4227
|
-
|
|
4075
|
+
const identifiers = [];
|
|
4076
|
+
for (const identifier of args.identifiers) {
|
|
4077
|
+
const handle = await this.assertCanWrite(identifier, ctx);
|
|
4078
|
+
identifiers.push(handle.fetchIdentifier);
|
|
4079
|
+
}
|
|
4080
|
+
return await deleteDocuments(this.reactorClient, {
|
|
4081
|
+
...args,
|
|
4082
|
+
identifiers
|
|
4083
|
+
});
|
|
4228
4084
|
} catch (error) {
|
|
4229
4085
|
this.logger.error("Error in deleteDocuments(@args): @Error", args, error);
|
|
4230
4086
|
throw error;
|
|
4231
4087
|
}
|
|
4232
4088
|
},
|
|
4233
|
-
touchChannel: async (_parent, args) => {
|
|
4089
|
+
touchChannel: async (_parent, args, ctx) => {
|
|
4234
4090
|
this.logger.debug("touchChannel(@args)", args);
|
|
4235
4091
|
try {
|
|
4092
|
+
if (!this.authorizationService.isSupremeAdmin(ctx.user?.address)) {
|
|
4093
|
+
const driveId = DriveCollectionId.fromKey(args.input.collectionId).driveId;
|
|
4094
|
+
await this.assertCanReadCanonical(driveId, ctx);
|
|
4095
|
+
}
|
|
4236
4096
|
return await touchChannel(this.syncManager, args);
|
|
4237
4097
|
} catch (error) {
|
|
4238
4098
|
this.logger.error("Error in touchChannel(@args): @Error", args, error);
|
|
4239
4099
|
throw error;
|
|
4240
4100
|
}
|
|
4241
4101
|
},
|
|
4242
|
-
pushSyncEnvelopes: async (_parent, args) => {
|
|
4102
|
+
pushSyncEnvelopes: async (_parent, args, ctx) => {
|
|
4243
4103
|
this.logger.debug("pushSyncEnvelopes(@args)", args);
|
|
4244
4104
|
try {
|
|
4105
|
+
const checkedOperations = /* @__PURE__ */ new Map();
|
|
4106
|
+
for (const envelope of args.envelopes) for (const op of envelope.operations ?? []) {
|
|
4107
|
+
const documentId = op.context.documentId;
|
|
4108
|
+
const operationType = op.operation.action.type;
|
|
4109
|
+
let checkedTypes = checkedOperations.get(documentId);
|
|
4110
|
+
if (!checkedTypes) {
|
|
4111
|
+
checkedTypes = /* @__PURE__ */ new Set();
|
|
4112
|
+
checkedOperations.set(documentId, checkedTypes);
|
|
4113
|
+
}
|
|
4114
|
+
if (checkedTypes.has(operationType)) continue;
|
|
4115
|
+
checkedTypes.add(operationType);
|
|
4116
|
+
await this.assertCanExecuteOperationCanonical(documentId, operationType, ctx);
|
|
4117
|
+
}
|
|
4245
4118
|
const mutableArgs = { envelopes: args.envelopes.map((envelope) => ({
|
|
4246
4119
|
type: envelope.type,
|
|
4247
4120
|
channelMeta: { id: envelope.channelMeta.id },
|
|
@@ -4272,31 +4145,43 @@ var ReactorSubgraph = class extends BaseSubgraph {
|
|
|
4272
4145
|
},
|
|
4273
4146
|
Subscription: {
|
|
4274
4147
|
documentChanges: {
|
|
4275
|
-
subscribe:
|
|
4148
|
+
subscribe: (rootValue, args, ctx, info) => {
|
|
4276
4149
|
this.logger.debug("documentChanges subscription started");
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
|
|
4150
|
+
return withFilter(() => {
|
|
4151
|
+
ensureGlobalDocumentSubscription(this.reactorClient);
|
|
4152
|
+
return getPubSub().asyncIterableIterator(SUBSCRIPTION_TRIGGERS.DOCUMENT_CHANGES);
|
|
4153
|
+
}, async (payload, filterArgs, filterCtx) => {
|
|
4154
|
+
if (!payload) return false;
|
|
4155
|
+
const search = {
|
|
4156
|
+
type: filterArgs?.search?.type ?? void 0,
|
|
4157
|
+
parentId: filterArgs?.search?.parentId ?? void 0
|
|
4158
|
+
};
|
|
4159
|
+
if (!matchesSearchFilter(payload.documentChanges, search)) return false;
|
|
4160
|
+
if (this.authorizationService.isSupremeAdmin(filterCtx?.user?.address)) return true;
|
|
4161
|
+
const documentIds = collectChangedDocumentIds(payload.documentChanges);
|
|
4162
|
+
if (documentIds.length === 0) return false;
|
|
4163
|
+
for (const documentId of documentIds) if (!await this.canReadDocument(documentId, filterCtx)) return false;
|
|
4164
|
+
return true;
|
|
4165
|
+
})(rootValue, args, ctx, info);
|
|
4166
|
+
},
|
|
4287
4167
|
resolve: (payload) => {
|
|
4288
4168
|
return toGqlDocumentChangeEvent(payload.documentChanges);
|
|
4289
4169
|
}
|
|
4290
4170
|
},
|
|
4291
4171
|
jobChanges: {
|
|
4292
|
-
subscribe:
|
|
4172
|
+
subscribe: (rootValue, args, ctx, info) => {
|
|
4293
4173
|
this.logger.debug("jobChanges(@args) subscription started", args);
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
|
|
4174
|
+
return withFilter(() => {
|
|
4175
|
+
ensureJobSubscription(this.reactorClient, args.jobId);
|
|
4176
|
+
return getPubSub().asyncIterableIterator(SUBSCRIPTION_TRIGGERS.JOB_CHANGES);
|
|
4177
|
+
}, async (payload, filterArgs, filterCtx) => {
|
|
4178
|
+
if (!payload || !filterArgs) return false;
|
|
4179
|
+
if (!matchesJobFilter(payload, filterArgs)) return false;
|
|
4180
|
+
if (this.authorizationService.isSupremeAdmin(filterCtx?.user?.address)) return true;
|
|
4181
|
+
if (!payload.documentId) return false;
|
|
4182
|
+
return this.canReadDocument(payload.documentId, filterCtx);
|
|
4183
|
+
})(rootValue, args, ctx, info);
|
|
4184
|
+
},
|
|
4300
4185
|
resolve: (payload) => {
|
|
4301
4186
|
return payload.jobChanges;
|
|
4302
4187
|
}
|
|
@@ -4320,10 +4205,10 @@ const ADMIN_USERS = getAdminUsers();
|
|
|
4320
4205
|
//#endregion
|
|
4321
4206
|
//#region src/graphql/system/version.ts
|
|
4322
4207
|
function getVersion() {
|
|
4323
|
-
return "6.2.0-
|
|
4208
|
+
return "6.2.0-rc.0";
|
|
4324
4209
|
}
|
|
4325
4210
|
function getGitHash() {
|
|
4326
|
-
return "
|
|
4211
|
+
return "51a030c045ddae8a53e9df8470ac97bb5b76b2c7";
|
|
4327
4212
|
}
|
|
4328
4213
|
function getGitUrl() {
|
|
4329
4214
|
return buildTreeUrl(getGitHash());
|
|
@@ -4806,10 +4691,10 @@ const DefaultCoreSubgraphs = [AnalyticsSubgraph, SystemSubgraph];
|
|
|
4806
4691
|
//#endregion
|
|
4807
4692
|
//#region src/migrations/001_create_document_permissions.ts
|
|
4808
4693
|
var _001_create_document_permissions_exports = /* @__PURE__ */ __exportAll({
|
|
4809
|
-
down: () => down$
|
|
4810
|
-
up: () => up$
|
|
4694
|
+
down: () => down$2,
|
|
4695
|
+
up: () => up$2
|
|
4811
4696
|
});
|
|
4812
|
-
async function up$
|
|
4697
|
+
async function up$2(db) {
|
|
4813
4698
|
await sql`
|
|
4814
4699
|
CREATE TABLE IF NOT EXISTS "DocumentPermission" (
|
|
4815
4700
|
"id" SERIAL PRIMARY KEY,
|
|
@@ -4883,7 +4768,7 @@ async function up$1(db) {
|
|
|
4883
4768
|
await sql`CREATE INDEX IF NOT EXISTS "operationgrouppermission_documentid_index" ON "OperationGroupPermission" ("documentId")`.execute(db);
|
|
4884
4769
|
await sql`CREATE INDEX IF NOT EXISTS "operationgrouppermission_groupid_index" ON "OperationGroupPermission" ("groupId")`.execute(db);
|
|
4885
4770
|
}
|
|
4886
|
-
async function down$
|
|
4771
|
+
async function down$2(db) {
|
|
4887
4772
|
await sql`DROP TABLE IF EXISTS "OperationGroupPermission"`.execute(db);
|
|
4888
4773
|
await sql`DROP TABLE IF EXISTS "OperationUserPermission"`.execute(db);
|
|
4889
4774
|
await sql`DROP TABLE IF EXISTS "DocumentGroupPermission"`.execute(db);
|
|
@@ -4894,10 +4779,10 @@ async function down$1(db) {
|
|
|
4894
4779
|
//#endregion
|
|
4895
4780
|
//#region src/migrations/002_add_document_protection.ts
|
|
4896
4781
|
var _002_add_document_protection_exports = /* @__PURE__ */ __exportAll({
|
|
4897
|
-
down: () => down,
|
|
4898
|
-
up: () => up
|
|
4782
|
+
down: () => down$1,
|
|
4783
|
+
up: () => up$1
|
|
4899
4784
|
});
|
|
4900
|
-
async function up(db) {
|
|
4785
|
+
async function up$1(db) {
|
|
4901
4786
|
await sql`
|
|
4902
4787
|
CREATE TABLE IF NOT EXISTS "DocumentProtection" (
|
|
4903
4788
|
"documentId" VARCHAR(255) PRIMARY KEY,
|
|
@@ -4910,10 +4795,80 @@ async function up(db) {
|
|
|
4910
4795
|
await sql`CREATE INDEX IF NOT EXISTS "documentprotection_owneraddress_index" ON "DocumentProtection" ("ownerAddress")`.execute(db);
|
|
4911
4796
|
await sql`CREATE INDEX IF NOT EXISTS "documentprotection_protected_index" ON "DocumentProtection" ("protected")`.execute(db);
|
|
4912
4797
|
}
|
|
4913
|
-
async function down(db) {
|
|
4798
|
+
async function down$1(db) {
|
|
4914
4799
|
await sql`DROP TABLE IF EXISTS "DocumentProtection"`.execute(db);
|
|
4915
4800
|
}
|
|
4916
4801
|
//#endregion
|
|
4802
|
+
//#region src/migrations/003_remove_group_tables.ts
|
|
4803
|
+
var _003_remove_group_tables_exports = /* @__PURE__ */ __exportAll({
|
|
4804
|
+
down: () => down,
|
|
4805
|
+
up: () => up
|
|
4806
|
+
});
|
|
4807
|
+
/**
|
|
4808
|
+
* Removes the group-permission feature: the Group, UserGroup,
|
|
4809
|
+
* DocumentGroupPermission, and OperationGroupPermission tables. The feature had
|
|
4810
|
+
* no consumers and disclosed ACL/membership metadata (AUTH_REVIEW S-H3/S-H5);
|
|
4811
|
+
* it is retired in favor of direct user grants, ownership, and protection.
|
|
4812
|
+
*
|
|
4813
|
+
* Tables are dropped in reverse dependency order. There are no DB-enforced
|
|
4814
|
+
* foreign keys, so the drops cannot violate referential integrity and do not
|
|
4815
|
+
* touch the surviving DocumentPermission / OperationUserPermission /
|
|
4816
|
+
* DocumentProtection tables.
|
|
4817
|
+
*/
|
|
4818
|
+
async function up(db) {
|
|
4819
|
+
await sql`DROP TABLE IF EXISTS "OperationGroupPermission"`.execute(db);
|
|
4820
|
+
await sql`DROP TABLE IF EXISTS "DocumentGroupPermission"`.execute(db);
|
|
4821
|
+
await sql`DROP TABLE IF EXISTS "UserGroup"`.execute(db);
|
|
4822
|
+
await sql`DROP TABLE IF EXISTS "Group"`.execute(db);
|
|
4823
|
+
}
|
|
4824
|
+
async function down(db) {
|
|
4825
|
+
await sql`
|
|
4826
|
+
CREATE TABLE IF NOT EXISTS "Group" (
|
|
4827
|
+
"id" SERIAL PRIMARY KEY,
|
|
4828
|
+
"name" VARCHAR(255) NOT NULL UNIQUE,
|
|
4829
|
+
"description" TEXT,
|
|
4830
|
+
"createdAt" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
4831
|
+
"updatedAt" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
4832
|
+
)
|
|
4833
|
+
`.execute(db);
|
|
4834
|
+
await sql`
|
|
4835
|
+
CREATE TABLE IF NOT EXISTS "UserGroup" (
|
|
4836
|
+
"userAddress" VARCHAR(255) NOT NULL,
|
|
4837
|
+
"groupId" INTEGER NOT NULL,
|
|
4838
|
+
"createdAt" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
4839
|
+
PRIMARY KEY ("userAddress", "groupId")
|
|
4840
|
+
)
|
|
4841
|
+
`.execute(db);
|
|
4842
|
+
await sql`CREATE INDEX IF NOT EXISTS "usergroup_groupid_index" ON "UserGroup" ("groupId")`.execute(db);
|
|
4843
|
+
await sql`
|
|
4844
|
+
CREATE TABLE IF NOT EXISTS "DocumentGroupPermission" (
|
|
4845
|
+
"id" SERIAL PRIMARY KEY,
|
|
4846
|
+
"documentId" VARCHAR(255) NOT NULL,
|
|
4847
|
+
"groupId" INTEGER NOT NULL,
|
|
4848
|
+
"permission" VARCHAR(20) NOT NULL CHECK ("permission" IN ('READ', 'WRITE', 'ADMIN')),
|
|
4849
|
+
"grantedBy" VARCHAR(255) NOT NULL,
|
|
4850
|
+
"createdAt" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
4851
|
+
"updatedAt" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
4852
|
+
UNIQUE ("documentId", "groupId")
|
|
4853
|
+
)
|
|
4854
|
+
`.execute(db);
|
|
4855
|
+
await sql`CREATE INDEX IF NOT EXISTS "documentgrouppermission_documentid_index" ON "DocumentGroupPermission" ("documentId")`.execute(db);
|
|
4856
|
+
await sql`CREATE INDEX IF NOT EXISTS "documentgrouppermission_groupid_index" ON "DocumentGroupPermission" ("groupId")`.execute(db);
|
|
4857
|
+
await sql`
|
|
4858
|
+
CREATE TABLE IF NOT EXISTS "OperationGroupPermission" (
|
|
4859
|
+
"id" SERIAL PRIMARY KEY,
|
|
4860
|
+
"documentId" VARCHAR(255) NOT NULL,
|
|
4861
|
+
"operationType" VARCHAR(255) NOT NULL,
|
|
4862
|
+
"groupId" INTEGER NOT NULL,
|
|
4863
|
+
"grantedBy" VARCHAR(255) NOT NULL,
|
|
4864
|
+
"createdAt" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
4865
|
+
UNIQUE ("documentId", "operationType", "groupId")
|
|
4866
|
+
)
|
|
4867
|
+
`.execute(db);
|
|
4868
|
+
await sql`CREATE INDEX IF NOT EXISTS "operationgrouppermission_documentid_index" ON "OperationGroupPermission" ("documentId")`.execute(db);
|
|
4869
|
+
await sql`CREATE INDEX IF NOT EXISTS "operationgrouppermission_groupid_index" ON "OperationGroupPermission" ("groupId")`.execute(db);
|
|
4870
|
+
}
|
|
4871
|
+
//#endregion
|
|
4917
4872
|
//#region src/migrations/index.ts
|
|
4918
4873
|
/**
|
|
4919
4874
|
* Custom migration provider that loads migrations from imported modules
|
|
@@ -4922,7 +4877,8 @@ var StaticMigrationProvider = class {
|
|
|
4922
4877
|
getMigrations() {
|
|
4923
4878
|
return Promise.resolve({
|
|
4924
4879
|
"001_create_document_permissions": _001_create_document_permissions_exports,
|
|
4925
|
-
"002_add_document_protection": _002_add_document_protection_exports
|
|
4880
|
+
"002_add_document_protection": _002_add_document_protection_exports,
|
|
4881
|
+
"003_remove_group_tables": _003_remove_group_tables_exports
|
|
4926
4882
|
});
|
|
4927
4883
|
}
|
|
4928
4884
|
};
|
|
@@ -4944,6 +4900,162 @@ async function runMigrations(db) {
|
|
|
4944
4900
|
}
|
|
4945
4901
|
}
|
|
4946
4902
|
//#endregion
|
|
4903
|
+
//#region src/services/auth.service.ts
|
|
4904
|
+
const DEFAULT_CREDENTIAL_CACHE_TTL_MS = 6e4;
|
|
4905
|
+
const CREDENTIAL_CACHE_MAX_ENTRIES = 1e3;
|
|
4906
|
+
var AuthService = class {
|
|
4907
|
+
config;
|
|
4908
|
+
credentialCache = /* @__PURE__ */ new Map();
|
|
4909
|
+
constructor(config) {
|
|
4910
|
+
this.config = config;
|
|
4911
|
+
}
|
|
4912
|
+
async authenticateRequest(request) {
|
|
4913
|
+
if (!this.config.enabled) return {
|
|
4914
|
+
user: void 0,
|
|
4915
|
+
admins: [],
|
|
4916
|
+
auth_enabled: false
|
|
4917
|
+
};
|
|
4918
|
+
const method = request.method;
|
|
4919
|
+
if (method === "OPTIONS" || method === "GET") return {
|
|
4920
|
+
user: void 0,
|
|
4921
|
+
admins: this.config.admins,
|
|
4922
|
+
auth_enabled: true
|
|
4923
|
+
};
|
|
4924
|
+
return this.verifyBearer(request.headers.get("authorization") ?? void 0);
|
|
4925
|
+
}
|
|
4926
|
+
/**
|
|
4927
|
+
* Verify a Bearer token regardless of HTTP method. Use this from non-GraphQL
|
|
4928
|
+
* middleware that must enforce authentication on every request.
|
|
4929
|
+
*/
|
|
4930
|
+
async verifyBearer(authorization) {
|
|
4931
|
+
if (!this.config.enabled) return {
|
|
4932
|
+
user: void 0,
|
|
4933
|
+
admins: [],
|
|
4934
|
+
auth_enabled: false
|
|
4935
|
+
};
|
|
4936
|
+
const token = authorization?.split(" ")[1];
|
|
4937
|
+
if (!token) return {
|
|
4938
|
+
user: void 0,
|
|
4939
|
+
admins: this.config.admins,
|
|
4940
|
+
auth_enabled: true
|
|
4941
|
+
};
|
|
4942
|
+
try {
|
|
4943
|
+
const verified = await this.verifyToken(token);
|
|
4944
|
+
if (!verified) return new Response(JSON.stringify({ error: "Verification failed" }), { status: 401 });
|
|
4945
|
+
const user = this.extractUserFromVerification(verified);
|
|
4946
|
+
if (!user) return new Response(JSON.stringify({ error: "Missing credentials" }), { status: 401 });
|
|
4947
|
+
if (!this.config.skipCredentialVerification) {
|
|
4948
|
+
if (!await this.verifyCredentialExists(user.address, user.chainId, verified.issuer)) return new Response(JSON.stringify({ error: "Credentials no longer valid" }), { status: 401 });
|
|
4949
|
+
}
|
|
4950
|
+
return {
|
|
4951
|
+
user,
|
|
4952
|
+
admins: this.config.admins,
|
|
4953
|
+
auth_enabled: true
|
|
4954
|
+
};
|
|
4955
|
+
} catch {
|
|
4956
|
+
return new Response(JSON.stringify({ error: "Authentication failed" }), { status: 401 });
|
|
4957
|
+
}
|
|
4958
|
+
}
|
|
4959
|
+
async authenticateWebSocketConnection(connectionParams) {
|
|
4960
|
+
if (!this.config.enabled) return null;
|
|
4961
|
+
const authHeader = connectionParams.authorization;
|
|
4962
|
+
if (!authHeader) throw new Error("Missing authorization in connection parameters");
|
|
4963
|
+
const token = authHeader.split(" ")[1];
|
|
4964
|
+
if (!token) throw new Error("Invalid authorization format");
|
|
4965
|
+
const verified = await this.verifyToken(token);
|
|
4966
|
+
if (!verified) throw new Error("Token verification failed");
|
|
4967
|
+
const user = this.extractUserFromVerification(verified);
|
|
4968
|
+
if (!user) throw new Error("Invalid credentials");
|
|
4969
|
+
if (!this.config.skipCredentialVerification) {
|
|
4970
|
+
if (!await this.verifyCredentialExists(user.address, user.chainId, verified.issuer)) throw new Error("Credentials no longer valid");
|
|
4971
|
+
}
|
|
4972
|
+
return user;
|
|
4973
|
+
}
|
|
4974
|
+
/**
|
|
4975
|
+
* Verify the auth bearer token
|
|
4976
|
+
*/
|
|
4977
|
+
async verifyToken(token) {
|
|
4978
|
+
return await verifyAuthBearerToken(token);
|
|
4979
|
+
}
|
|
4980
|
+
/**
|
|
4981
|
+
* Extract user information from verification result
|
|
4982
|
+
*/
|
|
4983
|
+
extractUserFromVerification(verified) {
|
|
4984
|
+
try {
|
|
4985
|
+
const { address, chainId, networkId } = verified.verifiableCredential.credentialSubject;
|
|
4986
|
+
if (!address || !chainId || !networkId) return null;
|
|
4987
|
+
return {
|
|
4988
|
+
address,
|
|
4989
|
+
chainId,
|
|
4990
|
+
networkId
|
|
4991
|
+
};
|
|
4992
|
+
} catch {
|
|
4993
|
+
return null;
|
|
4994
|
+
}
|
|
4995
|
+
}
|
|
4996
|
+
/**
|
|
4997
|
+
* Verify that the credential still exists on the Renown API.
|
|
4998
|
+
*
|
|
4999
|
+
* Results are cached per (address, chainId, issuer) for a short TTL so the
|
|
5000
|
+
* blocking external round-trip is not paid on every request. Concurrent
|
|
5001
|
+
* checks for the same key share a single in-flight request, and entries
|
|
5002
|
+
* that resolve to false are evicted immediately so failed or revoked
|
|
5003
|
+
* credentials are re-checked on the next request.
|
|
5004
|
+
*/
|
|
5005
|
+
verifyCredentialExists(address, chainId, appId) {
|
|
5006
|
+
const ttlMs = this.config.credentialVerificationCacheTtlMs ?? DEFAULT_CREDENTIAL_CACHE_TTL_MS;
|
|
5007
|
+
if (ttlMs <= 0) return this.fetchCredentialExists(address, chainId, appId);
|
|
5008
|
+
const key = `${address.toLowerCase()}:${chainId}:${appId}`;
|
|
5009
|
+
const now = Date.now();
|
|
5010
|
+
const cached = this.credentialCache.get(key);
|
|
5011
|
+
if (cached && cached.expiresAt > now) return cached.exists;
|
|
5012
|
+
this.pruneCredentialCache(now);
|
|
5013
|
+
const entry = {
|
|
5014
|
+
exists: this.fetchCredentialExists(address, chainId, appId).then((exists) => {
|
|
5015
|
+
if (!exists && this.credentialCache.get(key) === entry) this.credentialCache.delete(key);
|
|
5016
|
+
return exists;
|
|
5017
|
+
}),
|
|
5018
|
+
expiresAt: now + ttlMs
|
|
5019
|
+
};
|
|
5020
|
+
this.credentialCache.set(key, entry);
|
|
5021
|
+
return entry.exists;
|
|
5022
|
+
}
|
|
5023
|
+
/**
|
|
5024
|
+
* Enforce the cache size cap before inserting a new entry: drop expired
|
|
5025
|
+
* entries first, then evict oldest-inserted entries (insertion order
|
|
5026
|
+
* matches expiry order since the TTL is constant) until under the cap, so
|
|
5027
|
+
* a flood of distinct keys cannot grow the map without bound.
|
|
5028
|
+
*/
|
|
5029
|
+
pruneCredentialCache(now) {
|
|
5030
|
+
if (this.credentialCache.size < CREDENTIAL_CACHE_MAX_ENTRIES) return;
|
|
5031
|
+
for (const [key, entry] of this.credentialCache) if (entry.expiresAt <= now) this.credentialCache.delete(key);
|
|
5032
|
+
while (this.credentialCache.size >= CREDENTIAL_CACHE_MAX_ENTRIES) {
|
|
5033
|
+
const oldestKey = this.credentialCache.keys().next().value;
|
|
5034
|
+
if (oldestKey === void 0) break;
|
|
5035
|
+
this.credentialCache.delete(oldestKey);
|
|
5036
|
+
}
|
|
5037
|
+
}
|
|
5038
|
+
/**
|
|
5039
|
+
* Fetch the credential from the Renown API and validate it against the
|
|
5040
|
+
* expected address, chainId and issuer. Never throws; returns false on any
|
|
5041
|
+
* network or validation failure.
|
|
5042
|
+
*/
|
|
5043
|
+
async fetchCredentialExists(address, chainId, appId) {
|
|
5044
|
+
const url = `https://www.renown.id/api/auth/credential?address=${address}&chainId=${chainId}&connectId=${appId}&appId=${appId}`;
|
|
5045
|
+
try {
|
|
5046
|
+
const response = await fetch(url, { method: "GET" });
|
|
5047
|
+
if (response.status !== 200) return false;
|
|
5048
|
+
const credential = (await response.json()).credential;
|
|
5049
|
+
const appIdVerfied = credential.credentialSubject.id;
|
|
5050
|
+
const addressVerfied = credential.issuer.id.split(":")[4];
|
|
5051
|
+
const chainIdVerfied = credential.issuer.id.split(":")[3];
|
|
5052
|
+
return appIdVerfied === appId && addressVerfied.toLocaleLowerCase() === address.toLocaleLowerCase() && chainIdVerfied === chainId.toString();
|
|
5053
|
+
} catch {
|
|
5054
|
+
return false;
|
|
5055
|
+
}
|
|
5056
|
+
}
|
|
5057
|
+
};
|
|
5058
|
+
//#endregion
|
|
4947
5059
|
//#region src/services/document-permission.service.ts
|
|
4948
5060
|
/**
|
|
4949
5061
|
* Service for managing document-level permissions.
|
|
@@ -4954,7 +5066,7 @@ async function runMigrations(db) {
|
|
|
4954
5066
|
* - ADMIN: Can manage document permissions and settings
|
|
4955
5067
|
*
|
|
4956
5068
|
* Operation permissions:
|
|
4957
|
-
* - Users
|
|
5069
|
+
* - Users can be granted permission to execute specific operations
|
|
4958
5070
|
*/
|
|
4959
5071
|
var DocumentPermissionService = class {
|
|
4960
5072
|
config;
|
|
@@ -5033,209 +5145,7 @@ var DocumentPermissionService = class {
|
|
|
5033
5145
|
*/
|
|
5034
5146
|
async deleteAllDocumentPermissions(documentId) {
|
|
5035
5147
|
await this.db.deleteFrom("DocumentPermission").where("documentId", "=", documentId).execute();
|
|
5036
|
-
await this.db.deleteFrom("DocumentGroupPermission").where("documentId", "=", documentId).execute();
|
|
5037
5148
|
await this.db.deleteFrom("OperationUserPermission").where("documentId", "=", documentId).execute();
|
|
5038
|
-
await this.db.deleteFrom("OperationGroupPermission").where("documentId", "=", documentId).execute();
|
|
5039
|
-
}
|
|
5040
|
-
/**
|
|
5041
|
-
* Check if a user can read a document.
|
|
5042
|
-
* Returns true if user has READ, WRITE, or ADMIN permission (direct or via group)
|
|
5043
|
-
*/
|
|
5044
|
-
async canReadDocument(documentId, userAddress) {
|
|
5045
|
-
if (!userAddress) return false;
|
|
5046
|
-
if (await this.getUserPermission(documentId, userAddress) !== null) return true;
|
|
5047
|
-
return await this.getUserGroupPermission(documentId, userAddress) !== null;
|
|
5048
|
-
}
|
|
5049
|
-
/**
|
|
5050
|
-
* Check if a user can write to a document.
|
|
5051
|
-
* Returns true if user has WRITE or ADMIN permission (direct or via group)
|
|
5052
|
-
*/
|
|
5053
|
-
async canWriteDocument(documentId, userAddress) {
|
|
5054
|
-
if (!userAddress) return false;
|
|
5055
|
-
const directPermission = await this.getUserPermission(documentId, userAddress);
|
|
5056
|
-
if (directPermission === "WRITE" || directPermission === "ADMIN") return true;
|
|
5057
|
-
const groupPermission = await this.getUserGroupPermission(documentId, userAddress);
|
|
5058
|
-
return groupPermission === "WRITE" || groupPermission === "ADMIN";
|
|
5059
|
-
}
|
|
5060
|
-
/**
|
|
5061
|
-
* Check if a user can manage a document (change permissions, settings).
|
|
5062
|
-
* Returns true if user has ADMIN permission (direct or via group)
|
|
5063
|
-
*/
|
|
5064
|
-
async canManageDocument(documentId, userAddress) {
|
|
5065
|
-
if (!userAddress) return false;
|
|
5066
|
-
if (await this.getUserPermission(documentId, userAddress) === "ADMIN") return true;
|
|
5067
|
-
return await this.getUserGroupPermission(documentId, userAddress) === "ADMIN";
|
|
5068
|
-
}
|
|
5069
|
-
/**
|
|
5070
|
-
* Check if a user can read a document, including parent permission inheritance.
|
|
5071
|
-
* Returns true if user has permission on the document OR any parent in the hierarchy.
|
|
5072
|
-
*/
|
|
5073
|
-
async canRead(documentId, userAddress, getParentIds) {
|
|
5074
|
-
if (await this.canReadDocument(documentId, userAddress)) return true;
|
|
5075
|
-
const parentIds = await getParentIds(documentId);
|
|
5076
|
-
for (const parentId of parentIds) if (await this.canRead(parentId, userAddress, getParentIds)) return true;
|
|
5077
|
-
return false;
|
|
5078
|
-
}
|
|
5079
|
-
/**
|
|
5080
|
-
* Check if a user can write to a document, including parent permission inheritance.
|
|
5081
|
-
* Returns true if user has write permission on the document OR any parent in the hierarchy.
|
|
5082
|
-
*/
|
|
5083
|
-
async canWrite(documentId, userAddress, getParentIds) {
|
|
5084
|
-
if (await this.canWriteDocument(documentId, userAddress)) return true;
|
|
5085
|
-
const parentIds = await getParentIds(documentId);
|
|
5086
|
-
for (const parentId of parentIds) if (await this.canWrite(parentId, userAddress, getParentIds)) return true;
|
|
5087
|
-
return false;
|
|
5088
|
-
}
|
|
5089
|
-
/**
|
|
5090
|
-
* Filter a list of document IDs to only include those the user can read.
|
|
5091
|
-
*/
|
|
5092
|
-
async filterReadableDocuments(documentIds, userAddress, getParentIds) {
|
|
5093
|
-
const results = [];
|
|
5094
|
-
for (const docId of documentIds) if (await this.canRead(docId, userAddress, getParentIds)) results.push(docId);
|
|
5095
|
-
return results;
|
|
5096
|
-
}
|
|
5097
|
-
/**
|
|
5098
|
-
* Create a new group
|
|
5099
|
-
*/
|
|
5100
|
-
async createGroup(name, description) {
|
|
5101
|
-
const now = /* @__PURE__ */ new Date();
|
|
5102
|
-
await this.db.insertInto("Group").values({
|
|
5103
|
-
name,
|
|
5104
|
-
description: description ?? null,
|
|
5105
|
-
createdAt: now,
|
|
5106
|
-
updatedAt: now
|
|
5107
|
-
}).execute();
|
|
5108
|
-
return await this.db.selectFrom("Group").select([
|
|
5109
|
-
"id",
|
|
5110
|
-
"name",
|
|
5111
|
-
"description",
|
|
5112
|
-
"createdAt",
|
|
5113
|
-
"updatedAt"
|
|
5114
|
-
]).where("name", "=", name).executeTakeFirstOrThrow();
|
|
5115
|
-
}
|
|
5116
|
-
/**
|
|
5117
|
-
* Delete a group and all its associations
|
|
5118
|
-
*/
|
|
5119
|
-
async deleteGroup(groupId) {
|
|
5120
|
-
await this.db.deleteFrom("OperationGroupPermission").where("groupId", "=", groupId).execute();
|
|
5121
|
-
await this.db.deleteFrom("DocumentGroupPermission").where("groupId", "=", groupId).execute();
|
|
5122
|
-
await this.db.deleteFrom("UserGroup").where("groupId", "=", groupId).execute();
|
|
5123
|
-
await this.db.deleteFrom("Group").where("id", "=", groupId).execute();
|
|
5124
|
-
}
|
|
5125
|
-
/**
|
|
5126
|
-
* Get a group by ID
|
|
5127
|
-
*/
|
|
5128
|
-
async getGroup(groupId) {
|
|
5129
|
-
return await this.db.selectFrom("Group").select([
|
|
5130
|
-
"id",
|
|
5131
|
-
"name",
|
|
5132
|
-
"description",
|
|
5133
|
-
"createdAt",
|
|
5134
|
-
"updatedAt"
|
|
5135
|
-
]).where("id", "=", groupId).executeTakeFirst() ?? null;
|
|
5136
|
-
}
|
|
5137
|
-
/**
|
|
5138
|
-
* List all groups
|
|
5139
|
-
*/
|
|
5140
|
-
async listGroups() {
|
|
5141
|
-
return this.db.selectFrom("Group").select([
|
|
5142
|
-
"id",
|
|
5143
|
-
"name",
|
|
5144
|
-
"description",
|
|
5145
|
-
"createdAt",
|
|
5146
|
-
"updatedAt"
|
|
5147
|
-
]).execute();
|
|
5148
|
-
}
|
|
5149
|
-
/**
|
|
5150
|
-
* Add a user to a group
|
|
5151
|
-
*/
|
|
5152
|
-
async addUserToGroup(userAddress, groupId) {
|
|
5153
|
-
const now = /* @__PURE__ */ new Date();
|
|
5154
|
-
const normalizedAddress = userAddress.toLowerCase();
|
|
5155
|
-
await this.db.insertInto("UserGroup").values({
|
|
5156
|
-
userAddress: normalizedAddress,
|
|
5157
|
-
groupId,
|
|
5158
|
-
createdAt: now
|
|
5159
|
-
}).onConflict((oc) => oc.columns(["userAddress", "groupId"]).doNothing()).execute();
|
|
5160
|
-
}
|
|
5161
|
-
/**
|
|
5162
|
-
* Remove a user from a group
|
|
5163
|
-
*/
|
|
5164
|
-
async removeUserFromGroup(userAddress, groupId) {
|
|
5165
|
-
await this.db.deleteFrom("UserGroup").where("userAddress", "=", userAddress.toLowerCase()).where("groupId", "=", groupId).execute();
|
|
5166
|
-
}
|
|
5167
|
-
/**
|
|
5168
|
-
* Get all groups a user belongs to
|
|
5169
|
-
*/
|
|
5170
|
-
async getUserGroups(userAddress) {
|
|
5171
|
-
return this.db.selectFrom("UserGroup").innerJoin("Group", "Group.id", "UserGroup.groupId").select([
|
|
5172
|
-
"Group.id",
|
|
5173
|
-
"Group.name",
|
|
5174
|
-
"Group.description",
|
|
5175
|
-
"Group.createdAt",
|
|
5176
|
-
"Group.updatedAt"
|
|
5177
|
-
]).where("UserGroup.userAddress", "=", userAddress.toLowerCase()).execute();
|
|
5178
|
-
}
|
|
5179
|
-
/**
|
|
5180
|
-
* Get all members of a group
|
|
5181
|
-
*/
|
|
5182
|
-
async getGroupMembers(groupId) {
|
|
5183
|
-
return (await this.db.selectFrom("UserGroup").select("userAddress").where("groupId", "=", groupId).execute()).map((r) => r.userAddress);
|
|
5184
|
-
}
|
|
5185
|
-
/**
|
|
5186
|
-
* Grant a group permission on a document
|
|
5187
|
-
*/
|
|
5188
|
-
async grantGroupPermission(documentId, groupId, permission, grantedBy) {
|
|
5189
|
-
const now = /* @__PURE__ */ new Date();
|
|
5190
|
-
await this.db.insertInto("DocumentGroupPermission").values({
|
|
5191
|
-
documentId,
|
|
5192
|
-
groupId,
|
|
5193
|
-
permission,
|
|
5194
|
-
grantedBy: grantedBy.toLowerCase(),
|
|
5195
|
-
createdAt: now,
|
|
5196
|
-
updatedAt: now
|
|
5197
|
-
}).onConflict((oc) => oc.columns(["documentId", "groupId"]).doUpdateSet({
|
|
5198
|
-
permission,
|
|
5199
|
-
grantedBy: grantedBy.toLowerCase(),
|
|
5200
|
-
updatedAt: now
|
|
5201
|
-
})).execute();
|
|
5202
|
-
return await this.db.selectFrom("DocumentGroupPermission").select([
|
|
5203
|
-
"documentId",
|
|
5204
|
-
"groupId",
|
|
5205
|
-
"permission",
|
|
5206
|
-
"grantedBy",
|
|
5207
|
-
"createdAt",
|
|
5208
|
-
"updatedAt"
|
|
5209
|
-
]).where("documentId", "=", documentId).where("groupId", "=", groupId).executeTakeFirstOrThrow();
|
|
5210
|
-
}
|
|
5211
|
-
/**
|
|
5212
|
-
* Revoke a group's permission on a document
|
|
5213
|
-
*/
|
|
5214
|
-
async revokeGroupPermission(documentId, groupId) {
|
|
5215
|
-
await this.db.deleteFrom("DocumentGroupPermission").where("documentId", "=", documentId).where("groupId", "=", groupId).execute();
|
|
5216
|
-
}
|
|
5217
|
-
/**
|
|
5218
|
-
* Get all group permissions for a document
|
|
5219
|
-
*/
|
|
5220
|
-
async getDocumentGroupPermissions(documentId) {
|
|
5221
|
-
return this.db.selectFrom("DocumentGroupPermission").select([
|
|
5222
|
-
"documentId",
|
|
5223
|
-
"groupId",
|
|
5224
|
-
"permission",
|
|
5225
|
-
"grantedBy",
|
|
5226
|
-
"createdAt",
|
|
5227
|
-
"updatedAt"
|
|
5228
|
-
]).where("documentId", "=", documentId).execute();
|
|
5229
|
-
}
|
|
5230
|
-
/**
|
|
5231
|
-
* Get best permission level a user has on a document via groups
|
|
5232
|
-
*/
|
|
5233
|
-
async getUserGroupPermission(documentId, userAddress) {
|
|
5234
|
-
const result = await this.db.selectFrom("DocumentGroupPermission").innerJoin("UserGroup", "UserGroup.groupId", "DocumentGroupPermission.groupId").select("DocumentGroupPermission.permission").where("DocumentGroupPermission.documentId", "=", documentId).where("UserGroup.userAddress", "=", userAddress.toLowerCase()).execute();
|
|
5235
|
-
if (result.length === 0) return null;
|
|
5236
|
-
if (result.some((r) => r.permission === "ADMIN")) return "ADMIN";
|
|
5237
|
-
if (result.some((r) => r.permission === "WRITE")) return "WRITE";
|
|
5238
|
-
return "READ";
|
|
5239
5149
|
}
|
|
5240
5150
|
/**
|
|
5241
5151
|
* Grant a user permission to execute an operation on a document
|
|
@@ -5269,36 +5179,6 @@ var DocumentPermissionService = class {
|
|
|
5269
5179
|
await this.db.deleteFrom("OperationUserPermission").where("documentId", "=", documentId).where("operationType", "=", operationType).where("userAddress", "=", userAddress.toLowerCase()).execute();
|
|
5270
5180
|
}
|
|
5271
5181
|
/**
|
|
5272
|
-
* Grant a group permission to execute an operation on a document
|
|
5273
|
-
*/
|
|
5274
|
-
async grantGroupOperationPermission(documentId, operationType, groupId, grantedBy) {
|
|
5275
|
-
const now = /* @__PURE__ */ new Date();
|
|
5276
|
-
await this.db.insertInto("OperationGroupPermission").values({
|
|
5277
|
-
documentId,
|
|
5278
|
-
operationType,
|
|
5279
|
-
groupId,
|
|
5280
|
-
grantedBy: grantedBy.toLowerCase(),
|
|
5281
|
-
createdAt: now
|
|
5282
|
-
}).onConflict((oc) => oc.columns([
|
|
5283
|
-
"documentId",
|
|
5284
|
-
"operationType",
|
|
5285
|
-
"groupId"
|
|
5286
|
-
]).doNothing()).execute();
|
|
5287
|
-
return await this.db.selectFrom("OperationGroupPermission").select([
|
|
5288
|
-
"documentId",
|
|
5289
|
-
"operationType",
|
|
5290
|
-
"groupId",
|
|
5291
|
-
"grantedBy",
|
|
5292
|
-
"createdAt"
|
|
5293
|
-
]).where("documentId", "=", documentId).where("operationType", "=", operationType).where("groupId", "=", groupId).executeTakeFirstOrThrow();
|
|
5294
|
-
}
|
|
5295
|
-
/**
|
|
5296
|
-
* Revoke a group's permission to execute an operation
|
|
5297
|
-
*/
|
|
5298
|
-
async revokeGroupOperationPermission(documentId, operationType, groupId) {
|
|
5299
|
-
await this.db.deleteFrom("OperationGroupPermission").where("documentId", "=", documentId).where("operationType", "=", operationType).where("groupId", "=", groupId).execute();
|
|
5300
|
-
}
|
|
5301
|
-
/**
|
|
5302
5182
|
* Get all users with permission to execute an operation
|
|
5303
5183
|
*/
|
|
5304
5184
|
async getOperationUserPermissions(documentId, operationType) {
|
|
@@ -5311,35 +5191,18 @@ var DocumentPermissionService = class {
|
|
|
5311
5191
|
]).where("documentId", "=", documentId).where("operationType", "=", operationType).execute();
|
|
5312
5192
|
}
|
|
5313
5193
|
/**
|
|
5314
|
-
*
|
|
5315
|
-
*/
|
|
5316
|
-
async getOperationGroupPermissions(documentId, operationType) {
|
|
5317
|
-
return this.db.selectFrom("OperationGroupPermission").select([
|
|
5318
|
-
"documentId",
|
|
5319
|
-
"operationType",
|
|
5320
|
-
"groupId",
|
|
5321
|
-
"grantedBy",
|
|
5322
|
-
"createdAt"
|
|
5323
|
-
]).where("documentId", "=", documentId).where("operationType", "=", operationType).execute();
|
|
5324
|
-
}
|
|
5325
|
-
/**
|
|
5326
|
-
* Check if a user can execute a specific operation on a document.
|
|
5327
|
-
* Returns true if user has direct permission or is in a group with permission.
|
|
5194
|
+
* Whether an operation-permission row exists for the user on this operation.
|
|
5328
5195
|
*/
|
|
5329
|
-
async
|
|
5330
|
-
if (!userAddress) return false;
|
|
5196
|
+
async hasOperationGrant(documentId, operationType, userAddress) {
|
|
5331
5197
|
const normalizedAddress = userAddress.toLowerCase();
|
|
5332
|
-
|
|
5333
|
-
return !!await this.db.selectFrom("OperationGroupPermission").innerJoin("UserGroup", "UserGroup.groupId", "OperationGroupPermission.groupId").select("OperationGroupPermission.groupId").where("OperationGroupPermission.documentId", "=", documentId).where("OperationGroupPermission.operationType", "=", operationType).where("UserGroup.userAddress", "=", normalizedAddress).executeTakeFirst();
|
|
5198
|
+
return !!await this.db.selectFrom("OperationUserPermission").select("userAddress").where("documentId", "=", documentId).where("operationType", "=", operationType).where("userAddress", "=", normalizedAddress).executeTakeFirst();
|
|
5334
5199
|
}
|
|
5335
5200
|
/**
|
|
5336
5201
|
* Check if an operation has any permissions set (is restricted)
|
|
5337
5202
|
*/
|
|
5338
5203
|
async isOperationRestricted(documentId, operationType) {
|
|
5339
5204
|
const userPermCount = await this.db.selectFrom("OperationUserPermission").select(sql`count(*)`.as("count")).where("documentId", "=", documentId).where("operationType", "=", operationType).executeTakeFirst();
|
|
5340
|
-
|
|
5341
|
-
const groupPermCount = await this.db.selectFrom("OperationGroupPermission").select(sql`count(*)`.as("count")).where("documentId", "=", documentId).where("operationType", "=", operationType).executeTakeFirst();
|
|
5342
|
-
return groupPermCount !== void 0 && Number(groupPermCount.count) > 0;
|
|
5205
|
+
return userPermCount !== void 0 && Number(userPermCount.count) > 0;
|
|
5343
5206
|
}
|
|
5344
5207
|
/**
|
|
5345
5208
|
* Check if a specific document has a protection row set to true.
|
|
@@ -5454,6 +5317,60 @@ var DocumentPermissionService = class {
|
|
|
5454
5317
|
}
|
|
5455
5318
|
};
|
|
5456
5319
|
//#endregion
|
|
5320
|
+
//#region src/services/get-parent-ids.ts
|
|
5321
|
+
/**
|
|
5322
|
+
* The canonical parent-document resolver used for permission inheritance:
|
|
5323
|
+
* a document's parents are the sources of its incoming "child"
|
|
5324
|
+
* relationships. Lookup failures resolve to "no parents" so a relationship
|
|
5325
|
+
* store outage degrades to no inherited permissions rather than an error.
|
|
5326
|
+
*/
|
|
5327
|
+
function createGetParentIdsFn(reactorClient) {
|
|
5328
|
+
return async (documentId) => {
|
|
5329
|
+
try {
|
|
5330
|
+
return (await reactorClient.getIncomingRelationships(documentId, "child")).results.map((doc) => doc.header.id);
|
|
5331
|
+
} catch {
|
|
5332
|
+
return [];
|
|
5333
|
+
}
|
|
5334
|
+
};
|
|
5335
|
+
}
|
|
5336
|
+
//#endregion
|
|
5337
|
+
//#region src/services/mcp-request-authorizer.ts
|
|
5338
|
+
/**
|
|
5339
|
+
* Authorization gate for the /mcp endpoint (AUTH_REVIEW S-C1). MCP tools have
|
|
5340
|
+
* unrestricted reactor access, so access is limited to supreme admins when
|
|
5341
|
+
* auth is enabled. OPEN policy is public; any other policy without an
|
|
5342
|
+
* AuthService fails closed.
|
|
5343
|
+
*/
|
|
5344
|
+
function createMcpRequestAuthorizer(authService, authorizationService) {
|
|
5345
|
+
return async (req) => {
|
|
5346
|
+
if (!authService) {
|
|
5347
|
+
if (authorizationService.config.policy === AuthorizationPolicy.OPEN) return { authorized: true };
|
|
5348
|
+
return {
|
|
5349
|
+
authorized: false,
|
|
5350
|
+
status: 401,
|
|
5351
|
+
message: "Authentication required"
|
|
5352
|
+
};
|
|
5353
|
+
}
|
|
5354
|
+
const context = await authService.verifyBearer(req.headers.authorization);
|
|
5355
|
+
if (context instanceof Response) return {
|
|
5356
|
+
authorized: false,
|
|
5357
|
+
status: context.status,
|
|
5358
|
+
message: "Authentication failed"
|
|
5359
|
+
};
|
|
5360
|
+
if (!context.user) return {
|
|
5361
|
+
authorized: false,
|
|
5362
|
+
status: 401,
|
|
5363
|
+
message: "Authentication required"
|
|
5364
|
+
};
|
|
5365
|
+
if (!authorizationService.isSupremeAdmin(context.user.address)) return {
|
|
5366
|
+
authorized: false,
|
|
5367
|
+
status: 403,
|
|
5368
|
+
message: "Forbidden: MCP access requires an administrator"
|
|
5369
|
+
};
|
|
5370
|
+
return { authorized: true };
|
|
5371
|
+
};
|
|
5372
|
+
}
|
|
5373
|
+
//#endregion
|
|
5457
5374
|
//#region src/utils/db.ts
|
|
5458
5375
|
function isPG(connectionString) {
|
|
5459
5376
|
if (connectionString.startsWith("postgresql://") || connectionString.startsWith("postgres://")) return true;
|
|
@@ -5586,6 +5503,17 @@ const DEFAULT_PORT = 4e3;
|
|
|
5586
5503
|
function assertAuthRequiredForDocumentPermissions(authEnabled, documentPermissionsRequested) {
|
|
5587
5504
|
if (!authEnabled && documentPermissionsRequested) throw new Error("Document permissions require authentication: AUTH_ENABLED is false but document permissions were requested (DOCUMENT_PERMISSIONS_ENABLED=true or a documentPermissionService was provided). Enable authentication (AUTH_ENABLED=true, or auth.enabled in the config file) or disable document permissions.");
|
|
5588
5505
|
}
|
|
5506
|
+
/**
|
|
5507
|
+
* Refuses SKIP_CREDENTIAL_VERIFICATION at boot outside tests or an explicit
|
|
5508
|
+
* opt-in: it removes the only binding between a token's claimed address and its
|
|
5509
|
+
* signing key. Fail-closed — unset NODE_ENV counts as production.
|
|
5510
|
+
*/
|
|
5511
|
+
function assertSkipCredentialVerificationAllowed(authEnabled, skipCredentialVerification, env) {
|
|
5512
|
+
if (!authEnabled || !skipCredentialVerification) return;
|
|
5513
|
+
const inAutomatedTest = env.VITEST === "true" || env.NODE_ENV === "test";
|
|
5514
|
+
const acknowledged = env.ALLOW_INSECURE_SKIP_CREDENTIAL_VERIFICATION === "true";
|
|
5515
|
+
if (!inAutomatedTest && !acknowledged) throw new Error("SKIP_CREDENTIAL_VERIFICATION is set but refused: it disables the live Renown credential check — the only check binding a token's claimed address to the key that signed it — so honoring it allows identity spoofing, including of admins. It is never safe in production. For local or sandbox use, also set ALLOW_INSECURE_SKIP_CREDENTIAL_VERIFICATION=true to acknowledge the risk; automated test runs (VITEST=true or NODE_ENV=test) are exempt.");
|
|
5516
|
+
}
|
|
5589
5517
|
function createReadinessGate() {
|
|
5590
5518
|
let ready = false;
|
|
5591
5519
|
return {
|
|
@@ -5633,11 +5561,8 @@ function makeDbClosers(knexInstance, pglite) {
|
|
|
5633
5561
|
/**
|
|
5634
5562
|
* Sets up the subgraph manager and registers subgraphs
|
|
5635
5563
|
*/
|
|
5636
|
-
async function setupGraphQLManager(httpAdapter, authFetchMiddleware, httpServer, wsServer, client, relationalDb, analyticsStore, syncManager, subgraphs, logger, authorizationService,
|
|
5637
|
-
const graphqlManager = new GraphQLManager(config.basePath, httpServer, wsServer, client, relationalDb, analyticsStore, syncManager, logger, httpAdapter, await createGatewayAdapter("apollo", logger), {
|
|
5638
|
-
enabled: auth?.enabled ?? false,
|
|
5639
|
-
admins: auth?.admins ?? []
|
|
5640
|
-
}, documentPermissionService, { enableDocumentModelSubgraphs }, port, authorizationService, reactorDriveClient);
|
|
5564
|
+
async function setupGraphQLManager(httpAdapter, authFetchMiddleware, httpServer, wsServer, client, relationalDb, analyticsStore, syncManager, subgraphs, logger, authorizationService, authService, documentPermissionService, enableDocumentModelSubgraphs, port, reactorDriveClient) {
|
|
5565
|
+
const graphqlManager = new GraphQLManager(config.basePath, httpServer, wsServer, client, relationalDb, analyticsStore, syncManager, logger, httpAdapter, await createGatewayAdapter("apollo", logger), authService, documentPermissionService, { enableDocumentModelSubgraphs }, port, authorizationService, reactorDriveClient);
|
|
5641
5566
|
await graphqlManager.init(subgraphs.core, authFetchMiddleware);
|
|
5642
5567
|
for (const [, collection] of subgraphs.extended.entries()) for (const subgraph of collection) await graphqlManager.registerSubgraph(subgraph, "graphql");
|
|
5643
5568
|
await graphqlManager.updateRouter();
|
|
@@ -5711,6 +5636,7 @@ async function startServer(httpAdapter, port, httpsOptions, logger) {
|
|
|
5711
5636
|
async function _setupCommonInfrastructure(options) {
|
|
5712
5637
|
const port = options.port ?? DEFAULT_PORT;
|
|
5713
5638
|
const { adapter: httpAdapter } = await createHttpAdapter("express");
|
|
5639
|
+
const logger = options.logger ?? defaultLogger;
|
|
5714
5640
|
let admins = [];
|
|
5715
5641
|
let authEnabled = false;
|
|
5716
5642
|
if (options.configFile) {
|
|
@@ -5721,16 +5647,23 @@ async function _setupCommonInfrastructure(options) {
|
|
|
5721
5647
|
admins = options.auth.admins.map((a) => a.toLowerCase());
|
|
5722
5648
|
authEnabled = options.auth.enabled;
|
|
5723
5649
|
}
|
|
5724
|
-
const { AUTH_ENABLED, ADMINS, DEFAULT_PROTECTION, DOCUMENT_PERMISSIONS_ENABLED, SKIP_CREDENTIAL_VERIFICATION } = process.env;
|
|
5650
|
+
const { AUTH_ENABLED, ADMINS, DEFAULT_PROTECTION, DOCUMENT_PERMISSIONS_ENABLED, SKIP_CREDENTIAL_VERIFICATION, CREDENTIAL_VERIFICATION_CACHE_TTL_MS } = process.env;
|
|
5725
5651
|
if (AUTH_ENABLED !== void 0) authEnabled = AUTH_ENABLED === "true";
|
|
5726
5652
|
if (ADMINS !== void 0) admins = ADMINS.split(",").map((a) => a.toLowerCase());
|
|
5727
5653
|
let defaultProtection = false;
|
|
5728
5654
|
if (DEFAULT_PROTECTION !== void 0) defaultProtection = DEFAULT_PROTECTION.toLowerCase() === "true";
|
|
5729
5655
|
let skipCredentialVerification = false;
|
|
5730
5656
|
if (SKIP_CREDENTIAL_VERIFICATION !== void 0) skipCredentialVerification = SKIP_CREDENTIAL_VERIFICATION === "true";
|
|
5657
|
+
let credentialVerificationCacheTtlMs;
|
|
5658
|
+
if (CREDENTIAL_VERIFICATION_CACHE_TTL_MS !== void 0) {
|
|
5659
|
+
const parsed = Number(CREDENTIAL_VERIFICATION_CACHE_TTL_MS);
|
|
5660
|
+
if (CREDENTIAL_VERIFICATION_CACHE_TTL_MS.trim() !== "" && Number.isFinite(parsed) && parsed >= 0) credentialVerificationCacheTtlMs = parsed;
|
|
5661
|
+
else logger.warn(`Ignoring invalid CREDENTIAL_VERIFICATION_CACHE_TTL_MS="${CREDENTIAL_VERIFICATION_CACHE_TTL_MS}" (expected a non-negative number of milliseconds; 0 disables caching) — using the default TTL`);
|
|
5662
|
+
}
|
|
5731
5663
|
const documentPermissionsRequested = options.documentPermissionService !== void 0 || DOCUMENT_PERMISSIONS_ENABLED === "true";
|
|
5732
5664
|
assertAuthRequiredForDocumentPermissions(authEnabled, documentPermissionsRequested);
|
|
5733
|
-
|
|
5665
|
+
assertSkipCredentialVerificationAllowed(authEnabled, skipCredentialVerification, process.env);
|
|
5666
|
+
if (authEnabled && skipCredentialVerification) logger.warn("SECURITY: SKIP_CREDENTIAL_VERIFICATION is enabled — Renown credential verification is disabled and a bearer token's claimed address is NOT cryptographically bound to its signing key. Identity is unverifiable; use only in development or test.");
|
|
5734
5667
|
httpAdapter.getRoute("/health", () => new Response("OK", { status: 200 }));
|
|
5735
5668
|
const readiness = createReadinessGate();
|
|
5736
5669
|
httpAdapter.getRoute("/ready", () => readiness.isReady() ? new Response("OK", { status: 200 }) : new Response("starting", { status: 503 }));
|
|
@@ -5749,7 +5682,8 @@ async function _setupCommonInfrastructure(options) {
|
|
|
5749
5682
|
authService = new AuthService({
|
|
5750
5683
|
enabled: authEnabled,
|
|
5751
5684
|
admins,
|
|
5752
|
-
skipCredentialVerification
|
|
5685
|
+
skipCredentialVerification,
|
|
5686
|
+
credentialVerificationCacheTtlMs
|
|
5753
5687
|
});
|
|
5754
5688
|
authFetchMiddleware = createAuthFetchMiddleware(authService);
|
|
5755
5689
|
}
|
|
@@ -5766,12 +5700,11 @@ async function _setupCommonInfrastructure(options) {
|
|
|
5766
5700
|
logger.info("Document permission service initialized");
|
|
5767
5701
|
}
|
|
5768
5702
|
const policy = documentPermissionService ? AuthorizationPolicy.DOCUMENT_PERMISSIONS : authEnabled ? AuthorizationPolicy.ADMIN_ONLY : AuthorizationPolicy.OPEN;
|
|
5769
|
-
const
|
|
5703
|
+
const authorizationConfig = {
|
|
5770
5704
|
admins,
|
|
5771
5705
|
defaultProtection,
|
|
5772
5706
|
policy
|
|
5773
|
-
}
|
|
5774
|
-
logger.info(`Authorization service initialized (policy: ${policy})`);
|
|
5707
|
+
};
|
|
5775
5708
|
const attachmentStoragePath = resolveAttachmentStoragePath(options);
|
|
5776
5709
|
await mkdir(attachmentStoragePath, { recursive: true });
|
|
5777
5710
|
const { db: attachmentDb, knex: attachmentKnex, pglite: attachmentPglite } = getDbClient(options.dbPath, options.pgliteFactory);
|
|
@@ -5791,14 +5724,10 @@ async function _setupCommonInfrastructure(options) {
|
|
|
5791
5724
|
httpAdapter,
|
|
5792
5725
|
authFetchMiddleware,
|
|
5793
5726
|
authService,
|
|
5794
|
-
auth: {
|
|
5795
|
-
enabled: authEnabled,
|
|
5796
|
-
admins
|
|
5797
|
-
},
|
|
5798
5727
|
relationalDb,
|
|
5799
5728
|
analyticsStore,
|
|
5800
5729
|
documentPermissionService,
|
|
5801
|
-
|
|
5730
|
+
authorizationConfig,
|
|
5802
5731
|
attachments,
|
|
5803
5732
|
packages,
|
|
5804
5733
|
dbClosers,
|
|
@@ -5808,7 +5737,7 @@ async function _setupCommonInfrastructure(options) {
|
|
|
5808
5737
|
/**
|
|
5809
5738
|
* Private helper function containing common setup logic for API initialization
|
|
5810
5739
|
*/
|
|
5811
|
-
async function _setupAPI(reactorClient, syncManager, reactorProcessorManager, httpAdapter, authFetchMiddleware, authService, port, packages, relationalDb, analyticsStore, documentPermissionService, processors, subgraphs, options,
|
|
5740
|
+
async function _setupAPI(reactorClient, syncManager, reactorProcessorManager, httpAdapter, authFetchMiddleware, authService, port, packages, relationalDb, analyticsStore, documentPermissionService, processors, subgraphs, options, processorApp, readModels, attachments, authorizationConfig, documentModelRegistry, dbClosers = [], reactorDriveClient) {
|
|
5812
5741
|
const hostModule = {
|
|
5813
5742
|
relationalDb,
|
|
5814
5743
|
analyticsStore,
|
|
@@ -5853,6 +5782,8 @@ async function _setupAPI(reactorClient, syncManager, reactorProcessorManager, ht
|
|
|
5853
5782
|
}))).flat());
|
|
5854
5783
|
}
|
|
5855
5784
|
const { httpServer, wsServer } = await startServer(httpAdapter, port, options.https, logger);
|
|
5785
|
+
const authorizationService = createAuthorizationService(authorizationConfig, documentPermissionService, createGetParentIdsFn(reactorClient));
|
|
5786
|
+
logger.info(`Authorization service initialized (policy: ${authorizationConfig.policy})`);
|
|
5856
5787
|
const coreSubgraphs = DefaultCoreSubgraphs.slice();
|
|
5857
5788
|
coreSubgraphs.push(ReactorSubgraph);
|
|
5858
5789
|
if (documentPermissionService) {
|
|
@@ -5862,12 +5793,13 @@ async function _setupAPI(reactorClient, syncManager, reactorProcessorManager, ht
|
|
|
5862
5793
|
const graphqlManager = await setupGraphQLManager(httpAdapter, authFetchMiddleware, httpServer, wsServer, reactorClient, relationalDb, analyticsStore, syncManager, {
|
|
5863
5794
|
extended: subgraphs,
|
|
5864
5795
|
core: coreSubgraphs
|
|
5865
|
-
}, logger.child(["graphql-manager"]), authorizationService,
|
|
5796
|
+
}, logger.child(["graphql-manager"]), authorizationService, authService, documentPermissionService, options.enableDocumentModelSubgraphs, port, reactorDriveClient);
|
|
5866
5797
|
setupEventListeners(packages, graphqlManager, reactorProcessorManager, hostModule, documentModelRegistry);
|
|
5867
5798
|
if (mcpServerEnabled) {
|
|
5868
5799
|
await setupMcpServer({
|
|
5869
5800
|
client: reactorClient,
|
|
5870
|
-
syncManager
|
|
5801
|
+
syncManager,
|
|
5802
|
+
authorizeRequest: createMcpRequestAuthorizer(authService, authorizationService)
|
|
5871
5803
|
}, httpAdapter);
|
|
5872
5804
|
logger.info(`MCP server available at http://localhost:${port}/mcp`);
|
|
5873
5805
|
}
|
|
@@ -5924,7 +5856,7 @@ function buildApiDispose(args) {
|
|
|
5924
5856
|
};
|
|
5925
5857
|
}
|
|
5926
5858
|
async function initializeAndStartAPI(clientInitializer, options, processorApp) {
|
|
5927
|
-
const { port, httpAdapter, authFetchMiddleware, authService,
|
|
5859
|
+
const { port, httpAdapter, authFetchMiddleware, authService, relationalDb, analyticsStore, documentPermissionService, authorizationConfig, attachments, packages, dbClosers, readiness } = await _setupCommonInfrastructure(options);
|
|
5928
5860
|
const { documentModels, processors, subgraphs } = await packages.init();
|
|
5929
5861
|
const { module: reactorClientModule, reactorDriveClient } = await clientInitializer(documentModels);
|
|
5930
5862
|
const reactorClient = reactorClientModule.client;
|
|
@@ -5935,7 +5867,7 @@ async function initializeAndStartAPI(clientInitializer, options, processorApp) {
|
|
|
5935
5867
|
const documentModelRegistry = reactorClientModule.reactorModule?.documentModelRegistry;
|
|
5936
5868
|
if (!documentModelRegistry) throw new Error("DocumentModelRegistry not available from ReactorClientModule");
|
|
5937
5869
|
return {
|
|
5938
|
-
...await _setupAPI(reactorClient, syncManager, reactorProcessorManager, httpAdapter, authFetchMiddleware, authService, port, packages, relationalDb, analyticsStore, documentPermissionService, processors, subgraphs, options,
|
|
5870
|
+
...await _setupAPI(reactorClient, syncManager, reactorProcessorManager, httpAdapter, authFetchMiddleware, authService, port, packages, relationalDb, analyticsStore, documentPermissionService, processors, subgraphs, options, processorApp, (reactorClientModule.reactorModule?.readModelCoordinator)?.readModels ?? [], attachments, authorizationConfig, documentModelRegistry, dbClosers, reactorDriveClient),
|
|
5939
5871
|
client: reactorClient,
|
|
5940
5872
|
syncManager,
|
|
5941
5873
|
documentModelRegistry,
|
|
@@ -6043,7 +5975,7 @@ var PackageManagementService = class {
|
|
|
6043
5975
|
}
|
|
6044
5976
|
};
|
|
6045
5977
|
//#endregion
|
|
6046
|
-
export { ADMIN_USERS, ActionContextInputSchema, ActionInputSchema, AddRelationshipDocument, AnalyticsSubgraph, AuthService, AuthSubgraph, BaseSubgraph, ChannelMetaInputSchema, CreateDocumentDocument, CreateEmptyDocumentDocument, DeleteDocumentDocument, DeleteDocumentsDocument, DocumentChangeType, DocumentChangeTypeSchema, DocumentChangesDocument, DocumentOperationsFilterInputSchema, DocumentPermissionService, FindDocumentsDocument, GetDocumentDocument, GetDocumentIncomingRelationshipsDocument, GetDocumentModelsDocument, GetDocumentOperationsDocument, GetDocumentOutgoingRelationshipsDocument, GetDocumentWithOperationsDocument, GetJobStatusDocument, GraphQLManager, HttpDocumentModelLoader, HttpPackageLoader, ImportPackageLoader, InMemoryPackageStorage, JobChangesDocument, MoveRelationshipDocument, MutateDocumentAsyncDocument, MutateDocumentDocument, OperationContextInputSchema, OperationInputSchema, OperationWithContextInputSchema, OperationsFilterInputSchema, PackageManagementService, PackageManager, PackagesSubgraph, PagingInputSchema, PhDocumentFieldsFragmentDoc, PollSyncEnvelopesDocument, PropagationMode, PropagationModeSchema, PushSyncEnvelopesDocument, ReactorSignerAppInputSchema, ReactorSignerInputSchema, ReactorSignerUserInputSchema, ReactorSubgraph, RemoteCursorInputSchema, RemoteFilterInputSchema, RemoveRelationshipDocument, RenameDocumentDocument, SearchFilterInputSchema, SetPreferredEditorDocument, SyncEnvelopeInputSchema, SyncEnvelopeType, SyncEnvelopeTypeSchema, SystemSubgraph, TouchChannelDocument, TouchChannelInputSchema, ViewFilterInputSchema, assertAuthRequiredForDocumentPermissions, buildGraphQlDocument, buildGraphQlDriveDocument, buildGraphqlOperation, buildGraphqlOperations, buildSubgraphSchemaModule, createAuthFetchMiddleware, createGatewayAdapter, createHttpAdapter, createMergedSchema, createReactorGraphQLClient, createSchema, definedNonNullAnySchema, driveIdFromUrl, extractSubgraphsFromModule, generateDocumentModelSchema, getAuthContext, getDbClient, getDocumentModelSchemaName, getDocumentModelTypeDefs, getGitHash, getGitUrl, getSdk, getUniqueDocumentModels, getVersion, initAnalyticsStoreSql, initializeAndStartAPI, isDefinedNonNullAny, isExpectedLoaderMiss, isSubgraphClass, parseDriveUrl, renderGraphqlPlayground };
|
|
5978
|
+
export { ADMIN_USERS, ActionContextInputSchema, ActionInputSchema, AddRelationshipDocument, AnalyticsSubgraph, AuthService, AuthSubgraph, AuthorizationPolicy, AuthorizedDocumentHandle, BaseSubgraph, ChannelMetaInputSchema, CreateDocumentDocument, CreateEmptyDocumentDocument, DeleteDocumentDocument, DeleteDocumentsDocument, DocumentChangeType, DocumentChangeTypeSchema, DocumentChangesDocument, DocumentOperationsFilterInputSchema, DocumentPermissionService, FindDocumentsDocument, GetDocumentDocument, GetDocumentIncomingRelationshipsDocument, GetDocumentModelsDocument, GetDocumentOperationsDocument, GetDocumentOutgoingRelationshipsDocument, GetDocumentWithOperationsDocument, GetJobStatusDocument, GraphQLManager, HttpDocumentModelLoader, HttpPackageLoader, ImportPackageLoader, InMemoryPackageStorage, JobChangesDocument, MoveRelationshipDocument, MutateDocumentAsyncDocument, MutateDocumentDocument, OperationContextInputSchema, OperationInputSchema, OperationWithContextInputSchema, OperationsFilterInputSchema, PackageManagementService, PackageManager, PackagesSubgraph, PagingInputSchema, PhDocumentFieldsFragmentDoc, PollSyncEnvelopesDocument, PropagationMode, PropagationModeSchema, PushSyncEnvelopesDocument, ReactorSignerAppInputSchema, ReactorSignerInputSchema, ReactorSignerUserInputSchema, ReactorSubgraph, RemoteCursorInputSchema, RemoteFilterInputSchema, RemoveRelationshipDocument, RenameDocumentDocument, SearchFilterInputSchema, SetPreferredEditorDocument, SyncEnvelopeInputSchema, SyncEnvelopeType, SyncEnvelopeTypeSchema, SystemSubgraph, TouchChannelDocument, TouchChannelInputSchema, ViewFilterInputSchema, assertAuthRequiredForDocumentPermissions, assertSkipCredentialVerificationAllowed, buildGraphQlDocument, buildGraphQlDriveDocument, buildGraphqlOperation, buildGraphqlOperations, buildSubgraphSchemaModule, createAuthFetchMiddleware, createGatewayAdapter, createHttpAdapter, createMergedSchema, createReactorGraphQLClient, createSchema, definedNonNullAnySchema, driveIdFromUrl, extractSubgraphsFromModule, generateDocumentModelSchema, getAuthContext, getDbClient, getDocumentModelSchemaName, getDocumentModelTypeDefs, getGitHash, getGitUrl, getSdk, getUniqueDocumentModels, getVersion, initAnalyticsStoreSql, initializeAndStartAPI, isDefinedNonNullAny, isExpectedLoaderMiss, isSubgraphClass, parseDriveUrl, renderGraphqlPlayground };
|
|
6047
5979
|
|
|
6048
5980
|
//# sourceMappingURL=index.mjs.map
|
|
6049
|
-
//# debugId=
|
|
5981
|
+
//# debugId=88c66104-0cca-57b8-b3ad-93532b51c2b3
|