@appxdigital/appx-core 0.1.120 → 0.1.121-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +564 -533
  3. package/dist/common/interceptors/prisma.interceptor.d.ts.map +1 -1
  4. package/dist/common/interceptors/prisma.interceptor.js +18 -6
  5. package/dist/common/setup/core-config.d.ts +66 -0
  6. package/dist/common/setup/core-config.d.ts.map +1 -0
  7. package/dist/common/setup/core-config.js +67 -0
  8. package/dist/common/setup/setup-core-security.d.ts +62 -0
  9. package/dist/common/setup/setup-core-security.d.ts.map +1 -0
  10. package/dist/common/setup/setup-core-security.js +40 -0
  11. package/dist/config/generate-all.js +4 -0
  12. package/dist/config/generate-controllers.js +19 -2
  13. package/dist/config/generate-dtos.d.ts +2 -0
  14. package/dist/config/generate-dtos.d.ts.map +1 -0
  15. package/dist/config/generate-dtos.js +199 -0
  16. package/dist/config/utils.d.ts +14 -0
  17. package/dist/config/utils.d.ts.map +1 -1
  18. package/dist/config/utils.js +35 -2
  19. package/dist/index.d.ts +2 -0
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +2 -0
  22. package/dist/modules/auth/auth.controller.d.ts.map +1 -1
  23. package/dist/modules/auth/auth.controller.js +6 -2
  24. package/dist/modules/auth/auth.service.d.ts.map +1 -1
  25. package/dist/modules/core/core.controller.d.ts.map +1 -1
  26. package/dist/modules/core/core.controller.js +12 -5
  27. package/dist/modules/user/user.service.d.ts.map +1 -1
  28. package/dist/modules/user/user.service.js +7 -3
  29. package/dist/prisma/prisma.service.d.ts +45 -0
  30. package/dist/prisma/prisma.service.d.ts.map +1 -1
  31. package/dist/prisma/prisma.service.js +208 -3
  32. package/package.json +108 -86
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,4BAA4B,CAAC;AAC3C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,yBAAyB,CAAC;AACxC,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,iCAAiC,CAAC;AAChD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,sCAAsC,CAAC;AACrD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,uCAAuC,CAAC;AACtD,cAAc,yBAAyB,CAAC;AACxC,cAAc,0CAA0C,CAAC;AACzD,cAAc,wCAAwC,CAAC;AACvD,cAAc,iCAAiC,CAAC;AAChD,cAAc,6CAA6C,CAAC;AAC5D,cAAc,oBAAoB,CAAC;AACnC,cAAc,mCAAmC,CAAC;AAClD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,wCAAwC,CAAC;AACvD,cAAc,+CAA+C,CAAC;AAC9D,cAAc,uCAAuC,CAAC;AACtD,cAAc,oCAAoC,CAAC;AACnD,cAAc,0CAA0C,CAAC;AACzD,cAAc,wCAAwC,CAAC;AACvD,cAAc,oCAAoC,CAAC;AACnD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,qCAAqC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,4BAA4B,CAAC;AAC3C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,yBAAyB,CAAC;AACxC,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,iCAAiC,CAAC;AAChD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,sCAAsC,CAAC;AACrD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,uCAAuC,CAAC;AACtD,cAAc,yBAAyB,CAAC;AACxC,cAAc,0CAA0C,CAAC;AACzD,cAAc,wCAAwC,CAAC;AACvD,cAAc,iCAAiC,CAAC;AAChD,cAAc,6CAA6C,CAAC;AAC5D,cAAc,oBAAoB,CAAC;AACnC,cAAc,mCAAmC,CAAC;AAClD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,wCAAwC,CAAC;AACvD,cAAc,+CAA+C,CAAC;AAC9D,cAAc,uCAAuC,CAAC;AACtD,cAAc,oCAAoC,CAAC;AACnD,cAAc,0CAA0C,CAAC;AACzD,cAAc,oCAAoC,CAAC;AACnD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,wCAAwC,CAAC;AACvD,cAAc,oCAAoC,CAAC;AACnD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,qCAAqC,CAAC"}
package/dist/index.js CHANGED
@@ -38,6 +38,8 @@ __exportStar(require("./common/interfaces/storage-service.interface"), exports);
38
38
  __exportStar(require("./modules/file/file-upload.controller"), exports);
39
39
  __exportStar(require("./modules/file/file-upload.service"), exports);
40
40
  __exportStar(require("./common/decorators/permission.decorator"), exports);
41
+ __exportStar(require("./common/setup/setup-core-security"), exports);
42
+ __exportStar(require("./common/setup/core-config"), exports);
41
43
  __exportStar(require("./common/interceptors/file.interceptor"), exports);
42
44
  __exportStar(require("./modules/auth/authenticated.guard"), exports);
43
45
  __exportStar(require("./modules/auth/jwt-auth.guard"), exports);
@@ -1 +1 @@
1
- {"version":3,"file":"auth.controller.d.ts","sourceRoot":"","sources":["../../../src/modules/auth/auth.controller.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,WAAW,EAAC,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAC,WAAW,EAAC,MAAM,oBAAoB,CAAC;AAG/C,OAAO,EAAC,OAAO,EAAE,QAAQ,EAAC,MAAM,SAAS,CAAC;AAI1C,qBACa,cAAc;IACX,SAAS,CAAC,QAAQ,CAAC,WAAW,EAAE,WAAW;gBAAxB,WAAW,EAAE,WAAW;IAGjD,QAAQ,CAAS,WAAW,EAAE,WAAW;;;;IAMzC,KAAK,CAAQ,GAAG,EAAE,OAAO,EAAS,GAAG,EAAE,QAAQ;IAW/C,MAAM,CAAQ,GAAG,EAAE,OAAO,EAAS,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAoBhE,EAAE,CAAQ,GAAG,EAAE,OAAO;;;IAQtB,iBAAiB,CAAQ,GAAG,EAAE,OAAO;;;IAOrC,gBAAgB,CAAQ,GAAG,EAAE,OAAO,EAAS,GAAG,EAAE,QAAQ;IAK1D,cAAc,CAAkB,MAAM,EAAE,MAAM,EAAS,GAAG,EAAE,OAAO;;;IAUnE,oBAAoB,CACF,SAAS,EAAE,MAAM,EAC9B,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,QAAQ;IAgBlB,QAAQ,CAAQ,GAAG,EAAE,OAAO;;;;;IAM5B,aAAa,CAAQ,GAAG,EAAE,OAAO;;;;IAOjC,SAAS,CAAQ,GAAG,EAAE,OAAO;;;CAQtC"}
1
+ {"version":3,"file":"auth.controller.d.ts","sourceRoot":"","sources":["../../../src/modules/auth/auth.controller.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,WAAW,EAAC,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAC,WAAW,EAAC,MAAM,oBAAoB,CAAC;AAG/C,OAAO,EAAC,OAAO,EAAE,QAAQ,EAAC,MAAM,SAAS,CAAC;AAI1C,qBACa,cAAc;IACX,SAAS,CAAC,QAAQ,CAAC,WAAW,EAAE,WAAW;gBAAxB,WAAW,EAAE,WAAW;IAGjD,QAAQ,CAAS,WAAW,EAAE,WAAW;;;;IAMzC,KAAK,CAAQ,GAAG,EAAE,OAAO,EAAS,GAAG,EAAE,QAAQ;IAW/C,MAAM,CAAQ,GAAG,EAAE,OAAO,EAAS,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAoBhE,EAAE,CAAQ,GAAG,EAAE,OAAO;;;IAQtB,iBAAiB,CAAQ,GAAG,EAAE,OAAO;;;IAOrC,gBAAgB,CAAQ,GAAG,EAAE,OAAO,EAAS,GAAG,EAAE,QAAQ;IAM1D,cAAc,CAAkB,MAAM,EAAE,MAAM,EAAS,GAAG,EAAE,OAAO;;;IAanE,oBAAoB,CACF,SAAS,EAAE,MAAM,EAC9B,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,QAAQ;IAgBlB,QAAQ,CAAQ,GAAG,EAAE,OAAO;;;;;IAM5B,aAAa,CAAQ,GAAG,EAAE,OAAO;;;;IAOjC,SAAS,CAAQ,GAAG,EAAE,OAAO;;;CAQtC"}
@@ -64,7 +64,9 @@ let AuthController = exports.AuthController = class AuthController {
64
64
  return this.authService.closeAllUserSessions(req, res);
65
65
  }
66
66
  async getAllSessions(userId, req) {
67
- //TODO - Implement a more dynamic way to handle role access
67
+ // AuthenticatedGuard gates authentication explicitly; the role check
68
+ // below is the authorization step. De-hardcoding 'ADMIN' into the
69
+ // permissions system is deferred (see ROADMAP / ).
68
70
  if (req.user?.role !== 'ADMIN') {
69
71
  throw new common_1.ForbiddenException('Access denied.');
70
72
  }
@@ -72,7 +74,7 @@ let AuthController = exports.AuthController = class AuthController {
72
74
  return { sessions };
73
75
  }
74
76
  async closeSpecificSession(sessionId, req, res) {
75
- //TODO - Implement a more dynamic way to handle role access
77
+ // See getAllSessions: AuthenticatedGuard = authn, role check = authz.
76
78
  if (req.user?.role !== 'ADMIN') {
77
79
  throw new common_1.ForbiddenException('Access denied.');
78
80
  }
@@ -151,6 +153,7 @@ __decorate([
151
153
  __metadata("design:returntype", Promise)
152
154
  ], AuthController.prototype, "closeAllSessions", null);
153
155
  __decorate([
156
+ (0, common_1.UseGuards)(authenticated_guard_1.AuthenticatedGuard),
154
157
  (0, common_1.Get)('sessions/:userId'),
155
158
  __param(0, (0, common_1.Param)('userId')),
156
159
  __param(1, (0, common_1.Req)()),
@@ -159,6 +162,7 @@ __decorate([
159
162
  __metadata("design:returntype", Promise)
160
163
  ], AuthController.prototype, "getAllSessions", null);
161
164
  __decorate([
165
+ (0, common_1.UseGuards)(authenticated_guard_1.AuthenticatedGuard),
162
166
  (0, common_1.Post)('sessions/:sessionId/close'),
163
167
  __param(0, (0, common_1.Param)('sessionId')),
164
168
  __param(1, (0, common_1.Req)()),
@@ -1 +1 @@
1
- {"version":3,"file":"auth.service.d.ts","sourceRoot":"","sources":["../../../src/modules/auth/auth.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,WAAW,EAAC,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAC,WAAW,EAAC,MAAM,oBAAoB,CAAC;AAE/C,OAAO,EAAC,aAAa,EAAC,MAAM,6BAA6B,CAAC;AAC1D,OAAO,EAAC,aAAa,EAAC,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAC,OAAO,EAAE,QAAQ,EAAC,MAAM,SAAS,CAAC;AAC1C,OAAO,EAAC,UAAU,EAAC,MAAM,aAAa,CAAC;AAKvC,qBACa,WAAW;IAIhB,SAAS,CAAC,QAAQ,CAAC,WAAW,EAAE,WAAW;IAC3C,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,aAAa;IACxC,SAAS,CAAC,QAAQ,CAAC,UAAU,EAAE,UAAU;IACzC,SAAS,CAAC,QAAQ,CAAC,aAAa,EAAE,aAAa;IANnD,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;gBAGpB,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,aAAa,EACrB,UAAU,EAAE,UAAU,EACtB,aAAa,EAAE,aAAa;IAK7C,QAAQ,CAAC,WAAW,EAAE,WAAW;;;;IA2BjC,KAAK,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,GAAG,CAAA;KAAC,CAAC;IA8B1D,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBlD,YAAY,CACd,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,GACtB,OAAO,CAAC,GAAG,CAAC;IAuBT,cAAc,CAAC,GAAG,EAAE,OAAO;IAW3B,iBAAiB,CAAC,GAAG,EAAE,OAAO;IAW9B,oBAAoB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ;IA0BhD,mBAAmB,CAAC,MAAM,EAAE,MAAM;IAMlC,oBAAoB,CAAC,SAAS,EAAE,MAAM;IAOtC,kBAAkB,CAAC,OAAO,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;YActC,cAAc;IAkCtB,aAAa,CAAC,yBAAyB,EAAE,GAAG,GAAG,OAAO,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAC,CAAC;IA4B3G,OAAO,CAAC,WAAW;IAUb,QAAQ,CAAC,iBAAiB,EAAE,GAAG,GAAG,OAAO,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,GAAG,CAAA;KAAC,CAAC;IAcnG,0BAA0B,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAUlE"}
1
+ {"version":3,"file":"auth.service.d.ts","sourceRoot":"","sources":["../../../src/modules/auth/auth.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,WAAW,EAAC,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAC,WAAW,EAAC,MAAM,oBAAoB,CAAC;AAE/C,OAAO,EAAC,aAAa,EAAC,MAAM,6BAA6B,CAAC;AAC1D,OAAO,EAAC,aAAa,EAAC,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAC,OAAO,EAAE,QAAQ,EAAC,MAAM,SAAS,CAAC;AAC1C,OAAO,EAAC,UAAU,EAAiB,MAAM,aAAa,CAAC;AAKvD,qBACa,WAAW;IAIhB,SAAS,CAAC,QAAQ,CAAC,WAAW,EAAE,WAAW;IAC3C,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,aAAa;IACxC,SAAS,CAAC,QAAQ,CAAC,UAAU,EAAE,UAAU;IACzC,SAAS,CAAC,QAAQ,CAAC,aAAa,EAAE,aAAa;IANnD,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;gBAGpB,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,aAAa,EACrB,UAAU,EAAE,UAAU,EACtB,aAAa,EAAE,aAAa;IAK7C,QAAQ,CAAC,WAAW,EAAE,WAAW;;;;IA2BjC,KAAK,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,GAAG,CAAA;KAAC,CAAC;IA8B1D,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBlD,YAAY,CACd,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,GACtB,OAAO,CAAC,GAAG,CAAC;IAuBT,cAAc,CAAC,GAAG,EAAE,OAAO;IAW3B,iBAAiB,CAAC,GAAG,EAAE,OAAO;IAW9B,oBAAoB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ;IA0BhD,mBAAmB,CAAC,MAAM,EAAE,MAAM;IAMlC,oBAAoB,CAAC,SAAS,EAAE,MAAM;IAOtC,kBAAkB,CAAC,OAAO,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;YActC,cAAc;IAkCtB,aAAa,CAAC,yBAAyB,EAAE,GAAG,GAAG,OAAO,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAC,CAAC;IA4B3G,OAAO,CAAC,WAAW;IAUb,QAAQ,CAAC,iBAAiB,EAAE,GAAG,GAAG,OAAO,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,GAAG,CAAA;KAAC,CAAC;IAcnG,0BAA0B,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAUlE"}
@@ -1 +1 @@
1
- {"version":3,"file":"core.controller.d.ts","sourceRoot":"","sources":["../../../src/modules/core/core.controller.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,WAAW,EAAC,MAAM,gBAAgB,CAAC;AAI3C,OAAO,EAAC,kBAAkB,EAAC,MAAM,yCAAyC,CAAC;AAQ3E,8BACsB,cAAc,CAAC,CAAC;IAM9B,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC;IAL9C,MAAM,KAAK,UAAU,IAAI,MAAM,CAE9B;IAED,SAAS,aACc,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC;IAK9C,SAAS,CAAC,QAAQ,CAAC,kBAAkB,EAAG,kBAAkB,CAAC;IAIrD,OAAO;IAMP,OAAO,CAAc,EAAE,EAAE,MAAM;IAM/B,MAAM,CAAS,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;KAAC;IAoBtD,MAAM,CAAc,EAAE,EAAE,MAAM,EAAU,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;IAoBxD,MAAM,CAAc,EAAE,EAAE,MAAM;CAGvC"}
1
+ {"version":3,"file":"core.controller.d.ts","sourceRoot":"","sources":["../../../src/modules/core/core.controller.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,WAAW,EAAC,MAAM,gBAAgB,CAAC;AAI3C,OAAO,EAAC,kBAAkB,EAAC,MAAM,yCAAyC,CAAC;AAS3E,8BACsB,cAAc,CAAC,CAAC;IAM9B,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC;IAL9C,MAAM,KAAK,UAAU,IAAI,MAAM,CAE9B;IAED,SAAS,aACc,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC;IAK9C,SAAS,CAAC,QAAQ,CAAC,kBAAkB,EAAG,kBAAkB,CAAC;IAIrD,OAAO;IAMP,OAAO,CAAc,EAAE,EAAE,MAAM;IAM/B,MAAM,CAAS,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;KAAC;IAuBtD,MAAM,CAAc,EAAE,EAAE,MAAM,EAAU,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;IAsBxD,MAAM,CAAc,EAAE,EAAE,MAAM;CAGvC"}
@@ -39,10 +39,15 @@ let CoreController = exports.CoreController = CoreController_1 = class CoreContr
39
39
  const model = this.constructor.entityName;
40
40
  const rolePermissions = this.permissionsService.getPermissionsConfig()[model]?.[role];
41
41
  const actionPermission = rolePermissions?.['create'];
42
- if (typeof actionPermission !== 'string' &&
43
- actionPermission?.setUserIdField) {
44
- const userIdField = actionPermission.setUserIdField;
45
- data[userIdField] = user.id;
42
+ if (typeof actionPermission !== 'string' && actionPermission) {
43
+ // Strip restricted fields first, then force the server-owned user id
44
+ // (so setUserIdField cannot be clobbered by a restricted-field strip).
45
+ for (const field of (actionPermission.restrictedFields ?? [])) {
46
+ delete data[field];
47
+ }
48
+ if (actionPermission.setUserIdField) {
49
+ data[actionPermission.setUserIdField] = user.id;
50
+ }
46
51
  }
47
52
  return this.service.create(data);
48
53
  }
@@ -51,7 +56,9 @@ let CoreController = exports.CoreController = CoreController_1 = class CoreContr
51
56
  const role = user?.role || 'GUEST';
52
57
  const model = this.constructor.entityName;
53
58
  const rolePermissions = this.permissionsService.getPermissionsConfig()[model]?.[role];
54
- const actionPermission = rolePermissions?.['update'];
59
+ // The action key is 'updateMany' (matches @Permission('updateMany') and
60
+ // the proxy) — reading 'update' here meant restrictedFields never applied.
61
+ const actionPermission = rolePermissions?.['updateMany'];
55
62
  let restrictedFields = [];
56
63
  if (typeof actionPermission !== 'string' && actionPermission?.restrictedFields) {
57
64
  restrictedFields = actionPermission.restrictedFields;
@@ -1 +1 @@
1
- {"version":3,"file":"user.service.d.ts","sourceRoot":"","sources":["../../../src/modules/user/user.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,aAAa,EAAC,MAAM,6BAA6B,CAAC;AAC1D,OAAO,EAAC,eAAe,EAAC,MAAM,wCAAwC,CAAC;AACvE,OAAO,EAAC,WAAW,EAAC,MAAM,sBAAsB,CAAC;AAEjD,OAAO,EAAC,IAAI,EAAC,MAAM,gBAAgB,CAAC;AAEpC,qBACa,WAAY,SAAQ,WAAW,CAAC,IAAI,CAAC;IAClC,SAAS,CAAC,MAAM,EAAE,aAAa;gBAArB,MAAM,EAAE,aAAa;IAIrC,UAAU,CAAC,eAAe,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAM3D,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;IAUpD,WAAW,CAAC,KAAK,EAAE,MAAM;CAYlC"}
1
+ {"version":3,"file":"user.service.d.ts","sourceRoot":"","sources":["../../../src/modules/user/user.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,aAAa,EAAC,MAAM,6BAA6B,CAAC;AAC1D,OAAO,EAAC,eAAe,EAAC,MAAM,wCAAwC,CAAC;AACvE,OAAO,EAAC,WAAW,EAAC,MAAM,sBAAsB,CAAC;AAEjD,OAAO,EAAC,IAAI,EAAC,MAAM,gBAAgB,CAAC;AAEpC,qBACa,WAAY,SAAQ,WAAW,CAAC,IAAI,CAAC;IAClC,SAAS,CAAC,MAAM,EAAE,aAAa;gBAArB,MAAM,EAAE,aAAa;IAIrC,UAAU,CAAC,eAAe,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAY3D,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;IAUpD,WAAW,CAAC,KAAK,EAAE,MAAM;CAYlC"}
@@ -19,9 +19,13 @@ let UserService = exports.UserService = class UserService extends core_service_1
19
19
  this.prisma = prisma;
20
20
  }
21
21
  async createUser(createUserInput) {
22
- return this.prisma.user.create({
23
- data: createUserInput,
24
- });
22
+ // Registration is an unauthenticated framework flow (role GUEST has no
23
+ // create permission). Its input is already constrained by RegisterDto,
24
+ // so bypass access filtering — otherwise the create default-deny would
25
+ // reject sign-up.
26
+ return this.prisma.user.create({ data: createUserInput },
27
+ // @ts-ignore — options arg is accepted by the proxy delegate
28
+ { BYPASS_FILTERING: true });
25
29
  }
26
30
  async findByField(field, value) {
27
31
  const where = {
@@ -17,6 +17,13 @@ export declare class PrismaService {
17
17
  private readonly permissionsConfig;
18
18
  private fieldConfigs;
19
19
  prismaClient: PrismaClient;
20
+ /**
21
+ * The un-proxied Prisma client. Used internally (e.g. create-condition
22
+ * checks) to look up an already-existing parent row without re-applying
23
+ * that model's access filtering — matching "would a findFirst with these
24
+ * conditions match".
25
+ */
26
+ private rawClient;
20
27
  constructor(prismaClient: PrismaClient, permissionsConfig: PermissionsConfigType);
21
28
  debugQueries(enable: boolean): void;
22
29
  withExposedModels(models: string[], callback: () => Promise<void>): Promise<void>;
@@ -62,6 +69,44 @@ export declare class PrismaService {
62
69
  * @throws ForbiddenException if no valid `where` conditions are present after applying permissions.
63
70
  */
64
71
  private applyWhereConditions;
72
+ /**
73
+ * Enforces permission `conditions` on `create` / `createMany`.
74
+ *
75
+ * Conditions cannot be pushed into a `WHERE` on insert, so instead we verify
76
+ * the incoming data *would* satisfy them — i.e. a `findFirst` with those
77
+ * conditions would return the new row. Own-scalar fields are checked against
78
+ * `data`; relation conditions are checked by looking up the referenced parent
79
+ * (which lets Prisma evaluate arbitrarily-nested relation conditions for us).
80
+ *
81
+ * Also default-denies: create is only allowed for a role/action that has an
82
+ * explicit permission (or 'ALL'), mirroring find/update/delete. Framework
83
+ * flows that must create without a permission (registration, sessions,
84
+ * tokens) pass BYPASS_FILTERING and never reach here.
85
+ *
86
+ * Fails **closed**: throws on a violation and on any condition shape it
87
+ * cannot evaluate, rather than silently allowing the insert.
88
+ */
89
+ private enforceCreateConditions;
90
+ /**
91
+ * True only in an explicit local-development environment. Deliberately NOT
92
+ * `!== 'production'` — staging and other envs should behave like production
93
+ * (no extra diagnostic queries/logging). Only `development` / `dev` opt in.
94
+ */
95
+ private isDevMode;
96
+ /**
97
+ * Dev-only diagnostic. After a create that passed its create-conditions,
98
+ * checks whether the new row also satisfies the model's *find* conditions —
99
+ * if not, the creator can't read it back (create rule broader than find
100
+ * rule). Logs a warning; never affects the create. Runs only in development
101
+ * (gated by the caller).
102
+ */
103
+ private warnIfCreatedRowNotFindable;
104
+ /** Recursively evaluates a (placeholder-resolved) condition object against a to-be-created row. */
105
+ private matchesCreateConditions;
106
+ /** Evaluates a scalar-field condition (value or Prisma operator object) against the row's value. */
107
+ private matchesCreateScalar;
108
+ /** Verifies the parent referenced by a belongsTo condition exists and satisfies the nested condition. */
109
+ private matchesCreateRelation;
65
110
  /**
66
111
  * Builds dynamic conditions based on the type of clause (OR, AND, or field conditions).
67
112
  * Each condition is processed, and placeholders (like `$USER_ID`) are replaced with actual values.
@@ -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,OAAO,EAAC,iBAAiB,EAAC,MAAM,kBAAkB,CAAC;AAEnD,eAAO,MAAM,iBAAiB;mBACX,MAAM,EAAE;EACvB,CAAC;AAEL,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;IAO5B,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC;IAWjE,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;IAqDX;;;;;;;;OAQG;IACH,OAAO,CAAC,kBAAkB;IAqC1B,IAAI,2BAA2B,QAM9B;IAED;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,oBAAoB;IA8K5B;;;;;;;OAOG;WACW,gBAAgB,CAAC,UAAU,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,GAAG,GAAG,GAAG;IAgBjE;;;;;;;OAOG;IACH,OAAO,CAAC,MAAM,CAAC,mBAAmB;IA2BlC;;;;;;;OAOG;IACH,OAAO,CAAC,eAAe;IAUvB;;;;;;;;;;OAUG;IACH,OAAO,CAAC,iBAAiB;IA8EzB,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;QAC3B,UAAU,EAAE,OAAO,CAAA;KACtB;IAmBD,gBAAgB,CAAC,WAAW,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;CAgBzF"}
1
+ {"version":3,"file":"prisma.service.d.ts","sourceRoot":"","sources":["../../src/prisma/prisma.service.ts"],"names":[],"mappings":"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"}
@@ -54,6 +54,10 @@ let PrismaService = exports.PrismaService = PrismaService_1 = class PrismaServic
54
54
  return this.prismaClient.$transaction(fn);
55
55
  }
56
56
  proxyModels() {
57
+ // Capture the un-proxied client before wrapping it (used by internal
58
+ // lookups that must bypass this proxy's access filtering — see
59
+ // enforceCreateConditions).
60
+ this.rawClient = this.prismaClient;
57
61
  // Proxy client to intercept model calls
58
62
  this.prismaClient = new Proxy(this.prismaClient, {
59
63
  get: (target, propKey) => {
@@ -85,8 +89,15 @@ let PrismaService = exports.PrismaService = PrismaService_1 = class PrismaServic
85
89
  else {
86
90
  this.debug(`Skipping field omission for ${String(propKey)}.${String(methodKey)}()`);
87
91
  }
88
- if (!options?.BYPASS_FILTERING && !['create', 'createMany'].includes(methodKey.toString())) {
89
- params = this.applyWhereConditions(String(propKey), userRole, params, user, methodKey);
92
+ if (!options?.BYPASS_FILTERING) {
93
+ if (['create', 'createMany'].includes(methodKey.toString())) {
94
+ // Conditions can't be pushed into a WHERE on insert, so
95
+ // validate the incoming data satisfies them before insert.
96
+ await this.enforceCreateConditions(String(propKey), userRole, params, user, methodKey.toString());
97
+ }
98
+ else {
99
+ params = this.applyWhereConditions(String(propKey), userRole, params, user, methodKey);
100
+ }
90
101
  }
91
102
  else {
92
103
  this.debug(`Skipping permission filtering for ${String(propKey)}.${String(methodKey)}()`);
@@ -95,7 +106,14 @@ let PrismaService = exports.PrismaService = PrismaService_1 = class PrismaServic
95
106
  delete params.select;
96
107
  }
97
108
  this.debug(`Executing ${String(propKey)}.${String(methodKey)}() with params: ${JSON.stringify(params)}`);
98
- return contextModel[methodKey](params);
109
+ const result = await contextModel[methodKey](params);
110
+ // Dev-only: warn if a just-created row wouldn't be readable by its
111
+ // creator (create conditions broader than find conditions). Skipped
112
+ // outside development so no extra query runs on the hot path.
113
+ if (methodKey === 'create' && !options?.BYPASS_FILTERING && this.isDevMode()) {
114
+ await this.warnIfCreatedRowNotFindable(String(propKey), userRole, result);
115
+ }
116
+ return result;
99
117
  };
100
118
  }
101
119
  return model[methodKey];
@@ -380,6 +398,193 @@ let PrismaService = exports.PrismaService = PrismaService_1 = class PrismaServic
380
398
  }
381
399
  return args;
382
400
  }
401
+ /**
402
+ * Enforces permission `conditions` on `create` / `createMany`.
403
+ *
404
+ * Conditions cannot be pushed into a `WHERE` on insert, so instead we verify
405
+ * the incoming data *would* satisfy them — i.e. a `findFirst` with those
406
+ * conditions would return the new row. Own-scalar fields are checked against
407
+ * `data`; relation conditions are checked by looking up the referenced parent
408
+ * (which lets Prisma evaluate arbitrarily-nested relation conditions for us).
409
+ *
410
+ * Also default-denies: create is only allowed for a role/action that has an
411
+ * explicit permission (or 'ALL'), mirroring find/update/delete. Framework
412
+ * flows that must create without a permission (registration, sessions,
413
+ * tokens) pass BYPASS_FILTERING and never reach here.
414
+ *
415
+ * Fails **closed**: throws on a violation and on any condition shape it
416
+ * cannot evaluate, rather than silently allowing the insert.
417
+ */
418
+ async enforceCreateConditions(modelName, userRole, params, user, action) {
419
+ const normalizedName = modelName.toLowerCase().trim();
420
+ const exposedModels = (exports.CorePrismaContext.getStore()?.exposedModels || []).map((m) => m.toLowerCase());
421
+ if (exposedModels.includes(normalizedName))
422
+ return;
423
+ const permissions = this.normalizedPermissionsConfig[normalizedName]?.[userRole];
424
+ if (!permissions) {
425
+ throw new common_1.HttpException(`No permissions defined for role ${userRole} on model '${modelName}' — create is denied by default. Define a '${action}' permission (or 'ALL').`, common_1.HttpStatus.FORBIDDEN);
426
+ }
427
+ const actionPermissions = permissions[action] ?? permissions['create'];
428
+ if (!actionPermissions) {
429
+ throw new common_1.HttpException(`Role ${userRole} has no '${action}' permission on model '${modelName}' — create is denied. Define it (or 'ALL').`, common_1.HttpStatus.FORBIDDEN);
430
+ }
431
+ if (actionPermissions === 'ALL')
432
+ return;
433
+ const conditions = actionPermissions.conditions;
434
+ if (!conditions)
435
+ return; // permission defined but no conditions (e.g. setUserIdField only) → allow
436
+ const condObj = Array.isArray(conditions) ? { AND: conditions } : conditions;
437
+ const resolved = PrismaService_1._buildConditions(condObj, user);
438
+ const data = params?.data;
439
+ const rows = Array.isArray(data) ? data : [data ?? {}];
440
+ for (const row of rows) {
441
+ const ok = await this.matchesCreateConditions(resolved, row || {}, modelName);
442
+ if (!ok) {
443
+ throw new common_1.ForbiddenException(`Not allowed to create ${modelName}: the record does not satisfy the '${action}' permission conditions for role ${userRole}.`);
444
+ }
445
+ }
446
+ }
447
+ /**
448
+ * True only in an explicit local-development environment. Deliberately NOT
449
+ * `!== 'production'` — staging and other envs should behave like production
450
+ * (no extra diagnostic queries/logging). Only `development` / `dev` opt in.
451
+ */
452
+ isDevMode() {
453
+ const env = process.env.NODE_ENV;
454
+ return env === 'development' || env === 'dev';
455
+ }
456
+ /**
457
+ * Dev-only diagnostic. After a create that passed its create-conditions,
458
+ * checks whether the new row also satisfies the model's *find* conditions —
459
+ * if not, the creator can't read it back (create rule broader than find
460
+ * rule). Logs a warning; never affects the create. Runs only in development
461
+ * (gated by the caller).
462
+ */
463
+ async warnIfCreatedRowNotFindable(modelKey, userRole, created) {
464
+ try {
465
+ if (!created || created.id === undefined || created.id === null)
466
+ return;
467
+ const permissions = this.normalizedPermissionsConfig[modelKey.toLowerCase()]?.[userRole];
468
+ const findPerm = permissions?.['findFirst'] ?? permissions?.['findMany'];
469
+ if (!findPerm || findPerm === 'ALL' || !findPerm.conditions)
470
+ return;
471
+ const found = await this.prismaClient[modelKey].findFirst({ where: { id: created.id } });
472
+ if (!found) {
473
+ console.warn('\x1b[33m', `[APPX-CORE PRISMA] Created ${modelKey} id=${created.id} as role ${userRole}, but it does not satisfy the model's find conditions — the creator cannot read it back. Ensure your find conditions are at least as permissive as your create conditions.`, '\x1b[0m');
474
+ }
475
+ }
476
+ catch {
477
+ // Dev diagnostic only — never let it affect the create.
478
+ }
479
+ }
480
+ /** Recursively evaluates a (placeholder-resolved) condition object against a to-be-created row. */
481
+ async matchesCreateConditions(cond, row, modelName) {
482
+ for (const key of Object.keys(cond)) {
483
+ const val = cond[key];
484
+ if (key === 'AND') {
485
+ const arr = Array.isArray(val) ? val : [val];
486
+ for (const c of arr)
487
+ if (!(await this.matchesCreateConditions(c, row, modelName)))
488
+ return false;
489
+ continue;
490
+ }
491
+ if (key === 'OR') {
492
+ const arr = Array.isArray(val) ? val : [val];
493
+ let any = false;
494
+ for (const c of arr)
495
+ if (await this.matchesCreateConditions(c, row, modelName)) {
496
+ any = true;
497
+ break;
498
+ }
499
+ if (!any)
500
+ return false;
501
+ continue;
502
+ }
503
+ if (key === 'NOT') {
504
+ const arr = Array.isArray(val) ? val : [val];
505
+ for (const c of arr)
506
+ if (await this.matchesCreateConditions(c, row, modelName))
507
+ return false;
508
+ continue;
509
+ }
510
+ const relation = this.getRelation(modelName, key);
511
+ if (relation) {
512
+ if (!(await this.matchesCreateRelation(relation, key, val, row, modelName)))
513
+ return false;
514
+ continue;
515
+ }
516
+ if (!this.matchesCreateScalar(row[key], val, key, modelName))
517
+ return false;
518
+ }
519
+ return true;
520
+ }
521
+ /** Evaluates a scalar-field condition (value or Prisma operator object) against the row's value. */
522
+ matchesCreateScalar(actual, cond, key, modelName) {
523
+ if (cond === null)
524
+ return actual === null || actual === undefined;
525
+ if (cond instanceof Date || typeof cond !== 'object')
526
+ return actual === cond;
527
+ for (const op of Object.keys(cond)) {
528
+ const opv = cond[op];
529
+ switch (op) {
530
+ case 'equals':
531
+ if (actual !== opv)
532
+ return false;
533
+ break;
534
+ case 'not':
535
+ if (actual === opv)
536
+ return false;
537
+ break;
538
+ case 'in':
539
+ if (!Array.isArray(opv) || !opv.includes(actual))
540
+ return false;
541
+ break;
542
+ case 'notIn':
543
+ if (Array.isArray(opv) && opv.includes(actual))
544
+ return false;
545
+ break;
546
+ case 'lt':
547
+ if (!(actual < opv))
548
+ return false;
549
+ break;
550
+ case 'lte':
551
+ if (!(actual <= opv))
552
+ return false;
553
+ break;
554
+ case 'gt':
555
+ if (!(actual > opv))
556
+ return false;
557
+ break;
558
+ case 'gte':
559
+ if (!(actual >= opv))
560
+ return false;
561
+ break;
562
+ default:
563
+ throw new common_1.ForbiddenException(`create on ${modelName}: unsupported operator '${op}' on field '${key}' in a create condition. Use a simpler condition or setUserIdField.`);
564
+ }
565
+ }
566
+ return true;
567
+ }
568
+ /** Verifies the parent referenced by a belongsTo condition exists and satisfies the nested condition. */
569
+ async matchesCreateRelation(relation, field, nestedCond, row, modelName) {
570
+ if (relation.relation !== 'belongsTo' || !relation.referencingColumn) {
571
+ throw new common_1.ForbiddenException(`create on ${modelName}: unsupported relation condition on '${field}' — a new row has no related '${field}' yet to match. Constrain the scalar foreign key instead.`);
572
+ }
573
+ const fkValue = row[relation.referencingColumn];
574
+ if (fkValue === undefined || fkValue === null)
575
+ return false;
576
+ const delegate = relation.model.charAt(0).toLowerCase() + relation.model.slice(1);
577
+ // Use the request's transaction client if present (to see rows created
578
+ // earlier in the same request), else the raw client. Either way, access
579
+ // filtering on the parent is intentionally NOT re-applied — we check
580
+ // only the condition.
581
+ const client = nestjs_request_context_1.RequestContext.currentContext?.req.prisma || this.rawClient;
582
+ const found = await client[delegate].findFirst({
583
+ where: { AND: [{ [relation.foreignKey]: fkValue }, nestedCond] },
584
+ select: { [relation.foreignKey]: true },
585
+ });
586
+ return !!found;
587
+ }
383
588
  /**
384
589
  * Builds dynamic conditions based on the type of clause (OR, AND, or field conditions).
385
590
  * Each condition is processed, and placeholders (like `$USER_ID`) are replaced with actual values.