@burdenoff/be-sdk 2026.510.79

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 (220) hide show
  1. package/README.md +173 -0
  2. package/configs/eslint/index.js +492 -0
  3. package/configs/tsconfig/tsconfig.json +53 -0
  4. package/dist/EventQueueFactory-6NMSGECR.js +8 -0
  5. package/dist/EventQueueFactory-6NMSGECR.js.map +1 -0
  6. package/dist/auth/index.d.ts +66 -0
  7. package/dist/auth/index.js +8 -0
  8. package/dist/auth/index.js.map +1 -0
  9. package/dist/cache/index.d.ts +2 -0
  10. package/dist/cache/index.js +6 -0
  11. package/dist/cache/index.js.map +1 -0
  12. package/dist/captcha/index.d.ts +1 -0
  13. package/dist/captcha/index.js +4 -0
  14. package/dist/captcha/index.js.map +1 -0
  15. package/dist/chunk-2HANKZZM.js +245 -0
  16. package/dist/chunk-2HANKZZM.js.map +1 -0
  17. package/dist/chunk-2SD5L2F2.js +816 -0
  18. package/dist/chunk-2SD5L2F2.js.map +1 -0
  19. package/dist/chunk-3O2RQOPV.js +1068 -0
  20. package/dist/chunk-3O2RQOPV.js.map +1 -0
  21. package/dist/chunk-4B43DZIS.js +560 -0
  22. package/dist/chunk-4B43DZIS.js.map +1 -0
  23. package/dist/chunk-4XAEES25.js +2444 -0
  24. package/dist/chunk-4XAEES25.js.map +1 -0
  25. package/dist/chunk-4XGGWW36.js +1405 -0
  26. package/dist/chunk-4XGGWW36.js.map +1 -0
  27. package/dist/chunk-5LXDPGDH.js +245 -0
  28. package/dist/chunk-5LXDPGDH.js.map +1 -0
  29. package/dist/chunk-AI7L22ZJ.js +472 -0
  30. package/dist/chunk-AI7L22ZJ.js.map +1 -0
  31. package/dist/chunk-CBZNP6RF.js +286 -0
  32. package/dist/chunk-CBZNP6RF.js.map +1 -0
  33. package/dist/chunk-ELLX3K54.js +613 -0
  34. package/dist/chunk-ELLX3K54.js.map +1 -0
  35. package/dist/chunk-F5XEAGHQ.js +1875 -0
  36. package/dist/chunk-F5XEAGHQ.js.map +1 -0
  37. package/dist/chunk-FNZEQOJY.js +628 -0
  38. package/dist/chunk-FNZEQOJY.js.map +1 -0
  39. package/dist/chunk-GTEMQXCA.js +516 -0
  40. package/dist/chunk-GTEMQXCA.js.map +1 -0
  41. package/dist/chunk-HIWTWIGQ.js +226 -0
  42. package/dist/chunk-HIWTWIGQ.js.map +1 -0
  43. package/dist/chunk-IYE2NTRX.js +764 -0
  44. package/dist/chunk-IYE2NTRX.js.map +1 -0
  45. package/dist/chunk-J26YGR2U.js +1484 -0
  46. package/dist/chunk-J26YGR2U.js.map +1 -0
  47. package/dist/chunk-JG2FTZPI.js +509 -0
  48. package/dist/chunk-JG2FTZPI.js.map +1 -0
  49. package/dist/chunk-JVBTNSPG.js +1551 -0
  50. package/dist/chunk-JVBTNSPG.js.map +1 -0
  51. package/dist/chunk-L7ZMN2FJ.js +12 -0
  52. package/dist/chunk-L7ZMN2FJ.js.map +1 -0
  53. package/dist/chunk-M5IMU3US.js +473 -0
  54. package/dist/chunk-M5IMU3US.js.map +1 -0
  55. package/dist/chunk-MLW2HPS5.js +1267 -0
  56. package/dist/chunk-MLW2HPS5.js.map +1 -0
  57. package/dist/chunk-MNCGUYLJ.js +269 -0
  58. package/dist/chunk-MNCGUYLJ.js.map +1 -0
  59. package/dist/chunk-MOCUBSTW.js +373 -0
  60. package/dist/chunk-MOCUBSTW.js.map +1 -0
  61. package/dist/chunk-NGMEI42S.js +103 -0
  62. package/dist/chunk-NGMEI42S.js.map +1 -0
  63. package/dist/chunk-NUB5GQTU.js +270 -0
  64. package/dist/chunk-NUB5GQTU.js.map +1 -0
  65. package/dist/chunk-OVSYMNCI.js +145 -0
  66. package/dist/chunk-OVSYMNCI.js.map +1 -0
  67. package/dist/chunk-QLVKMWFA.js +94 -0
  68. package/dist/chunk-QLVKMWFA.js.map +1 -0
  69. package/dist/chunk-SAG6ZYWQ.js +89 -0
  70. package/dist/chunk-SAG6ZYWQ.js.map +1 -0
  71. package/dist/chunk-SAK2JCA7.js +198 -0
  72. package/dist/chunk-SAK2JCA7.js.map +1 -0
  73. package/dist/chunk-SBS4S2PJ.js +100 -0
  74. package/dist/chunk-SBS4S2PJ.js.map +1 -0
  75. package/dist/chunk-SNEOLWXT.js +148 -0
  76. package/dist/chunk-SNEOLWXT.js.map +1 -0
  77. package/dist/chunk-TKBD2ONF.js +564 -0
  78. package/dist/chunk-TKBD2ONF.js.map +1 -0
  79. package/dist/chunk-UBXFIIAW.js +127 -0
  80. package/dist/chunk-UBXFIIAW.js.map +1 -0
  81. package/dist/chunk-VI4VSNTI.js +57 -0
  82. package/dist/chunk-VI4VSNTI.js.map +1 -0
  83. package/dist/chunk-VWCN5PMD.js +268 -0
  84. package/dist/chunk-VWCN5PMD.js.map +1 -0
  85. package/dist/chunk-XC2YD6CF.js +913 -0
  86. package/dist/chunk-XC2YD6CF.js.map +1 -0
  87. package/dist/chunk-XE4OXN2W.js +10 -0
  88. package/dist/chunk-XE4OXN2W.js.map +1 -0
  89. package/dist/chunk-YMZEUTD2.js +110 -0
  90. package/dist/chunk-YMZEUTD2.js.map +1 -0
  91. package/dist/chunk-Z4IRLBWO.js +758 -0
  92. package/dist/chunk-Z4IRLBWO.js.map +1 -0
  93. package/dist/client/internal-client.d.ts +7 -0
  94. package/dist/client/internal-client.js +5 -0
  95. package/dist/client/internal-client.js.map +1 -0
  96. package/dist/client-CSjIrYKS.d.ts +77 -0
  97. package/dist/config-UKE2E3BW.js +4 -0
  98. package/dist/config-UKE2E3BW.js.map +1 -0
  99. package/dist/crypto/index.d.ts +1 -0
  100. package/dist/crypto/index.js +4 -0
  101. package/dist/crypto/index.js.map +1 -0
  102. package/dist/database/index.d.ts +117 -0
  103. package/dist/database/index.js +7 -0
  104. package/dist/database/index.js.map +1 -0
  105. package/dist/dataloader/index.d.ts +1 -0
  106. package/dist/dataloader/index.js +4 -0
  107. package/dist/dataloader/index.js.map +1 -0
  108. package/dist/esm-K6H7EIWK.js +1450 -0
  109. package/dist/esm-K6H7EIWK.js.map +1 -0
  110. package/dist/event-queue/index.d.ts +6 -0
  111. package/dist/event-queue/index.js +10 -0
  112. package/dist/event-queue/index.js.map +1 -0
  113. package/dist/export/index.d.ts +2 -0
  114. package/dist/export/index.js +9 -0
  115. package/dist/export/index.js.map +1 -0
  116. package/dist/feature-flags/index.d.ts +1 -0
  117. package/dist/feature-flags/index.js +4 -0
  118. package/dist/feature-flags/index.js.map +1 -0
  119. package/dist/gateway/index.d.ts +4 -0
  120. package/dist/gateway/index.js +8 -0
  121. package/dist/gateway/index.js.map +1 -0
  122. package/dist/gateway/security/index.d.ts +2 -0
  123. package/dist/gateway/security/index.js +5 -0
  124. package/dist/gateway/security/index.js.map +1 -0
  125. package/dist/graph/index.d.ts +9 -0
  126. package/dist/graph/index.js +13 -0
  127. package/dist/graph/index.js.map +1 -0
  128. package/dist/graph-emitter-Bk0FqD53.d.ts +47 -0
  129. package/dist/graphql/index.d.ts +2 -0
  130. package/dist/graphql/index.js +7 -0
  131. package/dist/graphql/index.js.map +1 -0
  132. package/dist/graphql/services/index.d.ts +24 -0
  133. package/dist/graphql/services/index.js +5 -0
  134. package/dist/graphql/services/index.js.map +1 -0
  135. package/dist/graphql-BAVrUgBF.d.ts +22 -0
  136. package/dist/i18n/index.d.ts +2 -0
  137. package/dist/i18n/index.js +5 -0
  138. package/dist/i18n/index.js.map +1 -0
  139. package/dist/i18n-DA6IPnYy.d.ts +101 -0
  140. package/dist/index-4gKQKZ2B.d.ts +71 -0
  141. package/dist/index-B18rrFru.d.ts +42 -0
  142. package/dist/index-B70b5mYS.d.ts +33 -0
  143. package/dist/index-BMq3lFjj.d.ts +247 -0
  144. package/dist/index-BODgvRtL.d.ts +316 -0
  145. package/dist/index-BS2idbnr.d.ts +874 -0
  146. package/dist/index-BV3G48cH.d.ts +124 -0
  147. package/dist/index-CHbVw5M7.d.ts +73 -0
  148. package/dist/index-Cq_xNjcv.d.ts +602 -0
  149. package/dist/index-CrQgidpx.d.ts +77 -0
  150. package/dist/index-CyVEg86m.d.ts +309 -0
  151. package/dist/index-DOcBnmMe.d.ts +166 -0
  152. package/dist/index-DSOpj8DI.d.ts +17 -0
  153. package/dist/index-DW9q22GH.d.ts +75 -0
  154. package/dist/index-DlFDFyWD.d.ts +55 -0
  155. package/dist/index-DoK0bjhH.d.ts +607 -0
  156. package/dist/index-Dtf6FBaD.d.ts +199 -0
  157. package/dist/index-KVWGHMbS.d.ts +415 -0
  158. package/dist/index-PaojfSdd.d.ts +71 -0
  159. package/dist/index-RU_M5c74.d.ts +163 -0
  160. package/dist/index-UhkH2eaB.d.ts +192 -0
  161. package/dist/index-e_18DzAg.d.ts +18 -0
  162. package/dist/index-jOuh_TKn.d.ts +59 -0
  163. package/dist/index.d.ts +584 -0
  164. package/dist/index.js +2626 -0
  165. package/dist/index.js.map +1 -0
  166. package/dist/init/index.d.ts +2 -0
  167. package/dist/init/index.js +12 -0
  168. package/dist/init/index.js.map +1 -0
  169. package/dist/instrumentation/index.d.ts +3 -0
  170. package/dist/instrumentation/index.js +5 -0
  171. package/dist/instrumentation/index.js.map +1 -0
  172. package/dist/layer/index.d.ts +2 -0
  173. package/dist/layer/index.js +6 -0
  174. package/dist/layer/index.js.map +1 -0
  175. package/dist/layer-DDip18KY.d.ts +67 -0
  176. package/dist/logger/index.d.ts +2 -0
  177. package/dist/logger/index.js +4 -0
  178. package/dist/logger/index.js.map +1 -0
  179. package/dist/middleware/index.d.ts +3 -0
  180. package/dist/middleware/index.js +5 -0
  181. package/dist/middleware/index.js.map +1 -0
  182. package/dist/notifications/index.d.ts +4 -0
  183. package/dist/notifications/index.js +7 -0
  184. package/dist/notifications/index.js.map +1 -0
  185. package/dist/observability/index.d.ts +3 -0
  186. package/dist/observability/index.js +5 -0
  187. package/dist/observability/index.js.map +1 -0
  188. package/dist/pino-logger-RYLGXPHZ.js +5 -0
  189. package/dist/pino-logger-RYLGXPHZ.js.map +1 -0
  190. package/dist/prisma/generator/index.d.ts +1 -0
  191. package/dist/prisma/generator/index.js +228 -0
  192. package/dist/prisma/generator/index.js.map +1 -0
  193. package/dist/prisma/index.d.ts +5 -0
  194. package/dist/prisma/index.js +12 -0
  195. package/dist/prisma/index.js.map +1 -0
  196. package/dist/prisma-extension-krXnYnrN.d.ts +30 -0
  197. package/dist/rbac/index.d.ts +116 -0
  198. package/dist/rbac/index.js +7 -0
  199. package/dist/rbac/index.js.map +1 -0
  200. package/dist/sdk-THO3IWEE.js +136 -0
  201. package/dist/sdk-THO3IWEE.js.map +1 -0
  202. package/dist/server/index.d.ts +359 -0
  203. package/dist/server/index.js +884 -0
  204. package/dist/server/index.js.map +1 -0
  205. package/dist/telemetry/index.d.ts +83 -0
  206. package/dist/telemetry/index.js +209 -0
  207. package/dist/telemetry/index.js.map +1 -0
  208. package/dist/temporal/index.d.ts +148 -0
  209. package/dist/temporal/index.js +6 -0
  210. package/dist/temporal/index.js.map +1 -0
  211. package/dist/tracker/index.d.ts +5 -0
  212. package/dist/tracker/index.js +9 -0
  213. package/dist/tracker/index.js.map +1 -0
  214. package/dist/types-Bo2wkjSP.d.ts +35 -0
  215. package/dist/utils/index.d.ts +1 -0
  216. package/dist/utils/index.js +4 -0
  217. package/dist/utils/index.js.map +1 -0
  218. package/dist/workspace-guard-LHBNK4TG.js +5 -0
  219. package/dist/workspace-guard-LHBNK4TG.js.map +1 -0
  220. package/package.json +241 -0
@@ -0,0 +1,1484 @@
1
+ import { ValidationError, PermissionDeniedError } from './chunk-SAG6ZYWQ.js';
2
+ import { DEFAULT_GATEWAY_ROUTING } from './chunk-NGMEI42S.js';
3
+ import { logger } from './chunk-CBZNP6RF.js';
4
+ import { __name } from './chunk-XE4OXN2W.js';
5
+ import { GraphQLError } from 'graphql';
6
+ import { GraphQLClient } from 'graphql-request';
7
+
8
+ var LRUCache = class LRUCache2 {
9
+ static {
10
+ __name(this, "LRUCache");
11
+ }
12
+ cache = /* @__PURE__ */ new Map();
13
+ maxSize;
14
+ ttl;
15
+ constructor(maxSize, ttl) {
16
+ this.maxSize = maxSize;
17
+ this.ttl = ttl;
18
+ }
19
+ get(key) {
20
+ const entry = this.cache.get(key);
21
+ if (!entry) return void 0;
22
+ if (Date.now() > entry.expiry) {
23
+ this.cache.delete(key);
24
+ return void 0;
25
+ }
26
+ this.cache.delete(key);
27
+ this.cache.set(key, entry);
28
+ return entry.value;
29
+ }
30
+ set(key, value) {
31
+ if (this.cache.size >= this.maxSize) {
32
+ const firstKey = this.cache.keys().next().value;
33
+ if (firstKey !== void 0) {
34
+ this.cache.delete(firstKey);
35
+ }
36
+ }
37
+ this.cache.set(key, {
38
+ value,
39
+ expiry: Date.now() + this.ttl
40
+ });
41
+ }
42
+ clear() {
43
+ this.cache.clear();
44
+ }
45
+ /**
46
+ * Invalidate all entries whose key starts with the given prefix.
47
+ * Used to evict all cached permissions for a specific actor.
48
+ */
49
+ invalidateByPrefix(prefix) {
50
+ let count = 0;
51
+ for (const key of [
52
+ ...this.cache.keys()
53
+ ]) {
54
+ if (typeof key === "string" && key.startsWith(prefix)) {
55
+ this.cache.delete(key);
56
+ count++;
57
+ }
58
+ }
59
+ return count;
60
+ }
61
+ };
62
+ var RBACClient = class RBACClient2 {
63
+ static {
64
+ __name(this, "RBACClient");
65
+ }
66
+ client;
67
+ cache;
68
+ config;
69
+ constructor(config) {
70
+ this.client = new GraphQLClient(config.rbacServiceUrl);
71
+ this.config = {
72
+ failClosed: config.failClosed !== false,
73
+ layer: config.layer || "wspace"
74
+ };
75
+ if (config.enableCaching !== false) {
76
+ this.cache = new LRUCache(
77
+ config.cacheMaxSize || 1e4,
78
+ config.cacheTTL || 6e4
79
+ // 1 minute
80
+ );
81
+ }
82
+ }
83
+ /**
84
+ * Parse permission string into resourceType and action
85
+ * @example "project:read" => { resourceType: "project", action: "read" }
86
+ * @example "rbac:role:create" => { resourceType: "rbac", action: "role:create" }
87
+ */
88
+ parsePermission(permission) {
89
+ const colonIndex = permission.indexOf(":");
90
+ if (colonIndex === -1) {
91
+ return {
92
+ resourceType: permission,
93
+ action: "read"
94
+ };
95
+ }
96
+ return {
97
+ resourceType: permission.substring(0, colonIndex),
98
+ action: permission.substring(colonIndex + 1)
99
+ };
100
+ }
101
+ /**
102
+ * Check if actor has permission using the new PBAC checkPermission query
103
+ */
104
+ async checkPermission(actorId, actorType, scopeId, permission, resourceId) {
105
+ const { resourceType, action } = this.parsePermission(permission);
106
+ const cacheKey = `${actorId}:${actorType}:${scopeId}:${resourceType}:${action}:${resourceId || ""}`;
107
+ if (this.cache) {
108
+ const cached = this.cache.get(cacheKey);
109
+ if (cached !== void 0) {
110
+ logger.debug("[RBAC Directive] Cache hit", {
111
+ actorId,
112
+ actorType,
113
+ scopeId,
114
+ resourceType,
115
+ action,
116
+ resourceId,
117
+ hasPermission: cached
118
+ });
119
+ return {
120
+ hasPermission: cached,
121
+ reason: cached ? "Cached: Permission granted" : "Cached: Permission denied"
122
+ };
123
+ }
124
+ }
125
+ const CHECK_PERMISSION_QUERY = `
126
+ query CheckPermission($input: PermissionCheckInput!) {
127
+ checkPermission(input: $input) {
128
+ allowed
129
+ decisionPath
130
+ roleName
131
+ roleLevel
132
+ overrideType
133
+ }
134
+ }
135
+ `;
136
+ const input = {
137
+ actorId,
138
+ actorType,
139
+ scopeId,
140
+ resourceType,
141
+ action,
142
+ resourceId
143
+ };
144
+ try {
145
+ logger.debug("[RBAC Directive] Checking permission via RBAC service (PBAC)", {
146
+ input,
147
+ layer: this.config.layer
148
+ });
149
+ const result = await this.client.request(CHECK_PERMISSION_QUERY, {
150
+ input
151
+ });
152
+ const hasPermission = result.checkPermission.allowed;
153
+ const decisionPath = result.checkPermission.decisionPath;
154
+ if (this.cache) {
155
+ this.cache.set(cacheKey, hasPermission);
156
+ }
157
+ logger.debug("[RBAC Directive] Permission check result", {
158
+ actorId,
159
+ resourceType,
160
+ action,
161
+ hasPermission,
162
+ decisionPath
163
+ });
164
+ return {
165
+ hasPermission,
166
+ reason: decisionPath
167
+ };
168
+ } catch (error) {
169
+ logger.error("[RBAC Directive] RBAC service error", {
170
+ error,
171
+ input,
172
+ layer: this.config.layer
173
+ });
174
+ if (this.config.failClosed) {
175
+ return {
176
+ hasPermission: false,
177
+ reason: "RBAC service unavailable - access denied (fail-closed)"
178
+ };
179
+ } else {
180
+ logger.warn("[RBAC Directive] Fail-open mode: allowing access despite RBAC error");
181
+ return {
182
+ hasPermission: true,
183
+ reason: "RBAC service unavailable - access allowed (fail-open)"
184
+ };
185
+ }
186
+ }
187
+ }
188
+ /**
189
+ * Clear permission cache (useful for testing or when permissions change)
190
+ */
191
+ clearCache() {
192
+ this.cache?.clear();
193
+ }
194
+ /**
195
+ * Invalidate cached permissions for a specific actor.
196
+ * Call this when an actor's roles or permissions change to avoid stale cache hits.
197
+ *
198
+ * @param actorId - The actor whose cached permissions should be evicted
199
+ * @returns Number of cache entries invalidated
200
+ */
201
+ invalidateForActor(actorId) {
202
+ if (!this.cache) return 0;
203
+ const count = this.cache.invalidateByPrefix(`${actorId}:`);
204
+ if (count > 0) {
205
+ logger.info("[RBAC Directive] Invalidated cached permissions for actor", {
206
+ actorId,
207
+ entriesRemoved: count
208
+ });
209
+ }
210
+ return count;
211
+ }
212
+ /**
213
+ * Get the configured layer
214
+ */
215
+ getLayer() {
216
+ return this.config.layer;
217
+ }
218
+ };
219
+ function getValueAtPath(obj, path) {
220
+ const parts = path.split(".");
221
+ let current = obj;
222
+ for (const part of parts) {
223
+ if (current === null || current === void 0) {
224
+ return void 0;
225
+ }
226
+ if (typeof current !== "object") {
227
+ return void 0;
228
+ }
229
+ current = current[part];
230
+ }
231
+ return typeof current === "string" ? current : void 0;
232
+ }
233
+ __name(getValueAtPath, "getValueAtPath");
234
+ function getDirectiveArgument(directive, name) {
235
+ const arg = directive.arguments?.find((a) => a.name.value === name);
236
+ if (arg && arg.value.kind === "StringValue") {
237
+ return arg.value.value;
238
+ }
239
+ return void 0;
240
+ }
241
+ __name(getDirectiveArgument, "getDirectiveArgument");
242
+ function parseFieldArguments(field, variableValues) {
243
+ const args = {};
244
+ for (const arg of field.arguments || []) {
245
+ const argName = arg.name.value;
246
+ const argValue = arg.value;
247
+ if (argValue.kind === "Variable") {
248
+ args[argName] = variableValues[argValue.name.value];
249
+ } else if (argValue.kind === "ObjectValue") {
250
+ args[argName] = parseObjectValue(argValue, variableValues);
251
+ } else if ("value" in argValue) {
252
+ args[argName] = argValue.value;
253
+ }
254
+ }
255
+ return args;
256
+ }
257
+ __name(parseFieldArguments, "parseFieldArguments");
258
+ function parseObjectValue(obj, variableValues) {
259
+ const result = {};
260
+ for (const field of obj.fields || []) {
261
+ const fieldName = field.name.value;
262
+ const fieldValue = field.value;
263
+ if (fieldValue.kind === "Variable") {
264
+ result[fieldName] = variableValues[fieldValue.name.value];
265
+ } else if (fieldValue.kind === "ObjectValue") {
266
+ result[fieldName] = parseObjectValue(fieldValue, variableValues);
267
+ } else if ("value" in fieldValue) {
268
+ result[fieldName] = fieldValue.value;
269
+ }
270
+ }
271
+ return result;
272
+ }
273
+ __name(parseObjectValue, "parseObjectValue");
274
+ function toActorId(userId) {
275
+ return userId;
276
+ }
277
+ __name(toActorId, "toActorId");
278
+ function sanitizeHeadersForLogging(headers) {
279
+ const sanitized = {};
280
+ for (const [key, value] of Object.entries(headers)) {
281
+ const lowerKey = key.toLowerCase();
282
+ if (lowerKey.includes("auth") || lowerKey.includes("token") || lowerKey.includes("password") || lowerKey.includes("secret") || lowerKey.includes("key") || lowerKey.includes("credential")) {
283
+ sanitized[key] = "***REDACTED***";
284
+ } else {
285
+ sanitized[key] = value;
286
+ }
287
+ }
288
+ return sanitized;
289
+ }
290
+ __name(sanitizeHeadersForLogging, "sanitizeHeadersForLogging");
291
+ function getHeader(context, key) {
292
+ if (context.request?.headers) {
293
+ if (typeof context.request.headers.get === "function") {
294
+ const value2 = context.request.headers.get(key);
295
+ if (value2) return value2;
296
+ }
297
+ const requestHeaders = context.request.headers;
298
+ const value = requestHeaders[key];
299
+ if (value) return value;
300
+ }
301
+ if (context.headers) {
302
+ const contextHeaders = context.headers;
303
+ const value = contextHeaders[key];
304
+ if (value) return value;
305
+ }
306
+ return void 0;
307
+ }
308
+ __name(getHeader, "getHeader");
309
+ function validateBypassTimestamp(timestamp) {
310
+ try {
311
+ const timestampMs = parseInt(timestamp, 10);
312
+ if (isNaN(timestampMs)) {
313
+ return false;
314
+ }
315
+ const now = Date.now();
316
+ const age = now - timestampMs;
317
+ const MAX_AGE = 60 * 1e3;
318
+ if (age > MAX_AGE || age < 0) {
319
+ logger.warn("[RBAC Directive] Stale bypass timestamp", {
320
+ age,
321
+ timestamp: timestampMs,
322
+ now
323
+ });
324
+ return false;
325
+ }
326
+ return true;
327
+ } catch (error) {
328
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
329
+ logger.warn("[RBAC Directive] Invalid bypass timestamp format", {
330
+ timestamp,
331
+ error: errorMessage
332
+ });
333
+ return false;
334
+ }
335
+ }
336
+ __name(validateBypassTimestamp, "validateBypassTimestamp");
337
+ function checkBypassHeaders(context) {
338
+ const actorType = getHeader(context, "x-actor-type");
339
+ const actorId = getHeader(context, "x-actor-id");
340
+ if (actorType === "service" || actorType === "super_admin") {
341
+ logger.debug("[RBAC Directive] Actor type bypass - service/super_admin", {
342
+ actorType,
343
+ actorId
344
+ });
345
+ return {
346
+ shouldBypass: true
347
+ };
348
+ }
349
+ logger.debug("[RBAC Directive] Checking bypass headers - context structure", {
350
+ hasRequest: !!context.request,
351
+ hasRequestHeaders: !!context.request?.headers,
352
+ hasContextHeaders: !!context.headers,
353
+ requestHeadersType: context.request?.headers ? typeof context.request.headers : "undefined",
354
+ contextHeadersType: context.headers ? typeof context.headers : "undefined",
355
+ actorType,
356
+ actorId
357
+ });
358
+ const allHeaders = {};
359
+ if (context.request?.headers) {
360
+ if (typeof context.request.headers.get === "function") {
361
+ logger.debug("[RBAC Directive] Headers object has get method");
362
+ } else {
363
+ const headers = context.request.headers;
364
+ Object.keys(headers).forEach((key) => {
365
+ const value = headers[key];
366
+ if (value) allHeaders[key] = value;
367
+ });
368
+ const sanitizedHeaders = sanitizeHeadersForLogging(allHeaders);
369
+ logger.debug("[RBAC Directive] Request headers (plain object)", {
370
+ headers: sanitizedHeaders
371
+ });
372
+ }
373
+ }
374
+ if (context.headers) {
375
+ const headers = context.headers;
376
+ const contextHeadersObj = {};
377
+ Object.keys(headers).forEach((key) => {
378
+ const value = headers[key];
379
+ if (value) contextHeadersObj[key] = value;
380
+ });
381
+ const sanitizedHeaders = sanitizeHeadersForLogging(contextHeadersObj);
382
+ logger.debug("[RBAC Directive] Context headers", {
383
+ headers: sanitizedHeaders
384
+ });
385
+ }
386
+ const bypassHeader = getHeader(context, "x-rbac-bypass");
387
+ const bypassSource = getHeader(context, "x-bypass-source");
388
+ const bypassTimestamp = getHeader(context, "x-bypass-timestamp");
389
+ logger.debug("[RBAC Directive] Extracted bypass headers", {
390
+ bypassHeader,
391
+ bypassSource,
392
+ bypassTimestamp
393
+ });
394
+ const bypassAttempted = !!(bypassHeader || bypassSource || bypassTimestamp);
395
+ if (!bypassHeader || !bypassSource || !bypassTimestamp) {
396
+ logger.debug("[RBAC Directive] Missing bypass headers - bypass NOT granted", {
397
+ hasRbacBypass: !!bypassHeader,
398
+ hasBypassSource: !!bypassSource,
399
+ hasBypassTimestamp: !!bypassTimestamp
400
+ });
401
+ if (bypassAttempted) {
402
+ return {
403
+ shouldBypass: false,
404
+ bypassContext: {
405
+ bypassAttempted: true,
406
+ bypassSource,
407
+ bypassTimestamp,
408
+ bypassFailureReason: "Missing required bypass headers"
409
+ }
410
+ };
411
+ }
412
+ return {
413
+ shouldBypass: false
414
+ };
415
+ }
416
+ if (bypassHeader !== "true") {
417
+ logger.warn("[RBAC Directive] Invalid bypass header value", {
418
+ value: bypassHeader
419
+ });
420
+ return {
421
+ shouldBypass: false,
422
+ bypassContext: {
423
+ bypassAttempted: true,
424
+ bypassSource,
425
+ bypassTimestamp,
426
+ bypassFailureReason: `Invalid bypass header value: ${bypassHeader}`
427
+ }
428
+ };
429
+ }
430
+ if (!validateBypassTimestamp(bypassTimestamp)) {
431
+ logger.warn("[RBAC Directive] Bypass rejected: stale timestamp", {
432
+ source: bypassSource,
433
+ timestamp: bypassTimestamp
434
+ });
435
+ return {
436
+ shouldBypass: false,
437
+ bypassContext: {
438
+ bypassAttempted: true,
439
+ bypassSource,
440
+ bypassTimestamp,
441
+ bypassFailureReason: "Stale or invalid timestamp (replay attack prevention)"
442
+ }
443
+ };
444
+ }
445
+ logger.debug("[RBAC Directive] Valid bypass headers detected", {
446
+ source: bypassSource,
447
+ timestamp: bypassTimestamp
448
+ });
449
+ return {
450
+ shouldBypass: true
451
+ };
452
+ }
453
+ __name(checkBypassHeaders, "checkBypassHeaders");
454
+ function verifyAuthentication(contextValue, fieldName, permission) {
455
+ const ctx = contextValue;
456
+ const user = ctx.user ?? (ctx.actor?.id ? {
457
+ id: ctx.actor.id,
458
+ email: ctx.actor.email,
459
+ workspace_id: ctx.workspace?.id
460
+ } : ctx.actorId ? {
461
+ id: ctx.actorId,
462
+ workspace_id: ctx.workspace?.id
463
+ } : void 0);
464
+ logger.debug("[RBAC Directive] Checking authentication", {
465
+ hasUser: !!user,
466
+ userId: user?.id,
467
+ fieldName
468
+ });
469
+ if (!user?.id) {
470
+ logger.warn("[RBAC Directive] Authentication required but user not found", {
471
+ fieldName,
472
+ permission
473
+ });
474
+ throw new GraphQLError("Authentication required", {
475
+ extensions: {
476
+ code: "UNAUTHENTICATED",
477
+ field: fieldName,
478
+ permission
479
+ }
480
+ });
481
+ }
482
+ return user;
483
+ }
484
+ __name(verifyAuthentication, "verifyAuthentication");
485
+ async function resolveResourceId(resourceIdField, selection, variableValues, config, _rbacClient, _ctx, permission, fieldName) {
486
+ let resourceId;
487
+ const fieldArgs = parseFieldArguments(selection, variableValues || {});
488
+ if (resourceIdField) {
489
+ resourceId = getValueAtPath(fieldArgs, resourceIdField);
490
+ if (!resourceId) {
491
+ logger.error("[RBAC Directive] Resource ID not found at specified path", {
492
+ resourceIdField,
493
+ fieldName,
494
+ permission,
495
+ availableArgs: Object.keys(fieldArgs)
496
+ });
497
+ throw new GraphQLError(`Resource ID not found at specified path "${resourceIdField}" for permission "${permission}"`, {
498
+ extensions: {
499
+ code: "INVALID_RESOURCE_CONFIGURATION",
500
+ field: fieldName,
501
+ permission,
502
+ resourceIdField,
503
+ hint: `Check that the field path "${resourceIdField}" exists in your arguments`
504
+ }
505
+ });
506
+ }
507
+ logger.debug("[RBAC Directive] Using explicit resourceIdField", {
508
+ resourceIdField,
509
+ resourceId,
510
+ fieldName
511
+ });
512
+ } else {
513
+ const defaultPaths = [
514
+ "id",
515
+ "input.id"
516
+ ];
517
+ for (const path of defaultPaths) {
518
+ resourceId = getValueAtPath(fieldArgs, path);
519
+ if (resourceId) {
520
+ logger.debug("[RBAC Directive] Auto-resolved resource ID from convention", {
521
+ path,
522
+ resourceId,
523
+ fieldName,
524
+ permission
525
+ });
526
+ break;
527
+ }
528
+ }
529
+ if (!resourceId) {
530
+ const contextWorkspaceId = _ctx.workspace?.id;
531
+ if (contextWorkspaceId) {
532
+ resourceId = contextWorkspaceId;
533
+ logger.debug("[RBAC Directive] Using workspace ID from context headers", {
534
+ resourceId,
535
+ fieldName,
536
+ permission
537
+ });
538
+ } else {
539
+ logger.error("[RBAC Directive] No resource ID available for permission check", {
540
+ permission,
541
+ fieldName,
542
+ triedPaths: defaultPaths,
543
+ availableArgs: Object.keys(fieldArgs)
544
+ });
545
+ throw new GraphQLError(`Cannot check permission "${permission}": No resource ID available. Tried default paths: ${defaultPaths.join(", ")}. Either provide one of these fields in your arguments, specify resourceIdField in the directive, or pass x-workspace-id header.`, {
546
+ extensions: {
547
+ code: "INVALID_RESOURCE_CONFIGURATION",
548
+ field: fieldName,
549
+ permission,
550
+ triedPaths: defaultPaths,
551
+ hint: 'Add resourceIdField parameter to @requiresPermission directive (e.g., resourceIdField: "input.workspaceId") or pass x-workspace-id header'
552
+ }
553
+ });
554
+ }
555
+ }
556
+ }
557
+ if (resourceId && config.resourceResolver) {
558
+ const resolved = await config.resourceResolver(resourceId, permission.split(":")[0]);
559
+ if (!resolved) {
560
+ logger.warn("[RBAC Directive] Custom resource resolver returned null", {
561
+ originalResourceId: resourceId,
562
+ permission,
563
+ fieldName
564
+ });
565
+ throw new GraphQLError("Resource not found or invalid", {
566
+ extensions: {
567
+ code: "FORBIDDEN",
568
+ field: fieldName,
569
+ permission,
570
+ resourceId,
571
+ hint: "Resource resolver could not resolve the resource ID"
572
+ }
573
+ });
574
+ }
575
+ logger.debug("[RBAC Directive] Custom resource resolver applied", {
576
+ originalResourceId: resourceId,
577
+ resolvedResourceId: resolved,
578
+ fieldName
579
+ });
580
+ return resolved;
581
+ }
582
+ return resourceId;
583
+ }
584
+ __name(resolveResourceId, "resolveResourceId");
585
+ function extractScopeId(ctx, layer) {
586
+ if (layer === "wspace") {
587
+ const workspaceId = getHeader(ctx, "x-workspace-id") || ctx.workspace?.id;
588
+ if (!workspaceId) {
589
+ throw new GraphQLError("Workspace ID required for workspace-layer RBAC", {
590
+ extensions: {
591
+ code: "BAD_REQUEST",
592
+ hint: "Pass x-workspace-id header or ensure workspace context is set"
593
+ }
594
+ });
595
+ }
596
+ return workspaceId;
597
+ } else {
598
+ const tenantId = getHeader(ctx, "x-tenant-id") || ctx.tenant?.id || "global";
599
+ return tenantId;
600
+ }
601
+ }
602
+ __name(extractScopeId, "extractScopeId");
603
+ function extractActorType(ctx) {
604
+ const actorTypeHeader = getHeader(ctx, "x-actor-type");
605
+ if (actorTypeHeader) {
606
+ const upperType = actorTypeHeader.toUpperCase();
607
+ if ([
608
+ "USER",
609
+ "BOT",
610
+ "INTEGRATION",
611
+ "DELEGATED_AGENT"
612
+ ].includes(upperType)) {
613
+ return upperType;
614
+ }
615
+ }
616
+ return "USER";
617
+ }
618
+ __name(extractActorType, "extractActorType");
619
+ async function checkAndEnforcePermission(rbacClient, ctx, actorId, permission, resolvedResourceId, userId, fieldName, bypassContext) {
620
+ const layer = rbacClient.getLayer();
621
+ const actorType = extractActorType(ctx);
622
+ const scopeId = extractScopeId(ctx, layer);
623
+ logger.info("[RBAC Directive] Checking permission", {
624
+ userId,
625
+ actorId,
626
+ actorType,
627
+ scopeId,
628
+ permission,
629
+ resourceId: resolvedResourceId,
630
+ fieldName,
631
+ layer,
632
+ bypassAttempted: bypassContext?.bypassAttempted ?? false
633
+ });
634
+ const { hasPermission, reason } = await rbacClient.checkPermission(actorId, actorType, scopeId, permission, resolvedResourceId);
635
+ if (!hasPermission) {
636
+ logger.warn("[RBAC Directive] Permission denied", {
637
+ userId,
638
+ actorType,
639
+ scopeId,
640
+ permission,
641
+ reason,
642
+ fieldName,
643
+ bypassContext
644
+ });
645
+ const reasonSuffix = reason ? ` - ${reason}` : "";
646
+ const errorMessage = `Forbidden: Missing permission "${permission}"${reasonSuffix}`;
647
+ const extensions = {
648
+ code: "FORBIDDEN",
649
+ permission,
650
+ reason,
651
+ field: fieldName,
652
+ resourceId: resolvedResourceId,
653
+ scopeId,
654
+ layer
655
+ };
656
+ if (bypassContext?.bypassAttempted) {
657
+ extensions["bypassAttempted"] = true;
658
+ extensions["bypassSource"] = bypassContext.bypassSource;
659
+ extensions["bypassTimestamp"] = bypassContext.bypassTimestamp;
660
+ extensions["bypassFailureReason"] = bypassContext.bypassFailureReason;
661
+ }
662
+ throw new GraphQLError(errorMessage, {
663
+ extensions
664
+ });
665
+ }
666
+ logger.info("[RBAC Directive] Permission granted", {
667
+ userId,
668
+ permission,
669
+ fieldName,
670
+ reason
671
+ });
672
+ }
673
+ __name(checkAndEnforcePermission, "checkAndEnforcePermission");
674
+ function createRBACDirective(config) {
675
+ const skipRbac = process.env["SKIP_RBAC"] === "true" || config.skipRbac === true;
676
+ if (skipRbac) {
677
+ const isProduction = process.env["NODE_ENV"] === "production";
678
+ if (isProduction) {
679
+ throw new Error("[RBAC Directive] FATAL: SKIP_RBAC cannot be enabled in production environments. This would bypass all authorization checks and is a critical security vulnerability. Remove SKIP_RBAC=true from your environment configuration.");
680
+ }
681
+ logger.warn("[RBAC Directive] \u26A0\uFE0F RBAC CHECKS ARE DISABLED (SKIP_RBAC=true) - DO NOT USE IN PRODUCTION!");
682
+ return {
683
+ async onExecute({ args }) {
684
+ const { document } = args;
685
+ const operation = document.definitions.find((def) => def.kind === "OperationDefinition");
686
+ const operationName = operation?.name?.value || "anonymous";
687
+ logger.warn("[RBAC Directive] RBAC check skipped for operation", {
688
+ operationName
689
+ });
690
+ }
691
+ };
692
+ }
693
+ const rbacClient = new RBACClient(config);
694
+ return {
695
+ // 🔒 CRITICAL: Use onExecute (not onExecuteDone) to block BEFORE execution
696
+ async onExecute({ args }) {
697
+ logger.debug("[RBAC Directive] onExecute called");
698
+ const { schema, document, contextValue, variableValues } = args;
699
+ const operation = document.definitions.find((def) => def.kind === "OperationDefinition");
700
+ if (!operation) {
701
+ logger.debug("[RBAC Directive] No operation definition found, skipping");
702
+ return;
703
+ }
704
+ const typeName = operation.operation === "mutation" ? "Mutation" : "Query";
705
+ const type = schema.getType(typeName);
706
+ if (!type || !("getFields" in type)) {
707
+ logger.debug(`[RBAC Directive] Type ${typeName} not found, skipping`);
708
+ return;
709
+ }
710
+ const fields = type.getFields();
711
+ for (const selection of operation.selectionSet.selections) {
712
+ if (selection.kind !== "Field") {
713
+ continue;
714
+ }
715
+ const fieldName = selection.name.value;
716
+ const fieldDef = fields[fieldName];
717
+ if (!fieldDef?.astNode) {
718
+ continue;
719
+ }
720
+ const directive = fieldDef.astNode.directives?.find((d) => d.name.value === "requiresPermission");
721
+ if (!directive) {
722
+ continue;
723
+ }
724
+ const permission = getDirectiveArgument(directive, "permission");
725
+ const resourceIdField = getDirectiveArgument(directive, "resourceIdField");
726
+ if (!permission) {
727
+ throw new GraphQLError("Invalid @requiresPermission directive: permission argument is required and must be a string", {
728
+ extensions: {
729
+ code: "INTERNAL_SERVER_ERROR",
730
+ field: fieldName
731
+ }
732
+ });
733
+ }
734
+ const ctx = contextValue;
735
+ const bypassCheckResult = checkBypassHeaders(ctx);
736
+ if (bypassCheckResult.shouldBypass) {
737
+ const bypassSource = getHeader(ctx, "x-bypass-source");
738
+ const bypassTimestamp = getHeader(ctx, "x-bypass-timestamp");
739
+ logger.info("[RBAC Directive] RBAC bypassed for internal service call", {
740
+ field: fieldName,
741
+ permission,
742
+ resource: resourceIdField,
743
+ bypassSource,
744
+ bypassTimestamp,
745
+ operationType: operation.operation
746
+ });
747
+ continue;
748
+ }
749
+ const user = verifyAuthentication(contextValue, fieldName, permission);
750
+ let resolvedResourceId;
751
+ try {
752
+ resolvedResourceId = await resolveResourceId(resourceIdField, selection, variableValues, config, rbacClient, ctx, permission, fieldName);
753
+ } catch (error) {
754
+ if (!resourceIdField) {
755
+ logger.debug("[RBAC Directive] No resource ID resolved, checking permission at scope level", {
756
+ fieldName,
757
+ permission
758
+ });
759
+ } else {
760
+ throw error;
761
+ }
762
+ }
763
+ const actorId = toActorId(user.id);
764
+ await checkAndEnforcePermission(rbacClient, ctx, actorId, permission, resolvedResourceId, user.id, fieldName, bypassCheckResult.bypassContext);
765
+ }
766
+ }
767
+ };
768
+ }
769
+ __name(createRBACDirective, "createRBACDirective");
770
+ function buildRBACServiceUrl(layer) {
771
+ const routing = DEFAULT_GATEWAY_ROUTING[layer];
772
+ const gateway = layer === "wspace" ? routing.internalGateway : routing.crossLayerGateway;
773
+ return `http://${gateway.host}:${gateway.port}${gateway.graphqlPath}`;
774
+ }
775
+ __name(buildRBACServiceUrl, "buildRBACServiceUrl");
776
+ function createRBACDirectiveForLayer(layer, config) {
777
+ const envPrefix = layer.toUpperCase();
778
+ const rbacServiceUrl = config?.rbacServiceUrl || process.env[`${envPrefix}_RBAC_GATEWAY_URL`] || process.env["RBAC_GATEWAY_URL"] || buildRBACServiceUrl(layer);
779
+ const rbacLayer = layer;
780
+ logger.info("[RBAC Directive] Creating layer-aware RBAC directive", {
781
+ layer: rbacLayer,
782
+ rbacServiceUrl,
783
+ enableCaching: config?.enableCaching ?? true
784
+ });
785
+ return createRBACDirective({
786
+ rbacServiceUrl,
787
+ layer: rbacLayer,
788
+ enableCaching: config?.enableCaching ?? true,
789
+ cacheTTL: config?.cacheTTL,
790
+ cacheMaxSize: config?.cacheMaxSize,
791
+ timeout: config?.timeout,
792
+ failClosed: config?.failClosed,
793
+ resourceResolver: config?.resourceResolver,
794
+ skipRbac: config?.skipRbac
795
+ });
796
+ }
797
+ __name(createRBACDirectiveForLayer, "createRBACDirectiveForLayer");
798
+ function createRBACDirectiveFromEnv(serviceName, config) {
799
+ const layer = process.env["SERVICE_LAYER"] || (serviceName.startsWith("global-") ? "global" : "wspace");
800
+ return createRBACDirectiveForLayer(layer, config);
801
+ }
802
+ __name(createRBACDirectiveFromEnv, "createRBACDirectiveFromEnv");
803
+
804
+ // src/rbac/index.ts
805
+ var RBACModule = class {
806
+ static {
807
+ __name(this, "RBACModule");
808
+ }
809
+ client;
810
+ context;
811
+ constructor(client, context) {
812
+ this.client = client;
813
+ this.context = context;
814
+ }
815
+ /**
816
+ * Check if user has permission (returns boolean)
817
+ */
818
+ async checkPermission(options) {
819
+ try {
820
+ await this.requirePermission(options);
821
+ return true;
822
+ } catch {
823
+ return false;
824
+ }
825
+ }
826
+ /**
827
+ * Require permission (throws if denied)
828
+ */
829
+ async requirePermission(options) {
830
+ if (!options.userId) {
831
+ throw new ValidationError("User ID is required");
832
+ }
833
+ if (!options.action) {
834
+ throw new ValidationError("Action is required");
835
+ }
836
+ if (!options.resourceType) {
837
+ throw new ValidationError("Resource type is required");
838
+ }
839
+ logger.debug("[RBAC] Checking permission", options);
840
+ const query = `
841
+ query CheckPermission($input: PermissionCheckInput!) {
842
+ checkPermission(input: $input) {
843
+ allowed
844
+ decisionPath
845
+ }
846
+ }
847
+ `;
848
+ const variables = {
849
+ input: {
850
+ actorId: options.userId,
851
+ actorType: options.actorType || "USER",
852
+ scopeId: options.workspaceId || this.context.workspaceId || this.context.tenantId || "",
853
+ resourceType: options.resourceType,
854
+ action: options.action,
855
+ resourceId: options.resourceId
856
+ }
857
+ };
858
+ const response = options.workspaceId || this.context.workspaceId ? await this.client.query(query, variables, this.context) : await this.client.tenantQuery(query, variables, this.context);
859
+ if (!response.checkPermission.allowed) {
860
+ throw new PermissionDeniedError(`Permission denied: ${response.checkPermission.decisionPath}`, {
861
+ action: options.action,
862
+ resourceType: options.resourceType,
863
+ resourceId: options.resourceId,
864
+ userId: options.userId
865
+ });
866
+ }
867
+ logger.debug("[RBAC] Permission granted", {
868
+ ...options,
869
+ decisionPath: response.checkPermission.decisionPath
870
+ });
871
+ }
872
+ /**
873
+ * Get user roles in workspace
874
+ * Uses the new PBAC actorRoleAssignments query
875
+ */
876
+ async getUserRoles(userId, workspaceId) {
877
+ logger.debug("[RBAC] Getting user roles", {
878
+ userId,
879
+ workspaceId
880
+ });
881
+ const query = `
882
+ query GetActorRoleAssignments($actorId: String!, $scopeId: String!) {
883
+ actorRoleAssignments(actorId: $actorId, scopeId: $scopeId) {
884
+ id
885
+ role {
886
+ id
887
+ name
888
+ description
889
+ defaultPermissions {
890
+ resourceType
891
+ level
892
+ }
893
+ }
894
+ }
895
+ }
896
+ `;
897
+ const variables = {
898
+ actorId: userId,
899
+ scopeId: workspaceId
900
+ };
901
+ const contextWithWorkspace = {
902
+ ...this.context,
903
+ workspaceId
904
+ };
905
+ const response = await this.client.query(query, variables, contextWithWorkspace);
906
+ return response.actorRoleAssignments.map((assignment) => ({
907
+ id: assignment.role.id,
908
+ name: assignment.role.name,
909
+ description: assignment.role.description,
910
+ defaultPermissions: assignment.role.defaultPermissions
911
+ }));
912
+ }
913
+ /**
914
+ * Assign role to user
915
+ * Uses the new PBAC assignRoleToActor mutation
916
+ */
917
+ async assignRoleToUser(userId, roleId, workspaceId, actorType = "USER") {
918
+ logger.debug("[RBAC] Assigning role to user", {
919
+ userId,
920
+ roleId,
921
+ workspaceId,
922
+ actorType
923
+ });
924
+ const mutation = `
925
+ mutation AssignRoleToActor($input: AssignRoleInput!) {
926
+ assignRoleToActor(input: $input) {
927
+ id
928
+ actorId
929
+ roleId
930
+ scopeId
931
+ }
932
+ }
933
+ `;
934
+ const variables = {
935
+ input: {
936
+ actorId: userId,
937
+ actorType,
938
+ roleId,
939
+ scopeId: workspaceId
940
+ }
941
+ };
942
+ const contextWithWorkspace = {
943
+ ...this.context,
944
+ workspaceId
945
+ };
946
+ await this.client.query(mutation, variables, contextWithWorkspace);
947
+ logger.info("[RBAC] Role assigned to actor", {
948
+ userId,
949
+ roleId,
950
+ workspaceId
951
+ });
952
+ return true;
953
+ }
954
+ /**
955
+ * Remove role from user
956
+ * Uses the new PBAC removeRoleFromActor mutation
957
+ * Note: This first finds the assignment ID, then removes it
958
+ */
959
+ async removeRoleFromUser(userId, roleId, workspaceId) {
960
+ logger.debug("[RBAC] Removing role from user", {
961
+ userId,
962
+ roleId,
963
+ workspaceId
964
+ });
965
+ const contextWithWorkspace = {
966
+ ...this.context,
967
+ workspaceId
968
+ };
969
+ const findQuery = `
970
+ query FindAssignment($actorId: String!, $scopeId: String!) {
971
+ actorRoleAssignments(actorId: $actorId, scopeId: $scopeId) {
972
+ id
973
+ roleId
974
+ }
975
+ }
976
+ `;
977
+ const findResponse = await this.client.query(findQuery, {
978
+ actorId: userId,
979
+ scopeId: workspaceId
980
+ }, contextWithWorkspace);
981
+ const assignment = findResponse.actorRoleAssignments.find((a) => a.roleId === roleId);
982
+ if (!assignment) {
983
+ logger.warn("[RBAC] No role assignment found to remove", {
984
+ userId,
985
+ roleId,
986
+ workspaceId
987
+ });
988
+ return false;
989
+ }
990
+ const mutation = `
991
+ mutation RemoveRoleFromActor($assignmentId: ID!) {
992
+ removeRoleFromActor(assignmentId: $assignmentId)
993
+ }
994
+ `;
995
+ await this.client.query(mutation, {
996
+ assignmentId: assignment.id
997
+ }, contextWithWorkspace);
998
+ logger.info("[RBAC] Role removed from actor", {
999
+ userId,
1000
+ roleId,
1001
+ workspaceId,
1002
+ assignmentId: assignment.id
1003
+ });
1004
+ return true;
1005
+ }
1006
+ /**
1007
+ * Create custom role with default permissions
1008
+ *
1009
+ * Uses the new PBAC createRole mutation and setRoleDefaultPermission for permissions.
1010
+ *
1011
+ * @param name - Role name
1012
+ * @param description - Role description
1013
+ * @param defaultPermissions - Array of default permission configurations
1014
+ * (e.g., [{ resourceType: 'project', level: 'ADMIN' }])
1015
+ * OR legacy permission strings (converted to ADMIN level)
1016
+ * @param workspaceId - Workspace ID (scopeId)
1017
+ */
1018
+ async createRole(name, description, defaultPermissions, workspaceId) {
1019
+ logger.debug("[RBAC] Creating role", {
1020
+ name,
1021
+ workspaceId,
1022
+ permissionCount: defaultPermissions.length
1023
+ });
1024
+ const contextWithWorkspace = {
1025
+ ...this.context,
1026
+ workspaceId
1027
+ };
1028
+ const createRoleMutation = `
1029
+ mutation CreateRole($input: CreateRoleInput!) {
1030
+ createRole(input: $input) {
1031
+ id
1032
+ name
1033
+ description
1034
+ defaultPermissions {
1035
+ resourceType
1036
+ level
1037
+ }
1038
+ }
1039
+ }
1040
+ `;
1041
+ const createRoleVariables = {
1042
+ input: {
1043
+ name,
1044
+ description,
1045
+ scopeId: workspaceId,
1046
+ scopeType: "workspace"
1047
+ }
1048
+ };
1049
+ const createResponse = await this.client.query(createRoleMutation, createRoleVariables, contextWithWorkspace);
1050
+ const roleId = createResponse.createRole.id;
1051
+ logger.info("[RBAC] Role created", {
1052
+ name,
1053
+ roleId
1054
+ });
1055
+ if (defaultPermissions.length > 0) {
1056
+ logger.debug("[RBAC] Setting default permissions for role", {
1057
+ roleId,
1058
+ count: defaultPermissions.length
1059
+ });
1060
+ const normalizedPermissions = defaultPermissions.map((perm) => {
1061
+ if (typeof perm === "string") {
1062
+ const resourceType = perm.split(":")[0] || perm;
1063
+ return {
1064
+ resourceType,
1065
+ level: "ADMIN"
1066
+ };
1067
+ }
1068
+ return perm;
1069
+ });
1070
+ const permMap = /* @__PURE__ */ new Map();
1071
+ const levelPriority = {
1072
+ ADMIN: 4,
1073
+ EDITOR: 3,
1074
+ VIEWER: 2,
1075
+ NONE: 1
1076
+ };
1077
+ for (const perm of normalizedPermissions) {
1078
+ const existing = permMap.get(perm.resourceType);
1079
+ if (!existing || levelPriority[perm.level] > levelPriority[existing]) {
1080
+ permMap.set(perm.resourceType, perm.level);
1081
+ }
1082
+ }
1083
+ const setPermissionMutation = `
1084
+ mutation SetRoleDefaultPermission($input: SetRoleDefaultPermissionInput!) {
1085
+ setRoleDefaultPermission(input: $input) {
1086
+ id
1087
+ defaultPermissions {
1088
+ resourceType
1089
+ level
1090
+ }
1091
+ }
1092
+ }
1093
+ `;
1094
+ const failedPermissions = [];
1095
+ for (const [resourceType, level] of permMap) {
1096
+ try {
1097
+ await this.client.query(setPermissionMutation, {
1098
+ input: {
1099
+ roleId,
1100
+ resourceType,
1101
+ level
1102
+ }
1103
+ }, contextWithWorkspace);
1104
+ logger.debug("[RBAC] Set default permission", {
1105
+ roleId,
1106
+ resourceType,
1107
+ level
1108
+ });
1109
+ } catch (error) {
1110
+ const errorMsg = error instanceof Error ? error.message : String(error);
1111
+ logger.error("[RBAC] Failed to set default permission", {
1112
+ roleId,
1113
+ resourceType,
1114
+ level,
1115
+ error: errorMsg
1116
+ });
1117
+ failedPermissions.push({
1118
+ resourceType,
1119
+ level,
1120
+ error: errorMsg
1121
+ });
1122
+ }
1123
+ }
1124
+ if (failedPermissions.length > 0) {
1125
+ const failedTypes = failedPermissions.map((p) => p.resourceType).join(", ");
1126
+ throw new ValidationError(`Role created but failed to set ${failedPermissions.length} default permission(s): ${failedTypes}. Role ID: ${roleId}. Check logs for details.`);
1127
+ }
1128
+ logger.info("[RBAC] Default permissions set for role", {
1129
+ roleId,
1130
+ count: permMap.size
1131
+ });
1132
+ }
1133
+ const roleQuery = `
1134
+ query GetRole($id: ID!) {
1135
+ role(id: $id) {
1136
+ id
1137
+ name
1138
+ description
1139
+ defaultPermissions {
1140
+ resourceType
1141
+ level
1142
+ }
1143
+ }
1144
+ }
1145
+ `;
1146
+ const roleResponse = await this.client.query(roleQuery, {
1147
+ id: roleId
1148
+ }, contextWithWorkspace);
1149
+ const role = roleResponse.role || createResponse.createRole;
1150
+ return {
1151
+ id: role.id,
1152
+ name: role.name,
1153
+ description: role.description,
1154
+ defaultPermissions: role.defaultPermissions
1155
+ };
1156
+ }
1157
+ /**
1158
+ * Get all available action definitions (replaces getAvailablePermissions)
1159
+ * Returns actions organized by resource type
1160
+ */
1161
+ async getAvailablePermissions() {
1162
+ logger.debug("[RBAC] Getting available action definitions");
1163
+ const resourceTypesQuery = `
1164
+ query GetResourceTypes {
1165
+ resourceTypes
1166
+ }
1167
+ `;
1168
+ const hasWorkspace = !!this.context.workspaceId;
1169
+ const resourceTypesResponse = hasWorkspace ? await this.client.query(resourceTypesQuery, {}, this.context) : await this.client.tenantQuery(resourceTypesQuery, {}, this.context);
1170
+ const permissions = [];
1171
+ const actionsQuery = `
1172
+ query GetActionDefinitions($resourceType: String!) {
1173
+ actionDefinitions(resourceType: $resourceType) {
1174
+ id
1175
+ action
1176
+ resourceType
1177
+ description
1178
+ }
1179
+ }
1180
+ `;
1181
+ for (const resourceType of resourceTypesResponse.resourceTypes) {
1182
+ const actionsResponse = hasWorkspace ? await this.client.query(actionsQuery, {
1183
+ resourceType
1184
+ }, this.context) : await this.client.tenantQuery(actionsQuery, {
1185
+ resourceType
1186
+ }, this.context);
1187
+ for (const action of actionsResponse.actionDefinitions) {
1188
+ permissions.push({
1189
+ id: action.id,
1190
+ action: action.action,
1191
+ resourceType: action.resourceType,
1192
+ description: action.description
1193
+ });
1194
+ }
1195
+ }
1196
+ return permissions;
1197
+ }
1198
+ /**
1199
+ * Check multiple permissions at once
1200
+ * Uses the new PBAC checkPermissions query
1201
+ */
1202
+ async checkMultiplePermissions(checks) {
1203
+ logger.debug("[RBAC] Checking multiple permissions", {
1204
+ count: checks.length
1205
+ });
1206
+ const query = `
1207
+ query CheckPermissions($inputs: [PermissionCheckInput!]!) {
1208
+ checkPermissions(inputs: $inputs) {
1209
+ allowed
1210
+ decisionPath
1211
+ }
1212
+ }
1213
+ `;
1214
+ const inputs = checks.map((check) => ({
1215
+ actorId: check.userId,
1216
+ actorType: check.actorType || "USER",
1217
+ scopeId: check.workspaceId || this.context.workspaceId || this.context.tenantId || "",
1218
+ resourceType: check.resourceType,
1219
+ action: check.action,
1220
+ resourceId: check.resourceId
1221
+ }));
1222
+ const hasWorkspace = checks.some((c) => c.workspaceId) || !!this.context.workspaceId;
1223
+ const response = hasWorkspace ? await this.client.query(query, {
1224
+ inputs
1225
+ }, this.context) : await this.client.tenantQuery(query, {
1226
+ inputs
1227
+ }, this.context);
1228
+ return response.checkPermissions.map((result) => ({
1229
+ allowed: result.allowed,
1230
+ reason: result.decisionPath
1231
+ }));
1232
+ }
1233
+ /**
1234
+ * Get effective permissions for a user in a workspace
1235
+ * New method that uses the PBAC effectivePermissions query
1236
+ */
1237
+ async getEffectivePermissions(userId, workspaceId, actorType = "USER") {
1238
+ logger.debug("[RBAC] Getting effective permissions", {
1239
+ userId,
1240
+ workspaceId,
1241
+ actorType
1242
+ });
1243
+ const query = `
1244
+ query EffectivePermissions($actorId: String!, $actorType: ActorType!, $scopeId: String!) {
1245
+ effectivePermissions(actorId: $actorId, actorType: $actorType, scopeId: $scopeId) {
1246
+ actorId
1247
+ scopeId
1248
+ permissions {
1249
+ resourceType
1250
+ level
1251
+ allowedActions
1252
+ source
1253
+ }
1254
+ }
1255
+ }
1256
+ `;
1257
+ const variables = {
1258
+ actorId: userId,
1259
+ actorType,
1260
+ scopeId: workspaceId
1261
+ };
1262
+ const contextWithWorkspace = {
1263
+ ...this.context,
1264
+ workspaceId
1265
+ };
1266
+ const response = await this.client.query(query, variables, contextWithWorkspace);
1267
+ return response.effectivePermissions;
1268
+ }
1269
+ /**
1270
+ * Initialize workspace roles by cloning system roles
1271
+ * New method for workspace initialization
1272
+ */
1273
+ async initializeWorkspaceRoles(workspaceId, ownerId) {
1274
+ logger.debug("[RBAC] Initializing workspace roles", {
1275
+ workspaceId,
1276
+ ownerId
1277
+ });
1278
+ const mutation = `
1279
+ mutation InitializeWorkspaceRoles($workspaceId: String!, $ownerId: String!) {
1280
+ initializeWorkspaceRoles(workspaceId: $workspaceId, ownerId: $ownerId) {
1281
+ success
1282
+ rolesCreated
1283
+ roleNames
1284
+ ownerAssigned
1285
+ message
1286
+ }
1287
+ }
1288
+ `;
1289
+ const contextWithWorkspace = {
1290
+ ...this.context,
1291
+ workspaceId
1292
+ };
1293
+ const response = await this.client.query(mutation, {
1294
+ workspaceId,
1295
+ ownerId
1296
+ }, contextWithWorkspace);
1297
+ logger.info("[RBAC] Workspace roles initialized", response.initializeWorkspaceRoles);
1298
+ return response.initializeWorkspaceRoles;
1299
+ }
1300
+ /**
1301
+ * Update an existing role
1302
+ * Uses the new PBAC updateRole mutation
1303
+ *
1304
+ * @param roleId - Role ID to update
1305
+ * @param updates - Fields to update (name, description, priority)
1306
+ * @param workspaceId - Workspace ID (scopeId)
1307
+ */
1308
+ async updateRole(roleId, updates, workspaceId) {
1309
+ logger.debug("[RBAC] Updating role", {
1310
+ roleId,
1311
+ updates,
1312
+ workspaceId
1313
+ });
1314
+ const contextWithWorkspace = {
1315
+ ...this.context,
1316
+ workspaceId
1317
+ };
1318
+ const mutation = `
1319
+ mutation UpdateRole($id: ID!, $input: UpdateRoleInput!) {
1320
+ updateRole(id: $id, input: $input) {
1321
+ id
1322
+ name
1323
+ description
1324
+ priority
1325
+ defaultPermissions {
1326
+ resourceType
1327
+ level
1328
+ }
1329
+ }
1330
+ }
1331
+ `;
1332
+ const response = await this.client.query(mutation, {
1333
+ id: roleId,
1334
+ input: updates
1335
+ }, contextWithWorkspace);
1336
+ logger.info("[RBAC] Role updated", {
1337
+ roleId,
1338
+ name: response.updateRole.name
1339
+ });
1340
+ return {
1341
+ id: response.updateRole.id,
1342
+ name: response.updateRole.name,
1343
+ description: response.updateRole.description,
1344
+ defaultPermissions: response.updateRole.defaultPermissions
1345
+ };
1346
+ }
1347
+ /**
1348
+ * Delete a role
1349
+ * Uses the new PBAC deleteRole mutation
1350
+ *
1351
+ * @param roleId - Role ID to delete
1352
+ * @param workspaceId - Workspace ID (scopeId)
1353
+ * @param reassignToRoleId - Optional role ID to reassign existing actors to
1354
+ */
1355
+ async deleteRole(roleId, workspaceId, reassignToRoleId) {
1356
+ logger.debug("[RBAC] Deleting role", {
1357
+ roleId,
1358
+ workspaceId,
1359
+ reassignToRoleId
1360
+ });
1361
+ const contextWithWorkspace = {
1362
+ ...this.context,
1363
+ workspaceId
1364
+ };
1365
+ const mutation = `
1366
+ mutation DeleteRole($id: ID!, $reassignToRoleId: ID) {
1367
+ deleteRole(id: $id, reassignToRoleId: $reassignToRoleId)
1368
+ }
1369
+ `;
1370
+ const response = await this.client.query(mutation, {
1371
+ id: roleId,
1372
+ reassignToRoleId
1373
+ }, contextWithWorkspace);
1374
+ logger.info("[RBAC] Role deleted", {
1375
+ roleId,
1376
+ success: response.deleteRole
1377
+ });
1378
+ return response.deleteRole;
1379
+ }
1380
+ /**
1381
+ * List roles in a workspace
1382
+ * Uses the new PBAC roles query
1383
+ *
1384
+ * @param workspaceId - Workspace ID (scopeId)
1385
+ * @param options - Filter and pagination options
1386
+ */
1387
+ async listRoles(workspaceId, options) {
1388
+ logger.debug("[RBAC] Listing roles", {
1389
+ workspaceId,
1390
+ options
1391
+ });
1392
+ const contextWithWorkspace = {
1393
+ ...this.context,
1394
+ workspaceId
1395
+ };
1396
+ const query = `
1397
+ query ListRoles($scopeId: String!, $filter: RoleFilterInput, $pagination: PaginationInput) {
1398
+ roles(scopeId: $scopeId, filter: $filter, pagination: $pagination) {
1399
+ id
1400
+ name
1401
+ description
1402
+ isSystem
1403
+ priority
1404
+ defaultPermissions {
1405
+ resourceType
1406
+ level
1407
+ }
1408
+ }
1409
+ }
1410
+ `;
1411
+ const variables = {
1412
+ scopeId: workspaceId,
1413
+ filter: options?.includeSystem !== void 0 ? {
1414
+ includeSystem: options.includeSystem
1415
+ } : void 0,
1416
+ pagination: {
1417
+ limit: options?.limit ?? 50,
1418
+ offset: options?.offset ?? 0
1419
+ }
1420
+ };
1421
+ const response = await this.client.query(query, variables, contextWithWorkspace);
1422
+ logger.debug("[RBAC] Roles listed", {
1423
+ workspaceId,
1424
+ count: response.roles.length
1425
+ });
1426
+ return response.roles.map((role) => ({
1427
+ id: role.id,
1428
+ name: role.name,
1429
+ description: role.description,
1430
+ defaultPermissions: role.defaultPermissions
1431
+ }));
1432
+ }
1433
+ /**
1434
+ * Get a specific role by ID
1435
+ * Uses the new PBAC role query
1436
+ *
1437
+ * @param roleId - Role ID
1438
+ * @param workspaceId - Workspace ID (scopeId)
1439
+ */
1440
+ async getRole(roleId, workspaceId) {
1441
+ logger.debug("[RBAC] Getting role", {
1442
+ roleId,
1443
+ workspaceId
1444
+ });
1445
+ const contextWithWorkspace = {
1446
+ ...this.context,
1447
+ workspaceId
1448
+ };
1449
+ const query = `
1450
+ query GetRole($id: ID!) {
1451
+ role(id: $id) {
1452
+ id
1453
+ name
1454
+ description
1455
+ isSystem
1456
+ priority
1457
+ defaultPermissions {
1458
+ resourceType
1459
+ level
1460
+ }
1461
+ }
1462
+ }
1463
+ `;
1464
+ const response = await this.client.query(query, {
1465
+ id: roleId
1466
+ }, contextWithWorkspace);
1467
+ if (!response.role) {
1468
+ logger.debug("[RBAC] Role not found", {
1469
+ roleId
1470
+ });
1471
+ return null;
1472
+ }
1473
+ return {
1474
+ id: response.role.id,
1475
+ name: response.role.name,
1476
+ description: response.role.description,
1477
+ defaultPermissions: response.role.defaultPermissions
1478
+ };
1479
+ }
1480
+ };
1481
+
1482
+ export { RBACClient, RBACModule, createRBACDirective, createRBACDirectiveForLayer, createRBACDirectiveFromEnv };
1483
+ //# sourceMappingURL=chunk-J26YGR2U.js.map
1484
+ //# sourceMappingURL=chunk-J26YGR2U.js.map