@appxdigital/appx-core 0.1.91 → 0.1.92
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/common/decorators/permission.decorator.d.ts +1 -1
- package/dist/common/decorators/permission.decorator.d.ts.map +1 -1
- package/dist/common/decorators/permission.decorator.js +1 -1
- package/dist/common/interceptors/prisma.interceptor.d.ts.map +1 -1
- package/dist/common/interceptors/prisma.interceptor.js +4 -0
- package/dist/prisma/prisma.service.d.ts.map +1 -1
- package/dist/prisma/prisma.service.js +40 -24
- package/package.json +1 -1
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export declare const PERMISSION_METADATA_KEY = "permission";
|
|
2
|
-
export declare const Permission: (action: string) => import("@nestjs/common").CustomDecorator<string>;
|
|
2
|
+
export declare const Permission: (action: string, expose_models?: string[]) => import("@nestjs/common").CustomDecorator<string>;
|
|
3
3
|
//# sourceMappingURL=permission.decorator.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"permission.decorator.d.ts","sourceRoot":"","sources":["../../../src/common/decorators/permission.decorator.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,uBAAuB,eAAe,CAAC;AAEpD,eAAO,MAAM,UAAU,GAAI,QAAQ,MAAM,
|
|
1
|
+
{"version":3,"file":"permission.decorator.d.ts","sourceRoot":"","sources":["../../../src/common/decorators/permission.decorator.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,uBAAuB,eAAe,CAAC;AAEpD,eAAO,MAAM,UAAU,GAAI,QAAQ,MAAM,EAAE,gBAAe,MAAM,EAAO,qDACN,CAAC"}
|
|
@@ -3,5 +3,5 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.Permission = exports.PERMISSION_METADATA_KEY = void 0;
|
|
4
4
|
const common_1 = require("@nestjs/common");
|
|
5
5
|
exports.PERMISSION_METADATA_KEY = 'permission';
|
|
6
|
-
const Permission = (action) => (0, common_1.SetMetadata)(exports.PERMISSION_METADATA_KEY, { action });
|
|
6
|
+
const Permission = (action, expose_models = []) => (0, common_1.SetMetadata)(exports.PERMISSION_METADATA_KEY, { action, expose_models });
|
|
7
7
|
exports.Permission = Permission;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prisma.interceptor.d.ts","sourceRoot":"","sources":["../../../src/common/interceptors/prisma.interceptor.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"prisma.interceptor.d.ts","sourceRoot":"","sources":["../../../src/common/interceptors/prisma.interceptor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,WAAW,EAAE,gBAAgB,EAAc,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAC3F,OAAO,EAAC,aAAa,EAAC,MAAM,6BAA6B,CAAC;AAC1D,OAAO,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AACvC,OAAO,EAAC,aAAa,EAAC,MAAM,gBAAgB,CAAC;AAG7C,OAAO,EAAC,UAAU,EAAa,MAAM,MAAM,CAAC;AAK5C,qBACa,iBAAkB,YAAW,eAAe;IAIjD,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,SAAS;IACjB,OAAO,CAAC,aAAa;IALzB,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAS;gBAG1B,aAAa,EAAE,aAAa,EACrC,SAAS,EAAE,SAAS,EACpB,aAAa,EAAE,aAAa;IAKxC,SAAS,CAAC,OAAO,EAAE,gBAAgB,EAAE,IAAI,EAAE,WAAW,GAAG,UAAU,CAAC,GAAG,CAAC;CAyD3E"}
|
|
@@ -18,6 +18,7 @@ const nestjs_request_context_1 = require("nestjs-request-context");
|
|
|
18
18
|
const operators_1 = require("rxjs/operators");
|
|
19
19
|
const rxjs_1 = require("rxjs");
|
|
20
20
|
const error_handler_1 = require("../utils/error-handler");
|
|
21
|
+
const permission_decorator_1 = require("../decorators/permission.decorator");
|
|
21
22
|
let PrismaInterceptor = class PrismaInterceptor {
|
|
22
23
|
constructor(prismaService, reflector, configService) {
|
|
23
24
|
this.prismaService = prismaService;
|
|
@@ -30,6 +31,9 @@ let PrismaInterceptor = class PrismaInterceptor {
|
|
|
30
31
|
const req = handlerType === 'graphql'
|
|
31
32
|
? context.getArgByIndex(2) // GraphQL context
|
|
32
33
|
: context.switchToHttp().getRequest(); // HTTP context
|
|
34
|
+
// Attach expose_models metadata if needed
|
|
35
|
+
const permissionMetadata = this.reflector.get(permission_decorator_1.PERMISSION_METADATA_KEY, context.getHandler()) || {};
|
|
36
|
+
nestjs_request_context_1.RequestContext.currentContext.req.prismaExposedModels = permissionMetadata['expose_models'] || [];
|
|
33
37
|
const useTransaction = this.reflector.get('useTransaction', context.getHandler()) ?? this.defaultUseTransaction === 'true';
|
|
34
38
|
if (useTransaction) {
|
|
35
39
|
return new rxjs_1.Observable((observer) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prisma.service.d.ts","sourceRoot":"","sources":["../../src/prisma/prisma.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,MAAM,EAAE,YAAY,EAAC,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAC,qBAAqB,EAAC,MAAM,yCAAyC,CAAC;AAE9E,OAAO,KAAK,EAAC,YAAY,IAAI,aAAa,EAAC,MAAM,gBAAgB,CAAC;AAGlE,KAAK,QAAQ,GAAG,MAAM,aAAa,CAAC;AAEpC,oDAAoD;AACpD,MAAM,MAAM,iBAAiB,GAAG;IAC5B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC9B,CAAC;AAGF,MAAM,MAAM,eAAe,CAAC,CAAC,SAAS,QAAQ,IAAI,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,YAAY,GAAG,mBAAmB,GAAG,QAAQ,GAAG,QAAQ,CAAC,CAAC;AAEtI,qBACa,aAAa;IAMY,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IALpE,OAAO,CAAC,YAAY,CAA2B;IAC/C,YAAY,EAAE,YAAY,CAAC;gBAGvB,YAAY,EAAE,YAAY,EACqB,iBAAiB,EAAE,qBAAqB;IAO3F,YAAY,CAAC,MAAM,EAAE,OAAO;IAI5B,OAAO,CAAC,KAAK;IAQb,YAAY,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,iBAAiB,KAAK,OAAO,CAAC,CAAC,CAAC;IAIpE,WAAW;IAuDX;;;;OAIG;IACH,gBAAgB,CAAC,CAAC,SAAS,QAAQ,EAAE,KAAK,EAAE,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC;IAYlE,IAAI,KAAK,IAAI,aAAa,CAUzB;IAED,IAAI,IAAI,IAAI,eAAe,CAAC,MAAM,CAAC,CAElC;IAED,IAAI,OAAO,IAAI,eAAe,CAAC,SAAS,CAAC,CAExC;IAED,IAAI,gBAAgB,QAEnB;IAED;;;OAGG;IACH,WAAW;IAoDX;;;;;;;;OAQG;IACH,OAAO,CAAC,kBAAkB;IAmC1B,IAAI,2BAA2B,QAM9B;IAED;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,oBAAoB;
|
|
1
|
+
{"version":3,"file":"prisma.service.d.ts","sourceRoot":"","sources":["../../src/prisma/prisma.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,MAAM,EAAE,YAAY,EAAC,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAC,qBAAqB,EAAC,MAAM,yCAAyC,CAAC;AAE9E,OAAO,KAAK,EAAC,YAAY,IAAI,aAAa,EAAC,MAAM,gBAAgB,CAAC;AAGlE,KAAK,QAAQ,GAAG,MAAM,aAAa,CAAC;AAEpC,oDAAoD;AACpD,MAAM,MAAM,iBAAiB,GAAG;IAC5B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC9B,CAAC;AAGF,MAAM,MAAM,eAAe,CAAC,CAAC,SAAS,QAAQ,IAAI,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,YAAY,GAAG,mBAAmB,GAAG,QAAQ,GAAG,QAAQ,CAAC,CAAC;AAEtI,qBACa,aAAa;IAMY,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IALpE,OAAO,CAAC,YAAY,CAA2B;IAC/C,YAAY,EAAE,YAAY,CAAC;gBAGvB,YAAY,EAAE,YAAY,EACqB,iBAAiB,EAAE,qBAAqB;IAO3F,YAAY,CAAC,MAAM,EAAE,OAAO;IAI5B,OAAO,CAAC,KAAK;IAQb,YAAY,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,iBAAiB,KAAK,OAAO,CAAC,CAAC,CAAC;IAIpE,WAAW;IAuDX;;;;OAIG;IACH,gBAAgB,CAAC,CAAC,SAAS,QAAQ,EAAE,KAAK,EAAE,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC;IAYlE,IAAI,KAAK,IAAI,aAAa,CAUzB;IAED,IAAI,IAAI,IAAI,eAAe,CAAC,MAAM,CAAC,CAElC;IAED,IAAI,OAAO,IAAI,eAAe,CAAC,SAAS,CAAC,CAExC;IAED,IAAI,gBAAgB,QAEnB;IAED;;;OAGG;IACH,WAAW;IAoDX;;;;;;;;OAQG;IACH,OAAO,CAAC,kBAAkB;IAmC1B,IAAI,2BAA2B,QAM9B;IAED;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,oBAAoB;IAwI5B;;;;;;;OAOG;IACH,OAAO,CAAC,eAAe;IAgBvB;;;;;;;OAOG;IACH,OAAO,CAAC,mBAAmB;IA2B3B;;;;;;;OAOG;IACH,OAAO,CAAC,eAAe;IAUvB;;;;;;;;;OASG;IACH,OAAO,CAAC,iBAAiB;IAkDzB,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,eAAe,CAAC,EAAE,IAAI,GAAG;QAC5E,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,iBAAiB,CAAC,EAAE,MAAM,CAAC;KAC9B;IAkBD,gBAAgB,CAAC,WAAW,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;CAgBzF"}
|
|
@@ -229,13 +229,20 @@ let PrismaService = class PrismaService {
|
|
|
229
229
|
const permissionsConfig = this.normalizedPermissionsConfig;
|
|
230
230
|
const normalizedName = modelName.toLowerCase().trim();
|
|
231
231
|
const permissions = permissionsConfig[normalizedName]?.[userRole];
|
|
232
|
-
|
|
233
|
-
|
|
232
|
+
let actionPermissions;
|
|
233
|
+
// If model is exposed, permissions is ALL
|
|
234
|
+
if (nestjs_request_context_1.RequestContext.currentContext.req.prismaExposedModels.map((m) => m.toLowerCase()).includes(modelName.toLowerCase())) {
|
|
235
|
+
actionPermissions = 'ALL';
|
|
234
236
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
237
|
+
else {
|
|
238
|
+
if (!permissions) {
|
|
239
|
+
throw new common_1.HttpException(`No permissions found for model '${modelName}' and role ${userRole}`, common_1.HttpStatus.FORBIDDEN);
|
|
240
|
+
}
|
|
241
|
+
actionPermissions = this.selectPermission(permissions, action.toString(), modelName, userRole);
|
|
242
|
+
if (!actionPermissions) {
|
|
243
|
+
console.debug(`No permissions found for action '${modelName}.${String(action)}()' on role ${userRole}`);
|
|
244
|
+
throw new common_1.HttpException('Missing permissions on model ' + modelName, common_1.HttpStatus.FORBIDDEN);
|
|
245
|
+
}
|
|
239
246
|
}
|
|
240
247
|
if (args.select) {
|
|
241
248
|
for (const field of Object.keys(args.select)) {
|
|
@@ -247,10 +254,19 @@ let PrismaService = class PrismaService {
|
|
|
247
254
|
if (relation.relation === 'belongsTo') {
|
|
248
255
|
this.debug(`Found 1:1 / *:1 (belongsTo) relation to model '${relation.model}' from model '${modelName}' via field '${field}'. Filter will be applied to main conditions...`);
|
|
249
256
|
const relatedPermissions = this.selectPermission(permissionsConfig[relation.model.toLowerCase()]?.[userRole] || {}, action.toString(), relation.model, userRole);
|
|
257
|
+
// If model is exposed, do not apply conditions
|
|
258
|
+
if (nestjs_request_context_1.RequestContext.currentContext.req.prismaExposedModels.map((m) => m.toLowerCase()).includes(relation.model.toLowerCase())) {
|
|
259
|
+
this.debug(`Related model '${relation.model}' is exposed via @Permission() decorator. Skipping conditions for action '${String(action)}' on role ${userRole}.`);
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
250
262
|
if (!relatedPermissions) {
|
|
251
263
|
console.debug(`No permissions found for action '${relation.model}.${String(action)}()' on role ${userRole}`);
|
|
252
264
|
throw new common_1.HttpException('Missing permissions on model ' + relation.model, common_1.HttpStatus.FORBIDDEN);
|
|
253
265
|
}
|
|
266
|
+
if (relatedPermissions === 'ALL') {
|
|
267
|
+
this.debug(`Related model '${relation.model}' has 'ALL' permissions for action '${String(action)}' on role ${userRole}. No conditions to apply.`);
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
254
270
|
belongsToQueue.push({
|
|
255
271
|
field,
|
|
256
272
|
relation,
|
|
@@ -277,23 +293,6 @@ let PrismaService = class PrismaService {
|
|
|
277
293
|
delete args.select[field].select.where;
|
|
278
294
|
}
|
|
279
295
|
}
|
|
280
|
-
if (actionPermissions === 'ALL' && belongsToQueue.length === 0 || action.toString().startsWith('create')) {
|
|
281
|
-
this.debug(`No conditions to apply for '${modelName}.${String(action)}()' on role ${userRole}`);
|
|
282
|
-
return args;
|
|
283
|
-
}
|
|
284
|
-
const whereClause = this.buildConditions(actionPermissions.conditions, user);
|
|
285
|
-
this.debug(`Applying where conditions for '${modelName}.${String(action)}()' on role ${userRole}: ${JSON.stringify(whereClause)}`);
|
|
286
|
-
if (!args.where) {
|
|
287
|
-
args.where = whereClause;
|
|
288
|
-
}
|
|
289
|
-
else {
|
|
290
|
-
args.where = { AND: [args.where, whereClause] };
|
|
291
|
-
}
|
|
292
|
-
// Failsafe: If there are no conditions at all and there were supposed to be, block access
|
|
293
|
-
if (Object.keys(args.where).length === 0 && !belongsToQueue?.length) {
|
|
294
|
-
this.debug(`Found a weird edge case. Contact Manuel Olveira @ AppX.`);
|
|
295
|
-
throw new common_1.ForbiddenException(`You are not authorized to access this record`);
|
|
296
|
-
}
|
|
297
296
|
if (belongsToQueue?.length > 0) {
|
|
298
297
|
this.debug(`Merging belongsTo relation conditions into main ${modelName}.`, 'warn');
|
|
299
298
|
}
|
|
@@ -301,7 +300,7 @@ let PrismaService = class PrismaService {
|
|
|
301
300
|
const { relatedPermissions, field } = entry;
|
|
302
301
|
this.debug(`Merging conditions for belongsTo relation field '${field}': ${JSON.stringify(relatedPermissions?.conditions)}`, 'info');
|
|
303
302
|
args.where = {
|
|
304
|
-
...args.where,
|
|
303
|
+
...(args.where || {}),
|
|
305
304
|
[field]: {
|
|
306
305
|
AND: [
|
|
307
306
|
args?.where?.[field] ?? {},
|
|
@@ -310,6 +309,23 @@ let PrismaService = class PrismaService {
|
|
|
310
309
|
}
|
|
311
310
|
};
|
|
312
311
|
}
|
|
312
|
+
// If model is exposed, do not apply conditions
|
|
313
|
+
if (nestjs_request_context_1.RequestContext.currentContext.req.prismaExposedModels.map((m) => m.toLowerCase()).includes(modelName.toLowerCase())) {
|
|
314
|
+
this.debug(`Model '${modelName}' is exposed via @Permission() decorator. Skipping conditions for action '${String(action)}' on role ${userRole}.`);
|
|
315
|
+
return args;
|
|
316
|
+
}
|
|
317
|
+
if (actionPermissions === 'ALL') {
|
|
318
|
+
this.debug(`No conditions to apply for '${modelName}.${String(action)}()' on role ${userRole}`);
|
|
319
|
+
return args;
|
|
320
|
+
}
|
|
321
|
+
const whereClause = this.buildConditions(actionPermissions.conditions, user);
|
|
322
|
+
this.debug(`Applying where conditions for '${modelName}.${String(action)}()' on role ${userRole}: ${JSON.stringify(whereClause)}`);
|
|
323
|
+
if (!args.where) {
|
|
324
|
+
args.where = whereClause;
|
|
325
|
+
}
|
|
326
|
+
else {
|
|
327
|
+
args.where = { AND: [args.where, whereClause] };
|
|
328
|
+
}
|
|
313
329
|
return args;
|
|
314
330
|
}
|
|
315
331
|
/**
|
package/package.json
CHANGED