@powerhousedao/vetra 6.0.0-dev.23 → 6.0.0-dev.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/dist/subgraphs/__tests__/app-module-permissions.test.d.ts +2 -0
  2. package/dist/subgraphs/__tests__/app-module-permissions.test.d.ts.map +1 -0
  3. package/dist/subgraphs/__tests__/app-module-permissions.test.js +436 -0
  4. package/dist/subgraphs/__tests__/permission-utils.test.d.ts +2 -0
  5. package/dist/subgraphs/__tests__/permission-utils.test.d.ts.map +1 -0
  6. package/dist/subgraphs/__tests__/permission-utils.test.js +505 -0
  7. package/dist/subgraphs/__tests__/vetra-read-model-permissions.test.d.ts +2 -0
  8. package/dist/subgraphs/__tests__/vetra-read-model-permissions.test.d.ts.map +1 -0
  9. package/dist/subgraphs/__tests__/vetra-read-model-permissions.test.js +319 -0
  10. package/dist/subgraphs/app-module/resolvers.d.ts.map +1 -1
  11. package/dist/subgraphs/app-module/resolvers.js +53 -9
  12. package/dist/subgraphs/document-editor/resolvers.d.ts.map +1 -1
  13. package/dist/subgraphs/document-editor/resolvers.js +45 -7
  14. package/dist/subgraphs/permission-utils.d.ts +31 -0
  15. package/dist/subgraphs/permission-utils.d.ts.map +1 -0
  16. package/dist/subgraphs/permission-utils.js +101 -0
  17. package/dist/subgraphs/processor-module/resolvers.d.ts.map +1 -1
  18. package/dist/subgraphs/processor-module/resolvers.js +49 -8
  19. package/dist/subgraphs/subgraph-module/resolvers.d.ts.map +1 -1
  20. package/dist/subgraphs/subgraph-module/resolvers.js +37 -5
  21. package/dist/subgraphs/vetra-package/resolvers.d.ts.map +1 -1
  22. package/dist/subgraphs/vetra-package/resolvers.js +69 -13
  23. package/dist/subgraphs/vetra-read-model/resolvers.d.ts +2 -2
  24. package/dist/subgraphs/vetra-read-model/resolvers.d.ts.map +1 -1
  25. package/dist/subgraphs/vetra-read-model/resolvers.js +16 -2
  26. package/dist/tsconfig.tsbuildinfo +1 -1
  27. package/dist/vitest.config.d.ts.map +1 -1
  28. package/dist/vitest.config.js +1 -0
  29. package/package.json +15 -15
@@ -0,0 +1,101 @@
1
+ import { GraphQLError } from "graphql";
2
+ /**
3
+ * Check if user has global read access (admin, user, or guest)
4
+ */
5
+ export function hasGlobalReadAccess(ctx) {
6
+ const isGlobalAdmin = ctx.isAdmin?.(ctx.user?.address ?? "");
7
+ const isGlobalUser = ctx.isUser?.(ctx.user?.address ?? "");
8
+ const isGlobalGuest = ctx.isGuest?.(ctx.user?.address ?? "") || process.env.FREE_ENTRY === "true";
9
+ return !!(isGlobalAdmin || isGlobalUser || isGlobalGuest);
10
+ }
11
+ /**
12
+ * Check if user has global write access (admin or user, not guest)
13
+ */
14
+ export function hasGlobalWriteAccess(ctx) {
15
+ const isGlobalAdmin = ctx.isAdmin?.(ctx.user?.address ?? "");
16
+ const isGlobalUser = ctx.isUser?.(ctx.user?.address ?? "");
17
+ return !!(isGlobalAdmin || isGlobalUser);
18
+ }
19
+ /**
20
+ * Get the parent IDs function for hierarchical permission checks
21
+ */
22
+ function getParentIdsFn(subgraph) {
23
+ return async (documentId) => {
24
+ try {
25
+ const result = await subgraph.reactorClient.getParents(documentId);
26
+ return result.results.map((doc) => doc.header.id);
27
+ }
28
+ catch {
29
+ return [];
30
+ }
31
+ };
32
+ }
33
+ /**
34
+ * Check if user can read a document (with hierarchy)
35
+ */
36
+ export async function canReadDocument(subgraph, documentId, ctx) {
37
+ // Global access allows reading
38
+ if (hasGlobalReadAccess(ctx)) {
39
+ return true;
40
+ }
41
+ // Check document-level permissions with hierarchy
42
+ if (subgraph.documentPermissionService) {
43
+ return subgraph.documentPermissionService.canRead(documentId, ctx.user?.address, getParentIdsFn(subgraph));
44
+ }
45
+ return false;
46
+ }
47
+ /**
48
+ * Check if user can write to a document (with hierarchy)
49
+ */
50
+ export async function canWriteDocument(subgraph, documentId, ctx) {
51
+ // Global write access allows writing
52
+ if (hasGlobalWriteAccess(ctx)) {
53
+ return true;
54
+ }
55
+ // Check document-level permissions with hierarchy
56
+ if (subgraph.documentPermissionService) {
57
+ return subgraph.documentPermissionService.canWrite(documentId, ctx.user?.address, getParentIdsFn(subgraph));
58
+ }
59
+ return false;
60
+ }
61
+ /**
62
+ * Throw an error if user cannot read the document
63
+ */
64
+ export async function assertCanRead(subgraph, documentId, ctx) {
65
+ const canRead = await canReadDocument(subgraph, documentId, ctx);
66
+ if (!canRead) {
67
+ throw new GraphQLError("Forbidden: insufficient permissions to read this document");
68
+ }
69
+ }
70
+ /**
71
+ * Throw an error if user cannot write to the document
72
+ */
73
+ export async function assertCanWrite(subgraph, documentId, ctx) {
74
+ const canWrite = await canWriteDocument(subgraph, documentId, ctx);
75
+ if (!canWrite) {
76
+ throw new GraphQLError("Forbidden: insufficient permissions to write to this document");
77
+ }
78
+ }
79
+ /**
80
+ * Check if user can execute a specific operation on a document.
81
+ * Throws an error if the operation is restricted and user lacks permission.
82
+ */
83
+ export async function assertCanExecuteOperation(subgraph, documentId, operationType, ctx) {
84
+ // Skip if no permission service
85
+ if (!subgraph.documentPermissionService) {
86
+ return;
87
+ }
88
+ // Global admins bypass operation-level restrictions
89
+ if (ctx.isAdmin?.(ctx.user?.address ?? "")) {
90
+ return;
91
+ }
92
+ // Check if this operation has any restrictions set
93
+ const isRestricted = await subgraph.documentPermissionService.isOperationRestricted(documentId, operationType);
94
+ if (isRestricted) {
95
+ // Operation is restricted, check if user has permission
96
+ const canExecute = await subgraph.documentPermissionService.canExecuteOperation(documentId, operationType, ctx.user?.address);
97
+ if (!canExecute) {
98
+ throw new GraphQLError(`Forbidden: insufficient permissions to execute operation "${operationType}" on this document`);
99
+ }
100
+ }
101
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"resolvers.d.ts","sourceRoot":"","sources":["../../../subgraphs/processor-module/resolvers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAiB/D,eAAO,MAAM,YAAY,GACvB,UAAU,YAAY,KACrB,MAAM,CAAC,MAAM,EAAE,OAAO,CAgNxB,CAAC"}
1
+ {"version":3,"file":"resolvers.d.ts","sourceRoot":"","sources":["../../../subgraphs/processor-module/resolvers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAW,MAAM,4BAA4B,CAAC;AA2BxE,eAAO,MAAM,YAAY,GACvB,UAAU,YAAY,KACrB,MAAM,CAAC,MAAM,EAAE,OAAO,CA0SxB,CAAC"}
@@ -1,17 +1,21 @@
1
1
  import { addFile } from "document-drive";
2
2
  import { setName } from "document-model";
3
+ import { GraphQLError } from "graphql";
3
4
  import { actions, processorModuleDocumentType, } from "@powerhousedao/vetra/document-models/processor-module";
5
+ import { assertCanRead, assertCanWrite, assertCanExecuteOperation, canReadDocument, hasGlobalReadAccess, hasGlobalWriteAccess, } from "../permission-utils.js";
4
6
  export const getResolvers = (subgraph) => {
5
7
  const reactor = subgraph.reactor;
6
8
  return {
7
9
  Query: {
8
- ProcessorModule: async () => {
10
+ ProcessorModule: (_, __, ctx) => {
9
11
  return {
10
12
  getDocument: async (args) => {
11
13
  const { docId, driveId } = args;
12
14
  if (!docId) {
13
15
  throw new Error("Document id is required");
14
16
  }
17
+ // Check read permission before accessing document
18
+ await assertCanRead(subgraph, docId, ctx);
15
19
  if (driveId) {
16
20
  const docIds = await reactor.getDocuments(driveId);
17
21
  if (!docIds.includes(docId)) {
@@ -32,6 +36,8 @@ export const getResolvers = (subgraph) => {
32
36
  },
33
37
  getDocuments: async (args) => {
34
38
  const { driveId } = args;
39
+ // Check read permission on drive before listing documents
40
+ await assertCanRead(subgraph, driveId, ctx);
35
41
  const docsIds = await reactor.getDocuments(driveId);
36
42
  const docs = await Promise.all(docsIds.map(async (docId) => {
37
43
  const doc = await reactor.getDocument(docId);
@@ -46,14 +52,34 @@ export const getResolvers = (subgraph) => {
46
52
  revision: doc.header?.revision?.global ?? 0,
47
53
  };
48
54
  }));
49
- return docs.filter((doc) => doc.header.documentType === processorModuleDocumentType);
55
+ const filteredByType = docs.filter((doc) => doc.header.documentType === processorModuleDocumentType);
56
+ // If user doesn't have global read access, filter by document-level permissions
57
+ if (!hasGlobalReadAccess(ctx) &&
58
+ subgraph.documentPermissionService) {
59
+ const filteredDocs = [];
60
+ for (const doc of filteredByType) {
61
+ const canRead = await canReadDocument(subgraph, doc.id, ctx);
62
+ if (canRead) {
63
+ filteredDocs.push(doc);
64
+ }
65
+ }
66
+ return filteredDocs;
67
+ }
68
+ return filteredByType;
50
69
  },
51
70
  };
52
71
  },
53
72
  },
54
73
  Mutation: {
55
- ProcessorModule_createDocument: async (_, args) => {
74
+ ProcessorModule_createDocument: async (_, args, ctx) => {
56
75
  const { driveId, name } = args;
76
+ // If creating under a drive, check write permission on drive
77
+ if (driveId) {
78
+ await assertCanWrite(subgraph, driveId, ctx);
79
+ }
80
+ else if (!hasGlobalWriteAccess(ctx)) {
81
+ throw new GraphQLError("Forbidden: insufficient permissions to create documents");
82
+ }
57
83
  const document = await reactor.addDocument(processorModuleDocumentType);
58
84
  if (driveId) {
59
85
  await reactor.addAction(driveId, addFile({
@@ -67,8 +93,11 @@ export const getResolvers = (subgraph) => {
67
93
  }
68
94
  return document.header.id;
69
95
  },
70
- ProcessorModule_setProcessorName: async (_, args) => {
96
+ ProcessorModule_setProcessorName: async (_, args, ctx) => {
71
97
  const { docId, input } = args;
98
+ // Check write permission before mutating document
99
+ await assertCanWrite(subgraph, docId, ctx);
100
+ await assertCanExecuteOperation(subgraph, docId, "SET_PROCESSOR_NAME", ctx);
72
101
  const doc = await reactor.getDocument(docId);
73
102
  if (!doc) {
74
103
  throw new Error("Document not found");
@@ -79,8 +108,11 @@ export const getResolvers = (subgraph) => {
79
108
  }
80
109
  return true;
81
110
  },
82
- ProcessorModule_setProcessorType: async (_, args) => {
111
+ ProcessorModule_setProcessorType: async (_, args, ctx) => {
83
112
  const { docId, input } = args;
113
+ // Check write permission before mutating document
114
+ await assertCanWrite(subgraph, docId, ctx);
115
+ await assertCanExecuteOperation(subgraph, docId, "SET_PROCESSOR_TYPE", ctx);
84
116
  const doc = await reactor.getDocument(docId);
85
117
  if (!doc) {
86
118
  throw new Error("Document not found");
@@ -91,8 +123,11 @@ export const getResolvers = (subgraph) => {
91
123
  }
92
124
  return true;
93
125
  },
94
- ProcessorModule_addDocumentType: async (_, args) => {
126
+ ProcessorModule_addDocumentType: async (_, args, ctx) => {
95
127
  const { docId, input } = args;
128
+ // Check write permission before mutating document
129
+ await assertCanWrite(subgraph, docId, ctx);
130
+ await assertCanExecuteOperation(subgraph, docId, "ADD_DOCUMENT_TYPE", ctx);
96
131
  const doc = await reactor.getDocument(docId);
97
132
  if (!doc) {
98
133
  throw new Error("Document not found");
@@ -103,8 +138,11 @@ export const getResolvers = (subgraph) => {
103
138
  }
104
139
  return true;
105
140
  },
106
- ProcessorModule_removeDocumentType: async (_, args) => {
141
+ ProcessorModule_removeDocumentType: async (_, args, ctx) => {
107
142
  const { docId, input } = args;
143
+ // Check write permission before mutating document
144
+ await assertCanWrite(subgraph, docId, ctx);
145
+ await assertCanExecuteOperation(subgraph, docId, "REMOVE_DOCUMENT_TYPE", ctx);
108
146
  const doc = await reactor.getDocument(docId);
109
147
  if (!doc) {
110
148
  throw new Error("Document not found");
@@ -115,8 +153,11 @@ export const getResolvers = (subgraph) => {
115
153
  }
116
154
  return true;
117
155
  },
118
- ProcessorModule_setProcessorStatus: async (_, args) => {
156
+ ProcessorModule_setProcessorStatus: async (_, args, ctx) => {
119
157
  const { docId, input } = args;
158
+ // Check write permission before mutating document
159
+ await assertCanWrite(subgraph, docId, ctx);
160
+ await assertCanExecuteOperation(subgraph, docId, "SET_PROCESSOR_STATUS", ctx);
120
161
  const doc = await reactor.getDocument(docId);
121
162
  if (!doc) {
122
163
  throw new Error("Document not found");
@@ -1 +1 @@
1
- {"version":3,"file":"resolvers.d.ts","sourceRoot":"","sources":["../../../subgraphs/subgraph-module/resolvers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAc/D,eAAO,MAAM,YAAY,GACvB,UAAU,YAAY,KACrB,MAAM,CAAC,MAAM,EAAE,OAAO,CAwIxB,CAAC"}
1
+ {"version":3,"file":"resolvers.d.ts","sourceRoot":"","sources":["../../../subgraphs/subgraph-module/resolvers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAW,MAAM,4BAA4B,CAAC;AAwBxE,eAAO,MAAM,YAAY,GACvB,UAAU,YAAY,KACrB,MAAM,CAAC,MAAM,EAAE,OAAO,CAiMxB,CAAC"}
@@ -1,17 +1,21 @@
1
1
  import { addFile } from "document-drive";
2
2
  import { setName } from "document-model";
3
+ import { GraphQLError } from "graphql";
3
4
  import { actions, subgraphModuleDocumentType, } from "@powerhousedao/vetra/document-models/subgraph-module";
5
+ import { assertCanRead, assertCanWrite, assertCanExecuteOperation, canReadDocument, hasGlobalReadAccess, hasGlobalWriteAccess, } from "../permission-utils.js";
4
6
  export const getResolvers = (subgraph) => {
5
7
  const reactor = subgraph.reactor;
6
8
  return {
7
9
  Query: {
8
- SubgraphModule: async () => {
10
+ SubgraphModule: (_, __, ctx) => {
9
11
  return {
10
12
  getDocument: async (args) => {
11
13
  const { docId, driveId } = args;
12
14
  if (!docId) {
13
15
  throw new Error("Document id is required");
14
16
  }
17
+ // Check read permission before accessing document
18
+ await assertCanRead(subgraph, docId, ctx);
15
19
  if (driveId) {
16
20
  const docIds = await reactor.getDocuments(driveId);
17
21
  if (!docIds.includes(docId)) {
@@ -32,6 +36,8 @@ export const getResolvers = (subgraph) => {
32
36
  },
33
37
  getDocuments: async (args) => {
34
38
  const { driveId } = args;
39
+ // Check read permission on drive before listing documents
40
+ await assertCanRead(subgraph, driveId, ctx);
35
41
  const docsIds = await reactor.getDocuments(driveId);
36
42
  const docs = await Promise.all(docsIds.map(async (docId) => {
37
43
  const doc = await reactor.getDocument(docId);
@@ -46,14 +52,34 @@ export const getResolvers = (subgraph) => {
46
52
  revision: doc.header?.revision?.global ?? 0,
47
53
  };
48
54
  }));
49
- return docs.filter((doc) => doc.header.documentType === subgraphModuleDocumentType);
55
+ const filteredByType = docs.filter((doc) => doc.header.documentType === subgraphModuleDocumentType);
56
+ // If user doesn't have global read access, filter by document-level permissions
57
+ if (!hasGlobalReadAccess(ctx) &&
58
+ subgraph.documentPermissionService) {
59
+ const filteredDocs = [];
60
+ for (const doc of filteredByType) {
61
+ const canRead = await canReadDocument(subgraph, doc.id, ctx);
62
+ if (canRead) {
63
+ filteredDocs.push(doc);
64
+ }
65
+ }
66
+ return filteredDocs;
67
+ }
68
+ return filteredByType;
50
69
  },
51
70
  };
52
71
  },
53
72
  },
54
73
  Mutation: {
55
- SubgraphModule_createDocument: async (_, args) => {
74
+ SubgraphModule_createDocument: async (_, args, ctx) => {
56
75
  const { driveId, name } = args;
76
+ // If creating under a drive, check write permission on drive
77
+ if (driveId) {
78
+ await assertCanWrite(subgraph, driveId, ctx);
79
+ }
80
+ else if (!hasGlobalWriteAccess(ctx)) {
81
+ throw new GraphQLError("Forbidden: insufficient permissions to create documents");
82
+ }
57
83
  const document = await reactor.addDocument(subgraphModuleDocumentType);
58
84
  if (driveId) {
59
85
  await reactor.addAction(driveId, addFile({
@@ -67,8 +93,11 @@ export const getResolvers = (subgraph) => {
67
93
  }
68
94
  return document.header.id;
69
95
  },
70
- SubgraphModule_setSubgraphName: async (_, args) => {
96
+ SubgraphModule_setSubgraphName: async (_, args, ctx) => {
71
97
  const { docId, input } = args;
98
+ // Check write permission before mutating document
99
+ await assertCanWrite(subgraph, docId, ctx);
100
+ await assertCanExecuteOperation(subgraph, docId, "SET_SUBGRAPH_NAME", ctx);
72
101
  const doc = await reactor.getDocument(docId);
73
102
  if (!doc) {
74
103
  throw new Error("Document not found");
@@ -79,8 +108,11 @@ export const getResolvers = (subgraph) => {
79
108
  }
80
109
  return true;
81
110
  },
82
- SubgraphModule_setSubgraphStatus: async (_, args) => {
111
+ SubgraphModule_setSubgraphStatus: async (_, args, ctx) => {
83
112
  const { docId, input } = args;
113
+ // Check write permission before mutating document
114
+ await assertCanWrite(subgraph, docId, ctx);
115
+ await assertCanExecuteOperation(subgraph, docId, "SET_SUBGRAPH_STATUS", ctx);
84
116
  const doc = await reactor.getDocument(docId);
85
117
  if (!doc) {
86
118
  throw new Error("Document not found");
@@ -1 +1 @@
1
- {"version":3,"file":"resolvers.d.ts","sourceRoot":"","sources":["../../../subgraphs/vetra-package/resolvers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAsB/D,eAAO,MAAM,YAAY,GACvB,UAAU,YAAY,KACrB,MAAM,CAAC,MAAM,EAAE,OAAO,CAuUxB,CAAC"}
1
+ {"version":3,"file":"resolvers.d.ts","sourceRoot":"","sources":["../../../subgraphs/vetra-package/resolvers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAW,MAAM,4BAA4B,CAAC;AAgCxE,eAAO,MAAM,YAAY,GACvB,UAAU,YAAY,KACrB,MAAM,CAAC,MAAM,EAAE,OAAO,CAwdxB,CAAC"}
@@ -1,17 +1,21 @@
1
1
  import { addFile } from "document-drive";
2
2
  import { setName } from "document-model";
3
+ import { GraphQLError } from "graphql";
3
4
  import { actions, vetraPackageDocumentType, } from "@powerhousedao/vetra/document-models/vetra-package";
5
+ import { assertCanRead, assertCanWrite, assertCanExecuteOperation, canReadDocument, hasGlobalReadAccess, hasGlobalWriteAccess, } from "../permission-utils.js";
4
6
  export const getResolvers = (subgraph) => {
5
7
  const reactor = subgraph.reactor;
6
8
  return {
7
9
  Query: {
8
- VetraPackage: async () => {
10
+ VetraPackage: (_, __, ctx) => {
9
11
  return {
10
12
  getDocument: async (args) => {
11
13
  const { docId, driveId } = args;
12
14
  if (!docId) {
13
15
  throw new Error("Document id is required");
14
16
  }
17
+ // Check read permission before accessing document
18
+ await assertCanRead(subgraph, docId, ctx);
15
19
  if (driveId) {
16
20
  const docIds = await reactor.getDocuments(driveId);
17
21
  if (!docIds.includes(docId)) {
@@ -32,6 +36,8 @@ export const getResolvers = (subgraph) => {
32
36
  },
33
37
  getDocuments: async (args) => {
34
38
  const { driveId } = args;
39
+ // Check read permission on drive before listing documents
40
+ await assertCanRead(subgraph, driveId, ctx);
35
41
  const docsIds = await reactor.getDocuments(driveId);
36
42
  const docs = await Promise.all(docsIds.map(async (docId) => {
37
43
  const doc = await reactor.getDocument(docId);
@@ -46,14 +52,34 @@ export const getResolvers = (subgraph) => {
46
52
  revision: doc.header?.revision?.global ?? 0,
47
53
  };
48
54
  }));
49
- return docs.filter((doc) => doc.header.documentType === vetraPackageDocumentType);
55
+ const filteredByType = docs.filter((doc) => doc.header.documentType === vetraPackageDocumentType);
56
+ // If user doesn't have global read access, filter by document-level permissions
57
+ if (!hasGlobalReadAccess(ctx) &&
58
+ subgraph.documentPermissionService) {
59
+ const filteredDocs = [];
60
+ for (const doc of filteredByType) {
61
+ const canRead = await canReadDocument(subgraph, doc.id, ctx);
62
+ if (canRead) {
63
+ filteredDocs.push(doc);
64
+ }
65
+ }
66
+ return filteredDocs;
67
+ }
68
+ return filteredByType;
50
69
  },
51
70
  };
52
71
  },
53
72
  },
54
73
  Mutation: {
55
- VetraPackage_createDocument: async (_, args) => {
74
+ VetraPackage_createDocument: async (_, args, ctx) => {
56
75
  const { driveId, name } = args;
76
+ // If creating under a drive, check write permission on drive
77
+ if (driveId) {
78
+ await assertCanWrite(subgraph, driveId, ctx);
79
+ }
80
+ else if (!hasGlobalWriteAccess(ctx)) {
81
+ throw new GraphQLError("Forbidden: insufficient permissions to create documents");
82
+ }
57
83
  const document = await reactor.addDocument(vetraPackageDocumentType);
58
84
  if (driveId) {
59
85
  await reactor.addAction(driveId, addFile({
@@ -67,8 +93,11 @@ export const getResolvers = (subgraph) => {
67
93
  }
68
94
  return document.header.id;
69
95
  },
70
- VetraPackage_setPackageName: async (_, args) => {
96
+ VetraPackage_setPackageName: async (_, args, ctx) => {
71
97
  const { docId, input } = args;
98
+ // Check write permission before mutating document
99
+ await assertCanWrite(subgraph, docId, ctx);
100
+ await assertCanExecuteOperation(subgraph, docId, "SET_PACKAGE_NAME", ctx);
72
101
  const doc = await reactor.getDocument(docId);
73
102
  if (!doc) {
74
103
  throw new Error("Document not found");
@@ -79,8 +108,11 @@ export const getResolvers = (subgraph) => {
79
108
  }
80
109
  return true;
81
110
  },
82
- VetraPackage_setPackageDescription: async (_, args) => {
111
+ VetraPackage_setPackageDescription: async (_, args, ctx) => {
83
112
  const { docId, input } = args;
113
+ // Check write permission before mutating document
114
+ await assertCanWrite(subgraph, docId, ctx);
115
+ await assertCanExecuteOperation(subgraph, docId, "SET_PACKAGE_DESCRIPTION", ctx);
84
116
  const doc = await reactor.getDocument(docId);
85
117
  if (!doc) {
86
118
  throw new Error("Document not found");
@@ -91,8 +123,11 @@ export const getResolvers = (subgraph) => {
91
123
  }
92
124
  return true;
93
125
  },
94
- VetraPackage_setPackageCategory: async (_, args) => {
126
+ VetraPackage_setPackageCategory: async (_, args, ctx) => {
95
127
  const { docId, input } = args;
128
+ // Check write permission before mutating document
129
+ await assertCanWrite(subgraph, docId, ctx);
130
+ await assertCanExecuteOperation(subgraph, docId, "SET_PACKAGE_CATEGORY", ctx);
96
131
  const doc = await reactor.getDocument(docId);
97
132
  if (!doc) {
98
133
  throw new Error("Document not found");
@@ -103,8 +138,11 @@ export const getResolvers = (subgraph) => {
103
138
  }
104
139
  return true;
105
140
  },
106
- VetraPackage_setPackageAuthor: async (_, args) => {
141
+ VetraPackage_setPackageAuthor: async (_, args, ctx) => {
107
142
  const { docId, input } = args;
143
+ // Check write permission before mutating document
144
+ await assertCanWrite(subgraph, docId, ctx);
145
+ await assertCanExecuteOperation(subgraph, docId, "SET_PACKAGE_AUTHOR", ctx);
108
146
  const doc = await reactor.getDocument(docId);
109
147
  if (!doc) {
110
148
  throw new Error("Document not found");
@@ -115,8 +153,11 @@ export const getResolvers = (subgraph) => {
115
153
  }
116
154
  return true;
117
155
  },
118
- VetraPackage_setPackageAuthorName: async (_, args) => {
156
+ VetraPackage_setPackageAuthorName: async (_, args, ctx) => {
119
157
  const { docId, input } = args;
158
+ // Check write permission before mutating document
159
+ await assertCanWrite(subgraph, docId, ctx);
160
+ await assertCanExecuteOperation(subgraph, docId, "SET_PACKAGE_AUTHOR_NAME", ctx);
120
161
  const doc = await reactor.getDocument(docId);
121
162
  if (!doc) {
122
163
  throw new Error("Document not found");
@@ -127,8 +168,11 @@ export const getResolvers = (subgraph) => {
127
168
  }
128
169
  return true;
129
170
  },
130
- VetraPackage_setPackageAuthorWebsite: async (_, args) => {
171
+ VetraPackage_setPackageAuthorWebsite: async (_, args, ctx) => {
131
172
  const { docId, input } = args;
173
+ // Check write permission before mutating document
174
+ await assertCanWrite(subgraph, docId, ctx);
175
+ await assertCanExecuteOperation(subgraph, docId, "SET_PACKAGE_AUTHOR_WEBSITE", ctx);
132
176
  const doc = await reactor.getDocument(docId);
133
177
  if (!doc) {
134
178
  throw new Error("Document not found");
@@ -139,8 +183,11 @@ export const getResolvers = (subgraph) => {
139
183
  }
140
184
  return true;
141
185
  },
142
- VetraPackage_addPackageKeyword: async (_, args) => {
186
+ VetraPackage_addPackageKeyword: async (_, args, ctx) => {
143
187
  const { docId, input } = args;
188
+ // Check write permission before mutating document
189
+ await assertCanWrite(subgraph, docId, ctx);
190
+ await assertCanExecuteOperation(subgraph, docId, "ADD_PACKAGE_KEYWORD", ctx);
144
191
  const doc = await reactor.getDocument(docId);
145
192
  if (!doc) {
146
193
  throw new Error("Document not found");
@@ -151,8 +198,11 @@ export const getResolvers = (subgraph) => {
151
198
  }
152
199
  return true;
153
200
  },
154
- VetraPackage_removePackageKeyword: async (_, args) => {
201
+ VetraPackage_removePackageKeyword: async (_, args, ctx) => {
155
202
  const { docId, input } = args;
203
+ // Check write permission before mutating document
204
+ await assertCanWrite(subgraph, docId, ctx);
205
+ await assertCanExecuteOperation(subgraph, docId, "REMOVE_PACKAGE_KEYWORD", ctx);
156
206
  const doc = await reactor.getDocument(docId);
157
207
  if (!doc) {
158
208
  throw new Error("Document not found");
@@ -163,8 +213,11 @@ export const getResolvers = (subgraph) => {
163
213
  }
164
214
  return true;
165
215
  },
166
- VetraPackage_setPackageGithubUrl: async (_, args) => {
216
+ VetraPackage_setPackageGithubUrl: async (_, args, ctx) => {
167
217
  const { docId, input } = args;
218
+ // Check write permission before mutating document
219
+ await assertCanWrite(subgraph, docId, ctx);
220
+ await assertCanExecuteOperation(subgraph, docId, "SET_PACKAGE_GITHUB_URL", ctx);
168
221
  const doc = await reactor.getDocument(docId);
169
222
  if (!doc) {
170
223
  throw new Error("Document not found");
@@ -175,8 +228,11 @@ export const getResolvers = (subgraph) => {
175
228
  }
176
229
  return true;
177
230
  },
178
- VetraPackage_setPackageNpmUrl: async (_, args) => {
231
+ VetraPackage_setPackageNpmUrl: async (_, args, ctx) => {
179
232
  const { docId, input } = args;
233
+ // Check write permission before mutating document
234
+ await assertCanWrite(subgraph, docId, ctx);
235
+ await assertCanExecuteOperation(subgraph, docId, "SET_PACKAGE_NPM_URL", ctx);
180
236
  const doc = await reactor.getDocument(docId);
181
237
  if (!doc) {
182
238
  throw new Error("Document not found");
@@ -1,3 +1,3 @@
1
- import type { ISubgraph } from "@powerhousedao/reactor-api";
2
- export declare const getResolvers: (subgraph: ISubgraph) => Record<string, unknown>;
1
+ import type { BaseSubgraph } from "@powerhousedao/reactor-api";
2
+ export declare const getResolvers: (subgraph: BaseSubgraph) => Record<string, unknown>;
3
3
  //# sourceMappingURL=resolvers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"resolvers.d.ts","sourceRoot":"","sources":["../../../subgraphs/vetra-read-model/resolvers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAI5D,eAAO,MAAM,YAAY,GAAI,UAAU,SAAS,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAiDxE,CAAC"}
1
+ {"version":3,"file":"resolvers.d.ts","sourceRoot":"","sources":["../../../subgraphs/vetra-read-model/resolvers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAW,MAAM,4BAA4B,CAAC;AAKxE,eAAO,MAAM,YAAY,GACvB,UAAU,YAAY,KACrB,MAAM,CAAC,MAAM,EAAE,OAAO,CAsExB,CAAC"}
@@ -1,9 +1,10 @@
1
1
  import { VetraReadModelProcessorLegacy } from "../../processors/vetra-read-model/index.legacy.js";
2
+ import { canReadDocument, hasGlobalReadAccess } from "../permission-utils.js";
2
3
  export const getResolvers = (subgraph) => {
3
4
  const db = subgraph.relationalDb;
4
5
  return {
5
6
  Query: {
6
- vetraPackages: async (parent, args) => {
7
+ vetraPackages: async (_parent, args, ctx) => {
7
8
  const { search, documentId_in } = args;
8
9
  const sortOrder = args.sortOrder || "asc";
9
10
  let query = VetraReadModelProcessorLegacy.query("vetra-packages", db)
@@ -16,7 +17,8 @@ export const getResolvers = (subgraph) => {
16
17
  query = query.where("document_id", "in", documentId_in);
17
18
  }
18
19
  query = query.orderBy("name", sortOrder);
19
- return (await query.execute()).map((pkg) => ({
20
+ const results = await query.execute();
21
+ const mappedResults = results.map((pkg) => ({
20
22
  ...pkg,
21
23
  documentId: pkg.document_id,
22
24
  name: pkg.name,
@@ -29,6 +31,18 @@ export const getResolvers = (subgraph) => {
29
31
  keywords: pkg.keywords,
30
32
  driveId: pkg.drive_id,
31
33
  }));
34
+ // If user doesn't have global read access, filter by document-level permissions
35
+ if (!hasGlobalReadAccess(ctx) && subgraph.documentPermissionService) {
36
+ const filteredResults = [];
37
+ for (const pkg of mappedResults) {
38
+ const canRead = await canReadDocument(subgraph, pkg.documentId, ctx);
39
+ if (canRead) {
40
+ filteredResults.push(pkg);
41
+ }
42
+ }
43
+ return filteredResults;
44
+ }
45
+ return mappedResults;
32
46
  },
33
47
  },
34
48
  };