@powerhousedao/vetra 6.0.0-dev.59 → 6.0.0-dev.60

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.
@@ -1,144 +1,30 @@
1
1
  import { GraphQLError } from "graphql";
2
- import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
3
- import { assertCanExecuteOperation, assertCanRead, assertCanWrite, canReadDocument, canWriteDocument, hasGlobalReadAccess, hasGlobalWriteAccess, } from "../permission-utils.js";
2
+ import { beforeEach, describe, expect, it, vi } from "vitest";
3
+ import { assertCanExecuteOperation, assertCanRead, assertCanWrite, canReadDocument, canWriteDocument, hasGlobalAdminAccess, } from "../permission-utils.js";
4
4
  describe("permission-utils", () => {
5
5
  // Helper to create context with different permission levels
6
6
  const createContext = (options) => ({
7
7
  user: options.userAddress ? { address: options.userAddress } : undefined,
8
8
  isAdmin: vi.fn().mockReturnValue(options.isAdmin ?? false),
9
- isUser: vi.fn().mockReturnValue(options.isUser ?? false),
10
- isGuest: vi.fn().mockReturnValue(options.isGuest ?? false),
11
9
  });
12
- describe("hasGlobalReadAccess", () => {
13
- afterEach(() => {
14
- delete process.env.FREE_ENTRY;
15
- });
16
- describe("Role-based access", () => {
17
- it("should return true when user is global admin", () => {
18
- const ctx = createContext({ isAdmin: true, userAddress: "0xadmin" });
19
- const result = hasGlobalReadAccess(ctx);
20
- expect(result).toBe(true);
21
- expect(ctx.isAdmin).toHaveBeenCalledWith("0xadmin");
22
- });
23
- it("should return true when user is global user", () => {
24
- const ctx = createContext({ isUser: true, userAddress: "0xuser" });
25
- const result = hasGlobalReadAccess(ctx);
26
- expect(result).toBe(true);
27
- expect(ctx.isUser).toHaveBeenCalledWith("0xuser");
28
- });
29
- it("should return true when user is global guest", () => {
30
- const ctx = createContext({ isGuest: true, userAddress: "0xguest" });
31
- const result = hasGlobalReadAccess(ctx);
32
- expect(result).toBe(true);
33
- expect(ctx.isGuest).toHaveBeenCalledWith("0xguest");
34
- });
35
- it("should return false when user has no global role", () => {
36
- const ctx = createContext({ userAddress: "0xnorole" });
37
- const result = hasGlobalReadAccess(ctx);
38
- expect(result).toBe(false);
39
- });
40
- it("should return false when user is not authenticated (no address)", () => {
41
- const ctx = createContext({});
42
- const result = hasGlobalReadAccess(ctx);
43
- expect(result).toBe(false);
44
- // Should call with empty string when no user address
45
- expect(ctx.isAdmin).toHaveBeenCalledWith("");
46
- });
47
- });
48
- describe("FREE_ENTRY environment variable", () => {
49
- it("should return true when FREE_ENTRY is 'true' regardless of roles", () => {
50
- process.env.FREE_ENTRY = "true";
51
- const ctx = createContext({ userAddress: "0xanyone" });
52
- const result = hasGlobalReadAccess(ctx);
53
- expect(result).toBe(true);
54
- });
55
- it("should return true when FREE_ENTRY is 'true' even without authentication", () => {
56
- process.env.FREE_ENTRY = "true";
57
- const ctx = createContext({});
58
- const result = hasGlobalReadAccess(ctx);
59
- expect(result).toBe(true);
60
- });
61
- it("should not grant access when FREE_ENTRY is 'false'", () => {
62
- process.env.FREE_ENTRY = "false";
63
- const ctx = createContext({ userAddress: "0xanyone" });
64
- const result = hasGlobalReadAccess(ctx);
65
- expect(result).toBe(false);
66
- });
67
- it("should not grant access when FREE_ENTRY is not set", () => {
68
- const ctx = createContext({ userAddress: "0xanyone" });
69
- const result = hasGlobalReadAccess(ctx);
70
- expect(result).toBe(false);
71
- });
72
- });
73
- describe("Combined roles", () => {
74
- it("should return true when user has multiple roles (admin + user)", () => {
75
- const ctx = createContext({
76
- isAdmin: true,
77
- isUser: true,
78
- userAddress: "0xmultirole",
79
- });
80
- const result = hasGlobalReadAccess(ctx);
81
- expect(result).toBe(true);
82
- });
83
- it("should return true when all roles are true (AUTH_ENABLED=false scenario)", () => {
84
- const ctx = createContext({
85
- isAdmin: true,
86
- isUser: true,
87
- isGuest: true,
88
- userAddress: "0xanyone",
89
- });
90
- const result = hasGlobalReadAccess(ctx);
91
- expect(result).toBe(true);
92
- });
93
- });
94
- });
95
- describe("hasGlobalWriteAccess", () => {
96
- describe("Role-based access", () => {
97
- it("should return true when user is global admin", () => {
98
- const ctx = createContext({ isAdmin: true, userAddress: "0xadmin" });
99
- const result = hasGlobalWriteAccess(ctx);
100
- expect(result).toBe(true);
101
- });
102
- it("should return true when user is global user", () => {
103
- const ctx = createContext({ isUser: true, userAddress: "0xuser" });
104
- const result = hasGlobalWriteAccess(ctx);
105
- expect(result).toBe(true);
106
- });
107
- it("should return false when user is only global guest", () => {
108
- const ctx = createContext({ isGuest: true, userAddress: "0xguest" });
109
- const result = hasGlobalWriteAccess(ctx);
110
- expect(result).toBe(false);
111
- });
112
- it("should return false when user has no global role", () => {
113
- const ctx = createContext({ userAddress: "0xnorole" });
114
- const result = hasGlobalWriteAccess(ctx);
115
- expect(result).toBe(false);
116
- });
117
- it("should return false when user is not authenticated", () => {
118
- const ctx = createContext({});
119
- const result = hasGlobalWriteAccess(ctx);
120
- expect(result).toBe(false);
121
- });
10
+ describe("hasGlobalAdminAccess", () => {
11
+ it("should return true when user is global admin", () => {
12
+ const ctx = createContext({ isAdmin: true, userAddress: "0xadmin" });
13
+ const result = hasGlobalAdminAccess(ctx);
14
+ expect(result).toBe(true);
15
+ expect(ctx.isAdmin).toHaveBeenCalledWith("0xadmin");
122
16
  });
123
- describe("Guest cannot write", () => {
124
- it("should return false for guest even with FREE_ENTRY", () => {
125
- process.env.FREE_ENTRY = "true";
126
- const ctx = createContext({ isGuest: true, userAddress: "0xguest" });
127
- const result = hasGlobalWriteAccess(ctx);
128
- expect(result).toBe(false);
129
- delete process.env.FREE_ENTRY;
130
- });
17
+ it("should return false when user is not admin", () => {
18
+ const ctx = createContext({ userAddress: "0xnorole" });
19
+ const result = hasGlobalAdminAccess(ctx);
20
+ expect(result).toBe(false);
131
21
  });
132
- describe("Combined roles", () => {
133
- it("should return true when user is both guest and user (user wins)", () => {
134
- const ctx = createContext({
135
- isGuest: true,
136
- isUser: true,
137
- userAddress: "0xmixed",
138
- });
139
- const result = hasGlobalWriteAccess(ctx);
140
- expect(result).toBe(true);
141
- });
22
+ it("should return false when user is not authenticated (no address)", () => {
23
+ const ctx = createContext({});
24
+ const result = hasGlobalAdminAccess(ctx);
25
+ expect(result).toBe(false);
26
+ // Should call with empty string when no user address
27
+ expect(ctx.isAdmin).toHaveBeenCalledWith("");
142
28
  });
143
29
  });
144
30
  describe("canReadDocument", () => {
@@ -162,18 +48,12 @@ describe("permission-utils", () => {
162
48
  };
163
49
  });
164
50
  describe("Global access bypass", () => {
165
- it("should return true immediately when user has global read access", async () => {
51
+ it("should return true immediately when user has global admin access", async () => {
166
52
  const ctx = createContext({ isAdmin: true, userAddress: "0xadmin" });
167
53
  const result = await canReadDocument(mockSubgraph, "doc-123", ctx);
168
54
  expect(result).toBe(true);
169
55
  expect(mockDocumentPermissionService.canRead).not.toHaveBeenCalled();
170
56
  });
171
- it("should return true for guest without checking document permissions", async () => {
172
- const ctx = createContext({ isGuest: true, userAddress: "0xguest" });
173
- const result = await canReadDocument(mockSubgraph, "doc-123", ctx);
174
- expect(result).toBe(true);
175
- expect(mockDocumentPermissionService.canRead).not.toHaveBeenCalled();
176
- });
177
57
  });
178
58
  describe("Document-level permissions", () => {
179
59
  it("should check document permission service when no global access", async () => {
@@ -267,19 +147,6 @@ describe("permission-utils", () => {
267
147
  expect(result).toBe(true);
268
148
  expect(mockDocumentPermissionService.canWrite).not.toHaveBeenCalled();
269
149
  });
270
- it("should return true immediately when user is global user", async () => {
271
- const ctx = createContext({ isUser: true, userAddress: "0xuser" });
272
- const result = await canWriteDocument(mockSubgraph, "doc-123", ctx);
273
- expect(result).toBe(true);
274
- expect(mockDocumentPermissionService.canWrite).not.toHaveBeenCalled();
275
- });
276
- it("should NOT return true for global guest (guests cannot write)", async () => {
277
- const ctx = createContext({ isGuest: true, userAddress: "0xguest" });
278
- const result = await canWriteDocument(mockSubgraph, "doc-123", ctx);
279
- // Guest has no global write access, so should check document permissions
280
- expect(mockDocumentPermissionService.canWrite).toHaveBeenCalled();
281
- expect(result).toBe(false);
282
- });
283
150
  });
284
151
  describe("Document-level permissions", () => {
285
152
  it("should check document permission service when no global write access", async () => {
@@ -385,10 +252,6 @@ describe("permission-utils", () => {
385
252
  const ctx = createContext({ userAddress: "0xunpermitted" });
386
253
  await expect(assertCanWrite(mockSubgraph, "doc-123", ctx)).rejects.toThrow("Forbidden: insufficient permissions to write to this document");
387
254
  });
388
- it("should throw for guest user (guests cannot write)", async () => {
389
- const ctx = createContext({ isGuest: true, userAddress: "0xguest" });
390
- await expect(assertCanWrite(mockSubgraph, "doc-123", ctx)).rejects.toThrow("Forbidden");
391
- });
392
255
  });
393
256
  describe("assertCanExecuteOperation", () => {
394
257
  let mockSubgraph;
@@ -457,30 +320,10 @@ describe("permission-utils", () => {
457
320
  });
458
321
  });
459
322
  describe("Edge Cases", () => {
460
- describe("Null/undefined context fields", () => {
461
- it("hasGlobalReadAccess should handle context without isAdmin function", () => {
462
- const ctx = {
463
- user: { address: "0xuser" },
464
- isUser: vi.fn().mockReturnValue(true),
465
- isGuest: vi.fn().mockReturnValue(false),
466
- };
467
- const result = hasGlobalReadAccess(ctx);
468
- expect(result).toBe(true);
469
- });
470
- it("hasGlobalWriteAccess should handle context without isUser function", () => {
471
- const ctx = {
472
- user: { address: "0xadmin" },
473
- isAdmin: vi.fn().mockReturnValue(true),
474
- isGuest: vi.fn().mockReturnValue(false),
475
- };
476
- const result = hasGlobalWriteAccess(ctx);
477
- expect(result).toBe(true);
478
- });
479
- });
480
323
  describe("Empty string user address", () => {
481
324
  it("should handle empty string user address", () => {
482
325
  const ctx = createContext({ userAddress: "" });
483
- const result = hasGlobalReadAccess(ctx);
326
+ const result = hasGlobalAdminAccess(ctx);
484
327
  expect(result).toBe(false);
485
328
  expect(ctx.isAdmin).toHaveBeenCalledWith("");
486
329
  });
@@ -1,4 +1,4 @@
1
- import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
1
+ import { beforeEach, describe, expect, it, vi } from "vitest";
2
2
  import { getResolvers } from "../vetra-read-model/resolvers.js";
3
3
  // Mock the VetraReadModelProcessorLegacy
4
4
  vi.mock("../../processors/vetra-read-model/index.legacy.js", () => ({
@@ -65,8 +65,6 @@ describe("VetraReadModel Subgraph Permission Checks", () => {
65
65
  const createContext = (options) => ({
66
66
  user: options.userAddress ? { address: options.userAddress } : undefined,
67
67
  isAdmin: vi.fn().mockReturnValue(options.isAdmin ?? false),
68
- isUser: vi.fn().mockReturnValue(options.isUser ?? false),
69
- isGuest: vi.fn().mockReturnValue(options.isGuest ?? false),
70
68
  });
71
69
  // Setup mock query chain
72
70
  const setupMockQuery = (packages) => {
@@ -92,7 +90,6 @@ describe("VetraReadModel Subgraph Permission Checks", () => {
92
90
  };
93
91
  beforeEach(() => {
94
92
  vi.clearAllMocks();
95
- delete process.env.FREE_ENTRY;
96
93
  // Create mock DocumentPermissionService
97
94
  mockDocumentPermissionService = {
98
95
  canRead: vi.fn().mockResolvedValue(false),
@@ -116,9 +113,6 @@ describe("VetraReadModel Subgraph Permission Checks", () => {
116
113
  // Get resolvers
117
114
  resolvers = getResolvers(mockSubgraph);
118
115
  });
119
- afterEach(() => {
120
- delete process.env.FREE_ENTRY;
121
- });
122
116
  describe("Query: vetraPackages", () => {
123
117
  const callVetraPackages = async (ctx, args = {}) => {
124
118
  const query = resolvers.Query?.vetraPackages;
@@ -132,28 +126,6 @@ describe("VetraReadModel Subgraph Permission Checks", () => {
132
126
  expect(result).toHaveLength(3);
133
127
  expect(mockDocumentPermissionService.canRead).not.toHaveBeenCalled();
134
128
  });
135
- it("should return all packages when user is global user", async () => {
136
- setupMockQuery(mockPackages);
137
- const ctx = createContext({ isUser: true, userAddress: "0xuser" });
138
- const result = await callVetraPackages(ctx);
139
- expect(result).toHaveLength(3);
140
- expect(mockDocumentPermissionService.canRead).not.toHaveBeenCalled();
141
- });
142
- it("should return all packages when user is global guest", async () => {
143
- setupMockQuery(mockPackages);
144
- const ctx = createContext({ isGuest: true, userAddress: "0xguest" });
145
- const result = await callVetraPackages(ctx);
146
- expect(result).toHaveLength(3);
147
- expect(mockDocumentPermissionService.canRead).not.toHaveBeenCalled();
148
- });
149
- it("should return all packages when FREE_ENTRY is true", async () => {
150
- process.env.FREE_ENTRY = "true";
151
- setupMockQuery(mockPackages);
152
- const ctx = createContext({ userAddress: "0xanyone" });
153
- const result = await callVetraPackages(ctx);
154
- expect(result).toHaveLength(3);
155
- expect(mockDocumentPermissionService.canRead).not.toHaveBeenCalled();
156
- });
157
129
  });
158
130
  describe("Document Permission Filtering", () => {
159
131
  it("should filter packages based on permissions when no global access", async () => {
@@ -276,8 +248,6 @@ describe("VetraReadModel Subgraph Permission Checks", () => {
276
248
  setupMockQuery(mockPackages);
277
249
  const ctx = createContext({
278
250
  isAdmin: true,
279
- isUser: true,
280
- isGuest: true,
281
251
  userAddress: "0xanyone",
282
252
  });
283
253
  const result = await (resolvers.Query?.vetraPackages)(null, {}, ctx);
@@ -1,18 +1,17 @@
1
1
  import type { BaseSubgraph, Context } from "@powerhousedao/reactor-api";
2
2
  /**
3
- * Check if user has global read access (admin, user, or guest)
3
+ * Check if user has global admin access.
4
+ * Legacy fallback when authorizationService is not available.
4
5
  */
5
- export declare function hasGlobalReadAccess(ctx: Context): boolean;
6
+ export declare function hasGlobalAdminAccess(ctx: Context): boolean;
6
7
  /**
7
- * Check if user has global write access (admin or user, not guest)
8
- */
9
- export declare function hasGlobalWriteAccess(ctx: Context): boolean;
10
- /**
11
- * Check if user can read a document (with hierarchy)
8
+ * Check if user can read a document (with hierarchy).
9
+ * Delegates to AuthorizationService when available.
12
10
  */
13
11
  export declare function canReadDocument(subgraph: BaseSubgraph, documentId: string, ctx: Context): Promise<boolean>;
14
12
  /**
15
- * Check if user can write to a document (with hierarchy)
13
+ * Check if user can write to a document (with hierarchy).
14
+ * Delegates to AuthorizationService when available.
16
15
  */
17
16
  export declare function canWriteDocument(subgraph: BaseSubgraph, documentId: string, ctx: Context): Promise<boolean>;
18
17
  /**
@@ -25,7 +24,7 @@ export declare function assertCanRead(subgraph: BaseSubgraph, documentId: string
25
24
  export declare function assertCanWrite(subgraph: BaseSubgraph, documentId: string, ctx: Context): Promise<void>;
26
25
  /**
27
26
  * Check if user can execute a specific operation on a document.
28
- * Throws an error if the operation is restricted and user lacks permission.
27
+ * Delegates to AuthorizationService.canMutate when available.
29
28
  */
30
29
  export declare function assertCanExecuteOperation(subgraph: BaseSubgraph, documentId: string, operationType: string, ctx: Context): Promise<void>;
31
30
  //# sourceMappingURL=permission-utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"permission-utils.d.ts","sourceRoot":"","sources":["../../subgraphs/permission-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAC;AAGxE;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAMzD;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAI1D;AAgBD;;GAEG;AACH,wBAAsB,eAAe,CACnC,QAAQ,EAAE,YAAY,EACtB,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,OAAO,GACX,OAAO,CAAC,OAAO,CAAC,CAgBlB;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,YAAY,EACtB,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,OAAO,GACX,OAAO,CAAC,OAAO,CAAC,CAgBlB;AAED;;GAEG;AACH,wBAAsB,aAAa,CACjC,QAAQ,EAAE,YAAY,EACtB,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,OAAO,GACX,OAAO,CAAC,IAAI,CAAC,CAOf;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,QAAQ,EAAE,YAAY,EACtB,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,OAAO,GACX,OAAO,CAAC,IAAI,CAAC,CAOf;AAED;;;GAGG;AACH,wBAAsB,yBAAyB,CAC7C,QAAQ,EAAE,YAAY,EACtB,UAAU,EAAE,MAAM,EAClB,aAAa,EAAE,MAAM,EACrB,GAAG,EAAE,OAAO,GACX,OAAO,CAAC,IAAI,CAAC,CAiCf"}
1
+ {"version":3,"file":"permission-utils.d.ts","sourceRoot":"","sources":["../../subgraphs/permission-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAC;AAiBxE;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAE1D;AAED;;;GAGG;AACH,wBAAsB,eAAe,CACnC,QAAQ,EAAE,YAAY,EACtB,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,OAAO,GACX,OAAO,CAAC,OAAO,CAAC,CAkBlB;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,YAAY,EACtB,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,OAAO,GACX,OAAO,CAAC,OAAO,CAAC,CAkBlB;AAED;;GAEG;AACH,wBAAsB,aAAa,CACjC,QAAQ,EAAE,YAAY,EACtB,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,OAAO,GACX,OAAO,CAAC,IAAI,CAAC,CAOf;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,QAAQ,EAAE,YAAY,EACtB,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,OAAO,GACX,OAAO,CAAC,IAAI,CAAC,CAOf;AAED;;;GAGG;AACH,wBAAsB,yBAAyB,CAC7C,QAAQ,EAAE,YAAY,EACtB,UAAU,EAAE,MAAM,EAClB,aAAa,EAAE,MAAM,EACrB,GAAG,EAAE,OAAO,GACX,OAAO,CAAC,IAAI,CAAC,CAwCf"}
@@ -1,21 +1,4 @@
1
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
2
  /**
20
3
  * Get the parent IDs function for hierarchical permission checks
21
4
  */
@@ -31,28 +14,39 @@ function getParentIdsFn(subgraph) {
31
14
  };
32
15
  }
33
16
  /**
34
- * Check if user can read a document (with hierarchy)
17
+ * Check if user has global admin access.
18
+ * Legacy fallback when authorizationService is not available.
19
+ */
20
+ export function hasGlobalAdminAccess(ctx) {
21
+ return !!ctx.isAdmin?.(ctx.user?.address ?? "");
22
+ }
23
+ /**
24
+ * Check if user can read a document (with hierarchy).
25
+ * Delegates to AuthorizationService when available.
35
26
  */
36
27
  export async function canReadDocument(subgraph, documentId, ctx) {
37
- // Global access allows reading
38
- if (hasGlobalReadAccess(ctx)) {
39
- return true;
28
+ if (subgraph.authorizationService) {
29
+ return subgraph.authorizationService.canRead(documentId, ctx.user?.address, getParentIdsFn(subgraph));
40
30
  }
41
- // Check document-level permissions with hierarchy
31
+ // Legacy fallback
32
+ if (hasGlobalAdminAccess(ctx))
33
+ return true;
42
34
  if (subgraph.documentPermissionService) {
43
35
  return subgraph.documentPermissionService.canRead(documentId, ctx.user?.address, getParentIdsFn(subgraph));
44
36
  }
45
37
  return false;
46
38
  }
47
39
  /**
48
- * Check if user can write to a document (with hierarchy)
40
+ * Check if user can write to a document (with hierarchy).
41
+ * Delegates to AuthorizationService when available.
49
42
  */
50
43
  export async function canWriteDocument(subgraph, documentId, ctx) {
51
- // Global write access allows writing
52
- if (hasGlobalWriteAccess(ctx)) {
53
- return true;
44
+ if (subgraph.authorizationService) {
45
+ return subgraph.authorizationService.canWrite(documentId, ctx.user?.address, getParentIdsFn(subgraph));
54
46
  }
55
- // Check document-level permissions with hierarchy
47
+ // Legacy fallback
48
+ if (hasGlobalAdminAccess(ctx))
49
+ return true;
56
50
  if (subgraph.documentPermissionService) {
57
51
  return subgraph.documentPermissionService.canWrite(documentId, ctx.user?.address, getParentIdsFn(subgraph));
58
52
  }
@@ -78,21 +72,23 @@ export async function assertCanWrite(subgraph, documentId, ctx) {
78
72
  }
79
73
  /**
80
74
  * Check if user can execute a specific operation on a document.
81
- * Throws an error if the operation is restricted and user lacks permission.
75
+ * Delegates to AuthorizationService.canMutate when available.
82
76
  */
83
77
  export async function assertCanExecuteOperation(subgraph, documentId, operationType, ctx) {
84
- // Skip if no permission service
85
- if (!subgraph.documentPermissionService) {
78
+ if (subgraph.authorizationService) {
79
+ const canMutate = await subgraph.authorizationService.canMutate(documentId, operationType, ctx.user?.address, getParentIdsFn(subgraph));
80
+ if (!canMutate) {
81
+ throw new GraphQLError(`Forbidden: insufficient permissions to execute operation "${operationType}" on this document`);
82
+ }
86
83
  return;
87
84
  }
88
- // Global admins bypass operation-level restrictions
89
- if (ctx.isAdmin?.(ctx.user?.address ?? "")) {
85
+ // Legacy fallback
86
+ if (!subgraph.documentPermissionService)
87
+ return;
88
+ if (ctx.isAdmin?.(ctx.user?.address ?? ""))
90
89
  return;
91
- }
92
- // Check if this operation has any restrictions set
93
90
  const isRestricted = await subgraph.documentPermissionService.isOperationRestricted(documentId, operationType);
94
91
  if (isRestricted) {
95
- // Operation is restricted, check if user has permission
96
92
  const canExecute = await subgraph.documentPermissionService.canExecuteOperation(documentId, operationType, ctx.user?.address);
97
93
  if (!canExecute) {
98
94
  throw new GraphQLError(`Forbidden: insufficient permissions to execute operation "${operationType}" on this document`);
@@ -1,5 +1,5 @@
1
1
  import { VetraReadModelProcessorLegacy } from "../../processors/vetra-read-model/index.legacy.js";
2
- import { canReadDocument, hasGlobalReadAccess } from "../permission-utils.js";
2
+ import { canReadDocument, hasGlobalAdminAccess } from "../permission-utils.js";
3
3
  export const getResolvers = (subgraph) => {
4
4
  const db = subgraph.relationalDb;
5
5
  return {
@@ -32,7 +32,7 @@ export const getResolvers = (subgraph) => {
32
32
  driveId: pkg.drive_id,
33
33
  }));
34
34
  // If user doesn't have global read access, filter by document-level permissions
35
- if (!hasGlobalReadAccess(ctx) && subgraph.documentPermissionService) {
35
+ if (!hasGlobalAdminAccess(ctx) && subgraph.documentPermissionService) {
36
36
  const filteredResults = [];
37
37
  for (const pkg of mappedResults) {
38
38
  const canRead = await canReadDocument(subgraph, pkg.documentId, ctx);