@auth-gate/rbac 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-HE57TIQI.mjs → chunk-ZFKXT2MP.mjs} +111 -21
- package/dist/cli.cjs +106 -21
- package/dist/cli.mjs +2 -1
- package/dist/index.cjs +199 -23
- package/dist/index.d.cts +124 -8
- package/dist/index.d.ts +124 -8
- package/dist/index.mjs +94 -3
- package/package.json +1 -1
|
@@ -14,6 +14,12 @@ var __spreadValues = (a, b) => {
|
|
|
14
14
|
}
|
|
15
15
|
return a;
|
|
16
16
|
};
|
|
17
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
18
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
19
|
+
}) : x)(function(x) {
|
|
20
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
21
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
22
|
+
});
|
|
17
23
|
|
|
18
24
|
// src/validator.ts
|
|
19
25
|
var KEY_PATTERN = /^[a-z][a-z0-9_]*$/;
|
|
@@ -63,15 +69,28 @@ function validateRbacConfig(config) {
|
|
|
63
69
|
);
|
|
64
70
|
}
|
|
65
71
|
}
|
|
66
|
-
|
|
72
|
+
const actionSet = /* @__PURE__ */ new Set();
|
|
73
|
+
for (const action of resource.actions) {
|
|
74
|
+
if (actionSet.has(action)) {
|
|
75
|
+
throw new Error(`Resource "${key}" has duplicate action: "${action}".`);
|
|
76
|
+
}
|
|
77
|
+
actionSet.add(action);
|
|
78
|
+
}
|
|
79
|
+
resourceActions.set(key, actionSet);
|
|
67
80
|
if (resource.scopes !== void 0) {
|
|
68
81
|
if (!Array.isArray(resource.scopes)) {
|
|
69
82
|
throw new Error(`Resource "${key}".scopes must be an array of strings.`);
|
|
70
83
|
}
|
|
84
|
+
const scopeSet = /* @__PURE__ */ new Set();
|
|
71
85
|
for (let i = 0; i < resource.scopes.length; i++) {
|
|
72
86
|
if (typeof resource.scopes[i] !== "string") {
|
|
73
87
|
throw new Error(`Resource "${key}".scopes[${i}] must be a string.`);
|
|
74
88
|
}
|
|
89
|
+
const scope = resource.scopes[i];
|
|
90
|
+
if (scopeSet.has(scope)) {
|
|
91
|
+
throw new Error(`Resource "${key}" has duplicate scope: "${scope}".`);
|
|
92
|
+
}
|
|
93
|
+
scopeSet.add(scope);
|
|
75
94
|
}
|
|
76
95
|
}
|
|
77
96
|
}
|
|
@@ -112,14 +131,40 @@ function validateRbacConfig(config) {
|
|
|
112
131
|
);
|
|
113
132
|
}
|
|
114
133
|
const value = actionsGrant[actionKey];
|
|
115
|
-
if (value !== true && typeof value !== "string" && !(value && typeof value === "object" && "when" in value && typeof value.when === "string")) {
|
|
134
|
+
if (value !== true && (typeof value !== "string" || value === "") && !(value && typeof value === "object" && "when" in value && typeof value.when === "string" && value.when !== "")) {
|
|
116
135
|
throw new Error(
|
|
117
|
-
`Role "${key}".grants.${resourceKey}.${actionKey} has invalid value. Expected true, a scope string, or { when: string }.`
|
|
136
|
+
`Role "${key}".grants.${resourceKey}.${actionKey} has invalid value. Expected true, a non-empty scope string, or { when: non-empty string }.`
|
|
118
137
|
);
|
|
119
138
|
}
|
|
139
|
+
if (typeof value === "string" && value !== "") {
|
|
140
|
+
const resource = resources[resourceKey];
|
|
141
|
+
if (resource.scopes && Array.isArray(resource.scopes)) {
|
|
142
|
+
const declaredScopes = new Set(resource.scopes);
|
|
143
|
+
if (!declaredScopes.has(value)) {
|
|
144
|
+
throw new Error(
|
|
145
|
+
`Role "${key}".grants.${resourceKey}.${actionKey} scope "${value}" is not in declared scopes for "${resourceKey}": ${[...declaredScopes].join(", ")}.`
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
120
150
|
}
|
|
121
151
|
}
|
|
122
152
|
}
|
|
153
|
+
const defaultRoles = [];
|
|
154
|
+
for (const key of roleKeys) {
|
|
155
|
+
const role = roles[key];
|
|
156
|
+
if (role.isDefault !== void 0 && typeof role.isDefault !== "boolean") {
|
|
157
|
+
throw new Error(`Role "${key}".isDefault must be a boolean.`);
|
|
158
|
+
}
|
|
159
|
+
if (role.isDefault === true) {
|
|
160
|
+
defaultRoles.push(key);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (defaultRoles.length > 1) {
|
|
164
|
+
throw new Error(
|
|
165
|
+
`Only one role may have isDefault: true. Found ${defaultRoles.length}: ${defaultRoles.join(", ")}.`
|
|
166
|
+
);
|
|
167
|
+
}
|
|
123
168
|
for (const key of roleKeys) {
|
|
124
169
|
const role = roles[key];
|
|
125
170
|
if (role.inherits !== void 0) {
|
|
@@ -185,6 +230,7 @@ function validateRbacConfig(config) {
|
|
|
185
230
|
renameTargets.set(role.renamedFrom, key);
|
|
186
231
|
}
|
|
187
232
|
}
|
|
233
|
+
const conditionKeys = /* @__PURE__ */ new Set();
|
|
188
234
|
if (c.conditions !== void 0) {
|
|
189
235
|
if (typeof c.conditions !== "object" || Array.isArray(c.conditions) || c.conditions === null) {
|
|
190
236
|
throw new Error("RBAC config `conditions` must be an object mapping names to functions.");
|
|
@@ -196,6 +242,23 @@ function validateRbacConfig(config) {
|
|
|
196
242
|
`Condition "${condKey}" must be a function. Got ${typeof condValue}.`
|
|
197
243
|
);
|
|
198
244
|
}
|
|
245
|
+
conditionKeys.add(condKey);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
for (const key of roleKeys) {
|
|
249
|
+
const role = roles[key];
|
|
250
|
+
const grants = role.grants;
|
|
251
|
+
for (const [resourceKey, actionGrants] of Object.entries(grants)) {
|
|
252
|
+
for (const [actionKey, value] of Object.entries(actionGrants)) {
|
|
253
|
+
if (value && typeof value === "object" && "when" in value) {
|
|
254
|
+
const whenKey = value.when;
|
|
255
|
+
if (!conditionKeys.has(whenKey)) {
|
|
256
|
+
throw new Error(
|
|
257
|
+
`Role "${key}".grants.${resourceKey}.${actionKey} references condition "${whenKey}" but it is not declared in conditions.` + (conditionKeys.size > 0 ? ` Declared conditions: ${[...conditionKeys].join(", ")}.` : ` No conditions are declared.`)
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
199
262
|
}
|
|
200
263
|
}
|
|
201
264
|
return config;
|
|
@@ -234,17 +297,27 @@ Run \`npx @auth-gate/rbac init\` to create one.`
|
|
|
234
297
|
}
|
|
235
298
|
|
|
236
299
|
// src/diff.ts
|
|
300
|
+
function deepEqual(a, b) {
|
|
301
|
+
if (a === b) return true;
|
|
302
|
+
if (a === null || b === null || typeof a !== "object" || typeof b !== "object") return false;
|
|
303
|
+
if (Array.isArray(a) !== Array.isArray(b)) return false;
|
|
304
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
305
|
+
if (a.length !== b.length) return false;
|
|
306
|
+
return a.every((val, i) => deepEqual(val, b[i]));
|
|
307
|
+
}
|
|
308
|
+
const keysA = Object.keys(a).sort();
|
|
309
|
+
const keysB = Object.keys(b).sort();
|
|
310
|
+
if (keysA.length !== keysB.length) return false;
|
|
311
|
+
return keysA.every(
|
|
312
|
+
(key, i) => key === keysB[i] && deepEqual(a[key], b[key])
|
|
313
|
+
);
|
|
314
|
+
}
|
|
237
315
|
function hashConditionSource(fn) {
|
|
238
|
-
const
|
|
239
|
-
|
|
240
|
-
for (let i = 0; i < src.length; i++) {
|
|
241
|
-
const ch = src.charCodeAt(i);
|
|
242
|
-
hash = (hash << 5) - hash + ch | 0;
|
|
243
|
-
}
|
|
244
|
-
return hash.toString(36);
|
|
316
|
+
const { createHash } = __require("crypto");
|
|
317
|
+
return createHash("sha256").update(fn.toString()).digest("hex").slice(0, 16);
|
|
245
318
|
}
|
|
246
319
|
function computeRbacDiff(config, server, memberCounts) {
|
|
247
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
|
|
320
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
|
|
248
321
|
const resourceOps = [];
|
|
249
322
|
const roleOps = [];
|
|
250
323
|
const conditionOps = [];
|
|
@@ -303,7 +376,7 @@ function computeRbacDiff(config, server, memberCounts) {
|
|
|
303
376
|
if (!existing && role.renamedFrom) {
|
|
304
377
|
existing = (_d = serverRoleByKey.get(role.renamedFrom)) != null ? _d : serverRoleByPreviousKey.get(role.renamedFrom);
|
|
305
378
|
if (existing) {
|
|
306
|
-
const members = (_e = memberCounts[existing.
|
|
379
|
+
const members = (_e = memberCounts[existing.configKey]) != null ? _e : 0;
|
|
307
380
|
roleOps.push({
|
|
308
381
|
type: "rename",
|
|
309
382
|
key,
|
|
@@ -327,16 +400,19 @@ function computeRbacDiff(config, server, memberCounts) {
|
|
|
327
400
|
if (((_f = existing.description) != null ? _f : void 0) !== ((_g = role.description) != null ? _g : void 0)) {
|
|
328
401
|
changes.push("description changed");
|
|
329
402
|
}
|
|
330
|
-
|
|
331
|
-
const configGrants = JSON.stringify(role.grants);
|
|
332
|
-
if (existingGrants !== configGrants) {
|
|
403
|
+
if (!deepEqual((_h = existing.grants) != null ? _h : null, (_i = role.grants) != null ? _i : null)) {
|
|
333
404
|
changes.push("grants changed");
|
|
334
405
|
}
|
|
335
|
-
const existingInherits = [...(
|
|
336
|
-
const configInherits = [...(
|
|
406
|
+
const existingInherits = [...(_j = existing.inherits) != null ? _j : []].sort().join(",");
|
|
407
|
+
const configInherits = [...(_k = role.inherits) != null ? _k : []].sort().join(",");
|
|
337
408
|
if (existingInherits !== configInherits) {
|
|
338
409
|
changes.push("inherits changed");
|
|
339
410
|
}
|
|
411
|
+
const existingDefault = (_l = existing.isDefault) != null ? _l : false;
|
|
412
|
+
const configDefault = (_m = role.isDefault) != null ? _m : false;
|
|
413
|
+
if (existingDefault !== configDefault) {
|
|
414
|
+
changes.push(`isDefault: ${existingDefault} -> ${configDefault}`);
|
|
415
|
+
}
|
|
340
416
|
if (changes.length > 0) {
|
|
341
417
|
roleOps.push({ type: "update", key, role, existing, changes });
|
|
342
418
|
}
|
|
@@ -346,7 +422,7 @@ function computeRbacDiff(config, server, memberCounts) {
|
|
|
346
422
|
for (const [key, role] of serverRoleByKey) {
|
|
347
423
|
if (renameMap.has(key)) continue;
|
|
348
424
|
if (role.isActive) {
|
|
349
|
-
const members = (
|
|
425
|
+
const members = (_n = memberCounts[role.configKey]) != null ? _n : 0;
|
|
350
426
|
if (members > 0) hasDestructive = true;
|
|
351
427
|
roleOps.push({ type: "archive", key, existing: role, assignedMembers: members });
|
|
352
428
|
}
|
|
@@ -433,6 +509,9 @@ function formatRbacDiff(diff, dryRun) {
|
|
|
433
509
|
chalk.dim(` inherits: [${[...op.role.inherits].join(", ")}]`)
|
|
434
510
|
);
|
|
435
511
|
}
|
|
512
|
+
if (op.role.isDefault) {
|
|
513
|
+
lines.push(chalk.dim(` isDefault: true`));
|
|
514
|
+
}
|
|
436
515
|
} else if (op.type === "update") {
|
|
437
516
|
lines.push(chalk.yellow(` ~ UPDATE role "${op.key}"`));
|
|
438
517
|
for (const change of op.changes) {
|
|
@@ -508,6 +587,11 @@ var RbacSyncClient = class {
|
|
|
508
587
|
constructor(config) {
|
|
509
588
|
this.baseUrl = config.baseUrl.replace(/\/$/, "");
|
|
510
589
|
this.apiKey = config.apiKey;
|
|
590
|
+
if (!this.baseUrl.startsWith("https://") && !this.baseUrl.startsWith("http://localhost") && !this.baseUrl.startsWith("http://127.0.0.1")) {
|
|
591
|
+
console.warn(
|
|
592
|
+
"WARNING: AUTHGATE_BASE_URL does not use HTTPS. API key may be transmitted insecurely."
|
|
593
|
+
);
|
|
594
|
+
}
|
|
511
595
|
}
|
|
512
596
|
async request(method, path, body) {
|
|
513
597
|
var _a, _b;
|
|
@@ -526,9 +610,12 @@ var RbacSyncClient = class {
|
|
|
526
610
|
let message;
|
|
527
611
|
try {
|
|
528
612
|
const json = JSON.parse(text);
|
|
529
|
-
message = (_b = (_a = json.error) != null ? _a : json.message) != null ? _b :
|
|
613
|
+
message = (_b = (_a = json.error) != null ? _a : json.message) != null ? _b : "Unknown error";
|
|
530
614
|
} catch (e) {
|
|
531
|
-
message = text;
|
|
615
|
+
message = text.length > 500 ? text.slice(0, 500) + "..." : text;
|
|
616
|
+
}
|
|
617
|
+
if (this.apiKey) {
|
|
618
|
+
message = message.replaceAll(this.apiKey, "[REDACTED]");
|
|
532
619
|
}
|
|
533
620
|
throw new Error(`API error (${res.status}): ${message}`);
|
|
534
621
|
}
|
|
@@ -548,7 +635,10 @@ var RbacSyncClient = class {
|
|
|
548
635
|
resources: config.resources,
|
|
549
636
|
roles: config.roles,
|
|
550
637
|
conditions: config.conditions ? Object.fromEntries(
|
|
551
|
-
Object.entries(config.conditions).map(([k]) => [
|
|
638
|
+
Object.entries(config.conditions).map(([k, fn]) => [
|
|
639
|
+
k,
|
|
640
|
+
{ key: k, sourceHash: hashConditionSource(fn) }
|
|
641
|
+
])
|
|
552
642
|
) : void 0,
|
|
553
643
|
force
|
|
554
644
|
});
|
package/dist/cli.cjs
CHANGED
|
@@ -89,15 +89,28 @@ function validateRbacConfig(config) {
|
|
|
89
89
|
);
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
|
-
|
|
92
|
+
const actionSet = /* @__PURE__ */ new Set();
|
|
93
|
+
for (const action of resource.actions) {
|
|
94
|
+
if (actionSet.has(action)) {
|
|
95
|
+
throw new Error(`Resource "${key}" has duplicate action: "${action}".`);
|
|
96
|
+
}
|
|
97
|
+
actionSet.add(action);
|
|
98
|
+
}
|
|
99
|
+
resourceActions.set(key, actionSet);
|
|
93
100
|
if (resource.scopes !== void 0) {
|
|
94
101
|
if (!Array.isArray(resource.scopes)) {
|
|
95
102
|
throw new Error(`Resource "${key}".scopes must be an array of strings.`);
|
|
96
103
|
}
|
|
104
|
+
const scopeSet = /* @__PURE__ */ new Set();
|
|
97
105
|
for (let i = 0; i < resource.scopes.length; i++) {
|
|
98
106
|
if (typeof resource.scopes[i] !== "string") {
|
|
99
107
|
throw new Error(`Resource "${key}".scopes[${i}] must be a string.`);
|
|
100
108
|
}
|
|
109
|
+
const scope = resource.scopes[i];
|
|
110
|
+
if (scopeSet.has(scope)) {
|
|
111
|
+
throw new Error(`Resource "${key}" has duplicate scope: "${scope}".`);
|
|
112
|
+
}
|
|
113
|
+
scopeSet.add(scope);
|
|
101
114
|
}
|
|
102
115
|
}
|
|
103
116
|
}
|
|
@@ -138,14 +151,40 @@ function validateRbacConfig(config) {
|
|
|
138
151
|
);
|
|
139
152
|
}
|
|
140
153
|
const value = actionsGrant[actionKey];
|
|
141
|
-
if (value !== true && typeof value !== "string" && !(value && typeof value === "object" && "when" in value && typeof value.when === "string")) {
|
|
154
|
+
if (value !== true && (typeof value !== "string" || value === "") && !(value && typeof value === "object" && "when" in value && typeof value.when === "string" && value.when !== "")) {
|
|
142
155
|
throw new Error(
|
|
143
|
-
`Role "${key}".grants.${resourceKey}.${actionKey} has invalid value. Expected true, a scope string, or { when: string }.`
|
|
156
|
+
`Role "${key}".grants.${resourceKey}.${actionKey} has invalid value. Expected true, a non-empty scope string, or { when: non-empty string }.`
|
|
144
157
|
);
|
|
145
158
|
}
|
|
159
|
+
if (typeof value === "string" && value !== "") {
|
|
160
|
+
const resource = resources[resourceKey];
|
|
161
|
+
if (resource.scopes && Array.isArray(resource.scopes)) {
|
|
162
|
+
const declaredScopes = new Set(resource.scopes);
|
|
163
|
+
if (!declaredScopes.has(value)) {
|
|
164
|
+
throw new Error(
|
|
165
|
+
`Role "${key}".grants.${resourceKey}.${actionKey} scope "${value}" is not in declared scopes for "${resourceKey}": ${[...declaredScopes].join(", ")}.`
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
146
170
|
}
|
|
147
171
|
}
|
|
148
172
|
}
|
|
173
|
+
const defaultRoles = [];
|
|
174
|
+
for (const key of roleKeys) {
|
|
175
|
+
const role = roles[key];
|
|
176
|
+
if (role.isDefault !== void 0 && typeof role.isDefault !== "boolean") {
|
|
177
|
+
throw new Error(`Role "${key}".isDefault must be a boolean.`);
|
|
178
|
+
}
|
|
179
|
+
if (role.isDefault === true) {
|
|
180
|
+
defaultRoles.push(key);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
if (defaultRoles.length > 1) {
|
|
184
|
+
throw new Error(
|
|
185
|
+
`Only one role may have isDefault: true. Found ${defaultRoles.length}: ${defaultRoles.join(", ")}.`
|
|
186
|
+
);
|
|
187
|
+
}
|
|
149
188
|
for (const key of roleKeys) {
|
|
150
189
|
const role = roles[key];
|
|
151
190
|
if (role.inherits !== void 0) {
|
|
@@ -211,6 +250,7 @@ function validateRbacConfig(config) {
|
|
|
211
250
|
renameTargets.set(role.renamedFrom, key);
|
|
212
251
|
}
|
|
213
252
|
}
|
|
253
|
+
const conditionKeys = /* @__PURE__ */ new Set();
|
|
214
254
|
if (c.conditions !== void 0) {
|
|
215
255
|
if (typeof c.conditions !== "object" || Array.isArray(c.conditions) || c.conditions === null) {
|
|
216
256
|
throw new Error("RBAC config `conditions` must be an object mapping names to functions.");
|
|
@@ -222,6 +262,23 @@ function validateRbacConfig(config) {
|
|
|
222
262
|
`Condition "${condKey}" must be a function. Got ${typeof condValue}.`
|
|
223
263
|
);
|
|
224
264
|
}
|
|
265
|
+
conditionKeys.add(condKey);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
for (const key of roleKeys) {
|
|
269
|
+
const role = roles[key];
|
|
270
|
+
const grants = role.grants;
|
|
271
|
+
for (const [resourceKey, actionGrants] of Object.entries(grants)) {
|
|
272
|
+
for (const [actionKey, value] of Object.entries(actionGrants)) {
|
|
273
|
+
if (value && typeof value === "object" && "when" in value) {
|
|
274
|
+
const whenKey = value.when;
|
|
275
|
+
if (!conditionKeys.has(whenKey)) {
|
|
276
|
+
throw new Error(
|
|
277
|
+
`Role "${key}".grants.${resourceKey}.${actionKey} references condition "${whenKey}" but it is not declared in conditions.` + (conditionKeys.size > 0 ? ` Declared conditions: ${[...conditionKeys].join(", ")}.` : ` No conditions are declared.`)
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
225
282
|
}
|
|
226
283
|
}
|
|
227
284
|
return config;
|
|
@@ -258,17 +315,27 @@ Run \`npx @auth-gate/rbac init\` to create one.`
|
|
|
258
315
|
}
|
|
259
316
|
|
|
260
317
|
// src/diff.ts
|
|
318
|
+
function deepEqual(a, b) {
|
|
319
|
+
if (a === b) return true;
|
|
320
|
+
if (a === null || b === null || typeof a !== "object" || typeof b !== "object") return false;
|
|
321
|
+
if (Array.isArray(a) !== Array.isArray(b)) return false;
|
|
322
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
323
|
+
if (a.length !== b.length) return false;
|
|
324
|
+
return a.every((val, i) => deepEqual(val, b[i]));
|
|
325
|
+
}
|
|
326
|
+
const keysA = Object.keys(a).sort();
|
|
327
|
+
const keysB = Object.keys(b).sort();
|
|
328
|
+
if (keysA.length !== keysB.length) return false;
|
|
329
|
+
return keysA.every(
|
|
330
|
+
(key, i) => key === keysB[i] && deepEqual(a[key], b[key])
|
|
331
|
+
);
|
|
332
|
+
}
|
|
261
333
|
function hashConditionSource(fn) {
|
|
262
|
-
const
|
|
263
|
-
|
|
264
|
-
for (let i = 0; i < src.length; i++) {
|
|
265
|
-
const ch = src.charCodeAt(i);
|
|
266
|
-
hash = (hash << 5) - hash + ch | 0;
|
|
267
|
-
}
|
|
268
|
-
return hash.toString(36);
|
|
334
|
+
const { createHash } = require("crypto");
|
|
335
|
+
return createHash("sha256").update(fn.toString()).digest("hex").slice(0, 16);
|
|
269
336
|
}
|
|
270
337
|
function computeRbacDiff(config, server, memberCounts) {
|
|
271
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
|
|
338
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
|
|
272
339
|
const resourceOps = [];
|
|
273
340
|
const roleOps = [];
|
|
274
341
|
const conditionOps = [];
|
|
@@ -327,7 +394,7 @@ function computeRbacDiff(config, server, memberCounts) {
|
|
|
327
394
|
if (!existing && role.renamedFrom) {
|
|
328
395
|
existing = (_d = serverRoleByKey.get(role.renamedFrom)) != null ? _d : serverRoleByPreviousKey.get(role.renamedFrom);
|
|
329
396
|
if (existing) {
|
|
330
|
-
const members = (_e = memberCounts[existing.
|
|
397
|
+
const members = (_e = memberCounts[existing.configKey]) != null ? _e : 0;
|
|
331
398
|
roleOps.push({
|
|
332
399
|
type: "rename",
|
|
333
400
|
key,
|
|
@@ -351,16 +418,19 @@ function computeRbacDiff(config, server, memberCounts) {
|
|
|
351
418
|
if (((_f = existing.description) != null ? _f : void 0) !== ((_g = role.description) != null ? _g : void 0)) {
|
|
352
419
|
changes.push("description changed");
|
|
353
420
|
}
|
|
354
|
-
|
|
355
|
-
const configGrants = JSON.stringify(role.grants);
|
|
356
|
-
if (existingGrants !== configGrants) {
|
|
421
|
+
if (!deepEqual((_h = existing.grants) != null ? _h : null, (_i = role.grants) != null ? _i : null)) {
|
|
357
422
|
changes.push("grants changed");
|
|
358
423
|
}
|
|
359
|
-
const existingInherits = [...(
|
|
360
|
-
const configInherits = [...(
|
|
424
|
+
const existingInherits = [...(_j = existing.inherits) != null ? _j : []].sort().join(",");
|
|
425
|
+
const configInherits = [...(_k = role.inherits) != null ? _k : []].sort().join(",");
|
|
361
426
|
if (existingInherits !== configInherits) {
|
|
362
427
|
changes.push("inherits changed");
|
|
363
428
|
}
|
|
429
|
+
const existingDefault = (_l = existing.isDefault) != null ? _l : false;
|
|
430
|
+
const configDefault = (_m = role.isDefault) != null ? _m : false;
|
|
431
|
+
if (existingDefault !== configDefault) {
|
|
432
|
+
changes.push(`isDefault: ${existingDefault} -> ${configDefault}`);
|
|
433
|
+
}
|
|
364
434
|
if (changes.length > 0) {
|
|
365
435
|
roleOps.push({ type: "update", key, role, existing, changes });
|
|
366
436
|
}
|
|
@@ -370,7 +440,7 @@ function computeRbacDiff(config, server, memberCounts) {
|
|
|
370
440
|
for (const [key, role] of serverRoleByKey) {
|
|
371
441
|
if (renameMap.has(key)) continue;
|
|
372
442
|
if (role.isActive) {
|
|
373
|
-
const members = (
|
|
443
|
+
const members = (_n = memberCounts[role.configKey]) != null ? _n : 0;
|
|
374
444
|
if (members > 0) hasDestructive = true;
|
|
375
445
|
roleOps.push({ type: "archive", key, existing: role, assignedMembers: members });
|
|
376
446
|
}
|
|
@@ -457,6 +527,9 @@ function formatRbacDiff(diff, dryRun) {
|
|
|
457
527
|
import_chalk.default.dim(` inherits: [${[...op.role.inherits].join(", ")}]`)
|
|
458
528
|
);
|
|
459
529
|
}
|
|
530
|
+
if (op.role.isDefault) {
|
|
531
|
+
lines.push(import_chalk.default.dim(` isDefault: true`));
|
|
532
|
+
}
|
|
460
533
|
} else if (op.type === "update") {
|
|
461
534
|
lines.push(import_chalk.default.yellow(` ~ UPDATE role "${op.key}"`));
|
|
462
535
|
for (const change of op.changes) {
|
|
@@ -532,6 +605,11 @@ var RbacSyncClient = class {
|
|
|
532
605
|
constructor(config) {
|
|
533
606
|
this.baseUrl = config.baseUrl.replace(/\/$/, "");
|
|
534
607
|
this.apiKey = config.apiKey;
|
|
608
|
+
if (!this.baseUrl.startsWith("https://") && !this.baseUrl.startsWith("http://localhost") && !this.baseUrl.startsWith("http://127.0.0.1")) {
|
|
609
|
+
console.warn(
|
|
610
|
+
"WARNING: AUTHGATE_BASE_URL does not use HTTPS. API key may be transmitted insecurely."
|
|
611
|
+
);
|
|
612
|
+
}
|
|
535
613
|
}
|
|
536
614
|
async request(method, path, body) {
|
|
537
615
|
var _a, _b;
|
|
@@ -550,9 +628,12 @@ var RbacSyncClient = class {
|
|
|
550
628
|
let message;
|
|
551
629
|
try {
|
|
552
630
|
const json = JSON.parse(text);
|
|
553
|
-
message = (_b = (_a = json.error) != null ? _a : json.message) != null ? _b :
|
|
631
|
+
message = (_b = (_a = json.error) != null ? _a : json.message) != null ? _b : "Unknown error";
|
|
554
632
|
} catch (e) {
|
|
555
|
-
message = text;
|
|
633
|
+
message = text.length > 500 ? text.slice(0, 500) + "..." : text;
|
|
634
|
+
}
|
|
635
|
+
if (this.apiKey) {
|
|
636
|
+
message = message.replaceAll(this.apiKey, "[REDACTED]");
|
|
556
637
|
}
|
|
557
638
|
throw new Error(`API error (${res.status}): ${message}`);
|
|
558
639
|
}
|
|
@@ -572,7 +653,10 @@ var RbacSyncClient = class {
|
|
|
572
653
|
resources: config.resources,
|
|
573
654
|
roles: config.roles,
|
|
574
655
|
conditions: config.conditions ? Object.fromEntries(
|
|
575
|
-
Object.entries(config.conditions).map(([k]) => [
|
|
656
|
+
Object.entries(config.conditions).map(([k, fn]) => [
|
|
657
|
+
k,
|
|
658
|
+
{ key: k, sourceHash: hashConditionSource(fn) }
|
|
659
|
+
])
|
|
576
660
|
) : void 0,
|
|
577
661
|
force
|
|
578
662
|
});
|
|
@@ -665,6 +749,7 @@ export const rbac = defineRbac({
|
|
|
665
749
|
},
|
|
666
750
|
viewer: {
|
|
667
751
|
name: "Viewer",
|
|
752
|
+
isDefault: true,
|
|
668
753
|
grants: {
|
|
669
754
|
documents: { read: true },
|
|
670
755
|
},
|
package/dist/cli.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
computeRbacDiff,
|
|
6
6
|
formatRbacDiff,
|
|
7
7
|
loadRbacConfig
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-ZFKXT2MP.mjs";
|
|
9
9
|
|
|
10
10
|
// src/cli.ts
|
|
11
11
|
import chalk from "chalk";
|
|
@@ -93,6 +93,7 @@ export const rbac = defineRbac({
|
|
|
93
93
|
},
|
|
94
94
|
viewer: {
|
|
95
95
|
name: "Viewer",
|
|
96
|
+
isDefault: true,
|
|
96
97
|
grants: {
|
|
97
98
|
documents: { read: true },
|
|
98
99
|
},
|