@albirex/platformatic-logto 1.6.3 → 1.7.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/lib/index.cjs +207 -100
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.cts +15 -5
- package/lib/index.d.ts +15 -5
- package/lib/index.js +206 -100
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
package/lib/index.cjs
CHANGED
|
@@ -34,6 +34,7 @@ __export(index_exports, {
|
|
|
34
34
|
deletePermissionsVersion: () => deletePermissionsVersion,
|
|
35
35
|
fastifyLogto: () => import_fastify_logto2.fastifyLogto,
|
|
36
36
|
findRuleForRequestUser: () => findRuleForRequestUser,
|
|
37
|
+
findScopeForRequestUser: () => findScopeForRequestUser,
|
|
37
38
|
getRequestFromContext: () => getRequestFromContext,
|
|
38
39
|
getRoles: () => getRoles,
|
|
39
40
|
incrementPermissionsVersion: () => incrementPermissionsVersion,
|
|
@@ -67,7 +68,7 @@ var ERROR_PREFIX = "PLT_DB_AUTH";
|
|
|
67
68
|
var Unauthorized = (0, import_error.default)(`${ERROR_PREFIX}_UNAUTHORIZED`, "operation not allowed", 401);
|
|
68
69
|
var UnauthorizedField = (0, import_error.default)(`${ERROR_PREFIX}_FIELD_UNAUTHORIZED`, "field not allowed: %s", 401);
|
|
69
70
|
var MissingNotNullableError = (0, import_error.default)(`${ERROR_PREFIX}_NOT_NULLABLE_MISSING`, 'missing not nullable field: "%s" in save rule for entity "%s"');
|
|
70
|
-
var PermissionsOutdated = (0, import_error.default)(`${ERROR_PREFIX}_VERSION_OUTDATED`, "Verision has been updated. Please logout and login again.",
|
|
71
|
+
var PermissionsOutdated = (0, import_error.default)(`${ERROR_PREFIX}_VERSION_OUTDATED`, "Verision has been updated. Please logout and login again.", 403);
|
|
71
72
|
|
|
72
73
|
// src/index.ts
|
|
73
74
|
var import_fastify_logto = __toESM(require("@albirex/fastify-logto"), 1);
|
|
@@ -133,6 +134,33 @@ function getRoles(request, roleKey, anonymousRole, isRolePath = false) {
|
|
|
133
134
|
}
|
|
134
135
|
return output;
|
|
135
136
|
}
|
|
137
|
+
function getScopes(request, scopesKey, anonymousRole, isScopePath = false) {
|
|
138
|
+
let output = [];
|
|
139
|
+
const user = request.user;
|
|
140
|
+
if (!user) {
|
|
141
|
+
output.push(anonymousRole);
|
|
142
|
+
return output;
|
|
143
|
+
}
|
|
144
|
+
let scopesRaw;
|
|
145
|
+
if (isScopePath) {
|
|
146
|
+
const roleKeys = scopesKey.split(".");
|
|
147
|
+
scopesRaw = user;
|
|
148
|
+
for (const key of roleKeys) {
|
|
149
|
+
scopesRaw = scopesRaw[key];
|
|
150
|
+
}
|
|
151
|
+
} else {
|
|
152
|
+
scopesRaw = user[scopesKey];
|
|
153
|
+
}
|
|
154
|
+
if (typeof scopesRaw === "string") {
|
|
155
|
+
output = scopesRaw.split(" ");
|
|
156
|
+
} else if (Array.isArray(scopesRaw)) {
|
|
157
|
+
output = scopesRaw;
|
|
158
|
+
}
|
|
159
|
+
if (output.length === 0) {
|
|
160
|
+
output.push(anonymousRole);
|
|
161
|
+
}
|
|
162
|
+
return output;
|
|
163
|
+
}
|
|
136
164
|
|
|
137
165
|
// src/index.ts
|
|
138
166
|
var PLT_ADMIN_ROLE = "platformatic-admin";
|
|
@@ -150,107 +178,18 @@ var platformaticLogto = (0, import_fastify_plugin.default)(async (app, opts) =>
|
|
|
150
178
|
});
|
|
151
179
|
app.log.info("Redis client registered for permissions version check");
|
|
152
180
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
181
|
+
if (opts.roleBasedAuth) {
|
|
182
|
+
app.register(import_fastify_logto.default, {
|
|
183
|
+
endpoint: opts.roleBasedAuth.logtoBaseUrl || "https://auth.example.com",
|
|
184
|
+
appId: opts.roleBasedAuth.logtoAppId || "your-app-id",
|
|
185
|
+
appSecret: opts.roleBasedAuth.logtoAppSecret || "your-app-secret"
|
|
186
|
+
});
|
|
187
|
+
}
|
|
158
188
|
await app.register(fastifyUser, opts.jwtPlugin);
|
|
189
|
+
const roleKey = opts.roleBasedAuth?.rolePath || opts.roleBasedAuth?.roleKey || "X-PLATFORMATIC-ROLE";
|
|
159
190
|
const adminSecret = opts.adminSecret;
|
|
160
|
-
const
|
|
161
|
-
const userKey = opts.userPath || opts.userKey || "X-PLATFORMATIC-USER-ID";
|
|
162
|
-
const isRolePath = !!opts.rolePath;
|
|
191
|
+
const isRolePath = !!opts.roleBasedAuth?.rolePath;
|
|
163
192
|
const anonymousRole = opts.anonymousRole || "anonymous";
|
|
164
|
-
async function composeLogToRules() {
|
|
165
|
-
const rolesResp = await app.logto.callAPI("/api/roles?type=User", "GET");
|
|
166
|
-
if (!rolesResp.ok) {
|
|
167
|
-
throw rolesResp;
|
|
168
|
-
}
|
|
169
|
-
const roles = await rolesResp.json();
|
|
170
|
-
const rules = [{
|
|
171
|
-
role: anonymousRole,
|
|
172
|
-
entities: Object.keys(app.platformatic.entities),
|
|
173
|
-
find: opts.allowAnonymous,
|
|
174
|
-
save: opts.allowAnonymous,
|
|
175
|
-
delete: opts.allowAnonymous
|
|
176
|
-
}];
|
|
177
|
-
for (const role of roles) {
|
|
178
|
-
const scopesResp = await app.logto.callAPI(`/api/roles/${role.id}/scopes`, "GET");
|
|
179
|
-
if (!scopesResp.ok) {
|
|
180
|
-
throw scopesResp;
|
|
181
|
-
}
|
|
182
|
-
const scopes = await scopesResp.json();
|
|
183
|
-
for (const scope of scopes) {
|
|
184
|
-
const roleName = role.name;
|
|
185
|
-
let [scopeAction, entity] = scope.name.split(":");
|
|
186
|
-
if (!app.platformatic.entities[entity]) {
|
|
187
|
-
app.log.debug(`Unknown entity '${entity}' in authorization rule`);
|
|
188
|
-
continue;
|
|
189
|
-
}
|
|
190
|
-
switch (scopeAction) {
|
|
191
|
-
case "create":
|
|
192
|
-
scopeAction = "save";
|
|
193
|
-
break;
|
|
194
|
-
case "read":
|
|
195
|
-
scopeAction = "find";
|
|
196
|
-
break;
|
|
197
|
-
case "update":
|
|
198
|
-
scopeAction = "updateMany";
|
|
199
|
-
break;
|
|
200
|
-
}
|
|
201
|
-
const checkExists = rules.find((r) => r.role === roleName && r.entity === entity);
|
|
202
|
-
if (checkExists) {
|
|
203
|
-
if (opts.checks) {
|
|
204
|
-
checkExists[scopeAction] = {
|
|
205
|
-
checks: {
|
|
206
|
-
userId: userKey
|
|
207
|
-
}
|
|
208
|
-
};
|
|
209
|
-
} else {
|
|
210
|
-
checkExists[scopeAction] = true;
|
|
211
|
-
}
|
|
212
|
-
} else {
|
|
213
|
-
const newRule = {
|
|
214
|
-
role: roleName,
|
|
215
|
-
entity
|
|
216
|
-
};
|
|
217
|
-
if (opts.checks) {
|
|
218
|
-
newRule[scopeAction] = {
|
|
219
|
-
checks: {
|
|
220
|
-
userId: userKey
|
|
221
|
-
}
|
|
222
|
-
};
|
|
223
|
-
} else {
|
|
224
|
-
newRule[scopeAction] = true;
|
|
225
|
-
}
|
|
226
|
-
if (opts.defaults) {
|
|
227
|
-
newRule.defaults = {
|
|
228
|
-
userId: userKey
|
|
229
|
-
};
|
|
230
|
-
}
|
|
231
|
-
rules.push(newRule);
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
const logRules = rules.reduce((prev, curr) => {
|
|
236
|
-
(prev[curr["role"]] ??= []).push(curr);
|
|
237
|
-
return prev;
|
|
238
|
-
}, {});
|
|
239
|
-
for (const key in logRules) {
|
|
240
|
-
app.log.info(`Rules set for role ${key}`);
|
|
241
|
-
for (const element of logRules[key]) {
|
|
242
|
-
const { entity, entities, role, ...other } = element;
|
|
243
|
-
app.log.info(` ${entity ?? entities.join(",")}: ${JSON.stringify(other)}`);
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
const missingEntities = Object.keys(app.platformatic.entities).filter((e) => !rules.map((r) => r.entity).includes(e));
|
|
247
|
-
if (missingEntities.length) {
|
|
248
|
-
app.log.warn(`Missing rules for entities: ${missingEntities.join(", ")}`);
|
|
249
|
-
}
|
|
250
|
-
app.log.debug("LogTo calculated rules");
|
|
251
|
-
app.log.debug(rules);
|
|
252
|
-
return rules;
|
|
253
|
-
}
|
|
254
193
|
app.decorateRequest("setupDBAuthorizationUser", setupUser);
|
|
255
194
|
async function setupUser() {
|
|
256
195
|
await this.extractUser();
|
|
@@ -286,7 +225,98 @@ var platformaticLogto = (0, import_fastify_plugin.default)(async (app, opts) =>
|
|
|
286
225
|
};
|
|
287
226
|
}
|
|
288
227
|
}
|
|
289
|
-
|
|
228
|
+
async function roleBasedAuth() {
|
|
229
|
+
const userKey = opts.roleBasedAuth.userKey || opts.roleBasedAuth.userPath || "X-PLATFORMATIC-USER-ID";
|
|
230
|
+
async function composeLogToRules() {
|
|
231
|
+
const rolesResp = await app.logto.callAPI("/api/roles?type=User", "GET");
|
|
232
|
+
if (!rolesResp.ok) {
|
|
233
|
+
throw rolesResp;
|
|
234
|
+
}
|
|
235
|
+
const roles = await rolesResp.json();
|
|
236
|
+
const rules = [{
|
|
237
|
+
role: anonymousRole,
|
|
238
|
+
entities: Object.keys(app.platformatic.entities),
|
|
239
|
+
find: opts.allowAnonymous,
|
|
240
|
+
save: opts.allowAnonymous,
|
|
241
|
+
delete: opts.allowAnonymous
|
|
242
|
+
}];
|
|
243
|
+
for (const role of roles) {
|
|
244
|
+
const scopesResp = await app.logto.callAPI(`/api/roles/${role.id}/scopes`, "GET");
|
|
245
|
+
if (!scopesResp.ok) {
|
|
246
|
+
throw scopesResp;
|
|
247
|
+
}
|
|
248
|
+
const scopes = await scopesResp.json();
|
|
249
|
+
for (const scope of scopes) {
|
|
250
|
+
const roleName = role.name;
|
|
251
|
+
let [scopeAction, entity] = scope.name.split(":");
|
|
252
|
+
if (!app.platformatic.entities[entity]) {
|
|
253
|
+
app.log.debug(`Unknown entity '${entity}' in authorization rule`);
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
switch (scopeAction) {
|
|
257
|
+
case "create":
|
|
258
|
+
scopeAction = "save";
|
|
259
|
+
break;
|
|
260
|
+
case "read":
|
|
261
|
+
scopeAction = "find";
|
|
262
|
+
break;
|
|
263
|
+
case "update":
|
|
264
|
+
scopeAction = "updateMany";
|
|
265
|
+
break;
|
|
266
|
+
}
|
|
267
|
+
const checkExists = rules.find((r) => r.role === roleName && r.entity === entity);
|
|
268
|
+
if (checkExists) {
|
|
269
|
+
if (opts.checks) {
|
|
270
|
+
checkExists[scopeAction] = {
|
|
271
|
+
checks: {
|
|
272
|
+
userId: userKey
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
} else {
|
|
276
|
+
checkExists[scopeAction] = true;
|
|
277
|
+
}
|
|
278
|
+
} else {
|
|
279
|
+
const newRule = {
|
|
280
|
+
role: roleName,
|
|
281
|
+
entity
|
|
282
|
+
};
|
|
283
|
+
if (opts.checks) {
|
|
284
|
+
newRule[scopeAction] = {
|
|
285
|
+
checks: {
|
|
286
|
+
userId: userKey
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
} else {
|
|
290
|
+
newRule[scopeAction] = true;
|
|
291
|
+
}
|
|
292
|
+
if (opts.defaults) {
|
|
293
|
+
newRule.defaults = {
|
|
294
|
+
userId: userKey
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
rules.push(newRule);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
const logRules = rules.reduce((prev, curr) => {
|
|
302
|
+
(prev[curr["role"]] ??= []).push(curr);
|
|
303
|
+
return prev;
|
|
304
|
+
}, {});
|
|
305
|
+
for (const key in logRules) {
|
|
306
|
+
app.log.info(`Rules set for role ${key}`);
|
|
307
|
+
for (const element of logRules[key]) {
|
|
308
|
+
const { entity, entities, role, ...other } = element;
|
|
309
|
+
app.log.info(` ${entity ?? entities.join(",")}: ${JSON.stringify(other)}`);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
const missingEntities = Object.keys(app.platformatic.entities).filter((e) => !rules.map((r) => r.entity).includes(e));
|
|
313
|
+
if (missingEntities.length) {
|
|
314
|
+
app.log.warn(`Missing rules for entities: ${missingEntities.join(", ")}`);
|
|
315
|
+
}
|
|
316
|
+
app.log.debug("LogTo calculated rules");
|
|
317
|
+
app.log.debug(rules);
|
|
318
|
+
return rules;
|
|
319
|
+
}
|
|
290
320
|
const logToRules = await composeLogToRules();
|
|
291
321
|
app.platformaticLogTo.rules = logToRules;
|
|
292
322
|
const entityRules = {};
|
|
@@ -426,6 +456,64 @@ var platformaticLogto = (0, import_fastify_plugin.default)(async (app, opts) =>
|
|
|
426
456
|
}
|
|
427
457
|
});
|
|
428
458
|
}
|
|
459
|
+
}
|
|
460
|
+
async function scopeBasedAuth() {
|
|
461
|
+
const scopesKey = opts.scopeBasedAuth.scopesPath || opts.scopeBasedAuth.scopesKey || "X-PLATFORMATIC-SCOPES";
|
|
462
|
+
for (const entityKey of Object.keys(app.platformatic.entities)) {
|
|
463
|
+
let useOriginal = function(ctx) {
|
|
464
|
+
return !ctx;
|
|
465
|
+
};
|
|
466
|
+
let userPropToFillForPublish;
|
|
467
|
+
const topicsWithoutChecks = false;
|
|
468
|
+
if (userPropToFillForPublish && topicsWithoutChecks) {
|
|
469
|
+
throw new Error(`Subscription for entity "${entityKey}" have conflictling rules across roles`);
|
|
470
|
+
}
|
|
471
|
+
app.platformatic.addEntityHooks(entityKey, {
|
|
472
|
+
async find(originalFind, { where, ctx, fields, ...restOpts } = {}) {
|
|
473
|
+
if (useOriginal(ctx)) {
|
|
474
|
+
return originalFind({ ...restOpts, where, ctx, fields });
|
|
475
|
+
}
|
|
476
|
+
await findScopeForRequestUser(ctx, entityKey, "find", scopesKey, anonymousRole, isRolePath);
|
|
477
|
+
return originalFind({ ...restOpts, where, ctx, fields });
|
|
478
|
+
},
|
|
479
|
+
async save(originalSave, { input, ctx, fields, ...restOpts }) {
|
|
480
|
+
if (useOriginal(ctx)) {
|
|
481
|
+
return originalSave({ ctx, input, fields, ...restOpts });
|
|
482
|
+
}
|
|
483
|
+
await findScopeForRequestUser(ctx, entityKey, "save", scopesKey, anonymousRole, isRolePath);
|
|
484
|
+
return originalSave({ input, ctx, fields, ...restOpts });
|
|
485
|
+
},
|
|
486
|
+
async insert(originalInsert, { inputs, ctx, fields, ...restOpts }) {
|
|
487
|
+
if (useOriginal(ctx)) {
|
|
488
|
+
return originalInsert({ inputs, ctx, fields, ...restOpts });
|
|
489
|
+
}
|
|
490
|
+
await findScopeForRequestUser(ctx, entityKey, "insert", scopesKey, anonymousRole, isRolePath);
|
|
491
|
+
return originalInsert({ inputs, ctx, fields, ...restOpts });
|
|
492
|
+
},
|
|
493
|
+
async delete(originalDelete, { where, ctx, fields, ...restOpts }) {
|
|
494
|
+
if (useOriginal(ctx)) {
|
|
495
|
+
return originalDelete({ where, ctx, fields, ...restOpts });
|
|
496
|
+
}
|
|
497
|
+
await findScopeForRequestUser(ctx, entityKey, "delete", scopesKey, anonymousRole, isRolePath);
|
|
498
|
+
return originalDelete({ where, ctx, fields, ...restOpts });
|
|
499
|
+
},
|
|
500
|
+
async updateMany(originalUpdateMany, { where, ctx, fields, ...restOpts }) {
|
|
501
|
+
if (useOriginal(ctx)) {
|
|
502
|
+
return originalUpdateMany({ ...restOpts, where, ctx, fields });
|
|
503
|
+
}
|
|
504
|
+
await findScopeForRequestUser(ctx, entityKey, "updateMany", scopesKey, anonymousRole, isRolePath);
|
|
505
|
+
return originalUpdateMany({ ...restOpts, where, ctx, fields });
|
|
506
|
+
}
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
app.addHook("onReady", async function() {
|
|
511
|
+
if (opts.roleBasedAuth) {
|
|
512
|
+
await roleBasedAuth();
|
|
513
|
+
}
|
|
514
|
+
if (opts.scopeBasedAuth) {
|
|
515
|
+
await scopeBasedAuth();
|
|
516
|
+
}
|
|
429
517
|
});
|
|
430
518
|
}, { name: "@albirex/platformatic-logto" });
|
|
431
519
|
async function fromRuleToWhere(ctx, rule, where, user) {
|
|
@@ -470,6 +558,24 @@ async function findRuleForRequestUser(ctx, rules, roleKey, anonymousRole, isRole
|
|
|
470
558
|
ctx.reply.request.log.trace({ roles, rule }, "found rule");
|
|
471
559
|
return rule;
|
|
472
560
|
}
|
|
561
|
+
async function findScopeForRequestUser(ctx, entityKey, action, scopesKey, anonymousRole, isScopePath = false) {
|
|
562
|
+
const request = getRequestFromContext(ctx);
|
|
563
|
+
await request.setupDBAuthorizationUser();
|
|
564
|
+
const scopes = getScopes(request, scopesKey, anonymousRole, isScopePath);
|
|
565
|
+
const scope = scopes.find((s) => {
|
|
566
|
+
if (action !== "find") {
|
|
567
|
+
return s === `${action}:${entityKey}`;
|
|
568
|
+
} else {
|
|
569
|
+
return s === `${action}:${entityKey}` || s === `read:${entityKey}`;
|
|
570
|
+
}
|
|
571
|
+
});
|
|
572
|
+
if (!scope) {
|
|
573
|
+
ctx.reply.request.log.warn("no scope found");
|
|
574
|
+
throw new Unauthorized();
|
|
575
|
+
}
|
|
576
|
+
ctx.reply.request.log.trace({ scopes, scope }, "found scope");
|
|
577
|
+
return scope;
|
|
578
|
+
}
|
|
473
579
|
function checkFieldsFromRule(rule, fields) {
|
|
474
580
|
if (!rule) {
|
|
475
581
|
throw new Unauthorized();
|
|
@@ -564,6 +670,7 @@ var index_default = platformaticLogto;
|
|
|
564
670
|
deletePermissionsVersion,
|
|
565
671
|
fastifyLogto,
|
|
566
672
|
findRuleForRequestUser,
|
|
673
|
+
findScopeForRequestUser,
|
|
567
674
|
getRequestFromContext,
|
|
568
675
|
getRoles,
|
|
569
676
|
incrementPermissionsVersion,
|
package/lib/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/utils/find-rule.ts","../src/utils/errors.ts","../src/utils/permissions-version.ts","../src/utils/utils.ts"],"sourcesContent":["import fp from 'fastify-plugin'\nimport * as fastifyUser from 'fastify-user'\n\nimport findRule from './utils/find-rule.js'\nimport { Unauthorized, UnauthorizedField, MissingNotNullableError, PermissionsOutdated } from './utils/errors.js'\nimport fastifyLogto from '@albirex/fastify-logto';\nimport fastifyRedis from '@fastify/redis';\nimport { FastifyInstance, FastifyPluginAsync } from 'fastify'\nimport type { FastifyUserPluginOptions } from 'fastify-user';\nimport type { Entity, PlatformaticContext } from '@platformatic/sql-mapper'\n\nexport { fastifyLogto } from '@albirex/fastify-logto';\nexport { incrementPermissionsVersion, deletePermissionsVersion } from './utils/permissions-version.js';\n\nimport { getRequestFromContext, getRoles } from './utils/utils.js'\nexport { getRequestFromContext, getRoles } from './utils/utils.js'\n\nconst PLT_ADMIN_ROLE = 'platformatic-admin'\n\nexport type PlatformaticRule = {\n role: string;\n entity?: string;\n entities?: string[];\n defaults?: Record<string, string>;\n checks?: boolean;\n find?: boolean;\n save?: boolean;\n delete?: boolean;\n [action: string]: unknown;\n};\n\nexport type RedisConfig = {\n host?: string;\n port?: number;\n password?: string;\n username?: string;\n db?: number;\n};\n\nexport type PlatformaticLogtoAuthOptions = {\n logtoBaseUrl?: string;\n logtoAppId?: string;\n logtoAppSecret?: string;\n adminSecret?: string;\n rolePath?: string;\n roleKey?: string;\n userPath?: string;\n userKey?: string;\n anonymousRole?: string;\n allowAnonymous?: boolean;\n checks?: boolean;\n defaults?: boolean;\n jwtPlugin: FastifyUserPluginOptions;\n redis?: RedisConfig;\n enableTokenVersionCheck?: boolean;\n sessionVersionKey?: string;\n};\n\nexport const platformaticLogto: FastifyPluginAsync<PlatformaticLogtoAuthOptions> = fp(async (app: FastifyInstance, opts: PlatformaticLogtoAuthOptions) => {\n app.decorate('platformaticLogTo', {\n opts\n })\n\n // Register Redis if configured\n if (opts.redis) {\n await app.register(fastifyRedis, {\n host: opts.redis.host || '127.0.0.1',\n port: opts.redis.port || 6379,\n password: opts.redis.password,\n username: opts.redis.username,\n db: opts.redis.db || 0,\n });\n app.log.info('Redis client registered for permissions version check');\n }\n\n app.register(fastifyLogto, {\n endpoint: opts.logtoBaseUrl || 'https://auth.example.com',\n appId: opts.logtoAppId || 'your-app-id',\n appSecret: opts.logtoAppSecret || 'your-app-secret',\n });\n\n await app.register(fastifyUser as unknown as FastifyPluginAsync, opts.jwtPlugin);\n\n const adminSecret = opts.adminSecret\n const roleKey = opts.rolePath || opts.roleKey || 'X-PLATFORMATIC-ROLE'\n const userKey = opts.userPath || opts.userKey || 'X-PLATFORMATIC-USER-ID'\n const isRolePath = !!opts.rolePath // if `true` the role is intepreted as path like `user.role`\n const anonymousRole = opts.anonymousRole || 'anonymous'\n\n async function composeLogToRules() {\n const rolesResp = await app.logto.callAPI('/api/roles?type=User', 'GET');\n\n if (!rolesResp.ok) {\n throw rolesResp;\n }\n\n const roles = await rolesResp.json();\n const rules: PlatformaticRule[] = [{\n role: anonymousRole,\n entities: Object.keys(app.platformatic.entities),\n find: opts.allowAnonymous,\n save: opts.allowAnonymous,\n delete: opts.allowAnonymous,\n }];\n\n for (const role of roles) {\n const scopesResp = await app.logto.callAPI(`/api/roles/${role.id}/scopes`, 'GET');\n\n if (!scopesResp.ok) {\n throw scopesResp;\n }\n\n const scopes = await scopesResp.json();\n\n for (const scope of scopes) {\n const roleName = role.name;\n // eslint-disable-next-line prefer-const\n let [scopeAction, entity] = scope.name.split(':');\n\n if (!app.platformatic.entities[entity]) {\n app.log.debug(`Unknown entity '${entity}' in authorization rule`)\n continue;\n }\n\n switch (scopeAction) {\n case 'create':\n scopeAction = 'save'\n break;\n case 'read':\n scopeAction = 'find'\n break;\n case 'update':\n scopeAction = 'updateMany'\n break;\n }\n\n const checkExists = rules.find(r => r.role === roleName && r.entity === entity);\n if (checkExists) {\n if (opts.checks) {\n checkExists[scopeAction] = {\n checks: {\n userId: userKey\n }\n };\n } else {\n checkExists[scopeAction] = true;\n }\n } else {\n const newRule: PlatformaticRule = {\n role: roleName,\n entity,\n };\n\n if (opts.checks) {\n newRule[scopeAction] = {\n checks: {\n userId: userKey\n }\n };\n } else {\n newRule[scopeAction] = true;\n }\n\n if (opts.defaults) {\n newRule.defaults = {\n userId: userKey\n };\n }\n\n rules.push(newRule);\n }\n }\n }\n\n const logRules = rules.reduce((prev, curr) => {\n (prev[curr['role']] ??= []).push(curr);\n return prev;\n }, {})\n\n for (const key in logRules) {\n app.log.info(`Rules set for role ${key}`);\n\n for (const element of logRules[key]) {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { entity, entities, role, ...other } = element;\n app.log.info(`\\t${entity ?? entities.join(',')}: ${JSON.stringify(other)}`);\n }\n }\n\n const missingEntities = Object.keys(app.platformatic.entities).filter((e) => !rules.map((r) => r.entity).includes(e));\n\n if (missingEntities.length) {\n app.log.warn(`Missing rules for entities: ${missingEntities.join(', ')}`);\n }\n\n app.log.debug('LogTo calculated rules');\n app.log.debug(rules);\n return rules;\n }\n\n app.decorateRequest('setupDBAuthorizationUser', setupUser)\n\n async function setupUser() {\n // if (!adminSecret) {\n await this.extractUser()\n // }\n\n let forceAdminRole = false\n if (adminSecret && this.headers['x-platformatic-admin-secret'] === adminSecret) {\n if (opts.jwtPlugin.jwt) {\n forceAdminRole = true\n } else {\n this.log.info('admin secret is valid')\n this.user = new Proxy(this.headers, {\n get: (target, key) => {\n let value;\n if (!target[key.toString()]) {\n const newKey = key.toString().toLowerCase()\n value = target[newKey]\n } else {\n value = target[key.toString()]\n }\n\n if (!value && key.toString().toLowerCase() === roleKey.toLowerCase()) {\n value = PLT_ADMIN_ROLE\n }\n return value\n },\n })\n }\n } else {\n await checkPermissionsVersion(app, opts, this.user)\n }\n\n if (forceAdminRole) {\n // We replace just the role in `request.user`, all the rest is untouched\n this.user = {\n // ...request.user,\n [roleKey]: this.headers['x-platformatic-role'] ? [this.headers['x-platformatic-role']] : [PLT_ADMIN_ROLE]\n }\n }\n }\n\n app.addHook('onReady', async function () {\n const logToRules = await composeLogToRules();\n\n app.platformaticLogTo.rules = logToRules;\n\n // TODO validate that there is at most a rule for a given role\n const entityRules = {};\n for (let i = 0; i < logToRules.length; i++) {\n const rule = logToRules[i]\n\n let ruleEntities = null\n if (rule.entity) {\n ruleEntities = [rule.entity]\n } else if (rule.entities) {\n ruleEntities = [...rule.entities]\n } else {\n throw new Error(`Missing entity in authorization rule ${i}`)\n }\n\n for (const ruleEntity of ruleEntities) {\n const newRule = { ...rule, entity: ruleEntity, entities: undefined }\n if (!app.platformatic.entities[newRule.entity]) {\n throw new Error(`Unknown entity '${ruleEntity}' in authorization rule ${i}`)\n }\n\n if (!entityRules[ruleEntity]) {\n entityRules[ruleEntity] = []\n }\n entityRules[ruleEntity].push(newRule)\n }\n }\n\n for (const entityKey of Object.keys(app.platformatic.entities)) {\n const rules = entityRules[entityKey] || []\n const type = app.platformatic.entities[entityKey]\n\n // We have subscriptions!\n let userPropToFillForPublish\n const topicsWithoutChecks = false\n\n // mqtt\n // if (app.platformatic.mq) {\n // for (const rule of rules) {\n // const checks = rule.find?.checks\n // if (typeof checks !== 'object') {\n // topicsWithoutChecks = !!rule.find\n // continue\n // }\n // const keys = Object.keys(checks)\n // if (keys.length !== 1) {\n // throw new Error(`Subscription requires that the role \"${rule.role}\" has only one check in the find rule for entity \"${rule.entity}\"`)\n // }\n // const key = keys[0]\n\n // const val = typeof checks[key] === 'object' ? checks[key].eq : checks[key]\n // if (userPropToFillForPublish && userPropToFillForPublish.val !== val) {\n // throw new Error('Unable to configure subscriptions and authorization due to multiple check clauses in find')\n // }\n // userPropToFillForPublish = { key, val }\n // }\n // }\n\n if (userPropToFillForPublish && topicsWithoutChecks) {\n throw new Error(`Subscription for entity \"${entityKey}\" have conflictling rules across roles`)\n }\n\n // MUST set this after doing the security checks on the subscriptions\n if (adminSecret) {\n rules.push({\n role: PLT_ADMIN_ROLE,\n find: true,\n save: true,\n delete: true,\n updateMany: true\n })\n }\n\n // If we have `fields` in save rules, we need to check if all the not-nullable\n // fields are specified\n checkSaveMandatoryFieldsInRules(type, rules)\n\n function useOriginal(ctx: PlatformaticContext) {\n return !ctx\n }\n\n app.platformatic.addEntityHooks(entityKey, {\n async find(originalFind, { where, ctx, fields, ...restOpts } = {}) {\n if (useOriginal(ctx)) {\n return originalFind({ ...restOpts, where, ctx, fields })\n }\n const request = getRequestFromContext(ctx)\n const rule = await findRuleForRequestUser(ctx, rules, roleKey, anonymousRole, isRolePath)\n checkFieldsFromRule(rule.find, fields || Object.keys(app.platformatic.entities[entityKey].fields))\n where = await fromRuleToWhere(ctx, rule.find, where, request.user)\n\n return originalFind({ ...restOpts, where, ctx, fields })\n },\n async save(originalSave, { input, ctx, fields, ...restOpts }) {\n if (useOriginal(ctx)) {\n return originalSave({ ctx, input, fields, ...restOpts })\n }\n const request = getRequestFromContext(ctx)\n const rule = await findRuleForRequestUser(ctx, rules, roleKey, anonymousRole, isRolePath)\n\n if (!rule.save) {\n throw new Unauthorized()\n }\n checkFieldsFromRule(rule.save, fields)\n checkInputFromRuleFields(rule.save, input)\n\n if (rule.defaults) {\n for (const key of Object.keys(rule.defaults)) {\n const defaults = rule.defaults[key]\n if (typeof defaults === 'function') {\n input[key] = await defaults({ user: request.user, ctx, input })\n } else {\n input[key] = request.user[defaults]\n }\n }\n }\n\n const hasAllPrimaryKeys = input[type.primaryKey] !== undefined;\n const whereConditions = {}\n whereConditions[type.primaryKey] = { eq: input[type.primaryKey] }\n\n if (hasAllPrimaryKeys) {\n const where = await fromRuleToWhere(ctx, rule.save, whereConditions, request.user)\n\n const found = await type.find({\n where,\n ctx,\n fields,\n })\n\n if (found.length === 0) {\n throw new Unauthorized()\n }\n\n return originalSave({ input, ctx, fields, ...restOpts })\n }\n\n return originalSave({ input, ctx, fields, ...restOpts })\n },\n\n async insert(originalInsert, { inputs, ctx, fields, ...restOpts }) {\n if (useOriginal(ctx)) {\n return originalInsert({ inputs, ctx, fields, ...restOpts })\n }\n const request = getRequestFromContext(ctx)\n const rule = await findRuleForRequestUser(ctx, rules, roleKey, anonymousRole, isRolePath)\n\n if (!rule.save) {\n throw new Unauthorized()\n }\n\n checkFieldsFromRule(rule.save, fields)\n checkInputFromRuleFields(rule.save, inputs)\n\n /* istanbul ignore else */\n if (rule.defaults) {\n for (const input of inputs) {\n for (const key of Object.keys(rule.defaults)) {\n const defaults = rule.defaults[key]\n if (typeof defaults === 'function') {\n input[key] = await defaults({ user: request.user, ctx, input })\n } else {\n input[key] = request.user[defaults]\n }\n }\n }\n }\n\n return originalInsert({ inputs, ctx, fields, ...restOpts })\n },\n\n async delete(originalDelete, { where, ctx, fields, ...restOpts }) {\n if (useOriginal(ctx)) {\n return originalDelete({ where, ctx, fields, ...restOpts })\n }\n const request = getRequestFromContext(ctx)\n const rule = await findRuleForRequestUser(ctx, rules, roleKey, anonymousRole, isRolePath)\n\n where = await fromRuleToWhere(ctx, rule.delete, where, request.user)\n\n return originalDelete({ where, ctx, fields, ...restOpts })\n },\n\n async updateMany(originalUpdateMany, { where, ctx, fields, ...restOpts }) {\n if (useOriginal(ctx)) {\n return originalUpdateMany({ ...restOpts, where, ctx, fields })\n }\n const request = getRequestFromContext(ctx)\n const rule = await findRuleForRequestUser(ctx, rules, roleKey, anonymousRole, isRolePath)\n\n where = await fromRuleToWhere(ctx, rule.updateMany, where, request.user)\n\n return originalUpdateMany({ ...restOpts, where, ctx, fields })\n },\n })\n }\n })\n}, { name: '@albirex/platformatic-logto' });\n\nasync function fromRuleToWhere(ctx: PlatformaticContext, rule, where, user) {\n if (!rule) {\n throw new Unauthorized()\n }\n const request = getRequestFromContext(ctx)\n /* istanbul ignore next */\n where = where || {}\n\n if (typeof rule === 'object') {\n const { checks } = rule\n\n /* istanbul ignore else */\n if (checks) {\n for (const key of Object.keys(checks)) {\n const clauses = checks[key]\n if (typeof clauses === 'string') {\n // case: \"userId\": \"X-PLATFORMATIC-USER-ID\"\n where[key] = {\n eq: request.user[clauses],\n }\n } else {\n // case:\n // userId: {\n // eq: 'X-PLATFORMATIC-USER-ID'\n // }\n for (const clauseKey of Object.keys(clauses)) {\n const clause = clauses[clauseKey]\n where[key] = {\n [clauseKey]: request.user[clause],\n }\n }\n }\n }\n }\n } else if (typeof rule === 'function') {\n where = await rule({ user, ctx, where })\n }\n return where\n}\n\nexport async function findRuleForRequestUser(ctx: PlatformaticContext, rules: PlatformaticRule[], roleKey: string, anonymousRole: string, isRolePath = false) {\n const request = getRequestFromContext(ctx)\n await request.setupDBAuthorizationUser()\n const roles = getRoles(request, roleKey, anonymousRole, isRolePath)\n const rule = findRule(rules, roles)\n if (!rule) {\n ctx.reply.request.log.warn({ roles, rules }, 'no rule for roles')\n throw new Unauthorized()\n }\n ctx.reply.request.log.trace({ roles, rule }, 'found rule')\n return rule\n}\n\nexport function checkFieldsFromRule(rule, fields) {\n if (!rule) {\n throw new Unauthorized()\n }\n const { fields: fieldsFromRule } = rule\n /* istanbul ignore else */\n if (fieldsFromRule) {\n for (const field of fields) {\n if (!fieldsFromRule.includes(field)) {\n throw new UnauthorizedField(field)\n }\n }\n }\n}\n\nconst validateInputs = (inputs, fieldsFromRule) => {\n for (const input of inputs) {\n const inputFields = Object.keys(input)\n for (const inputField of inputFields) {\n if (!fieldsFromRule.includes(inputField)) {\n throw new UnauthorizedField(inputField)\n }\n }\n }\n}\n\nfunction checkInputFromRuleFields(rule, inputs) {\n const { fields: fieldsFromRule } = rule\n /* istanbul ignore else */\n if (fieldsFromRule) {\n if (!Array.isArray(inputs)) {\n // save\n validateInputs([inputs], fieldsFromRule)\n } else {\n // insert\n validateInputs(inputs, fieldsFromRule)\n }\n }\n}\n\nfunction checkSaveMandatoryFieldsInRules(type: Entity, rules) {\n // List of not nullable, not PKs field to validate save/insert when allowed fields are specified on the rule\n const mandatoryFields =\n Object.values(type.fields)\n .filter(k => (!k.isNullable && !k.primaryKey))\n .map(({ camelcase }) => (camelcase))\n\n for (const rule of rules) {\n const { entity, save } = rule\n if (save && save.fields) {\n const fields = save.fields\n for (const mField of mandatoryFields) {\n if (!fields.includes(mField)) {\n throw new MissingNotNullableError(mField, entity)\n }\n }\n }\n }\n}\n\n/**\n * Check the permissions version\n * @throws PermissionsOutdated if versions don't match\n */\nasync function checkPermissionsVersion(app: FastifyInstance, opts: PlatformaticLogtoAuthOptions, user = null) {\n\n if (!opts.enableTokenVersionCheck || !opts.redis || !app.redis || !user) {\n return;\n }\n\n const sessionVersionKey = opts.sessionVersionKey || 'version'\n const tokenVersion = user?.[sessionVersionKey];\n const userId = user?.sub;\n\n try {\n const redisKey = `permissions:version:${userId}`;\n const currentVersionStr = await app.redis.get(redisKey);\n const currentVersion = parseInt(currentVersionStr, 10);\n\n if (currentVersionStr === null) {\n await app.redis.set(redisKey, tokenVersion.toString());\n app.log.debug({ userId, version: tokenVersion }, 'Initialized permissions version in Redis');\n if (tokenVersion > 1) {\n app.log.warn({\n userId,\n tokenVersion,\n currentVersion\n }, 'Permissions version mismatch detected');\n throw new PermissionsOutdated(); \n }\n return;\n }\n\n // Compare versions\n if (currentVersion !== tokenVersion) {\n app.log.warn({\n userId,\n tokenVersion,\n currentVersion\n }, 'Permissions version mismatch detected');\n throw new PermissionsOutdated();\n }\n\n app.log.trace({ userId, version: tokenVersion }, 'Token version check passed');\n } catch (error) {\n if (error.name === 'FastifyError' && error.code === 'PLT_DB_AUTH_VERSION_OUTDATED') {\n throw error;\n }\n app.log.error({ err: error, userId }, 'Error checking token version');\n }\n}\n\nexport default platformaticLogto;\n\ndeclare module 'fastify' {\n interface FastifyInstance {\n platformaticLogTo: {\n opts: PlatformaticLogtoAuthOptions,\n rules?: PlatformaticRule[]\n }\n }\n}","'use strict'\n\nimport { PlatformaticRule } from '../index.js'\n\nfunction findRule(rules: PlatformaticRule[], roles: string[]) {\n let found = null\n for (const rule of rules) {\n for (const role of roles) {\n if (rule.role === role) {\n found = rule\n break\n }\n }\n if (found) {\n break\n }\n }\n return found\n}\n\nexport default findRule\n","'use strict'\n\nimport createError from '@fastify/error'\n\nconst ERROR_PREFIX = 'PLT_DB_AUTH'\n\nexport const Unauthorized = createError(`${ERROR_PREFIX}_UNAUTHORIZED`, 'operation not allowed', 401)\nexport const UnauthorizedField = createError(`${ERROR_PREFIX}_FIELD_UNAUTHORIZED`, 'field not allowed: %s', 401)\nexport const MissingNotNullableError = createError(`${ERROR_PREFIX}_NOT_NULLABLE_MISSING`, 'missing not nullable field: \"%s\" in save rule for entity \"%s\"')\nexport const PermissionsOutdated = createError(`${ERROR_PREFIX}_VERSION_OUTDATED`, 'Verision has been updated. Please logout and login again.', 401)\n","'use strict'\n\nimport type { FastifyInstance } from 'fastify'\n\n/**\n * Increment the permissions version\n * @param app - Fastify instance with Redis client and Logto\n * @param userId - User ID\n * @returns New version number\n */\nexport async function incrementPermissionsVersion(app: FastifyInstance, userId: string): Promise<number> {\n if (!app.logto) {\n throw new Error('Logto client not available')\n }\n\n const userResp = await app.logto.callAPI(`/api/users/${userId}`, 'GET')\n const userData = await userResp.json()\n const existingCustomData = userData.customData || {}\n const currentVersion = existingCustomData.sessionVersion || 0\n const newVersion = currentVersion + 1\n\n await app.logto.callAPI(`/api/users/${userId}`, 'PATCH', JSON.stringify({ customData: { ...existingCustomData, sessionVersion: newVersion } }))\n\n if (app.redis) {\n const redisKey = `permissions:version:${userId}`\n await app.redis.set(redisKey, newVersion.toString())\n }\n\n return newVersion\n}\n\n/**\n * Delete the permissions version for a user\n * @param app - Fastify instance with Redis client\n * @param userId - User ID\n */\nexport async function deletePermissionsVersion(app: FastifyInstance, userId: string): Promise<void> {\n if (!app.redis) {\n throw new Error('Redis client not available')\n }\n\n const redisKey = `permissions:version:${userId}`\n await app.redis.del(redisKey)\n}\n\n","'use strict'\n\nexport function getRequestFromContext (ctx) {\n if (ctx && !ctx.reply) {\n throw new Error('Missing reply in context. You should call this function with { ctx: { reply }}')\n }\n return ctx.reply.request\n}\n\nexport function getRoles (request, roleKey, anonymousRole, isRolePath = false) {\n let output = []\n const user = request.user\n if (!user) {\n output.push(anonymousRole)\n return output\n }\n\n let rolesRaw\n if (isRolePath) {\n const roleKeys = roleKey.split('.')\n rolesRaw = user\n for (const key of roleKeys) {\n rolesRaw = rolesRaw[key]\n }\n } else {\n rolesRaw = user[roleKey]\n }\n\n if (typeof rolesRaw === 'string') {\n output = rolesRaw.split(',')\n } else if (Array.isArray(rolesRaw)) {\n output = rolesRaw\n }\n if (output.length === 0) {\n output.push(anonymousRole)\n }\n\n return output\n}\n"],"mappings":"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","names":["createError","import_fastify_logto","fp","fastifyRedis","fastifyLogto"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/utils/find-rule.ts","../src/utils/errors.ts","../src/utils/permissions-version.ts","../src/utils/utils.ts"],"sourcesContent":["import fp from 'fastify-plugin'\nimport * as fastifyUser from 'fastify-user'\n\nimport findRule from './utils/find-rule.js'\nimport { Unauthorized, UnauthorizedField, MissingNotNullableError, PermissionsOutdated } from './utils/errors.js'\nimport fastifyLogto from '@albirex/fastify-logto';\nimport fastifyRedis from '@fastify/redis';\nimport { FastifyInstance, FastifyPluginAsync } from 'fastify'\nimport type { FastifyUserPluginOptions } from 'fastify-user';\nimport type { Entity, PlatformaticContext } from '@platformatic/sql-mapper'\n\nexport { fastifyLogto } from '@albirex/fastify-logto';\nexport { incrementPermissionsVersion, deletePermissionsVersion } from './utils/permissions-version.js';\n\nimport { getRequestFromContext, getRoles, getScopes } from './utils/utils.js'\nexport { getRequestFromContext, getRoles } from './utils/utils.js'\n\nconst PLT_ADMIN_ROLE = 'platformatic-admin'\n\nexport type EntityActions = 'find' | 'save' | 'insert' | 'updateMany' | 'delete';\n\nexport type PlatformaticRule = {\n role: string;\n entity?: string;\n entities?: string[];\n defaults?: Record<string, string>;\n checks?: boolean;\n find?: boolean;\n save?: boolean;\n delete?: boolean;\n [action: string]: unknown;\n};\n\nexport type RedisConfig = {\n host?: string;\n port?: number;\n password?: string;\n username?: string;\n db?: number;\n};\n\nexport type PlatformaticLogToRoleBasedAuthOptions = {\n logtoBaseUrl?: string;\n logtoAppId?: string;\n logtoAppSecret?: string;\n rolePath?: string;\n roleKey?: string;\n userPath?: string;\n userKey?: string;\n}\n\nexport type PlatformaticLogToScopeBasedAuthOptions = {\n scopesPath?: string;\n scopesKey?: string;\n}\n\nexport type PlatformaticLogtoAuthOptions = {\n adminSecret?: string;\n roleBasedAuth?: PlatformaticLogToRoleBasedAuthOptions,\n scopeBasedAuth?: PlatformaticLogToScopeBasedAuthOptions,\n checks?: boolean;\n defaults?: boolean;\n anonymousRole?: string;\n allowAnonymous?: boolean;\n jwtPlugin: FastifyUserPluginOptions;\n redis?: RedisConfig;\n enableTokenVersionCheck?: boolean;\n sessionVersionKey?: string;\n};\n\nexport const platformaticLogto: FastifyPluginAsync<PlatformaticLogtoAuthOptions> = fp(async (app: FastifyInstance, opts: PlatformaticLogtoAuthOptions) => {\n app.decorate('platformaticLogTo', {\n opts\n })\n\n // Register Redis if configured\n if (opts.redis) {\n await app.register(fastifyRedis, {\n host: opts.redis.host || '127.0.0.1',\n port: opts.redis.port || 6379,\n password: opts.redis.password,\n username: opts.redis.username,\n db: opts.redis.db || 0,\n });\n app.log.info('Redis client registered for permissions version check');\n }\n\n if (opts.roleBasedAuth) {\n app.register(fastifyLogto, {\n endpoint: opts.roleBasedAuth.logtoBaseUrl || 'https://auth.example.com',\n appId: opts.roleBasedAuth.logtoAppId || 'your-app-id',\n appSecret: opts.roleBasedAuth.logtoAppSecret || 'your-app-secret',\n });\n }\n\n await app.register(fastifyUser as unknown as FastifyPluginAsync, opts.jwtPlugin);\n\n const roleKey = opts.roleBasedAuth?.rolePath || opts.roleBasedAuth?.roleKey || 'X-PLATFORMATIC-ROLE';\n const adminSecret = opts.adminSecret\n const isRolePath = !!opts.roleBasedAuth?.rolePath // if `true` the role is intepreted as path like `user.role`\n const anonymousRole = opts.anonymousRole || 'anonymous'\n\n app.decorateRequest('setupDBAuthorizationUser', setupUser)\n\n async function setupUser() {\n // if (!adminSecret) {\n await this.extractUser()\n // }\n\n let forceAdminRole = false\n if (adminSecret && this.headers['x-platformatic-admin-secret'] === adminSecret) {\n if (opts.jwtPlugin.jwt) {\n forceAdminRole = true\n } else {\n this.log.info('admin secret is valid')\n this.user = new Proxy(this.headers, {\n get: (target, key) => {\n let value;\n if (!target[key.toString()]) {\n const newKey = key.toString().toLowerCase()\n value = target[newKey]\n } else {\n value = target[key.toString()]\n }\n\n if (!value && key.toString().toLowerCase() === roleKey.toLowerCase()) {\n value = PLT_ADMIN_ROLE\n }\n return value\n },\n })\n }\n } else {\n await checkPermissionsVersion(app, opts, this.user)\n }\n\n if (forceAdminRole) {\n // We replace just the role in `request.user`, all the rest is untouched\n this.user = {\n // ...request.user,\n [roleKey]: this.headers['x-platformatic-role'] ? [this.headers['x-platformatic-role']] : [PLT_ADMIN_ROLE]\n }\n }\n }\n\n async function roleBasedAuth() {\n const userKey = opts.roleBasedAuth.userKey || opts.roleBasedAuth.userPath || 'X-PLATFORMATIC-USER-ID';\n\n async function composeLogToRules() {\n const rolesResp = await app.logto.callAPI('/api/roles?type=User', 'GET');\n\n if (!rolesResp.ok) {\n throw rolesResp;\n }\n\n const roles = await rolesResp.json();\n const rules: PlatformaticRule[] = [{\n role: anonymousRole,\n entities: Object.keys(app.platformatic.entities),\n find: opts.allowAnonymous,\n save: opts.allowAnonymous,\n delete: opts.allowAnonymous,\n }];\n\n for (const role of roles) {\n const scopesResp = await app.logto.callAPI(`/api/roles/${role.id}/scopes`, 'GET');\n\n if (!scopesResp.ok) {\n throw scopesResp;\n }\n\n const scopes = await scopesResp.json();\n\n for (const scope of scopes) {\n const roleName = role.name;\n // eslint-disable-next-line prefer-const\n let [scopeAction, entity] = scope.name.split(':');\n\n if (!app.platformatic.entities[entity]) {\n app.log.debug(`Unknown entity '${entity}' in authorization rule`)\n continue;\n }\n\n switch (scopeAction) {\n case 'create':\n scopeAction = 'save'\n break;\n case 'read':\n scopeAction = 'find'\n break;\n case 'update':\n scopeAction = 'updateMany'\n break;\n }\n\n const checkExists = rules.find(r => r.role === roleName && r.entity === entity);\n if (checkExists) {\n if (opts.checks) {\n checkExists[scopeAction] = {\n checks: {\n userId: userKey\n }\n };\n } else {\n checkExists[scopeAction] = true;\n }\n } else {\n const newRule: PlatformaticRule = {\n role: roleName,\n entity,\n };\n\n if (opts.checks) {\n newRule[scopeAction] = {\n checks: {\n userId: userKey\n }\n };\n } else {\n newRule[scopeAction] = true;\n }\n\n if (opts.defaults) {\n newRule.defaults = {\n userId: userKey\n };\n }\n\n rules.push(newRule);\n }\n }\n }\n\n const logRules = rules.reduce((prev, curr) => {\n (prev[curr['role']] ??= []).push(curr);\n return prev;\n }, {})\n\n for (const key in logRules) {\n app.log.info(`Rules set for role ${key}`);\n\n for (const element of logRules[key]) {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { entity, entities, role, ...other } = element;\n app.log.info(`\\t${entity ?? entities.join(',')}: ${JSON.stringify(other)}`);\n }\n }\n\n const missingEntities = Object.keys(app.platformatic.entities).filter((e) => !rules.map((r) => r.entity).includes(e));\n\n if (missingEntities.length) {\n app.log.warn(`Missing rules for entities: ${missingEntities.join(', ')}`);\n }\n\n app.log.debug('LogTo calculated rules');\n app.log.debug(rules);\n return rules;\n }\n\n const logToRules = await composeLogToRules();\n\n app.platformaticLogTo.rules = logToRules;\n\n // TODO validate that there is at most a rule for a given role\n const entityRules = {};\n for (let i = 0; i < logToRules.length; i++) {\n const rule = logToRules[i]\n\n let ruleEntities = null\n if (rule.entity) {\n ruleEntities = [rule.entity]\n } else if (rule.entities) {\n ruleEntities = [...rule.entities]\n } else {\n throw new Error(`Missing entity in authorization rule ${i}`)\n }\n\n for (const ruleEntity of ruleEntities) {\n const newRule = { ...rule, entity: ruleEntity, entities: undefined }\n if (!app.platformatic.entities[newRule.entity]) {\n throw new Error(`Unknown entity '${ruleEntity}' in authorization rule ${i}`)\n }\n\n if (!entityRules[ruleEntity]) {\n entityRules[ruleEntity] = []\n }\n entityRules[ruleEntity].push(newRule)\n }\n }\n\n for (const entityKey of Object.keys(app.platformatic.entities)) {\n const rules = entityRules[entityKey] || []\n const type = app.platformatic.entities[entityKey]\n\n // We have subscriptions!\n let userPropToFillForPublish\n const topicsWithoutChecks = false\n\n if (userPropToFillForPublish && topicsWithoutChecks) {\n throw new Error(`Subscription for entity \"${entityKey}\" have conflictling rules across roles`)\n }\n\n // MUST set this after doing the security checks on the subscriptions\n if (adminSecret) {\n rules.push({\n role: PLT_ADMIN_ROLE,\n find: true,\n save: true,\n delete: true,\n updateMany: true\n })\n }\n\n // If we have `fields` in save rules, we need to check if all the not-nullable\n // fields are specified\n checkSaveMandatoryFieldsInRules(type, rules)\n\n function useOriginal(ctx: PlatformaticContext) {\n return !ctx\n }\n\n app.platformatic.addEntityHooks(entityKey, {\n async find(originalFind, { where, ctx, fields, ...restOpts } = {}) {\n if (useOriginal(ctx)) {\n return originalFind({ ...restOpts, where, ctx, fields })\n }\n const request = getRequestFromContext(ctx)\n const rule = await findRuleForRequestUser(ctx, rules, roleKey, anonymousRole, isRolePath)\n checkFieldsFromRule(rule.find, fields || Object.keys(app.platformatic.entities[entityKey].fields))\n where = await fromRuleToWhere(ctx, rule.find, where, request.user)\n\n return originalFind({ ...restOpts, where, ctx, fields })\n },\n async save(originalSave, { input, ctx, fields, ...restOpts }) {\n if (useOriginal(ctx)) {\n return originalSave({ ctx, input, fields, ...restOpts })\n }\n const request = getRequestFromContext(ctx)\n const rule = await findRuleForRequestUser(ctx, rules, roleKey, anonymousRole, isRolePath)\n\n if (!rule.save) {\n throw new Unauthorized()\n }\n checkFieldsFromRule(rule.save, fields)\n checkInputFromRuleFields(rule.save, input)\n\n if (rule.defaults) {\n for (const key of Object.keys(rule.defaults)) {\n const defaults = rule.defaults[key]\n if (typeof defaults === 'function') {\n input[key] = await defaults({ user: request.user, ctx, input })\n } else {\n input[key] = request.user[defaults]\n }\n }\n }\n\n const hasAllPrimaryKeys = input[type.primaryKey] !== undefined;\n const whereConditions = {}\n whereConditions[type.primaryKey] = { eq: input[type.primaryKey] }\n\n if (hasAllPrimaryKeys) {\n const where = await fromRuleToWhere(ctx, rule.save, whereConditions, request.user)\n\n const found = await type.find({\n where,\n ctx,\n fields,\n })\n\n if (found.length === 0) {\n throw new Unauthorized()\n }\n\n return originalSave({ input, ctx, fields, ...restOpts })\n }\n\n return originalSave({ input, ctx, fields, ...restOpts })\n },\n\n async insert(originalInsert, { inputs, ctx, fields, ...restOpts }) {\n if (useOriginal(ctx)) {\n return originalInsert({ inputs, ctx, fields, ...restOpts })\n }\n const request = getRequestFromContext(ctx)\n const rule = await findRuleForRequestUser(ctx, rules, roleKey, anonymousRole, isRolePath)\n\n if (!rule.save) {\n throw new Unauthorized()\n }\n\n checkFieldsFromRule(rule.save, fields)\n checkInputFromRuleFields(rule.save, inputs)\n\n /* istanbul ignore else */\n if (rule.defaults) {\n for (const input of inputs) {\n for (const key of Object.keys(rule.defaults)) {\n const defaults = rule.defaults[key]\n if (typeof defaults === 'function') {\n input[key] = await defaults({ user: request.user, ctx, input })\n } else {\n input[key] = request.user[defaults]\n }\n }\n }\n }\n\n return originalInsert({ inputs, ctx, fields, ...restOpts })\n },\n\n async delete(originalDelete, { where, ctx, fields, ...restOpts }) {\n if (useOriginal(ctx)) {\n return originalDelete({ where, ctx, fields, ...restOpts })\n }\n const request = getRequestFromContext(ctx)\n const rule = await findRuleForRequestUser(ctx, rules, roleKey, anonymousRole, isRolePath)\n\n where = await fromRuleToWhere(ctx, rule.delete, where, request.user)\n\n return originalDelete({ where, ctx, fields, ...restOpts })\n },\n\n async updateMany(originalUpdateMany, { where, ctx, fields, ...restOpts }) {\n if (useOriginal(ctx)) {\n return originalUpdateMany({ ...restOpts, where, ctx, fields })\n }\n const request = getRequestFromContext(ctx)\n const rule = await findRuleForRequestUser(ctx, rules, roleKey, anonymousRole, isRolePath)\n\n where = await fromRuleToWhere(ctx, rule.updateMany, where, request.user)\n\n return originalUpdateMany({ ...restOpts, where, ctx, fields })\n },\n })\n }\n }\n\n async function scopeBasedAuth() {\n const scopesKey = opts.scopeBasedAuth.scopesPath || opts.scopeBasedAuth.scopesKey || 'X-PLATFORMATIC-SCOPES'\n for (const entityKey of Object.keys(app.platformatic.entities)) {\n // const type = app.platformatic.entities[entityKey]\n\n // We have subscriptions!\n let userPropToFillForPublish\n const topicsWithoutChecks = false\n\n if (userPropToFillForPublish && topicsWithoutChecks) {\n throw new Error(`Subscription for entity \"${entityKey}\" have conflictling rules across roles`)\n }\n\n function useOriginal(ctx: PlatformaticContext) {\n return !ctx\n }\n\n app.platformatic.addEntityHooks(entityKey, {\n async find(originalFind, { where, ctx, fields, ...restOpts } = {}) {\n if (useOriginal(ctx)) {\n return originalFind({ ...restOpts, where, ctx, fields })\n }\n await findScopeForRequestUser(ctx, entityKey, 'find', scopesKey, anonymousRole, isRolePath)\n\n // checkFieldsFromRule(scope.find, fields || Object.keys(app.platformatic.entities[entityKey].fields))\n // where = await fromRuleToWhere(ctx, scope.find, where, request.user)\n\n return originalFind({ ...restOpts, where, ctx, fields })\n },\n async save(originalSave, { input, ctx, fields, ...restOpts }) {\n if (useOriginal(ctx)) {\n return originalSave({ ctx, input, fields, ...restOpts })\n }\n await findScopeForRequestUser(ctx, entityKey, 'save', scopesKey, anonymousRole, isRolePath)\n\n // checkFieldsFromRule(rule.save, fields)\n // checkInputFromRuleFields(rule.save, input)\n\n // if (rule.defaults) {\n // for (const key of Object.keys(rule.defaults)) {\n // const defaults = rule.defaults[key]\n // if (typeof defaults === 'function') {\n // input[key] = await defaults({ user: request.user, ctx, input })\n // } else {\n // input[key] = request.user[defaults]\n // }\n // }\n // }\n\n // const hasAllPrimaryKeys = input[type.primaryKey] !== undefined;\n // const whereConditions = {}\n // whereConditions[type.primaryKey] = { eq: input[type.primaryKey] }\n\n // if (hasAllPrimaryKeys) {\n // const where = await fromRuleToWhere(ctx, rule.save, whereConditions, request.user)\n\n // const found = await type.find({\n // where,\n // ctx,\n // fields,\n // })\n\n // if (found.length === 0) {\n // throw new Unauthorized()\n // }\n\n // return originalSave({ input, ctx, fields, ...restOpts })\n // }\n\n return originalSave({ input, ctx, fields, ...restOpts })\n },\n\n async insert(originalInsert, { inputs, ctx, fields, ...restOpts }) {\n if (useOriginal(ctx)) {\n return originalInsert({ inputs, ctx, fields, ...restOpts })\n }\n await findScopeForRequestUser(ctx, entityKey, 'insert', scopesKey, anonymousRole, isRolePath)\n\n // checkFieldsFromRule(rule.save, fields)\n // checkInputFromRuleFields(rule.save, inputs)\n\n /* istanbul ignore else */\n // if (rule.defaults) {\n // for (const input of inputs) {\n // for (const key of Object.keys(rule.defaults)) {\n // const defaults = rule.defaults[key]\n // if (typeof defaults === 'function') {\n // input[key] = await defaults({ user: request.user, ctx, input })\n // } else {\n // input[key] = request.user[defaults]\n // }\n // }\n // }\n // }\n\n return originalInsert({ inputs, ctx, fields, ...restOpts })\n },\n\n async delete(originalDelete, { where, ctx, fields, ...restOpts }) {\n if (useOriginal(ctx)) {\n return originalDelete({ where, ctx, fields, ...restOpts })\n }\n await findScopeForRequestUser(ctx, entityKey, 'delete', scopesKey, anonymousRole, isRolePath)\n\n // where = await fromRuleToWhere(ctx, rule.delete, where, request.user)\n\n return originalDelete({ where, ctx, fields, ...restOpts })\n },\n\n async updateMany(originalUpdateMany, { where, ctx, fields, ...restOpts }) {\n if (useOriginal(ctx)) {\n return originalUpdateMany({ ...restOpts, where, ctx, fields })\n }\n await findScopeForRequestUser(ctx, entityKey, 'updateMany', scopesKey, anonymousRole, isRolePath)\n\n // where = await fromRuleToWhere(ctx, rule.updateMany, where, request.user)\n\n return originalUpdateMany({ ...restOpts, where, ctx, fields })\n },\n })\n }\n }\n\n app.addHook('onReady', async function () {\n if (opts.roleBasedAuth) {\n await roleBasedAuth();\n }\n\n if (opts.scopeBasedAuth) {\n await scopeBasedAuth();\n }\n })\n}, { name: '@albirex/platformatic-logto' });\n\nasync function fromRuleToWhere(ctx: PlatformaticContext, rule, where, user) {\n if (!rule) {\n throw new Unauthorized()\n }\n const request = getRequestFromContext(ctx)\n /* istanbul ignore next */\n where = where || {}\n\n if (typeof rule === 'object') {\n const { checks } = rule\n\n /* istanbul ignore else */\n if (checks) {\n for (const key of Object.keys(checks)) {\n const clauses = checks[key]\n if (typeof clauses === 'string') {\n // case: \"userId\": \"X-PLATFORMATIC-USER-ID\"\n where[key] = {\n eq: request.user[clauses],\n }\n } else {\n // case:\n // userId: {\n // eq: 'X-PLATFORMATIC-USER-ID'\n // }\n for (const clauseKey of Object.keys(clauses)) {\n const clause = clauses[clauseKey]\n where[key] = {\n [clauseKey]: request.user[clause],\n }\n }\n }\n }\n }\n } else if (typeof rule === 'function') {\n where = await rule({ user, ctx, where })\n }\n return where\n}\n\nexport async function findRuleForRequestUser(ctx: PlatformaticContext, rules: PlatformaticRule[], roleKey: string, anonymousRole: string, isRolePath = false) {\n const request = getRequestFromContext(ctx)\n await request.setupDBAuthorizationUser()\n const roles = getRoles(request, roleKey, anonymousRole, isRolePath)\n const rule = findRule(rules, roles)\n if (!rule) {\n ctx.reply.request.log.warn({ roles, rules }, 'no rule for roles')\n throw new Unauthorized()\n }\n ctx.reply.request.log.trace({ roles, rule }, 'found rule')\n return rule\n}\n\nexport async function findScopeForRequestUser(ctx: PlatformaticContext, entityKey: string, action: EntityActions, scopesKey: string, anonymousRole: string, isScopePath = false) {\n const request = getRequestFromContext(ctx)\n await request.setupDBAuthorizationUser()\n const scopes = getScopes(request, scopesKey, anonymousRole, isScopePath)\n const scope = scopes.find(s => {\n if (action !== 'find') {\n return s === `${action}:${entityKey}`\n }\n else {\n return s === `${action}:${entityKey}` || s === `read:${entityKey}`\n }\n });\n\n if (!scope) {\n ctx.reply.request.log.warn('no scope found')\n throw new Unauthorized()\n }\n ctx.reply.request.log.trace({ scopes, scope }, 'found scope')\n return scope\n}\n\nexport function checkFieldsFromRule(rule, fields) {\n if (!rule) {\n throw new Unauthorized()\n }\n const { fields: fieldsFromRule } = rule\n /* istanbul ignore else */\n if (fieldsFromRule) {\n for (const field of fields) {\n if (!fieldsFromRule.includes(field)) {\n throw new UnauthorizedField(field)\n }\n }\n }\n}\n\nconst validateInputs = (inputs, fieldsFromRule) => {\n for (const input of inputs) {\n const inputFields = Object.keys(input)\n for (const inputField of inputFields) {\n if (!fieldsFromRule.includes(inputField)) {\n throw new UnauthorizedField(inputField)\n }\n }\n }\n}\n\nfunction checkInputFromRuleFields(rule, inputs) {\n const { fields: fieldsFromRule } = rule\n /* istanbul ignore else */\n if (fieldsFromRule) {\n if (!Array.isArray(inputs)) {\n // save\n validateInputs([inputs], fieldsFromRule)\n } else {\n // insert\n validateInputs(inputs, fieldsFromRule)\n }\n }\n}\n\nfunction checkSaveMandatoryFieldsInRules(type: Entity, rules) {\n // List of not nullable, not PKs field to validate save/insert when allowed fields are specified on the rule\n const mandatoryFields =\n Object.values(type.fields)\n .filter(k => (!k.isNullable && !k.primaryKey))\n .map(({ camelcase }) => (camelcase))\n\n for (const rule of rules) {\n const { entity, save } = rule\n if (save && save.fields) {\n const fields = save.fields\n for (const mField of mandatoryFields) {\n if (!fields.includes(mField)) {\n throw new MissingNotNullableError(mField, entity)\n }\n }\n }\n }\n}\n\n/**\n * Check the permissions version\n * @throws PermissionsOutdated if versions don't match\n */\nasync function checkPermissionsVersion(app: FastifyInstance, opts: PlatformaticLogtoAuthOptions, user = null) {\n\n if (!opts.enableTokenVersionCheck || !opts.redis || !app.redis || !user) {\n return;\n }\n\n const sessionVersionKey = opts.sessionVersionKey || 'version'\n const tokenVersion = user?.[sessionVersionKey];\n const userId = user?.sub;\n\n try {\n const redisKey = `permissions:version:${userId}`;\n const currentVersionStr = await app.redis.get(redisKey);\n const currentVersion = parseInt(currentVersionStr, 10);\n\n if (currentVersionStr === null) {\n await app.redis.set(redisKey, tokenVersion.toString());\n app.log.debug({ userId, version: tokenVersion }, 'Initialized permissions version in Redis');\n if (tokenVersion > 1) {\n app.log.warn({\n userId,\n tokenVersion,\n currentVersion\n }, 'Permissions version mismatch detected');\n throw new PermissionsOutdated();\n }\n return;\n }\n\n // Compare versions\n if (currentVersion !== tokenVersion) {\n app.log.warn({\n userId,\n tokenVersion,\n currentVersion\n }, 'Permissions version mismatch detected');\n throw new PermissionsOutdated();\n }\n\n app.log.trace({ userId, version: tokenVersion }, 'Token version check passed');\n } catch (error) {\n if (error.name === 'FastifyError' && error.code === 'PLT_DB_AUTH_VERSION_OUTDATED') {\n throw error;\n }\n app.log.error({ err: error, userId }, 'Error checking token version');\n }\n}\n\nexport default platformaticLogto;\n\ndeclare module 'fastify' {\n interface FastifyInstance {\n platformaticLogTo: {\n opts: PlatformaticLogtoAuthOptions,\n rules?: PlatformaticRule[]\n }\n }\n}","'use strict'\n\nimport { PlatformaticRule } from '../index.js'\n\nfunction findRule(rules: PlatformaticRule[], roles: string[]) {\n let found = null\n for (const rule of rules) {\n for (const role of roles) {\n if (rule.role === role) {\n found = rule\n break\n }\n }\n if (found) {\n break\n }\n }\n return found\n}\n\nexport default findRule\n","'use strict'\n\nimport createError from '@fastify/error'\n\nconst ERROR_PREFIX = 'PLT_DB_AUTH'\n\nexport const Unauthorized = createError(`${ERROR_PREFIX}_UNAUTHORIZED`, 'operation not allowed', 401)\nexport const UnauthorizedField = createError(`${ERROR_PREFIX}_FIELD_UNAUTHORIZED`, 'field not allowed: %s', 401)\nexport const MissingNotNullableError = createError(`${ERROR_PREFIX}_NOT_NULLABLE_MISSING`, 'missing not nullable field: \"%s\" in save rule for entity \"%s\"')\nexport const PermissionsOutdated = createError(`${ERROR_PREFIX}_VERSION_OUTDATED`, 'Verision has been updated. Please logout and login again.', 403)\n","'use strict'\n\nimport type { FastifyInstance } from 'fastify'\n\n/**\n * Increment the permissions version\n * @param app - Fastify instance with Redis client and Logto\n * @param userId - User ID\n * @returns New version number\n */\nexport async function incrementPermissionsVersion(app: FastifyInstance, userId: string): Promise<number> {\n if (!app.logto) {\n throw new Error('Logto client not available')\n }\n\n const userResp = await app.logto.callAPI(`/api/users/${userId}`, 'GET')\n const userData = await userResp.json()\n const existingCustomData = userData.customData || {}\n const currentVersion = existingCustomData.sessionVersion || 0\n const newVersion = currentVersion + 1\n\n await app.logto.callAPI(`/api/users/${userId}`, 'PATCH', JSON.stringify({ customData: { ...existingCustomData, sessionVersion: newVersion } }))\n\n if (app.redis) {\n const redisKey = `permissions:version:${userId}`\n await app.redis.set(redisKey, newVersion.toString())\n }\n\n return newVersion\n}\n\n/**\n * Delete the permissions version for a user\n * @param app - Fastify instance with Redis client\n * @param userId - User ID\n */\nexport async function deletePermissionsVersion(app: FastifyInstance, userId: string): Promise<void> {\n if (!app.redis) {\n throw new Error('Redis client not available')\n }\n\n const redisKey = `permissions:version:${userId}`\n await app.redis.del(redisKey)\n}\n\n","'use strict'\n\nexport function getRequestFromContext (ctx) {\n if (ctx && !ctx.reply) {\n throw new Error('Missing reply in context. You should call this function with { ctx: { reply }}')\n }\n return ctx.reply.request\n}\n\nexport function getRoles (request, roleKey, anonymousRole, isRolePath = false) {\n let output = []\n const user = request.user\n if (!user) {\n output.push(anonymousRole)\n return output\n }\n\n let rolesRaw\n if (isRolePath) {\n const roleKeys = roleKey.split('.')\n rolesRaw = user\n for (const key of roleKeys) {\n rolesRaw = rolesRaw[key]\n }\n } else {\n rolesRaw = user[roleKey]\n }\n\n if (typeof rolesRaw === 'string') {\n output = rolesRaw.split(',')\n } else if (Array.isArray(rolesRaw)) {\n output = rolesRaw\n }\n if (output.length === 0) {\n output.push(anonymousRole)\n }\n\n return output\n}\n\nexport function getScopes(request, scopesKey, anonymousRole, isScopePath = false) {\n let output = []\n const user = request.user\n if (!user) {\n output.push(anonymousRole)\n return output\n }\n\n let scopesRaw\n if (isScopePath) {\n const roleKeys = scopesKey.split('.')\n scopesRaw = user\n for (const key of roleKeys) {\n scopesRaw = scopesRaw[key]\n }\n } else {\n scopesRaw = user[scopesKey]\n }\n\n if (typeof scopesRaw === 'string') {\n output = scopesRaw.split(' ')\n } else if (Array.isArray(scopesRaw)) {\n output = scopesRaw\n }\n if (output.length === 0) {\n output.push(anonymousRole)\n }\n\n return output\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,4BAAe;AACf,kBAA6B;;;ACG7B,SAAS,SAAS,OAA2B,OAAiB;AAC5D,MAAI,QAAQ;AACZ,aAAW,QAAQ,OAAO;AACxB,eAAW,QAAQ,OAAO;AACxB,UAAI,KAAK,SAAS,MAAM;AACtB,gBAAQ;AACR;AAAA,MACF;AAAA,IACF;AACA,QAAI,OAAO;AACT;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAEA,IAAO,oBAAQ;;;AClBf,mBAAwB;AAExB,IAAM,eAAe;AAEd,IAAM,mBAAe,aAAAA,SAAY,GAAG,YAAY,iBAAiB,yBAAyB,GAAG;AAC7F,IAAM,wBAAoB,aAAAA,SAAY,GAAG,YAAY,uBAAuB,yBAAyB,GAAG;AACxG,IAAM,8BAA0B,aAAAA,SAAY,GAAG,YAAY,yBAAyB,+DAA+D;AACnJ,IAAM,0BAAsB,aAAAA,SAAY,GAAG,YAAY,qBAAqB,6DAA6D,GAAG;;;AFJnJ,2BAAyB;AACzB,mBAAyB;AAKzB,IAAAC,wBAA6B;;;AGD7B,eAAsB,4BAA4B,KAAsB,QAAiC;AACrG,MAAI,CAAC,IAAI,OAAO;AACZ,UAAM,IAAI,MAAM,4BAA4B;AAAA,EAChD;AAEA,QAAM,WAAW,MAAM,IAAI,MAAM,QAAQ,cAAc,MAAM,IAAI,KAAK;AACtE,QAAM,WAAW,MAAM,SAAS,KAAK;AACrC,QAAM,qBAAqB,SAAS,cAAc,CAAC;AACnD,QAAM,iBAAiB,mBAAmB,kBAAkB;AAC5D,QAAM,aAAa,iBAAiB;AAEpC,QAAM,IAAI,MAAM,QAAQ,cAAc,MAAM,IAAI,SAAS,KAAK,UAAU,EAAE,YAAY,EAAE,GAAG,oBAAoB,gBAAgB,WAAW,EAAE,CAAC,CAAC;AAE9I,MAAI,IAAI,OAAO;AACX,UAAM,WAAW,uBAAuB,MAAM;AAC9C,UAAM,IAAI,MAAM,IAAI,UAAU,WAAW,SAAS,CAAC;AAAA,EACvD;AAEA,SAAO;AACX;AAOA,eAAsB,yBAAyB,KAAsB,QAA+B;AAChG,MAAI,CAAC,IAAI,OAAO;AACZ,UAAM,IAAI,MAAM,4BAA4B;AAAA,EAChD;AAEA,QAAM,WAAW,uBAAuB,MAAM;AAC9C,QAAM,IAAI,MAAM,IAAI,QAAQ;AAChC;;;ACzCO,SAAS,sBAAuB,KAAK;AAC1C,MAAI,OAAO,CAAC,IAAI,OAAO;AACrB,UAAM,IAAI,MAAM,gFAAgF;AAAA,EAClG;AACA,SAAO,IAAI,MAAM;AACnB;AAEO,SAAS,SAAU,SAAS,SAAS,eAAe,aAAa,OAAO;AAC7E,MAAI,SAAS,CAAC;AACd,QAAM,OAAO,QAAQ;AACrB,MAAI,CAAC,MAAM;AACT,WAAO,KAAK,aAAa;AACzB,WAAO;AAAA,EACT;AAEA,MAAI;AACJ,MAAI,YAAY;AACd,UAAM,WAAW,QAAQ,MAAM,GAAG;AAClC,eAAW;AACX,eAAW,OAAO,UAAU;AAC1B,iBAAW,SAAS,GAAG;AAAA,IACzB;AAAA,EACF,OAAO;AACL,eAAW,KAAK,OAAO;AAAA,EACzB;AAEA,MAAI,OAAO,aAAa,UAAU;AAChC,aAAS,SAAS,MAAM,GAAG;AAAA,EAC7B,WAAW,MAAM,QAAQ,QAAQ,GAAG;AAClC,aAAS;AAAA,EACX;AACA,MAAI,OAAO,WAAW,GAAG;AACvB,WAAO,KAAK,aAAa;AAAA,EAC3B;AAEA,SAAO;AACT;AAEO,SAAS,UAAU,SAAS,WAAW,eAAe,cAAc,OAAO;AAChF,MAAI,SAAS,CAAC;AACd,QAAM,OAAO,QAAQ;AACrB,MAAI,CAAC,MAAM;AACT,WAAO,KAAK,aAAa;AACzB,WAAO;AAAA,EACT;AAEA,MAAI;AACJ,MAAI,aAAa;AACf,UAAM,WAAW,UAAU,MAAM,GAAG;AACpC,gBAAY;AACZ,eAAW,OAAO,UAAU;AAC1B,kBAAY,UAAU,GAAG;AAAA,IAC3B;AAAA,EACF,OAAO;AACL,gBAAY,KAAK,SAAS;AAAA,EAC5B;AAEA,MAAI,OAAO,cAAc,UAAU;AACjC,aAAS,UAAU,MAAM,GAAG;AAAA,EAC9B,WAAW,MAAM,QAAQ,SAAS,GAAG;AACnC,aAAS;AAAA,EACX;AACA,MAAI,OAAO,WAAW,GAAG;AACvB,WAAO,KAAK,aAAa;AAAA,EAC3B;AAEA,SAAO;AACT;;;AJpDA,IAAM,iBAAiB;AAqDhB,IAAM,wBAAsE,sBAAAC,SAAG,OAAO,KAAsB,SAAuC;AACtJ,MAAI,SAAS,qBAAqB;AAAA,IAC9B;AAAA,EACJ,CAAC;AAGD,MAAI,KAAK,OAAO;AACZ,UAAM,IAAI,SAAS,aAAAC,SAAc;AAAA,MAC7B,MAAM,KAAK,MAAM,QAAQ;AAAA,MACzB,MAAM,KAAK,MAAM,QAAQ;AAAA,MACzB,UAAU,KAAK,MAAM;AAAA,MACrB,UAAU,KAAK,MAAM;AAAA,MACrB,IAAI,KAAK,MAAM,MAAM;AAAA,IACzB,CAAC;AACD,QAAI,IAAI,KAAK,uDAAuD;AAAA,EACxE;AAEA,MAAI,KAAK,eAAe;AACpB,QAAI,SAAS,qBAAAC,SAAc;AAAA,MACvB,UAAU,KAAK,cAAc,gBAAgB;AAAA,MAC7C,OAAO,KAAK,cAAc,cAAc;AAAA,MACxC,WAAW,KAAK,cAAc,kBAAkB;AAAA,IACpD,CAAC;AAAA,EACL;AAEA,QAAM,IAAI,SAAS,aAA8C,KAAK,SAAS;AAE/E,QAAM,UAAU,KAAK,eAAe,YAAY,KAAK,eAAe,WAAW;AAC/E,QAAM,cAAc,KAAK;AACzB,QAAM,aAAa,CAAC,CAAC,KAAK,eAAe;AACzC,QAAM,gBAAgB,KAAK,iBAAiB;AAE5C,MAAI,gBAAgB,4BAA4B,SAAS;AAEzD,iBAAe,YAAY;AAEvB,UAAM,KAAK,YAAY;AAGvB,QAAI,iBAAiB;AACrB,QAAI,eAAe,KAAK,QAAQ,6BAA6B,MAAM,aAAa;AAC5E,UAAI,KAAK,UAAU,KAAK;AACpB,yBAAiB;AAAA,MACrB,OAAO;AACH,aAAK,IAAI,KAAK,uBAAuB;AACrC,aAAK,OAAO,IAAI,MAAM,KAAK,SAAS;AAAA,UAChC,KAAK,CAAC,QAAQ,QAAQ;AAClB,gBAAI;AACJ,gBAAI,CAAC,OAAO,IAAI,SAAS,CAAC,GAAG;AACzB,oBAAM,SAAS,IAAI,SAAS,EAAE,YAAY;AAC1C,sBAAQ,OAAO,MAAM;AAAA,YACzB,OAAO;AACH,sBAAQ,OAAO,IAAI,SAAS,CAAC;AAAA,YACjC;AAEA,gBAAI,CAAC,SAAS,IAAI,SAAS,EAAE,YAAY,MAAM,QAAQ,YAAY,GAAG;AAClE,sBAAQ;AAAA,YACZ;AACA,mBAAO;AAAA,UACX;AAAA,QACJ,CAAC;AAAA,MACL;AAAA,IACJ,OAAO;AACH,YAAM,wBAAwB,KAAK,MAAM,KAAK,IAAI;AAAA,IACtD;AAEA,QAAI,gBAAgB;AAEhB,WAAK,OAAO;AAAA;AAAA,QAER,CAAC,OAAO,GAAG,KAAK,QAAQ,qBAAqB,IAAI,CAAC,KAAK,QAAQ,qBAAqB,CAAC,IAAI,CAAC,cAAc;AAAA,MAC5G;AAAA,IACJ;AAAA,EACJ;AAEA,iBAAe,gBAAgB;AAC3B,UAAM,UAAU,KAAK,cAAc,WAAW,KAAK,cAAc,YAAY;AAE7E,mBAAe,oBAAoB;AAC/B,YAAM,YAAY,MAAM,IAAI,MAAM,QAAQ,wBAAwB,KAAK;AAEvE,UAAI,CAAC,UAAU,IAAI;AACf,cAAM;AAAA,MACV;AAEA,YAAM,QAAQ,MAAM,UAAU,KAAK;AACnC,YAAM,QAA4B,CAAC;AAAA,QAC/B,MAAM;AAAA,QACN,UAAU,OAAO,KAAK,IAAI,aAAa,QAAQ;AAAA,QAC/C,MAAM,KAAK;AAAA,QACX,MAAM,KAAK;AAAA,QACX,QAAQ,KAAK;AAAA,MACjB,CAAC;AAED,iBAAW,QAAQ,OAAO;AACtB,cAAM,aAAa,MAAM,IAAI,MAAM,QAAQ,cAAc,KAAK,EAAE,WAAW,KAAK;AAEhF,YAAI,CAAC,WAAW,IAAI;AAChB,gBAAM;AAAA,QACV;AAEA,cAAM,SAAS,MAAM,WAAW,KAAK;AAErC,mBAAW,SAAS,QAAQ;AACxB,gBAAM,WAAW,KAAK;AAEtB,cAAI,CAAC,aAAa,MAAM,IAAI,MAAM,KAAK,MAAM,GAAG;AAEhD,cAAI,CAAC,IAAI,aAAa,SAAS,MAAM,GAAG;AACpC,gBAAI,IAAI,MAAM,mBAAmB,MAAM,yBAAyB;AAChE;AAAA,UACJ;AAEA,kBAAQ,aAAa;AAAA,YACjB,KAAK;AACD,4BAAc;AACd;AAAA,YACJ,KAAK;AACD,4BAAc;AACd;AAAA,YACJ,KAAK;AACD,4BAAc;AACd;AAAA,UACR;AAEA,gBAAM,cAAc,MAAM,KAAK,OAAK,EAAE,SAAS,YAAY,EAAE,WAAW,MAAM;AAC9E,cAAI,aAAa;AACb,gBAAI,KAAK,QAAQ;AACb,0BAAY,WAAW,IAAI;AAAA,gBACvB,QAAQ;AAAA,kBACJ,QAAQ;AAAA,gBACZ;AAAA,cACJ;AAAA,YACJ,OAAO;AACH,0BAAY,WAAW,IAAI;AAAA,YAC/B;AAAA,UACJ,OAAO;AACH,kBAAM,UAA4B;AAAA,cAC9B,MAAM;AAAA,cACN;AAAA,YACJ;AAEA,gBAAI,KAAK,QAAQ;AACb,sBAAQ,WAAW,IAAI;AAAA,gBACnB,QAAQ;AAAA,kBACJ,QAAQ;AAAA,gBACZ;AAAA,cACJ;AAAA,YACJ,OAAO;AACH,sBAAQ,WAAW,IAAI;AAAA,YAC3B;AAEA,gBAAI,KAAK,UAAU;AACf,sBAAQ,WAAW;AAAA,gBACf,QAAQ;AAAA,cACZ;AAAA,YACJ;AAEA,kBAAM,KAAK,OAAO;AAAA,UACtB;AAAA,QACJ;AAAA,MACJ;AAEA,YAAM,WAAW,MAAM,OAAO,CAAC,MAAM,SAAS;AAC1C,SAAC,KAAK,KAAK,MAAM,CAAC,MAAM,CAAC,GAAG,KAAK,IAAI;AACrC,eAAO;AAAA,MACX,GAAG,CAAC,CAAC;AAEL,iBAAW,OAAO,UAAU;AACxB,YAAI,IAAI,KAAK,sBAAsB,GAAG,EAAE;AAExC,mBAAW,WAAW,SAAS,GAAG,GAAG;AAEjC,gBAAM,EAAE,QAAQ,UAAU,MAAM,GAAG,MAAM,IAAI;AAC7C,cAAI,IAAI,KAAK,IAAK,UAAU,SAAS,KAAK,GAAG,CAAC,KAAK,KAAK,UAAU,KAAK,CAAC,EAAE;AAAA,QAC9E;AAAA,MACJ;AAEA,YAAM,kBAAkB,OAAO,KAAK,IAAI,aAAa,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;AAEpH,UAAI,gBAAgB,QAAQ;AACxB,YAAI,IAAI,KAAK,+BAA+B,gBAAgB,KAAK,IAAI,CAAC,EAAE;AAAA,MAC5E;AAEA,UAAI,IAAI,MAAM,wBAAwB;AACtC,UAAI,IAAI,MAAM,KAAK;AACnB,aAAO;AAAA,IACX;AAEA,UAAM,aAAa,MAAM,kBAAkB;AAE3C,QAAI,kBAAkB,QAAQ;AAG9B,UAAM,cAAc,CAAC;AACrB,aAAS,IAAI,GAAG,IAAI,WAAW,QAAQ,KAAK;AACxC,YAAM,OAAO,WAAW,CAAC;AAEzB,UAAI,eAAe;AACnB,UAAI,KAAK,QAAQ;AACb,uBAAe,CAAC,KAAK,MAAM;AAAA,MAC/B,WAAW,KAAK,UAAU;AACtB,uBAAe,CAAC,GAAG,KAAK,QAAQ;AAAA,MACpC,OAAO;AACH,cAAM,IAAI,MAAM,wCAAwC,CAAC,EAAE;AAAA,MAC/D;AAEA,iBAAW,cAAc,cAAc;AACnC,cAAM,UAAU,EAAE,GAAG,MAAM,QAAQ,YAAY,UAAU,OAAU;AACnE,YAAI,CAAC,IAAI,aAAa,SAAS,QAAQ,MAAM,GAAG;AAC5C,gBAAM,IAAI,MAAM,mBAAmB,UAAU,2BAA2B,CAAC,EAAE;AAAA,QAC/E;AAEA,YAAI,CAAC,YAAY,UAAU,GAAG;AAC1B,sBAAY,UAAU,IAAI,CAAC;AAAA,QAC/B;AACA,oBAAY,UAAU,EAAE,KAAK,OAAO;AAAA,MACxC;AAAA,IACJ;AAEA,eAAW,aAAa,OAAO,KAAK,IAAI,aAAa,QAAQ,GAAG;AA2B5D,UAAS,cAAT,SAAqB,KAA0B;AAC3C,eAAO,CAAC;AAAA,MACZ;AA5BA,YAAM,QAAQ,YAAY,SAAS,KAAK,CAAC;AACzC,YAAM,OAAO,IAAI,aAAa,SAAS,SAAS;AAGhD,UAAI;AACJ,YAAM,sBAAsB;AAE5B,UAAI,4BAA4B,qBAAqB;AACjD,cAAM,IAAI,MAAM,4BAA4B,SAAS,wCAAwC;AAAA,MACjG;AAGA,UAAI,aAAa;AACb,cAAM,KAAK;AAAA,UACP,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,QAAQ;AAAA,UACR,YAAY;AAAA,QAChB,CAAC;AAAA,MACL;AAIA,sCAAgC,MAAM,KAAK;AAM3C,UAAI,aAAa,eAAe,WAAW;AAAA,QACvC,MAAM,KAAK,cAAc,EAAE,OAAO,KAAK,QAAQ,GAAG,SAAS,IAAI,CAAC,GAAG;AAC/D,cAAI,YAAY,GAAG,GAAG;AAClB,mBAAO,aAAa,EAAE,GAAG,UAAU,OAAO,KAAK,OAAO,CAAC;AAAA,UAC3D;AACA,gBAAM,UAAU,sBAAsB,GAAG;AACzC,gBAAM,OAAO,MAAM,uBAAuB,KAAK,OAAO,SAAS,eAAe,UAAU;AACxF,8BAAoB,KAAK,MAAM,UAAU,OAAO,KAAK,IAAI,aAAa,SAAS,SAAS,EAAE,MAAM,CAAC;AACjG,kBAAQ,MAAM,gBAAgB,KAAK,KAAK,MAAM,OAAO,QAAQ,IAAI;AAEjE,iBAAO,aAAa,EAAE,GAAG,UAAU,OAAO,KAAK,OAAO,CAAC;AAAA,QAC3D;AAAA,QACA,MAAM,KAAK,cAAc,EAAE,OAAO,KAAK,QAAQ,GAAG,SAAS,GAAG;AAC1D,cAAI,YAAY,GAAG,GAAG;AAClB,mBAAO,aAAa,EAAE,KAAK,OAAO,QAAQ,GAAG,SAAS,CAAC;AAAA,UAC3D;AACA,gBAAM,UAAU,sBAAsB,GAAG;AACzC,gBAAM,OAAO,MAAM,uBAAuB,KAAK,OAAO,SAAS,eAAe,UAAU;AAExF,cAAI,CAAC,KAAK,MAAM;AACZ,kBAAM,IAAI,aAAa;AAAA,UAC3B;AACA,8BAAoB,KAAK,MAAM,MAAM;AACrC,mCAAyB,KAAK,MAAM,KAAK;AAEzC,cAAI,KAAK,UAAU;AACf,uBAAW,OAAO,OAAO,KAAK,KAAK,QAAQ,GAAG;AAC1C,oBAAM,WAAW,KAAK,SAAS,GAAG;AAClC,kBAAI,OAAO,aAAa,YAAY;AAChC,sBAAM,GAAG,IAAI,MAAM,SAAS,EAAE,MAAM,QAAQ,MAAM,KAAK,MAAM,CAAC;AAAA,cAClE,OAAO;AACH,sBAAM,GAAG,IAAI,QAAQ,KAAK,QAAQ;AAAA,cACtC;AAAA,YACJ;AAAA,UACJ;AAEA,gBAAM,oBAAoB,MAAM,KAAK,UAAU,MAAM;AACrD,gBAAM,kBAAkB,CAAC;AACzB,0BAAgB,KAAK,UAAU,IAAI,EAAE,IAAI,MAAM,KAAK,UAAU,EAAE;AAEhE,cAAI,mBAAmB;AACnB,kBAAM,QAAQ,MAAM,gBAAgB,KAAK,KAAK,MAAM,iBAAiB,QAAQ,IAAI;AAEjF,kBAAM,QAAQ,MAAM,KAAK,KAAK;AAAA,cAC1B;AAAA,cACA;AAAA,cACA;AAAA,YACJ,CAAC;AAED,gBAAI,MAAM,WAAW,GAAG;AACpB,oBAAM,IAAI,aAAa;AAAA,YAC3B;AAEA,mBAAO,aAAa,EAAE,OAAO,KAAK,QAAQ,GAAG,SAAS,CAAC;AAAA,UAC3D;AAEA,iBAAO,aAAa,EAAE,OAAO,KAAK,QAAQ,GAAG,SAAS,CAAC;AAAA,QAC3D;AAAA,QAEA,MAAM,OAAO,gBAAgB,EAAE,QAAQ,KAAK,QAAQ,GAAG,SAAS,GAAG;AAC/D,cAAI,YAAY,GAAG,GAAG;AAClB,mBAAO,eAAe,EAAE,QAAQ,KAAK,QAAQ,GAAG,SAAS,CAAC;AAAA,UAC9D;AACA,gBAAM,UAAU,sBAAsB,GAAG;AACzC,gBAAM,OAAO,MAAM,uBAAuB,KAAK,OAAO,SAAS,eAAe,UAAU;AAExF,cAAI,CAAC,KAAK,MAAM;AACZ,kBAAM,IAAI,aAAa;AAAA,UAC3B;AAEA,8BAAoB,KAAK,MAAM,MAAM;AACrC,mCAAyB,KAAK,MAAM,MAAM;AAG1C,cAAI,KAAK,UAAU;AACf,uBAAW,SAAS,QAAQ;AACxB,yBAAW,OAAO,OAAO,KAAK,KAAK,QAAQ,GAAG;AAC1C,sBAAM,WAAW,KAAK,SAAS,GAAG;AAClC,oBAAI,OAAO,aAAa,YAAY;AAChC,wBAAM,GAAG,IAAI,MAAM,SAAS,EAAE,MAAM,QAAQ,MAAM,KAAK,MAAM,CAAC;AAAA,gBAClE,OAAO;AACH,wBAAM,GAAG,IAAI,QAAQ,KAAK,QAAQ;AAAA,gBACtC;AAAA,cACJ;AAAA,YACJ;AAAA,UACJ;AAEA,iBAAO,eAAe,EAAE,QAAQ,KAAK,QAAQ,GAAG,SAAS,CAAC;AAAA,QAC9D;AAAA,QAEA,MAAM,OAAO,gBAAgB,EAAE,OAAO,KAAK,QAAQ,GAAG,SAAS,GAAG;AAC9D,cAAI,YAAY,GAAG,GAAG;AAClB,mBAAO,eAAe,EAAE,OAAO,KAAK,QAAQ,GAAG,SAAS,CAAC;AAAA,UAC7D;AACA,gBAAM,UAAU,sBAAsB,GAAG;AACzC,gBAAM,OAAO,MAAM,uBAAuB,KAAK,OAAO,SAAS,eAAe,UAAU;AAExF,kBAAQ,MAAM,gBAAgB,KAAK,KAAK,QAAQ,OAAO,QAAQ,IAAI;AAEnE,iBAAO,eAAe,EAAE,OAAO,KAAK,QAAQ,GAAG,SAAS,CAAC;AAAA,QAC7D;AAAA,QAEA,MAAM,WAAW,oBAAoB,EAAE,OAAO,KAAK,QAAQ,GAAG,SAAS,GAAG;AACtE,cAAI,YAAY,GAAG,GAAG;AAClB,mBAAO,mBAAmB,EAAE,GAAG,UAAU,OAAO,KAAK,OAAO,CAAC;AAAA,UACjE;AACA,gBAAM,UAAU,sBAAsB,GAAG;AACzC,gBAAM,OAAO,MAAM,uBAAuB,KAAK,OAAO,SAAS,eAAe,UAAU;AAExF,kBAAQ,MAAM,gBAAgB,KAAK,KAAK,YAAY,OAAO,QAAQ,IAAI;AAEvE,iBAAO,mBAAmB,EAAE,GAAG,UAAU,OAAO,KAAK,OAAO,CAAC;AAAA,QACjE;AAAA,MACJ,CAAC;AAAA,IACL;AAAA,EACJ;AAEA,iBAAe,iBAAiB;AAC5B,UAAM,YAAY,KAAK,eAAe,cAAc,KAAK,eAAe,aAAa;AACrF,eAAW,aAAa,OAAO,KAAK,IAAI,aAAa,QAAQ,GAAG;AAW5D,UAAS,cAAT,SAAqB,KAA0B;AAC3C,eAAO,CAAC;AAAA,MACZ;AATA,UAAI;AACJ,YAAM,sBAAsB;AAE5B,UAAI,4BAA4B,qBAAqB;AACjD,cAAM,IAAI,MAAM,4BAA4B,SAAS,wCAAwC;AAAA,MACjG;AAMA,UAAI,aAAa,eAAe,WAAW;AAAA,QACvC,MAAM,KAAK,cAAc,EAAE,OAAO,KAAK,QAAQ,GAAG,SAAS,IAAI,CAAC,GAAG;AAC/D,cAAI,YAAY,GAAG,GAAG;AAClB,mBAAO,aAAa,EAAE,GAAG,UAAU,OAAO,KAAK,OAAO,CAAC;AAAA,UAC3D;AACA,gBAAM,wBAAwB,KAAK,WAAW,QAAQ,WAAW,eAAe,UAAU;AAK1F,iBAAO,aAAa,EAAE,GAAG,UAAU,OAAO,KAAK,OAAO,CAAC;AAAA,QAC3D;AAAA,QACA,MAAM,KAAK,cAAc,EAAE,OAAO,KAAK,QAAQ,GAAG,SAAS,GAAG;AAC1D,cAAI,YAAY,GAAG,GAAG;AAClB,mBAAO,aAAa,EAAE,KAAK,OAAO,QAAQ,GAAG,SAAS,CAAC;AAAA,UAC3D;AACA,gBAAM,wBAAwB,KAAK,WAAW,QAAQ,WAAW,eAAe,UAAU;AAoC1F,iBAAO,aAAa,EAAE,OAAO,KAAK,QAAQ,GAAG,SAAS,CAAC;AAAA,QAC3D;AAAA,QAEA,MAAM,OAAO,gBAAgB,EAAE,QAAQ,KAAK,QAAQ,GAAG,SAAS,GAAG;AAC/D,cAAI,YAAY,GAAG,GAAG;AAClB,mBAAO,eAAe,EAAE,QAAQ,KAAK,QAAQ,GAAG,SAAS,CAAC;AAAA,UAC9D;AACA,gBAAM,wBAAwB,KAAK,WAAW,UAAU,WAAW,eAAe,UAAU;AAmB5F,iBAAO,eAAe,EAAE,QAAQ,KAAK,QAAQ,GAAG,SAAS,CAAC;AAAA,QAC9D;AAAA,QAEA,MAAM,OAAO,gBAAgB,EAAE,OAAO,KAAK,QAAQ,GAAG,SAAS,GAAG;AAC9D,cAAI,YAAY,GAAG,GAAG;AAClB,mBAAO,eAAe,EAAE,OAAO,KAAK,QAAQ,GAAG,SAAS,CAAC;AAAA,UAC7D;AACA,gBAAM,wBAAwB,KAAK,WAAW,UAAU,WAAW,eAAe,UAAU;AAI5F,iBAAO,eAAe,EAAE,OAAO,KAAK,QAAQ,GAAG,SAAS,CAAC;AAAA,QAC7D;AAAA,QAEA,MAAM,WAAW,oBAAoB,EAAE,OAAO,KAAK,QAAQ,GAAG,SAAS,GAAG;AACtE,cAAI,YAAY,GAAG,GAAG;AAClB,mBAAO,mBAAmB,EAAE,GAAG,UAAU,OAAO,KAAK,OAAO,CAAC;AAAA,UACjE;AACA,gBAAM,wBAAwB,KAAK,WAAW,cAAc,WAAW,eAAe,UAAU;AAIhG,iBAAO,mBAAmB,EAAE,GAAG,UAAU,OAAO,KAAK,OAAO,CAAC;AAAA,QACjE;AAAA,MACJ,CAAC;AAAA,IACL;AAAA,EACJ;AAEA,MAAI,QAAQ,WAAW,iBAAkB;AACrC,QAAI,KAAK,eAAe;AACpB,YAAM,cAAc;AAAA,IACxB;AAEA,QAAI,KAAK,gBAAgB;AACrB,YAAM,eAAe;AAAA,IACzB;AAAA,EACJ,CAAC;AACL,GAAG,EAAE,MAAM,8BAA8B,CAAC;AAE1C,eAAe,gBAAgB,KAA0B,MAAM,OAAO,MAAM;AACxE,MAAI,CAAC,MAAM;AACP,UAAM,IAAI,aAAa;AAAA,EAC3B;AACA,QAAM,UAAU,sBAAsB,GAAG;AAEzC,UAAQ,SAAS,CAAC;AAElB,MAAI,OAAO,SAAS,UAAU;AAC1B,UAAM,EAAE,OAAO,IAAI;AAGnB,QAAI,QAAQ;AACR,iBAAW,OAAO,OAAO,KAAK,MAAM,GAAG;AACnC,cAAM,UAAU,OAAO,GAAG;AAC1B,YAAI,OAAO,YAAY,UAAU;AAE7B,gBAAM,GAAG,IAAI;AAAA,YACT,IAAI,QAAQ,KAAK,OAAO;AAAA,UAC5B;AAAA,QACJ,OAAO;AAKH,qBAAW,aAAa,OAAO,KAAK,OAAO,GAAG;AAC1C,kBAAM,SAAS,QAAQ,SAAS;AAChC,kBAAM,GAAG,IAAI;AAAA,cACT,CAAC,SAAS,GAAG,QAAQ,KAAK,MAAM;AAAA,YACpC;AAAA,UACJ;AAAA,QACJ;AAAA,MACJ;AAAA,IACJ;AAAA,EACJ,WAAW,OAAO,SAAS,YAAY;AACnC,YAAQ,MAAM,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;AAAA,EAC3C;AACA,SAAO;AACX;AAEA,eAAsB,uBAAuB,KAA0B,OAA2B,SAAiB,eAAuB,aAAa,OAAO;AAC1J,QAAM,UAAU,sBAAsB,GAAG;AACzC,QAAM,QAAQ,yBAAyB;AACvC,QAAM,QAAQ,SAAS,SAAS,SAAS,eAAe,UAAU;AAClE,QAAM,OAAO,kBAAS,OAAO,KAAK;AAClC,MAAI,CAAC,MAAM;AACP,QAAI,MAAM,QAAQ,IAAI,KAAK,EAAE,OAAO,MAAM,GAAG,mBAAmB;AAChE,UAAM,IAAI,aAAa;AAAA,EAC3B;AACA,MAAI,MAAM,QAAQ,IAAI,MAAM,EAAE,OAAO,KAAK,GAAG,YAAY;AACzD,SAAO;AACX;AAEA,eAAsB,wBAAwB,KAA0B,WAAmB,QAAuB,WAAmB,eAAuB,cAAc,OAAO;AAC7K,QAAM,UAAU,sBAAsB,GAAG;AACzC,QAAM,QAAQ,yBAAyB;AACvC,QAAM,SAAS,UAAU,SAAS,WAAW,eAAe,WAAW;AACvE,QAAM,QAAQ,OAAO,KAAK,OAAK;AAC3B,QAAI,WAAW,QAAQ;AACnB,aAAO,MAAM,GAAG,MAAM,IAAI,SAAS;AAAA,IACvC,OACK;AACD,aAAO,MAAM,GAAG,MAAM,IAAI,SAAS,MAAM,MAAM,QAAQ,SAAS;AAAA,IACpE;AAAA,EACJ,CAAC;AAED,MAAI,CAAC,OAAO;AACR,QAAI,MAAM,QAAQ,IAAI,KAAK,gBAAgB;AAC3C,UAAM,IAAI,aAAa;AAAA,EAC3B;AACA,MAAI,MAAM,QAAQ,IAAI,MAAM,EAAE,QAAQ,MAAM,GAAG,aAAa;AAC5D,SAAO;AACX;AAEO,SAAS,oBAAoB,MAAM,QAAQ;AAC9C,MAAI,CAAC,MAAM;AACP,UAAM,IAAI,aAAa;AAAA,EAC3B;AACA,QAAM,EAAE,QAAQ,eAAe,IAAI;AAEnC,MAAI,gBAAgB;AAChB,eAAW,SAAS,QAAQ;AACxB,UAAI,CAAC,eAAe,SAAS,KAAK,GAAG;AACjC,cAAM,IAAI,kBAAkB,KAAK;AAAA,MACrC;AAAA,IACJ;AAAA,EACJ;AACJ;AAEA,IAAM,iBAAiB,CAAC,QAAQ,mBAAmB;AAC/C,aAAW,SAAS,QAAQ;AACxB,UAAM,cAAc,OAAO,KAAK,KAAK;AACrC,eAAW,cAAc,aAAa;AAClC,UAAI,CAAC,eAAe,SAAS,UAAU,GAAG;AACtC,cAAM,IAAI,kBAAkB,UAAU;AAAA,MAC1C;AAAA,IACJ;AAAA,EACJ;AACJ;AAEA,SAAS,yBAAyB,MAAM,QAAQ;AAC5C,QAAM,EAAE,QAAQ,eAAe,IAAI;AAEnC,MAAI,gBAAgB;AAChB,QAAI,CAAC,MAAM,QAAQ,MAAM,GAAG;AAExB,qBAAe,CAAC,MAAM,GAAG,cAAc;AAAA,IAC3C,OAAO;AAEH,qBAAe,QAAQ,cAAc;AAAA,IACzC;AAAA,EACJ;AACJ;AAEA,SAAS,gCAAgC,MAAc,OAAO;AAE1D,QAAM,kBACF,OAAO,OAAO,KAAK,MAAM,EACpB,OAAO,OAAM,CAAC,EAAE,cAAc,CAAC,EAAE,UAAW,EAC5C,IAAI,CAAC,EAAE,UAAU,MAAO,SAAU;AAE3C,aAAW,QAAQ,OAAO;AACtB,UAAM,EAAE,QAAQ,KAAK,IAAI;AACzB,QAAI,QAAQ,KAAK,QAAQ;AACrB,YAAM,SAAS,KAAK;AACpB,iBAAW,UAAU,iBAAiB;AAClC,YAAI,CAAC,OAAO,SAAS,MAAM,GAAG;AAC1B,gBAAM,IAAI,wBAAwB,QAAQ,MAAM;AAAA,QACpD;AAAA,MACJ;AAAA,IACJ;AAAA,EACJ;AACJ;AAMA,eAAe,wBAAwB,KAAsB,MAAoC,OAAO,MAAM;AAE1G,MAAI,CAAC,KAAK,2BAA2B,CAAC,KAAK,SAAS,CAAC,IAAI,SAAS,CAAC,MAAM;AACrE;AAAA,EACJ;AAEA,QAAM,oBAAoB,KAAK,qBAAqB;AACpD,QAAM,eAAe,OAAO,iBAAiB;AAC7C,QAAM,SAAS,MAAM;AAErB,MAAI;AACA,UAAM,WAAW,uBAAuB,MAAM;AAC9C,UAAM,oBAAoB,MAAM,IAAI,MAAM,IAAI,QAAQ;AACtD,UAAM,iBAAiB,SAAS,mBAAmB,EAAE;AAErD,QAAI,sBAAsB,MAAM;AAC5B,YAAM,IAAI,MAAM,IAAI,UAAU,aAAa,SAAS,CAAC;AACrD,UAAI,IAAI,MAAM,EAAE,QAAQ,SAAS,aAAa,GAAG,0CAA0C;AAC3F,UAAI,eAAe,GAAG;AAClB,YAAI,IAAI,KAAK;AAAA,UACT;AAAA,UACA;AAAA,UACA;AAAA,QACJ,GAAG,uCAAuC;AAC1C,cAAM,IAAI,oBAAoB;AAAA,MAClC;AACA;AAAA,IACJ;AAGA,QAAI,mBAAmB,cAAc;AACjC,UAAI,IAAI,KAAK;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,MACJ,GAAG,uCAAuC;AAC1C,YAAM,IAAI,oBAAoB;AAAA,IAClC;AAEA,QAAI,IAAI,MAAM,EAAE,QAAQ,SAAS,aAAa,GAAG,4BAA4B;AAAA,EACjF,SAAS,OAAO;AACZ,QAAI,MAAM,SAAS,kBAAkB,MAAM,SAAS,gCAAgC;AAChF,YAAM;AAAA,IACV;AACA,QAAI,IAAI,MAAM,EAAE,KAAK,OAAO,OAAO,GAAG,8BAA8B;AAAA,EACxE;AACJ;AAEA,IAAO,gBAAQ;","names":["createError","import_fastify_logto","fp","fastifyRedis","fastifyLogto"]}
|
package/lib/index.d.cts
CHANGED
|
@@ -20,6 +20,7 @@ declare function deletePermissionsVersion(app: FastifyInstance, userId: string):
|
|
|
20
20
|
declare function getRequestFromContext(ctx: any): any;
|
|
21
21
|
declare function getRoles(request: any, roleKey: any, anonymousRole: any, isRolePath?: boolean): any[];
|
|
22
22
|
|
|
23
|
+
type EntityActions = 'find' | 'save' | 'insert' | 'updateMany' | 'delete';
|
|
23
24
|
type PlatformaticRule = {
|
|
24
25
|
role: string;
|
|
25
26
|
entity?: string;
|
|
@@ -38,19 +39,27 @@ type RedisConfig = {
|
|
|
38
39
|
username?: string;
|
|
39
40
|
db?: number;
|
|
40
41
|
};
|
|
41
|
-
type
|
|
42
|
+
type PlatformaticLogToRoleBasedAuthOptions = {
|
|
42
43
|
logtoBaseUrl?: string;
|
|
43
44
|
logtoAppId?: string;
|
|
44
45
|
logtoAppSecret?: string;
|
|
45
|
-
adminSecret?: string;
|
|
46
46
|
rolePath?: string;
|
|
47
47
|
roleKey?: string;
|
|
48
48
|
userPath?: string;
|
|
49
49
|
userKey?: string;
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
};
|
|
51
|
+
type PlatformaticLogToScopeBasedAuthOptions = {
|
|
52
|
+
scopesPath?: string;
|
|
53
|
+
scopesKey?: string;
|
|
54
|
+
};
|
|
55
|
+
type PlatformaticLogtoAuthOptions = {
|
|
56
|
+
adminSecret?: string;
|
|
57
|
+
roleBasedAuth?: PlatformaticLogToRoleBasedAuthOptions;
|
|
58
|
+
scopeBasedAuth?: PlatformaticLogToScopeBasedAuthOptions;
|
|
52
59
|
checks?: boolean;
|
|
53
60
|
defaults?: boolean;
|
|
61
|
+
anonymousRole?: string;
|
|
62
|
+
allowAnonymous?: boolean;
|
|
54
63
|
jwtPlugin: FastifyUserPluginOptions;
|
|
55
64
|
redis?: RedisConfig;
|
|
56
65
|
enableTokenVersionCheck?: boolean;
|
|
@@ -58,6 +67,7 @@ type PlatformaticLogtoAuthOptions = {
|
|
|
58
67
|
};
|
|
59
68
|
declare const platformaticLogto: FastifyPluginAsync<PlatformaticLogtoAuthOptions>;
|
|
60
69
|
declare function findRuleForRequestUser(ctx: PlatformaticContext, rules: PlatformaticRule[], roleKey: string, anonymousRole: string, isRolePath?: boolean): Promise<any>;
|
|
70
|
+
declare function findScopeForRequestUser(ctx: PlatformaticContext, entityKey: string, action: EntityActions, scopesKey: string, anonymousRole: string, isScopePath?: boolean): Promise<any>;
|
|
61
71
|
declare function checkFieldsFromRule(rule: any, fields: any): void;
|
|
62
72
|
|
|
63
73
|
declare module 'fastify' {
|
|
@@ -69,4 +79,4 @@ declare module 'fastify' {
|
|
|
69
79
|
}
|
|
70
80
|
}
|
|
71
81
|
|
|
72
|
-
export { type PlatformaticLogtoAuthOptions, type PlatformaticRule, type RedisConfig, checkFieldsFromRule, platformaticLogto as default, deletePermissionsVersion, findRuleForRequestUser, getRequestFromContext, getRoles, incrementPermissionsVersion, platformaticLogto };
|
|
82
|
+
export { type EntityActions, type PlatformaticLogToRoleBasedAuthOptions, type PlatformaticLogToScopeBasedAuthOptions, type PlatformaticLogtoAuthOptions, type PlatformaticRule, type RedisConfig, checkFieldsFromRule, platformaticLogto as default, deletePermissionsVersion, findRuleForRequestUser, findScopeForRequestUser, getRequestFromContext, getRoles, incrementPermissionsVersion, platformaticLogto };
|
package/lib/index.d.ts
CHANGED
|
@@ -20,6 +20,7 @@ declare function deletePermissionsVersion(app: FastifyInstance, userId: string):
|
|
|
20
20
|
declare function getRequestFromContext(ctx: any): any;
|
|
21
21
|
declare function getRoles(request: any, roleKey: any, anonymousRole: any, isRolePath?: boolean): any[];
|
|
22
22
|
|
|
23
|
+
type EntityActions = 'find' | 'save' | 'insert' | 'updateMany' | 'delete';
|
|
23
24
|
type PlatformaticRule = {
|
|
24
25
|
role: string;
|
|
25
26
|
entity?: string;
|
|
@@ -38,19 +39,27 @@ type RedisConfig = {
|
|
|
38
39
|
username?: string;
|
|
39
40
|
db?: number;
|
|
40
41
|
};
|
|
41
|
-
type
|
|
42
|
+
type PlatformaticLogToRoleBasedAuthOptions = {
|
|
42
43
|
logtoBaseUrl?: string;
|
|
43
44
|
logtoAppId?: string;
|
|
44
45
|
logtoAppSecret?: string;
|
|
45
|
-
adminSecret?: string;
|
|
46
46
|
rolePath?: string;
|
|
47
47
|
roleKey?: string;
|
|
48
48
|
userPath?: string;
|
|
49
49
|
userKey?: string;
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
};
|
|
51
|
+
type PlatformaticLogToScopeBasedAuthOptions = {
|
|
52
|
+
scopesPath?: string;
|
|
53
|
+
scopesKey?: string;
|
|
54
|
+
};
|
|
55
|
+
type PlatformaticLogtoAuthOptions = {
|
|
56
|
+
adminSecret?: string;
|
|
57
|
+
roleBasedAuth?: PlatformaticLogToRoleBasedAuthOptions;
|
|
58
|
+
scopeBasedAuth?: PlatformaticLogToScopeBasedAuthOptions;
|
|
52
59
|
checks?: boolean;
|
|
53
60
|
defaults?: boolean;
|
|
61
|
+
anonymousRole?: string;
|
|
62
|
+
allowAnonymous?: boolean;
|
|
54
63
|
jwtPlugin: FastifyUserPluginOptions;
|
|
55
64
|
redis?: RedisConfig;
|
|
56
65
|
enableTokenVersionCheck?: boolean;
|
|
@@ -58,6 +67,7 @@ type PlatformaticLogtoAuthOptions = {
|
|
|
58
67
|
};
|
|
59
68
|
declare const platformaticLogto: FastifyPluginAsync<PlatformaticLogtoAuthOptions>;
|
|
60
69
|
declare function findRuleForRequestUser(ctx: PlatformaticContext, rules: PlatformaticRule[], roleKey: string, anonymousRole: string, isRolePath?: boolean): Promise<any>;
|
|
70
|
+
declare function findScopeForRequestUser(ctx: PlatformaticContext, entityKey: string, action: EntityActions, scopesKey: string, anonymousRole: string, isScopePath?: boolean): Promise<any>;
|
|
61
71
|
declare function checkFieldsFromRule(rule: any, fields: any): void;
|
|
62
72
|
|
|
63
73
|
declare module 'fastify' {
|
|
@@ -69,4 +79,4 @@ declare module 'fastify' {
|
|
|
69
79
|
}
|
|
70
80
|
}
|
|
71
81
|
|
|
72
|
-
export { type PlatformaticLogtoAuthOptions, type PlatformaticRule, type RedisConfig, checkFieldsFromRule, platformaticLogto as default, deletePermissionsVersion, findRuleForRequestUser, getRequestFromContext, getRoles, incrementPermissionsVersion, platformaticLogto };
|
|
82
|
+
export { type EntityActions, type PlatformaticLogToRoleBasedAuthOptions, type PlatformaticLogToScopeBasedAuthOptions, type PlatformaticLogtoAuthOptions, type PlatformaticRule, type RedisConfig, checkFieldsFromRule, platformaticLogto as default, deletePermissionsVersion, findRuleForRequestUser, findScopeForRequestUser, getRequestFromContext, getRoles, incrementPermissionsVersion, platformaticLogto };
|
package/lib/index.js
CHANGED
|
@@ -26,7 +26,7 @@ var ERROR_PREFIX = "PLT_DB_AUTH";
|
|
|
26
26
|
var Unauthorized = createError(`${ERROR_PREFIX}_UNAUTHORIZED`, "operation not allowed", 401);
|
|
27
27
|
var UnauthorizedField = createError(`${ERROR_PREFIX}_FIELD_UNAUTHORIZED`, "field not allowed: %s", 401);
|
|
28
28
|
var MissingNotNullableError = createError(`${ERROR_PREFIX}_NOT_NULLABLE_MISSING`, 'missing not nullable field: "%s" in save rule for entity "%s"');
|
|
29
|
-
var PermissionsOutdated = createError(`${ERROR_PREFIX}_VERSION_OUTDATED`, "Verision has been updated. Please logout and login again.",
|
|
29
|
+
var PermissionsOutdated = createError(`${ERROR_PREFIX}_VERSION_OUTDATED`, "Verision has been updated. Please logout and login again.", 403);
|
|
30
30
|
|
|
31
31
|
// src/index.ts
|
|
32
32
|
import fastifyLogto from "@albirex/fastify-logto";
|
|
@@ -92,6 +92,33 @@ function getRoles(request, roleKey, anonymousRole, isRolePath = false) {
|
|
|
92
92
|
}
|
|
93
93
|
return output;
|
|
94
94
|
}
|
|
95
|
+
function getScopes(request, scopesKey, anonymousRole, isScopePath = false) {
|
|
96
|
+
let output = [];
|
|
97
|
+
const user = request.user;
|
|
98
|
+
if (!user) {
|
|
99
|
+
output.push(anonymousRole);
|
|
100
|
+
return output;
|
|
101
|
+
}
|
|
102
|
+
let scopesRaw;
|
|
103
|
+
if (isScopePath) {
|
|
104
|
+
const roleKeys = scopesKey.split(".");
|
|
105
|
+
scopesRaw = user;
|
|
106
|
+
for (const key of roleKeys) {
|
|
107
|
+
scopesRaw = scopesRaw[key];
|
|
108
|
+
}
|
|
109
|
+
} else {
|
|
110
|
+
scopesRaw = user[scopesKey];
|
|
111
|
+
}
|
|
112
|
+
if (typeof scopesRaw === "string") {
|
|
113
|
+
output = scopesRaw.split(" ");
|
|
114
|
+
} else if (Array.isArray(scopesRaw)) {
|
|
115
|
+
output = scopesRaw;
|
|
116
|
+
}
|
|
117
|
+
if (output.length === 0) {
|
|
118
|
+
output.push(anonymousRole);
|
|
119
|
+
}
|
|
120
|
+
return output;
|
|
121
|
+
}
|
|
95
122
|
|
|
96
123
|
// src/index.ts
|
|
97
124
|
var PLT_ADMIN_ROLE = "platformatic-admin";
|
|
@@ -109,107 +136,18 @@ var platformaticLogto = fp(async (app, opts) => {
|
|
|
109
136
|
});
|
|
110
137
|
app.log.info("Redis client registered for permissions version check");
|
|
111
138
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
139
|
+
if (opts.roleBasedAuth) {
|
|
140
|
+
app.register(fastifyLogto, {
|
|
141
|
+
endpoint: opts.roleBasedAuth.logtoBaseUrl || "https://auth.example.com",
|
|
142
|
+
appId: opts.roleBasedAuth.logtoAppId || "your-app-id",
|
|
143
|
+
appSecret: opts.roleBasedAuth.logtoAppSecret || "your-app-secret"
|
|
144
|
+
});
|
|
145
|
+
}
|
|
117
146
|
await app.register(fastifyUser, opts.jwtPlugin);
|
|
147
|
+
const roleKey = opts.roleBasedAuth?.rolePath || opts.roleBasedAuth?.roleKey || "X-PLATFORMATIC-ROLE";
|
|
118
148
|
const adminSecret = opts.adminSecret;
|
|
119
|
-
const
|
|
120
|
-
const userKey = opts.userPath || opts.userKey || "X-PLATFORMATIC-USER-ID";
|
|
121
|
-
const isRolePath = !!opts.rolePath;
|
|
149
|
+
const isRolePath = !!opts.roleBasedAuth?.rolePath;
|
|
122
150
|
const anonymousRole = opts.anonymousRole || "anonymous";
|
|
123
|
-
async function composeLogToRules() {
|
|
124
|
-
const rolesResp = await app.logto.callAPI("/api/roles?type=User", "GET");
|
|
125
|
-
if (!rolesResp.ok) {
|
|
126
|
-
throw rolesResp;
|
|
127
|
-
}
|
|
128
|
-
const roles = await rolesResp.json();
|
|
129
|
-
const rules = [{
|
|
130
|
-
role: anonymousRole,
|
|
131
|
-
entities: Object.keys(app.platformatic.entities),
|
|
132
|
-
find: opts.allowAnonymous,
|
|
133
|
-
save: opts.allowAnonymous,
|
|
134
|
-
delete: opts.allowAnonymous
|
|
135
|
-
}];
|
|
136
|
-
for (const role of roles) {
|
|
137
|
-
const scopesResp = await app.logto.callAPI(`/api/roles/${role.id}/scopes`, "GET");
|
|
138
|
-
if (!scopesResp.ok) {
|
|
139
|
-
throw scopesResp;
|
|
140
|
-
}
|
|
141
|
-
const scopes = await scopesResp.json();
|
|
142
|
-
for (const scope of scopes) {
|
|
143
|
-
const roleName = role.name;
|
|
144
|
-
let [scopeAction, entity] = scope.name.split(":");
|
|
145
|
-
if (!app.platformatic.entities[entity]) {
|
|
146
|
-
app.log.debug(`Unknown entity '${entity}' in authorization rule`);
|
|
147
|
-
continue;
|
|
148
|
-
}
|
|
149
|
-
switch (scopeAction) {
|
|
150
|
-
case "create":
|
|
151
|
-
scopeAction = "save";
|
|
152
|
-
break;
|
|
153
|
-
case "read":
|
|
154
|
-
scopeAction = "find";
|
|
155
|
-
break;
|
|
156
|
-
case "update":
|
|
157
|
-
scopeAction = "updateMany";
|
|
158
|
-
break;
|
|
159
|
-
}
|
|
160
|
-
const checkExists = rules.find((r) => r.role === roleName && r.entity === entity);
|
|
161
|
-
if (checkExists) {
|
|
162
|
-
if (opts.checks) {
|
|
163
|
-
checkExists[scopeAction] = {
|
|
164
|
-
checks: {
|
|
165
|
-
userId: userKey
|
|
166
|
-
}
|
|
167
|
-
};
|
|
168
|
-
} else {
|
|
169
|
-
checkExists[scopeAction] = true;
|
|
170
|
-
}
|
|
171
|
-
} else {
|
|
172
|
-
const newRule = {
|
|
173
|
-
role: roleName,
|
|
174
|
-
entity
|
|
175
|
-
};
|
|
176
|
-
if (opts.checks) {
|
|
177
|
-
newRule[scopeAction] = {
|
|
178
|
-
checks: {
|
|
179
|
-
userId: userKey
|
|
180
|
-
}
|
|
181
|
-
};
|
|
182
|
-
} else {
|
|
183
|
-
newRule[scopeAction] = true;
|
|
184
|
-
}
|
|
185
|
-
if (opts.defaults) {
|
|
186
|
-
newRule.defaults = {
|
|
187
|
-
userId: userKey
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
|
-
rules.push(newRule);
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
const logRules = rules.reduce((prev, curr) => {
|
|
195
|
-
(prev[curr["role"]] ??= []).push(curr);
|
|
196
|
-
return prev;
|
|
197
|
-
}, {});
|
|
198
|
-
for (const key in logRules) {
|
|
199
|
-
app.log.info(`Rules set for role ${key}`);
|
|
200
|
-
for (const element of logRules[key]) {
|
|
201
|
-
const { entity, entities, role, ...other } = element;
|
|
202
|
-
app.log.info(` ${entity ?? entities.join(",")}: ${JSON.stringify(other)}`);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
const missingEntities = Object.keys(app.platformatic.entities).filter((e) => !rules.map((r) => r.entity).includes(e));
|
|
206
|
-
if (missingEntities.length) {
|
|
207
|
-
app.log.warn(`Missing rules for entities: ${missingEntities.join(", ")}`);
|
|
208
|
-
}
|
|
209
|
-
app.log.debug("LogTo calculated rules");
|
|
210
|
-
app.log.debug(rules);
|
|
211
|
-
return rules;
|
|
212
|
-
}
|
|
213
151
|
app.decorateRequest("setupDBAuthorizationUser", setupUser);
|
|
214
152
|
async function setupUser() {
|
|
215
153
|
await this.extractUser();
|
|
@@ -245,7 +183,98 @@ var platformaticLogto = fp(async (app, opts) => {
|
|
|
245
183
|
};
|
|
246
184
|
}
|
|
247
185
|
}
|
|
248
|
-
|
|
186
|
+
async function roleBasedAuth() {
|
|
187
|
+
const userKey = opts.roleBasedAuth.userKey || opts.roleBasedAuth.userPath || "X-PLATFORMATIC-USER-ID";
|
|
188
|
+
async function composeLogToRules() {
|
|
189
|
+
const rolesResp = await app.logto.callAPI("/api/roles?type=User", "GET");
|
|
190
|
+
if (!rolesResp.ok) {
|
|
191
|
+
throw rolesResp;
|
|
192
|
+
}
|
|
193
|
+
const roles = await rolesResp.json();
|
|
194
|
+
const rules = [{
|
|
195
|
+
role: anonymousRole,
|
|
196
|
+
entities: Object.keys(app.platformatic.entities),
|
|
197
|
+
find: opts.allowAnonymous,
|
|
198
|
+
save: opts.allowAnonymous,
|
|
199
|
+
delete: opts.allowAnonymous
|
|
200
|
+
}];
|
|
201
|
+
for (const role of roles) {
|
|
202
|
+
const scopesResp = await app.logto.callAPI(`/api/roles/${role.id}/scopes`, "GET");
|
|
203
|
+
if (!scopesResp.ok) {
|
|
204
|
+
throw scopesResp;
|
|
205
|
+
}
|
|
206
|
+
const scopes = await scopesResp.json();
|
|
207
|
+
for (const scope of scopes) {
|
|
208
|
+
const roleName = role.name;
|
|
209
|
+
let [scopeAction, entity] = scope.name.split(":");
|
|
210
|
+
if (!app.platformatic.entities[entity]) {
|
|
211
|
+
app.log.debug(`Unknown entity '${entity}' in authorization rule`);
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
switch (scopeAction) {
|
|
215
|
+
case "create":
|
|
216
|
+
scopeAction = "save";
|
|
217
|
+
break;
|
|
218
|
+
case "read":
|
|
219
|
+
scopeAction = "find";
|
|
220
|
+
break;
|
|
221
|
+
case "update":
|
|
222
|
+
scopeAction = "updateMany";
|
|
223
|
+
break;
|
|
224
|
+
}
|
|
225
|
+
const checkExists = rules.find((r) => r.role === roleName && r.entity === entity);
|
|
226
|
+
if (checkExists) {
|
|
227
|
+
if (opts.checks) {
|
|
228
|
+
checkExists[scopeAction] = {
|
|
229
|
+
checks: {
|
|
230
|
+
userId: userKey
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
} else {
|
|
234
|
+
checkExists[scopeAction] = true;
|
|
235
|
+
}
|
|
236
|
+
} else {
|
|
237
|
+
const newRule = {
|
|
238
|
+
role: roleName,
|
|
239
|
+
entity
|
|
240
|
+
};
|
|
241
|
+
if (opts.checks) {
|
|
242
|
+
newRule[scopeAction] = {
|
|
243
|
+
checks: {
|
|
244
|
+
userId: userKey
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
} else {
|
|
248
|
+
newRule[scopeAction] = true;
|
|
249
|
+
}
|
|
250
|
+
if (opts.defaults) {
|
|
251
|
+
newRule.defaults = {
|
|
252
|
+
userId: userKey
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
rules.push(newRule);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
const logRules = rules.reduce((prev, curr) => {
|
|
260
|
+
(prev[curr["role"]] ??= []).push(curr);
|
|
261
|
+
return prev;
|
|
262
|
+
}, {});
|
|
263
|
+
for (const key in logRules) {
|
|
264
|
+
app.log.info(`Rules set for role ${key}`);
|
|
265
|
+
for (const element of logRules[key]) {
|
|
266
|
+
const { entity, entities, role, ...other } = element;
|
|
267
|
+
app.log.info(` ${entity ?? entities.join(",")}: ${JSON.stringify(other)}`);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
const missingEntities = Object.keys(app.platformatic.entities).filter((e) => !rules.map((r) => r.entity).includes(e));
|
|
271
|
+
if (missingEntities.length) {
|
|
272
|
+
app.log.warn(`Missing rules for entities: ${missingEntities.join(", ")}`);
|
|
273
|
+
}
|
|
274
|
+
app.log.debug("LogTo calculated rules");
|
|
275
|
+
app.log.debug(rules);
|
|
276
|
+
return rules;
|
|
277
|
+
}
|
|
249
278
|
const logToRules = await composeLogToRules();
|
|
250
279
|
app.platformaticLogTo.rules = logToRules;
|
|
251
280
|
const entityRules = {};
|
|
@@ -385,6 +414,64 @@ var platformaticLogto = fp(async (app, opts) => {
|
|
|
385
414
|
}
|
|
386
415
|
});
|
|
387
416
|
}
|
|
417
|
+
}
|
|
418
|
+
async function scopeBasedAuth() {
|
|
419
|
+
const scopesKey = opts.scopeBasedAuth.scopesPath || opts.scopeBasedAuth.scopesKey || "X-PLATFORMATIC-SCOPES";
|
|
420
|
+
for (const entityKey of Object.keys(app.platformatic.entities)) {
|
|
421
|
+
let useOriginal = function(ctx) {
|
|
422
|
+
return !ctx;
|
|
423
|
+
};
|
|
424
|
+
let userPropToFillForPublish;
|
|
425
|
+
const topicsWithoutChecks = false;
|
|
426
|
+
if (userPropToFillForPublish && topicsWithoutChecks) {
|
|
427
|
+
throw new Error(`Subscription for entity "${entityKey}" have conflictling rules across roles`);
|
|
428
|
+
}
|
|
429
|
+
app.platformatic.addEntityHooks(entityKey, {
|
|
430
|
+
async find(originalFind, { where, ctx, fields, ...restOpts } = {}) {
|
|
431
|
+
if (useOriginal(ctx)) {
|
|
432
|
+
return originalFind({ ...restOpts, where, ctx, fields });
|
|
433
|
+
}
|
|
434
|
+
await findScopeForRequestUser(ctx, entityKey, "find", scopesKey, anonymousRole, isRolePath);
|
|
435
|
+
return originalFind({ ...restOpts, where, ctx, fields });
|
|
436
|
+
},
|
|
437
|
+
async save(originalSave, { input, ctx, fields, ...restOpts }) {
|
|
438
|
+
if (useOriginal(ctx)) {
|
|
439
|
+
return originalSave({ ctx, input, fields, ...restOpts });
|
|
440
|
+
}
|
|
441
|
+
await findScopeForRequestUser(ctx, entityKey, "save", scopesKey, anonymousRole, isRolePath);
|
|
442
|
+
return originalSave({ input, ctx, fields, ...restOpts });
|
|
443
|
+
},
|
|
444
|
+
async insert(originalInsert, { inputs, ctx, fields, ...restOpts }) {
|
|
445
|
+
if (useOriginal(ctx)) {
|
|
446
|
+
return originalInsert({ inputs, ctx, fields, ...restOpts });
|
|
447
|
+
}
|
|
448
|
+
await findScopeForRequestUser(ctx, entityKey, "insert", scopesKey, anonymousRole, isRolePath);
|
|
449
|
+
return originalInsert({ inputs, ctx, fields, ...restOpts });
|
|
450
|
+
},
|
|
451
|
+
async delete(originalDelete, { where, ctx, fields, ...restOpts }) {
|
|
452
|
+
if (useOriginal(ctx)) {
|
|
453
|
+
return originalDelete({ where, ctx, fields, ...restOpts });
|
|
454
|
+
}
|
|
455
|
+
await findScopeForRequestUser(ctx, entityKey, "delete", scopesKey, anonymousRole, isRolePath);
|
|
456
|
+
return originalDelete({ where, ctx, fields, ...restOpts });
|
|
457
|
+
},
|
|
458
|
+
async updateMany(originalUpdateMany, { where, ctx, fields, ...restOpts }) {
|
|
459
|
+
if (useOriginal(ctx)) {
|
|
460
|
+
return originalUpdateMany({ ...restOpts, where, ctx, fields });
|
|
461
|
+
}
|
|
462
|
+
await findScopeForRequestUser(ctx, entityKey, "updateMany", scopesKey, anonymousRole, isRolePath);
|
|
463
|
+
return originalUpdateMany({ ...restOpts, where, ctx, fields });
|
|
464
|
+
}
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
app.addHook("onReady", async function() {
|
|
469
|
+
if (opts.roleBasedAuth) {
|
|
470
|
+
await roleBasedAuth();
|
|
471
|
+
}
|
|
472
|
+
if (opts.scopeBasedAuth) {
|
|
473
|
+
await scopeBasedAuth();
|
|
474
|
+
}
|
|
388
475
|
});
|
|
389
476
|
}, { name: "@albirex/platformatic-logto" });
|
|
390
477
|
async function fromRuleToWhere(ctx, rule, where, user) {
|
|
@@ -429,6 +516,24 @@ async function findRuleForRequestUser(ctx, rules, roleKey, anonymousRole, isRole
|
|
|
429
516
|
ctx.reply.request.log.trace({ roles, rule }, "found rule");
|
|
430
517
|
return rule;
|
|
431
518
|
}
|
|
519
|
+
async function findScopeForRequestUser(ctx, entityKey, action, scopesKey, anonymousRole, isScopePath = false) {
|
|
520
|
+
const request = getRequestFromContext(ctx);
|
|
521
|
+
await request.setupDBAuthorizationUser();
|
|
522
|
+
const scopes = getScopes(request, scopesKey, anonymousRole, isScopePath);
|
|
523
|
+
const scope = scopes.find((s) => {
|
|
524
|
+
if (action !== "find") {
|
|
525
|
+
return s === `${action}:${entityKey}`;
|
|
526
|
+
} else {
|
|
527
|
+
return s === `${action}:${entityKey}` || s === `read:${entityKey}`;
|
|
528
|
+
}
|
|
529
|
+
});
|
|
530
|
+
if (!scope) {
|
|
531
|
+
ctx.reply.request.log.warn("no scope found");
|
|
532
|
+
throw new Unauthorized();
|
|
533
|
+
}
|
|
534
|
+
ctx.reply.request.log.trace({ scopes, scope }, "found scope");
|
|
535
|
+
return scope;
|
|
536
|
+
}
|
|
432
537
|
function checkFieldsFromRule(rule, fields) {
|
|
433
538
|
if (!rule) {
|
|
434
539
|
throw new Unauthorized();
|
|
@@ -523,6 +628,7 @@ export {
|
|
|
523
628
|
deletePermissionsVersion,
|
|
524
629
|
fastifyLogto2 as fastifyLogto,
|
|
525
630
|
findRuleForRequestUser,
|
|
631
|
+
findScopeForRequestUser,
|
|
526
632
|
getRequestFromContext,
|
|
527
633
|
getRoles,
|
|
528
634
|
incrementPermissionsVersion,
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/utils/find-rule.ts","../src/utils/errors.ts","../src/utils/permissions-version.ts","../src/utils/utils.ts"],"sourcesContent":["import fp from 'fastify-plugin'\nimport * as fastifyUser from 'fastify-user'\n\nimport findRule from './utils/find-rule.js'\nimport { Unauthorized, UnauthorizedField, MissingNotNullableError, PermissionsOutdated } from './utils/errors.js'\nimport fastifyLogto from '@albirex/fastify-logto';\nimport fastifyRedis from '@fastify/redis';\nimport { FastifyInstance, FastifyPluginAsync } from 'fastify'\nimport type { FastifyUserPluginOptions } from 'fastify-user';\nimport type { Entity, PlatformaticContext } from '@platformatic/sql-mapper'\n\nexport { fastifyLogto } from '@albirex/fastify-logto';\nexport { incrementPermissionsVersion, deletePermissionsVersion } from './utils/permissions-version.js';\n\nimport { getRequestFromContext, getRoles } from './utils/utils.js'\nexport { getRequestFromContext, getRoles } from './utils/utils.js'\n\nconst PLT_ADMIN_ROLE = 'platformatic-admin'\n\nexport type PlatformaticRule = {\n role: string;\n entity?: string;\n entities?: string[];\n defaults?: Record<string, string>;\n checks?: boolean;\n find?: boolean;\n save?: boolean;\n delete?: boolean;\n [action: string]: unknown;\n};\n\nexport type RedisConfig = {\n host?: string;\n port?: number;\n password?: string;\n username?: string;\n db?: number;\n};\n\nexport type PlatformaticLogtoAuthOptions = {\n logtoBaseUrl?: string;\n logtoAppId?: string;\n logtoAppSecret?: string;\n adminSecret?: string;\n rolePath?: string;\n roleKey?: string;\n userPath?: string;\n userKey?: string;\n anonymousRole?: string;\n allowAnonymous?: boolean;\n checks?: boolean;\n defaults?: boolean;\n jwtPlugin: FastifyUserPluginOptions;\n redis?: RedisConfig;\n enableTokenVersionCheck?: boolean;\n sessionVersionKey?: string;\n};\n\nexport const platformaticLogto: FastifyPluginAsync<PlatformaticLogtoAuthOptions> = fp(async (app: FastifyInstance, opts: PlatformaticLogtoAuthOptions) => {\n app.decorate('platformaticLogTo', {\n opts\n })\n\n // Register Redis if configured\n if (opts.redis) {\n await app.register(fastifyRedis, {\n host: opts.redis.host || '127.0.0.1',\n port: opts.redis.port || 6379,\n password: opts.redis.password,\n username: opts.redis.username,\n db: opts.redis.db || 0,\n });\n app.log.info('Redis client registered for permissions version check');\n }\n\n app.register(fastifyLogto, {\n endpoint: opts.logtoBaseUrl || 'https://auth.example.com',\n appId: opts.logtoAppId || 'your-app-id',\n appSecret: opts.logtoAppSecret || 'your-app-secret',\n });\n\n await app.register(fastifyUser as unknown as FastifyPluginAsync, opts.jwtPlugin);\n\n const adminSecret = opts.adminSecret\n const roleKey = opts.rolePath || opts.roleKey || 'X-PLATFORMATIC-ROLE'\n const userKey = opts.userPath || opts.userKey || 'X-PLATFORMATIC-USER-ID'\n const isRolePath = !!opts.rolePath // if `true` the role is intepreted as path like `user.role`\n const anonymousRole = opts.anonymousRole || 'anonymous'\n\n async function composeLogToRules() {\n const rolesResp = await app.logto.callAPI('/api/roles?type=User', 'GET');\n\n if (!rolesResp.ok) {\n throw rolesResp;\n }\n\n const roles = await rolesResp.json();\n const rules: PlatformaticRule[] = [{\n role: anonymousRole,\n entities: Object.keys(app.platformatic.entities),\n find: opts.allowAnonymous,\n save: opts.allowAnonymous,\n delete: opts.allowAnonymous,\n }];\n\n for (const role of roles) {\n const scopesResp = await app.logto.callAPI(`/api/roles/${role.id}/scopes`, 'GET');\n\n if (!scopesResp.ok) {\n throw scopesResp;\n }\n\n const scopes = await scopesResp.json();\n\n for (const scope of scopes) {\n const roleName = role.name;\n // eslint-disable-next-line prefer-const\n let [scopeAction, entity] = scope.name.split(':');\n\n if (!app.platformatic.entities[entity]) {\n app.log.debug(`Unknown entity '${entity}' in authorization rule`)\n continue;\n }\n\n switch (scopeAction) {\n case 'create':\n scopeAction = 'save'\n break;\n case 'read':\n scopeAction = 'find'\n break;\n case 'update':\n scopeAction = 'updateMany'\n break;\n }\n\n const checkExists = rules.find(r => r.role === roleName && r.entity === entity);\n if (checkExists) {\n if (opts.checks) {\n checkExists[scopeAction] = {\n checks: {\n userId: userKey\n }\n };\n } else {\n checkExists[scopeAction] = true;\n }\n } else {\n const newRule: PlatformaticRule = {\n role: roleName,\n entity,\n };\n\n if (opts.checks) {\n newRule[scopeAction] = {\n checks: {\n userId: userKey\n }\n };\n } else {\n newRule[scopeAction] = true;\n }\n\n if (opts.defaults) {\n newRule.defaults = {\n userId: userKey\n };\n }\n\n rules.push(newRule);\n }\n }\n }\n\n const logRules = rules.reduce((prev, curr) => {\n (prev[curr['role']] ??= []).push(curr);\n return prev;\n }, {})\n\n for (const key in logRules) {\n app.log.info(`Rules set for role ${key}`);\n\n for (const element of logRules[key]) {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { entity, entities, role, ...other } = element;\n app.log.info(`\\t${entity ?? entities.join(',')}: ${JSON.stringify(other)}`);\n }\n }\n\n const missingEntities = Object.keys(app.platformatic.entities).filter((e) => !rules.map((r) => r.entity).includes(e));\n\n if (missingEntities.length) {\n app.log.warn(`Missing rules for entities: ${missingEntities.join(', ')}`);\n }\n\n app.log.debug('LogTo calculated rules');\n app.log.debug(rules);\n return rules;\n }\n\n app.decorateRequest('setupDBAuthorizationUser', setupUser)\n\n async function setupUser() {\n // if (!adminSecret) {\n await this.extractUser()\n // }\n\n let forceAdminRole = false\n if (adminSecret && this.headers['x-platformatic-admin-secret'] === adminSecret) {\n if (opts.jwtPlugin.jwt) {\n forceAdminRole = true\n } else {\n this.log.info('admin secret is valid')\n this.user = new Proxy(this.headers, {\n get: (target, key) => {\n let value;\n if (!target[key.toString()]) {\n const newKey = key.toString().toLowerCase()\n value = target[newKey]\n } else {\n value = target[key.toString()]\n }\n\n if (!value && key.toString().toLowerCase() === roleKey.toLowerCase()) {\n value = PLT_ADMIN_ROLE\n }\n return value\n },\n })\n }\n } else {\n await checkPermissionsVersion(app, opts, this.user)\n }\n\n if (forceAdminRole) {\n // We replace just the role in `request.user`, all the rest is untouched\n this.user = {\n // ...request.user,\n [roleKey]: this.headers['x-platformatic-role'] ? [this.headers['x-platformatic-role']] : [PLT_ADMIN_ROLE]\n }\n }\n }\n\n app.addHook('onReady', async function () {\n const logToRules = await composeLogToRules();\n\n app.platformaticLogTo.rules = logToRules;\n\n // TODO validate that there is at most a rule for a given role\n const entityRules = {};\n for (let i = 0; i < logToRules.length; i++) {\n const rule = logToRules[i]\n\n let ruleEntities = null\n if (rule.entity) {\n ruleEntities = [rule.entity]\n } else if (rule.entities) {\n ruleEntities = [...rule.entities]\n } else {\n throw new Error(`Missing entity in authorization rule ${i}`)\n }\n\n for (const ruleEntity of ruleEntities) {\n const newRule = { ...rule, entity: ruleEntity, entities: undefined }\n if (!app.platformatic.entities[newRule.entity]) {\n throw new Error(`Unknown entity '${ruleEntity}' in authorization rule ${i}`)\n }\n\n if (!entityRules[ruleEntity]) {\n entityRules[ruleEntity] = []\n }\n entityRules[ruleEntity].push(newRule)\n }\n }\n\n for (const entityKey of Object.keys(app.platformatic.entities)) {\n const rules = entityRules[entityKey] || []\n const type = app.platformatic.entities[entityKey]\n\n // We have subscriptions!\n let userPropToFillForPublish\n const topicsWithoutChecks = false\n\n // mqtt\n // if (app.platformatic.mq) {\n // for (const rule of rules) {\n // const checks = rule.find?.checks\n // if (typeof checks !== 'object') {\n // topicsWithoutChecks = !!rule.find\n // continue\n // }\n // const keys = Object.keys(checks)\n // if (keys.length !== 1) {\n // throw new Error(`Subscription requires that the role \"${rule.role}\" has only one check in the find rule for entity \"${rule.entity}\"`)\n // }\n // const key = keys[0]\n\n // const val = typeof checks[key] === 'object' ? checks[key].eq : checks[key]\n // if (userPropToFillForPublish && userPropToFillForPublish.val !== val) {\n // throw new Error('Unable to configure subscriptions and authorization due to multiple check clauses in find')\n // }\n // userPropToFillForPublish = { key, val }\n // }\n // }\n\n if (userPropToFillForPublish && topicsWithoutChecks) {\n throw new Error(`Subscription for entity \"${entityKey}\" have conflictling rules across roles`)\n }\n\n // MUST set this after doing the security checks on the subscriptions\n if (adminSecret) {\n rules.push({\n role: PLT_ADMIN_ROLE,\n find: true,\n save: true,\n delete: true,\n updateMany: true\n })\n }\n\n // If we have `fields` in save rules, we need to check if all the not-nullable\n // fields are specified\n checkSaveMandatoryFieldsInRules(type, rules)\n\n function useOriginal(ctx: PlatformaticContext) {\n return !ctx\n }\n\n app.platformatic.addEntityHooks(entityKey, {\n async find(originalFind, { where, ctx, fields, ...restOpts } = {}) {\n if (useOriginal(ctx)) {\n return originalFind({ ...restOpts, where, ctx, fields })\n }\n const request = getRequestFromContext(ctx)\n const rule = await findRuleForRequestUser(ctx, rules, roleKey, anonymousRole, isRolePath)\n checkFieldsFromRule(rule.find, fields || Object.keys(app.platformatic.entities[entityKey].fields))\n where = await fromRuleToWhere(ctx, rule.find, where, request.user)\n\n return originalFind({ ...restOpts, where, ctx, fields })\n },\n async save(originalSave, { input, ctx, fields, ...restOpts }) {\n if (useOriginal(ctx)) {\n return originalSave({ ctx, input, fields, ...restOpts })\n }\n const request = getRequestFromContext(ctx)\n const rule = await findRuleForRequestUser(ctx, rules, roleKey, anonymousRole, isRolePath)\n\n if (!rule.save) {\n throw new Unauthorized()\n }\n checkFieldsFromRule(rule.save, fields)\n checkInputFromRuleFields(rule.save, input)\n\n if (rule.defaults) {\n for (const key of Object.keys(rule.defaults)) {\n const defaults = rule.defaults[key]\n if (typeof defaults === 'function') {\n input[key] = await defaults({ user: request.user, ctx, input })\n } else {\n input[key] = request.user[defaults]\n }\n }\n }\n\n const hasAllPrimaryKeys = input[type.primaryKey] !== undefined;\n const whereConditions = {}\n whereConditions[type.primaryKey] = { eq: input[type.primaryKey] }\n\n if (hasAllPrimaryKeys) {\n const where = await fromRuleToWhere(ctx, rule.save, whereConditions, request.user)\n\n const found = await type.find({\n where,\n ctx,\n fields,\n })\n\n if (found.length === 0) {\n throw new Unauthorized()\n }\n\n return originalSave({ input, ctx, fields, ...restOpts })\n }\n\n return originalSave({ input, ctx, fields, ...restOpts })\n },\n\n async insert(originalInsert, { inputs, ctx, fields, ...restOpts }) {\n if (useOriginal(ctx)) {\n return originalInsert({ inputs, ctx, fields, ...restOpts })\n }\n const request = getRequestFromContext(ctx)\n const rule = await findRuleForRequestUser(ctx, rules, roleKey, anonymousRole, isRolePath)\n\n if (!rule.save) {\n throw new Unauthorized()\n }\n\n checkFieldsFromRule(rule.save, fields)\n checkInputFromRuleFields(rule.save, inputs)\n\n /* istanbul ignore else */\n if (rule.defaults) {\n for (const input of inputs) {\n for (const key of Object.keys(rule.defaults)) {\n const defaults = rule.defaults[key]\n if (typeof defaults === 'function') {\n input[key] = await defaults({ user: request.user, ctx, input })\n } else {\n input[key] = request.user[defaults]\n }\n }\n }\n }\n\n return originalInsert({ inputs, ctx, fields, ...restOpts })\n },\n\n async delete(originalDelete, { where, ctx, fields, ...restOpts }) {\n if (useOriginal(ctx)) {\n return originalDelete({ where, ctx, fields, ...restOpts })\n }\n const request = getRequestFromContext(ctx)\n const rule = await findRuleForRequestUser(ctx, rules, roleKey, anonymousRole, isRolePath)\n\n where = await fromRuleToWhere(ctx, rule.delete, where, request.user)\n\n return originalDelete({ where, ctx, fields, ...restOpts })\n },\n\n async updateMany(originalUpdateMany, { where, ctx, fields, ...restOpts }) {\n if (useOriginal(ctx)) {\n return originalUpdateMany({ ...restOpts, where, ctx, fields })\n }\n const request = getRequestFromContext(ctx)\n const rule = await findRuleForRequestUser(ctx, rules, roleKey, anonymousRole, isRolePath)\n\n where = await fromRuleToWhere(ctx, rule.updateMany, where, request.user)\n\n return originalUpdateMany({ ...restOpts, where, ctx, fields })\n },\n })\n }\n })\n}, { name: '@albirex/platformatic-logto' });\n\nasync function fromRuleToWhere(ctx: PlatformaticContext, rule, where, user) {\n if (!rule) {\n throw new Unauthorized()\n }\n const request = getRequestFromContext(ctx)\n /* istanbul ignore next */\n where = where || {}\n\n if (typeof rule === 'object') {\n const { checks } = rule\n\n /* istanbul ignore else */\n if (checks) {\n for (const key of Object.keys(checks)) {\n const clauses = checks[key]\n if (typeof clauses === 'string') {\n // case: \"userId\": \"X-PLATFORMATIC-USER-ID\"\n where[key] = {\n eq: request.user[clauses],\n }\n } else {\n // case:\n // userId: {\n // eq: 'X-PLATFORMATIC-USER-ID'\n // }\n for (const clauseKey of Object.keys(clauses)) {\n const clause = clauses[clauseKey]\n where[key] = {\n [clauseKey]: request.user[clause],\n }\n }\n }\n }\n }\n } else if (typeof rule === 'function') {\n where = await rule({ user, ctx, where })\n }\n return where\n}\n\nexport async function findRuleForRequestUser(ctx: PlatformaticContext, rules: PlatformaticRule[], roleKey: string, anonymousRole: string, isRolePath = false) {\n const request = getRequestFromContext(ctx)\n await request.setupDBAuthorizationUser()\n const roles = getRoles(request, roleKey, anonymousRole, isRolePath)\n const rule = findRule(rules, roles)\n if (!rule) {\n ctx.reply.request.log.warn({ roles, rules }, 'no rule for roles')\n throw new Unauthorized()\n }\n ctx.reply.request.log.trace({ roles, rule }, 'found rule')\n return rule\n}\n\nexport function checkFieldsFromRule(rule, fields) {\n if (!rule) {\n throw new Unauthorized()\n }\n const { fields: fieldsFromRule } = rule\n /* istanbul ignore else */\n if (fieldsFromRule) {\n for (const field of fields) {\n if (!fieldsFromRule.includes(field)) {\n throw new UnauthorizedField(field)\n }\n }\n }\n}\n\nconst validateInputs = (inputs, fieldsFromRule) => {\n for (const input of inputs) {\n const inputFields = Object.keys(input)\n for (const inputField of inputFields) {\n if (!fieldsFromRule.includes(inputField)) {\n throw new UnauthorizedField(inputField)\n }\n }\n }\n}\n\nfunction checkInputFromRuleFields(rule, inputs) {\n const { fields: fieldsFromRule } = rule\n /* istanbul ignore else */\n if (fieldsFromRule) {\n if (!Array.isArray(inputs)) {\n // save\n validateInputs([inputs], fieldsFromRule)\n } else {\n // insert\n validateInputs(inputs, fieldsFromRule)\n }\n }\n}\n\nfunction checkSaveMandatoryFieldsInRules(type: Entity, rules) {\n // List of not nullable, not PKs field to validate save/insert when allowed fields are specified on the rule\n const mandatoryFields =\n Object.values(type.fields)\n .filter(k => (!k.isNullable && !k.primaryKey))\n .map(({ camelcase }) => (camelcase))\n\n for (const rule of rules) {\n const { entity, save } = rule\n if (save && save.fields) {\n const fields = save.fields\n for (const mField of mandatoryFields) {\n if (!fields.includes(mField)) {\n throw new MissingNotNullableError(mField, entity)\n }\n }\n }\n }\n}\n\n/**\n * Check the permissions version\n * @throws PermissionsOutdated if versions don't match\n */\nasync function checkPermissionsVersion(app: FastifyInstance, opts: PlatformaticLogtoAuthOptions, user = null) {\n\n if (!opts.enableTokenVersionCheck || !opts.redis || !app.redis || !user) {\n return;\n }\n\n const sessionVersionKey = opts.sessionVersionKey || 'version'\n const tokenVersion = user?.[sessionVersionKey];\n const userId = user?.sub;\n\n try {\n const redisKey = `permissions:version:${userId}`;\n const currentVersionStr = await app.redis.get(redisKey);\n const currentVersion = parseInt(currentVersionStr, 10);\n\n if (currentVersionStr === null) {\n await app.redis.set(redisKey, tokenVersion.toString());\n app.log.debug({ userId, version: tokenVersion }, 'Initialized permissions version in Redis');\n if (tokenVersion > 1) {\n app.log.warn({\n userId,\n tokenVersion,\n currentVersion\n }, 'Permissions version mismatch detected');\n throw new PermissionsOutdated(); \n }\n return;\n }\n\n // Compare versions\n if (currentVersion !== tokenVersion) {\n app.log.warn({\n userId,\n tokenVersion,\n currentVersion\n }, 'Permissions version mismatch detected');\n throw new PermissionsOutdated();\n }\n\n app.log.trace({ userId, version: tokenVersion }, 'Token version check passed');\n } catch (error) {\n if (error.name === 'FastifyError' && error.code === 'PLT_DB_AUTH_VERSION_OUTDATED') {\n throw error;\n }\n app.log.error({ err: error, userId }, 'Error checking token version');\n }\n}\n\nexport default platformaticLogto;\n\ndeclare module 'fastify' {\n interface FastifyInstance {\n platformaticLogTo: {\n opts: PlatformaticLogtoAuthOptions,\n rules?: PlatformaticRule[]\n }\n }\n}","'use strict'\n\nimport { PlatformaticRule } from '../index.js'\n\nfunction findRule(rules: PlatformaticRule[], roles: string[]) {\n let found = null\n for (const rule of rules) {\n for (const role of roles) {\n if (rule.role === role) {\n found = rule\n break\n }\n }\n if (found) {\n break\n }\n }\n return found\n}\n\nexport default findRule\n","'use strict'\n\nimport createError from '@fastify/error'\n\nconst ERROR_PREFIX = 'PLT_DB_AUTH'\n\nexport const Unauthorized = createError(`${ERROR_PREFIX}_UNAUTHORIZED`, 'operation not allowed', 401)\nexport const UnauthorizedField = createError(`${ERROR_PREFIX}_FIELD_UNAUTHORIZED`, 'field not allowed: %s', 401)\nexport const MissingNotNullableError = createError(`${ERROR_PREFIX}_NOT_NULLABLE_MISSING`, 'missing not nullable field: \"%s\" in save rule for entity \"%s\"')\nexport const PermissionsOutdated = createError(`${ERROR_PREFIX}_VERSION_OUTDATED`, 'Verision has been updated. Please logout and login again.', 401)\n","'use strict'\n\nimport type { FastifyInstance } from 'fastify'\n\n/**\n * Increment the permissions version\n * @param app - Fastify instance with Redis client and Logto\n * @param userId - User ID\n * @returns New version number\n */\nexport async function incrementPermissionsVersion(app: FastifyInstance, userId: string): Promise<number> {\n if (!app.logto) {\n throw new Error('Logto client not available')\n }\n\n const userResp = await app.logto.callAPI(`/api/users/${userId}`, 'GET')\n const userData = await userResp.json()\n const existingCustomData = userData.customData || {}\n const currentVersion = existingCustomData.sessionVersion || 0\n const newVersion = currentVersion + 1\n\n await app.logto.callAPI(`/api/users/${userId}`, 'PATCH', JSON.stringify({ customData: { ...existingCustomData, sessionVersion: newVersion } }))\n\n if (app.redis) {\n const redisKey = `permissions:version:${userId}`\n await app.redis.set(redisKey, newVersion.toString())\n }\n\n return newVersion\n}\n\n/**\n * Delete the permissions version for a user\n * @param app - Fastify instance with Redis client\n * @param userId - User ID\n */\nexport async function deletePermissionsVersion(app: FastifyInstance, userId: string): Promise<void> {\n if (!app.redis) {\n throw new Error('Redis client not available')\n }\n\n const redisKey = `permissions:version:${userId}`\n await app.redis.del(redisKey)\n}\n\n","'use strict'\n\nexport function getRequestFromContext (ctx) {\n if (ctx && !ctx.reply) {\n throw new Error('Missing reply in context. You should call this function with { ctx: { reply }}')\n }\n return ctx.reply.request\n}\n\nexport function getRoles (request, roleKey, anonymousRole, isRolePath = false) {\n let output = []\n const user = request.user\n if (!user) {\n output.push(anonymousRole)\n return output\n }\n\n let rolesRaw\n if (isRolePath) {\n const roleKeys = roleKey.split('.')\n rolesRaw = user\n for (const key of roleKeys) {\n rolesRaw = rolesRaw[key]\n }\n } else {\n rolesRaw = user[roleKey]\n }\n\n if (typeof rolesRaw === 'string') {\n output = rolesRaw.split(',')\n } else if (Array.isArray(rolesRaw)) {\n output = rolesRaw\n }\n if (output.length === 0) {\n output.push(anonymousRole)\n }\n\n return output\n}\n"],"mappings":"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","names":["fastifyLogto"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/utils/find-rule.ts","../src/utils/errors.ts","../src/utils/permissions-version.ts","../src/utils/utils.ts"],"sourcesContent":["import fp from 'fastify-plugin'\nimport * as fastifyUser from 'fastify-user'\n\nimport findRule from './utils/find-rule.js'\nimport { Unauthorized, UnauthorizedField, MissingNotNullableError, PermissionsOutdated } from './utils/errors.js'\nimport fastifyLogto from '@albirex/fastify-logto';\nimport fastifyRedis from '@fastify/redis';\nimport { FastifyInstance, FastifyPluginAsync } from 'fastify'\nimport type { FastifyUserPluginOptions } from 'fastify-user';\nimport type { Entity, PlatformaticContext } from '@platformatic/sql-mapper'\n\nexport { fastifyLogto } from '@albirex/fastify-logto';\nexport { incrementPermissionsVersion, deletePermissionsVersion } from './utils/permissions-version.js';\n\nimport { getRequestFromContext, getRoles, getScopes } from './utils/utils.js'\nexport { getRequestFromContext, getRoles } from './utils/utils.js'\n\nconst PLT_ADMIN_ROLE = 'platformatic-admin'\n\nexport type EntityActions = 'find' | 'save' | 'insert' | 'updateMany' | 'delete';\n\nexport type PlatformaticRule = {\n role: string;\n entity?: string;\n entities?: string[];\n defaults?: Record<string, string>;\n checks?: boolean;\n find?: boolean;\n save?: boolean;\n delete?: boolean;\n [action: string]: unknown;\n};\n\nexport type RedisConfig = {\n host?: string;\n port?: number;\n password?: string;\n username?: string;\n db?: number;\n};\n\nexport type PlatformaticLogToRoleBasedAuthOptions = {\n logtoBaseUrl?: string;\n logtoAppId?: string;\n logtoAppSecret?: string;\n rolePath?: string;\n roleKey?: string;\n userPath?: string;\n userKey?: string;\n}\n\nexport type PlatformaticLogToScopeBasedAuthOptions = {\n scopesPath?: string;\n scopesKey?: string;\n}\n\nexport type PlatformaticLogtoAuthOptions = {\n adminSecret?: string;\n roleBasedAuth?: PlatformaticLogToRoleBasedAuthOptions,\n scopeBasedAuth?: PlatformaticLogToScopeBasedAuthOptions,\n checks?: boolean;\n defaults?: boolean;\n anonymousRole?: string;\n allowAnonymous?: boolean;\n jwtPlugin: FastifyUserPluginOptions;\n redis?: RedisConfig;\n enableTokenVersionCheck?: boolean;\n sessionVersionKey?: string;\n};\n\nexport const platformaticLogto: FastifyPluginAsync<PlatformaticLogtoAuthOptions> = fp(async (app: FastifyInstance, opts: PlatformaticLogtoAuthOptions) => {\n app.decorate('platformaticLogTo', {\n opts\n })\n\n // Register Redis if configured\n if (opts.redis) {\n await app.register(fastifyRedis, {\n host: opts.redis.host || '127.0.0.1',\n port: opts.redis.port || 6379,\n password: opts.redis.password,\n username: opts.redis.username,\n db: opts.redis.db || 0,\n });\n app.log.info('Redis client registered for permissions version check');\n }\n\n if (opts.roleBasedAuth) {\n app.register(fastifyLogto, {\n endpoint: opts.roleBasedAuth.logtoBaseUrl || 'https://auth.example.com',\n appId: opts.roleBasedAuth.logtoAppId || 'your-app-id',\n appSecret: opts.roleBasedAuth.logtoAppSecret || 'your-app-secret',\n });\n }\n\n await app.register(fastifyUser as unknown as FastifyPluginAsync, opts.jwtPlugin);\n\n const roleKey = opts.roleBasedAuth?.rolePath || opts.roleBasedAuth?.roleKey || 'X-PLATFORMATIC-ROLE';\n const adminSecret = opts.adminSecret\n const isRolePath = !!opts.roleBasedAuth?.rolePath // if `true` the role is intepreted as path like `user.role`\n const anonymousRole = opts.anonymousRole || 'anonymous'\n\n app.decorateRequest('setupDBAuthorizationUser', setupUser)\n\n async function setupUser() {\n // if (!adminSecret) {\n await this.extractUser()\n // }\n\n let forceAdminRole = false\n if (adminSecret && this.headers['x-platformatic-admin-secret'] === adminSecret) {\n if (opts.jwtPlugin.jwt) {\n forceAdminRole = true\n } else {\n this.log.info('admin secret is valid')\n this.user = new Proxy(this.headers, {\n get: (target, key) => {\n let value;\n if (!target[key.toString()]) {\n const newKey = key.toString().toLowerCase()\n value = target[newKey]\n } else {\n value = target[key.toString()]\n }\n\n if (!value && key.toString().toLowerCase() === roleKey.toLowerCase()) {\n value = PLT_ADMIN_ROLE\n }\n return value\n },\n })\n }\n } else {\n await checkPermissionsVersion(app, opts, this.user)\n }\n\n if (forceAdminRole) {\n // We replace just the role in `request.user`, all the rest is untouched\n this.user = {\n // ...request.user,\n [roleKey]: this.headers['x-platformatic-role'] ? [this.headers['x-platformatic-role']] : [PLT_ADMIN_ROLE]\n }\n }\n }\n\n async function roleBasedAuth() {\n const userKey = opts.roleBasedAuth.userKey || opts.roleBasedAuth.userPath || 'X-PLATFORMATIC-USER-ID';\n\n async function composeLogToRules() {\n const rolesResp = await app.logto.callAPI('/api/roles?type=User', 'GET');\n\n if (!rolesResp.ok) {\n throw rolesResp;\n }\n\n const roles = await rolesResp.json();\n const rules: PlatformaticRule[] = [{\n role: anonymousRole,\n entities: Object.keys(app.platformatic.entities),\n find: opts.allowAnonymous,\n save: opts.allowAnonymous,\n delete: opts.allowAnonymous,\n }];\n\n for (const role of roles) {\n const scopesResp = await app.logto.callAPI(`/api/roles/${role.id}/scopes`, 'GET');\n\n if (!scopesResp.ok) {\n throw scopesResp;\n }\n\n const scopes = await scopesResp.json();\n\n for (const scope of scopes) {\n const roleName = role.name;\n // eslint-disable-next-line prefer-const\n let [scopeAction, entity] = scope.name.split(':');\n\n if (!app.platformatic.entities[entity]) {\n app.log.debug(`Unknown entity '${entity}' in authorization rule`)\n continue;\n }\n\n switch (scopeAction) {\n case 'create':\n scopeAction = 'save'\n break;\n case 'read':\n scopeAction = 'find'\n break;\n case 'update':\n scopeAction = 'updateMany'\n break;\n }\n\n const checkExists = rules.find(r => r.role === roleName && r.entity === entity);\n if (checkExists) {\n if (opts.checks) {\n checkExists[scopeAction] = {\n checks: {\n userId: userKey\n }\n };\n } else {\n checkExists[scopeAction] = true;\n }\n } else {\n const newRule: PlatformaticRule = {\n role: roleName,\n entity,\n };\n\n if (opts.checks) {\n newRule[scopeAction] = {\n checks: {\n userId: userKey\n }\n };\n } else {\n newRule[scopeAction] = true;\n }\n\n if (opts.defaults) {\n newRule.defaults = {\n userId: userKey\n };\n }\n\n rules.push(newRule);\n }\n }\n }\n\n const logRules = rules.reduce((prev, curr) => {\n (prev[curr['role']] ??= []).push(curr);\n return prev;\n }, {})\n\n for (const key in logRules) {\n app.log.info(`Rules set for role ${key}`);\n\n for (const element of logRules[key]) {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { entity, entities, role, ...other } = element;\n app.log.info(`\\t${entity ?? entities.join(',')}: ${JSON.stringify(other)}`);\n }\n }\n\n const missingEntities = Object.keys(app.platformatic.entities).filter((e) => !rules.map((r) => r.entity).includes(e));\n\n if (missingEntities.length) {\n app.log.warn(`Missing rules for entities: ${missingEntities.join(', ')}`);\n }\n\n app.log.debug('LogTo calculated rules');\n app.log.debug(rules);\n return rules;\n }\n\n const logToRules = await composeLogToRules();\n\n app.platformaticLogTo.rules = logToRules;\n\n // TODO validate that there is at most a rule for a given role\n const entityRules = {};\n for (let i = 0; i < logToRules.length; i++) {\n const rule = logToRules[i]\n\n let ruleEntities = null\n if (rule.entity) {\n ruleEntities = [rule.entity]\n } else if (rule.entities) {\n ruleEntities = [...rule.entities]\n } else {\n throw new Error(`Missing entity in authorization rule ${i}`)\n }\n\n for (const ruleEntity of ruleEntities) {\n const newRule = { ...rule, entity: ruleEntity, entities: undefined }\n if (!app.platformatic.entities[newRule.entity]) {\n throw new Error(`Unknown entity '${ruleEntity}' in authorization rule ${i}`)\n }\n\n if (!entityRules[ruleEntity]) {\n entityRules[ruleEntity] = []\n }\n entityRules[ruleEntity].push(newRule)\n }\n }\n\n for (const entityKey of Object.keys(app.platformatic.entities)) {\n const rules = entityRules[entityKey] || []\n const type = app.platformatic.entities[entityKey]\n\n // We have subscriptions!\n let userPropToFillForPublish\n const topicsWithoutChecks = false\n\n if (userPropToFillForPublish && topicsWithoutChecks) {\n throw new Error(`Subscription for entity \"${entityKey}\" have conflictling rules across roles`)\n }\n\n // MUST set this after doing the security checks on the subscriptions\n if (adminSecret) {\n rules.push({\n role: PLT_ADMIN_ROLE,\n find: true,\n save: true,\n delete: true,\n updateMany: true\n })\n }\n\n // If we have `fields` in save rules, we need to check if all the not-nullable\n // fields are specified\n checkSaveMandatoryFieldsInRules(type, rules)\n\n function useOriginal(ctx: PlatformaticContext) {\n return !ctx\n }\n\n app.platformatic.addEntityHooks(entityKey, {\n async find(originalFind, { where, ctx, fields, ...restOpts } = {}) {\n if (useOriginal(ctx)) {\n return originalFind({ ...restOpts, where, ctx, fields })\n }\n const request = getRequestFromContext(ctx)\n const rule = await findRuleForRequestUser(ctx, rules, roleKey, anonymousRole, isRolePath)\n checkFieldsFromRule(rule.find, fields || Object.keys(app.platformatic.entities[entityKey].fields))\n where = await fromRuleToWhere(ctx, rule.find, where, request.user)\n\n return originalFind({ ...restOpts, where, ctx, fields })\n },\n async save(originalSave, { input, ctx, fields, ...restOpts }) {\n if (useOriginal(ctx)) {\n return originalSave({ ctx, input, fields, ...restOpts })\n }\n const request = getRequestFromContext(ctx)\n const rule = await findRuleForRequestUser(ctx, rules, roleKey, anonymousRole, isRolePath)\n\n if (!rule.save) {\n throw new Unauthorized()\n }\n checkFieldsFromRule(rule.save, fields)\n checkInputFromRuleFields(rule.save, input)\n\n if (rule.defaults) {\n for (const key of Object.keys(rule.defaults)) {\n const defaults = rule.defaults[key]\n if (typeof defaults === 'function') {\n input[key] = await defaults({ user: request.user, ctx, input })\n } else {\n input[key] = request.user[defaults]\n }\n }\n }\n\n const hasAllPrimaryKeys = input[type.primaryKey] !== undefined;\n const whereConditions = {}\n whereConditions[type.primaryKey] = { eq: input[type.primaryKey] }\n\n if (hasAllPrimaryKeys) {\n const where = await fromRuleToWhere(ctx, rule.save, whereConditions, request.user)\n\n const found = await type.find({\n where,\n ctx,\n fields,\n })\n\n if (found.length === 0) {\n throw new Unauthorized()\n }\n\n return originalSave({ input, ctx, fields, ...restOpts })\n }\n\n return originalSave({ input, ctx, fields, ...restOpts })\n },\n\n async insert(originalInsert, { inputs, ctx, fields, ...restOpts }) {\n if (useOriginal(ctx)) {\n return originalInsert({ inputs, ctx, fields, ...restOpts })\n }\n const request = getRequestFromContext(ctx)\n const rule = await findRuleForRequestUser(ctx, rules, roleKey, anonymousRole, isRolePath)\n\n if (!rule.save) {\n throw new Unauthorized()\n }\n\n checkFieldsFromRule(rule.save, fields)\n checkInputFromRuleFields(rule.save, inputs)\n\n /* istanbul ignore else */\n if (rule.defaults) {\n for (const input of inputs) {\n for (const key of Object.keys(rule.defaults)) {\n const defaults = rule.defaults[key]\n if (typeof defaults === 'function') {\n input[key] = await defaults({ user: request.user, ctx, input })\n } else {\n input[key] = request.user[defaults]\n }\n }\n }\n }\n\n return originalInsert({ inputs, ctx, fields, ...restOpts })\n },\n\n async delete(originalDelete, { where, ctx, fields, ...restOpts }) {\n if (useOriginal(ctx)) {\n return originalDelete({ where, ctx, fields, ...restOpts })\n }\n const request = getRequestFromContext(ctx)\n const rule = await findRuleForRequestUser(ctx, rules, roleKey, anonymousRole, isRolePath)\n\n where = await fromRuleToWhere(ctx, rule.delete, where, request.user)\n\n return originalDelete({ where, ctx, fields, ...restOpts })\n },\n\n async updateMany(originalUpdateMany, { where, ctx, fields, ...restOpts }) {\n if (useOriginal(ctx)) {\n return originalUpdateMany({ ...restOpts, where, ctx, fields })\n }\n const request = getRequestFromContext(ctx)\n const rule = await findRuleForRequestUser(ctx, rules, roleKey, anonymousRole, isRolePath)\n\n where = await fromRuleToWhere(ctx, rule.updateMany, where, request.user)\n\n return originalUpdateMany({ ...restOpts, where, ctx, fields })\n },\n })\n }\n }\n\n async function scopeBasedAuth() {\n const scopesKey = opts.scopeBasedAuth.scopesPath || opts.scopeBasedAuth.scopesKey || 'X-PLATFORMATIC-SCOPES'\n for (const entityKey of Object.keys(app.platformatic.entities)) {\n // const type = app.platformatic.entities[entityKey]\n\n // We have subscriptions!\n let userPropToFillForPublish\n const topicsWithoutChecks = false\n\n if (userPropToFillForPublish && topicsWithoutChecks) {\n throw new Error(`Subscription for entity \"${entityKey}\" have conflictling rules across roles`)\n }\n\n function useOriginal(ctx: PlatformaticContext) {\n return !ctx\n }\n\n app.platformatic.addEntityHooks(entityKey, {\n async find(originalFind, { where, ctx, fields, ...restOpts } = {}) {\n if (useOriginal(ctx)) {\n return originalFind({ ...restOpts, where, ctx, fields })\n }\n await findScopeForRequestUser(ctx, entityKey, 'find', scopesKey, anonymousRole, isRolePath)\n\n // checkFieldsFromRule(scope.find, fields || Object.keys(app.platformatic.entities[entityKey].fields))\n // where = await fromRuleToWhere(ctx, scope.find, where, request.user)\n\n return originalFind({ ...restOpts, where, ctx, fields })\n },\n async save(originalSave, { input, ctx, fields, ...restOpts }) {\n if (useOriginal(ctx)) {\n return originalSave({ ctx, input, fields, ...restOpts })\n }\n await findScopeForRequestUser(ctx, entityKey, 'save', scopesKey, anonymousRole, isRolePath)\n\n // checkFieldsFromRule(rule.save, fields)\n // checkInputFromRuleFields(rule.save, input)\n\n // if (rule.defaults) {\n // for (const key of Object.keys(rule.defaults)) {\n // const defaults = rule.defaults[key]\n // if (typeof defaults === 'function') {\n // input[key] = await defaults({ user: request.user, ctx, input })\n // } else {\n // input[key] = request.user[defaults]\n // }\n // }\n // }\n\n // const hasAllPrimaryKeys = input[type.primaryKey] !== undefined;\n // const whereConditions = {}\n // whereConditions[type.primaryKey] = { eq: input[type.primaryKey] }\n\n // if (hasAllPrimaryKeys) {\n // const where = await fromRuleToWhere(ctx, rule.save, whereConditions, request.user)\n\n // const found = await type.find({\n // where,\n // ctx,\n // fields,\n // })\n\n // if (found.length === 0) {\n // throw new Unauthorized()\n // }\n\n // return originalSave({ input, ctx, fields, ...restOpts })\n // }\n\n return originalSave({ input, ctx, fields, ...restOpts })\n },\n\n async insert(originalInsert, { inputs, ctx, fields, ...restOpts }) {\n if (useOriginal(ctx)) {\n return originalInsert({ inputs, ctx, fields, ...restOpts })\n }\n await findScopeForRequestUser(ctx, entityKey, 'insert', scopesKey, anonymousRole, isRolePath)\n\n // checkFieldsFromRule(rule.save, fields)\n // checkInputFromRuleFields(rule.save, inputs)\n\n /* istanbul ignore else */\n // if (rule.defaults) {\n // for (const input of inputs) {\n // for (const key of Object.keys(rule.defaults)) {\n // const defaults = rule.defaults[key]\n // if (typeof defaults === 'function') {\n // input[key] = await defaults({ user: request.user, ctx, input })\n // } else {\n // input[key] = request.user[defaults]\n // }\n // }\n // }\n // }\n\n return originalInsert({ inputs, ctx, fields, ...restOpts })\n },\n\n async delete(originalDelete, { where, ctx, fields, ...restOpts }) {\n if (useOriginal(ctx)) {\n return originalDelete({ where, ctx, fields, ...restOpts })\n }\n await findScopeForRequestUser(ctx, entityKey, 'delete', scopesKey, anonymousRole, isRolePath)\n\n // where = await fromRuleToWhere(ctx, rule.delete, where, request.user)\n\n return originalDelete({ where, ctx, fields, ...restOpts })\n },\n\n async updateMany(originalUpdateMany, { where, ctx, fields, ...restOpts }) {\n if (useOriginal(ctx)) {\n return originalUpdateMany({ ...restOpts, where, ctx, fields })\n }\n await findScopeForRequestUser(ctx, entityKey, 'updateMany', scopesKey, anonymousRole, isRolePath)\n\n // where = await fromRuleToWhere(ctx, rule.updateMany, where, request.user)\n\n return originalUpdateMany({ ...restOpts, where, ctx, fields })\n },\n })\n }\n }\n\n app.addHook('onReady', async function () {\n if (opts.roleBasedAuth) {\n await roleBasedAuth();\n }\n\n if (opts.scopeBasedAuth) {\n await scopeBasedAuth();\n }\n })\n}, { name: '@albirex/platformatic-logto' });\n\nasync function fromRuleToWhere(ctx: PlatformaticContext, rule, where, user) {\n if (!rule) {\n throw new Unauthorized()\n }\n const request = getRequestFromContext(ctx)\n /* istanbul ignore next */\n where = where || {}\n\n if (typeof rule === 'object') {\n const { checks } = rule\n\n /* istanbul ignore else */\n if (checks) {\n for (const key of Object.keys(checks)) {\n const clauses = checks[key]\n if (typeof clauses === 'string') {\n // case: \"userId\": \"X-PLATFORMATIC-USER-ID\"\n where[key] = {\n eq: request.user[clauses],\n }\n } else {\n // case:\n // userId: {\n // eq: 'X-PLATFORMATIC-USER-ID'\n // }\n for (const clauseKey of Object.keys(clauses)) {\n const clause = clauses[clauseKey]\n where[key] = {\n [clauseKey]: request.user[clause],\n }\n }\n }\n }\n }\n } else if (typeof rule === 'function') {\n where = await rule({ user, ctx, where })\n }\n return where\n}\n\nexport async function findRuleForRequestUser(ctx: PlatformaticContext, rules: PlatformaticRule[], roleKey: string, anonymousRole: string, isRolePath = false) {\n const request = getRequestFromContext(ctx)\n await request.setupDBAuthorizationUser()\n const roles = getRoles(request, roleKey, anonymousRole, isRolePath)\n const rule = findRule(rules, roles)\n if (!rule) {\n ctx.reply.request.log.warn({ roles, rules }, 'no rule for roles')\n throw new Unauthorized()\n }\n ctx.reply.request.log.trace({ roles, rule }, 'found rule')\n return rule\n}\n\nexport async function findScopeForRequestUser(ctx: PlatformaticContext, entityKey: string, action: EntityActions, scopesKey: string, anonymousRole: string, isScopePath = false) {\n const request = getRequestFromContext(ctx)\n await request.setupDBAuthorizationUser()\n const scopes = getScopes(request, scopesKey, anonymousRole, isScopePath)\n const scope = scopes.find(s => {\n if (action !== 'find') {\n return s === `${action}:${entityKey}`\n }\n else {\n return s === `${action}:${entityKey}` || s === `read:${entityKey}`\n }\n });\n\n if (!scope) {\n ctx.reply.request.log.warn('no scope found')\n throw new Unauthorized()\n }\n ctx.reply.request.log.trace({ scopes, scope }, 'found scope')\n return scope\n}\n\nexport function checkFieldsFromRule(rule, fields) {\n if (!rule) {\n throw new Unauthorized()\n }\n const { fields: fieldsFromRule } = rule\n /* istanbul ignore else */\n if (fieldsFromRule) {\n for (const field of fields) {\n if (!fieldsFromRule.includes(field)) {\n throw new UnauthorizedField(field)\n }\n }\n }\n}\n\nconst validateInputs = (inputs, fieldsFromRule) => {\n for (const input of inputs) {\n const inputFields = Object.keys(input)\n for (const inputField of inputFields) {\n if (!fieldsFromRule.includes(inputField)) {\n throw new UnauthorizedField(inputField)\n }\n }\n }\n}\n\nfunction checkInputFromRuleFields(rule, inputs) {\n const { fields: fieldsFromRule } = rule\n /* istanbul ignore else */\n if (fieldsFromRule) {\n if (!Array.isArray(inputs)) {\n // save\n validateInputs([inputs], fieldsFromRule)\n } else {\n // insert\n validateInputs(inputs, fieldsFromRule)\n }\n }\n}\n\nfunction checkSaveMandatoryFieldsInRules(type: Entity, rules) {\n // List of not nullable, not PKs field to validate save/insert when allowed fields are specified on the rule\n const mandatoryFields =\n Object.values(type.fields)\n .filter(k => (!k.isNullable && !k.primaryKey))\n .map(({ camelcase }) => (camelcase))\n\n for (const rule of rules) {\n const { entity, save } = rule\n if (save && save.fields) {\n const fields = save.fields\n for (const mField of mandatoryFields) {\n if (!fields.includes(mField)) {\n throw new MissingNotNullableError(mField, entity)\n }\n }\n }\n }\n}\n\n/**\n * Check the permissions version\n * @throws PermissionsOutdated if versions don't match\n */\nasync function checkPermissionsVersion(app: FastifyInstance, opts: PlatformaticLogtoAuthOptions, user = null) {\n\n if (!opts.enableTokenVersionCheck || !opts.redis || !app.redis || !user) {\n return;\n }\n\n const sessionVersionKey = opts.sessionVersionKey || 'version'\n const tokenVersion = user?.[sessionVersionKey];\n const userId = user?.sub;\n\n try {\n const redisKey = `permissions:version:${userId}`;\n const currentVersionStr = await app.redis.get(redisKey);\n const currentVersion = parseInt(currentVersionStr, 10);\n\n if (currentVersionStr === null) {\n await app.redis.set(redisKey, tokenVersion.toString());\n app.log.debug({ userId, version: tokenVersion }, 'Initialized permissions version in Redis');\n if (tokenVersion > 1) {\n app.log.warn({\n userId,\n tokenVersion,\n currentVersion\n }, 'Permissions version mismatch detected');\n throw new PermissionsOutdated();\n }\n return;\n }\n\n // Compare versions\n if (currentVersion !== tokenVersion) {\n app.log.warn({\n userId,\n tokenVersion,\n currentVersion\n }, 'Permissions version mismatch detected');\n throw new PermissionsOutdated();\n }\n\n app.log.trace({ userId, version: tokenVersion }, 'Token version check passed');\n } catch (error) {\n if (error.name === 'FastifyError' && error.code === 'PLT_DB_AUTH_VERSION_OUTDATED') {\n throw error;\n }\n app.log.error({ err: error, userId }, 'Error checking token version');\n }\n}\n\nexport default platformaticLogto;\n\ndeclare module 'fastify' {\n interface FastifyInstance {\n platformaticLogTo: {\n opts: PlatformaticLogtoAuthOptions,\n rules?: PlatformaticRule[]\n }\n }\n}","'use strict'\n\nimport { PlatformaticRule } from '../index.js'\n\nfunction findRule(rules: PlatformaticRule[], roles: string[]) {\n let found = null\n for (const rule of rules) {\n for (const role of roles) {\n if (rule.role === role) {\n found = rule\n break\n }\n }\n if (found) {\n break\n }\n }\n return found\n}\n\nexport default findRule\n","'use strict'\n\nimport createError from '@fastify/error'\n\nconst ERROR_PREFIX = 'PLT_DB_AUTH'\n\nexport const Unauthorized = createError(`${ERROR_PREFIX}_UNAUTHORIZED`, 'operation not allowed', 401)\nexport const UnauthorizedField = createError(`${ERROR_PREFIX}_FIELD_UNAUTHORIZED`, 'field not allowed: %s', 401)\nexport const MissingNotNullableError = createError(`${ERROR_PREFIX}_NOT_NULLABLE_MISSING`, 'missing not nullable field: \"%s\" in save rule for entity \"%s\"')\nexport const PermissionsOutdated = createError(`${ERROR_PREFIX}_VERSION_OUTDATED`, 'Verision has been updated. Please logout and login again.', 403)\n","'use strict'\n\nimport type { FastifyInstance } from 'fastify'\n\n/**\n * Increment the permissions version\n * @param app - Fastify instance with Redis client and Logto\n * @param userId - User ID\n * @returns New version number\n */\nexport async function incrementPermissionsVersion(app: FastifyInstance, userId: string): Promise<number> {\n if (!app.logto) {\n throw new Error('Logto client not available')\n }\n\n const userResp = await app.logto.callAPI(`/api/users/${userId}`, 'GET')\n const userData = await userResp.json()\n const existingCustomData = userData.customData || {}\n const currentVersion = existingCustomData.sessionVersion || 0\n const newVersion = currentVersion + 1\n\n await app.logto.callAPI(`/api/users/${userId}`, 'PATCH', JSON.stringify({ customData: { ...existingCustomData, sessionVersion: newVersion } }))\n\n if (app.redis) {\n const redisKey = `permissions:version:${userId}`\n await app.redis.set(redisKey, newVersion.toString())\n }\n\n return newVersion\n}\n\n/**\n * Delete the permissions version for a user\n * @param app - Fastify instance with Redis client\n * @param userId - User ID\n */\nexport async function deletePermissionsVersion(app: FastifyInstance, userId: string): Promise<void> {\n if (!app.redis) {\n throw new Error('Redis client not available')\n }\n\n const redisKey = `permissions:version:${userId}`\n await app.redis.del(redisKey)\n}\n\n","'use strict'\n\nexport function getRequestFromContext (ctx) {\n if (ctx && !ctx.reply) {\n throw new Error('Missing reply in context. You should call this function with { ctx: { reply }}')\n }\n return ctx.reply.request\n}\n\nexport function getRoles (request, roleKey, anonymousRole, isRolePath = false) {\n let output = []\n const user = request.user\n if (!user) {\n output.push(anonymousRole)\n return output\n }\n\n let rolesRaw\n if (isRolePath) {\n const roleKeys = roleKey.split('.')\n rolesRaw = user\n for (const key of roleKeys) {\n rolesRaw = rolesRaw[key]\n }\n } else {\n rolesRaw = user[roleKey]\n }\n\n if (typeof rolesRaw === 'string') {\n output = rolesRaw.split(',')\n } else if (Array.isArray(rolesRaw)) {\n output = rolesRaw\n }\n if (output.length === 0) {\n output.push(anonymousRole)\n }\n\n return output\n}\n\nexport function getScopes(request, scopesKey, anonymousRole, isScopePath = false) {\n let output = []\n const user = request.user\n if (!user) {\n output.push(anonymousRole)\n return output\n }\n\n let scopesRaw\n if (isScopePath) {\n const roleKeys = scopesKey.split('.')\n scopesRaw = user\n for (const key of roleKeys) {\n scopesRaw = scopesRaw[key]\n }\n } else {\n scopesRaw = user[scopesKey]\n }\n\n if (typeof scopesRaw === 'string') {\n output = scopesRaw.split(' ')\n } else if (Array.isArray(scopesRaw)) {\n output = scopesRaw\n }\n if (output.length === 0) {\n output.push(anonymousRole)\n }\n\n return output\n}\n"],"mappings":"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","names":["fastifyLogto"]}
|