@lenne.tech/nest-server 11.41.9 → 11.42.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.
Files changed (58) hide show
  1. package/.claude/rules/configurable-features.md +3 -2
  2. package/.claude/rules/role-system.md +31 -0
  3. package/.claude/rules/testing.md +4 -4
  4. package/CLAUDE.md +6 -0
  5. package/FRAMEWORK-API.md +3 -2
  6. package/dist/core/common/helpers/graphql-ws-context.helper.js +1 -1
  7. package/dist/core/common/helpers/graphql-ws-context.helper.js.map +1 -1
  8. package/dist/core/common/helpers/outbound-url.helper.d.ts +14 -0
  9. package/dist/core/common/helpers/outbound-url.helper.js +234 -0
  10. package/dist/core/common/helpers/outbound-url.helper.js.map +1 -0
  11. package/dist/core/common/interfaces/server-options.interface.d.ts +5 -0
  12. package/dist/core/common/middleware/request-context.middleware.js +1 -0
  13. package/dist/core/common/middleware/request-context.middleware.js.map +1 -1
  14. package/dist/core/common/services/request-context.service.d.ts +1 -0
  15. package/dist/core/common/services/request-context.service.js.map +1 -1
  16. package/dist/core/modules/ai/core-ai-mcp.controller.js +5 -2
  17. package/dist/core/modules/ai/core-ai-mcp.controller.js.map +1 -1
  18. package/dist/core/modules/ai/services/core-ai-mcp.service.d.ts +1 -1
  19. package/dist/core/modules/ai/services/core-ai-mcp.service.js +3 -2
  20. package/dist/core/modules/ai/services/core-ai-mcp.service.js.map +1 -1
  21. package/dist/core/modules/ai/tools/ai-tool.registry.js +15 -2
  22. package/dist/core/modules/ai/tools/ai-tool.registry.js.map +1 -1
  23. package/dist/core/modules/tenant/core-tenant-role.helper.d.ts +2 -0
  24. package/dist/core/modules/tenant/core-tenant-role.helper.js +36 -0
  25. package/dist/core/modules/tenant/core-tenant-role.helper.js.map +1 -0
  26. package/dist/core/modules/tenant/core-tenant.guard.d.ts +7 -1
  27. package/dist/core/modules/tenant/core-tenant.guard.js +56 -4
  28. package/dist/core/modules/tenant/core-tenant.guard.js.map +1 -1
  29. package/dist/core/modules/user/core-user.service.d.ts +1 -0
  30. package/dist/core/modules/user/core-user.service.js +24 -0
  31. package/dist/core/modules/user/core-user.service.js.map +1 -1
  32. package/dist/index.d.ts +2 -0
  33. package/dist/index.js +2 -0
  34. package/dist/index.js.map +1 -1
  35. package/dist/tsconfig.build.tsbuildinfo +1 -1
  36. package/docs/REQUEST-LIFECYCLE.md +4 -2
  37. package/migration-guides/11.41.8-to-11.41.9.md +5 -0
  38. package/migration-guides/11.41.x-to-11.42.x.md +161 -0
  39. package/migration-guides/11.9.x-to-11.10.x.md +3 -2
  40. package/package.json +2 -1
  41. package/src/core/common/helpers/graphql-ws-context.helper.ts +1 -1
  42. package/src/core/common/helpers/outbound-url.helper.ts +310 -0
  43. package/src/core/common/interfaces/server-options.interface.ts +54 -0
  44. package/src/core/common/middleware/request-context.middleware.ts +1 -0
  45. package/src/core/common/services/request-context.service.ts +9 -0
  46. package/src/core/modules/ai/README.md +25 -0
  47. package/src/core/modules/ai/core-ai-mcp.controller.ts +22 -3
  48. package/src/core/modules/ai/services/core-ai-mcp.service.ts +9 -3
  49. package/src/core/modules/ai/tools/ai-tool.registry.ts +33 -3
  50. package/src/core/modules/better-auth/INTEGRATION-CHECKLIST.md +18 -11
  51. package/src/core/modules/tenant/INTEGRATION-CHECKLIST.md +14 -0
  52. package/src/core/modules/tenant/README.md +80 -8
  53. package/src/core/modules/tenant/core-tenant-role.helper.ts +75 -0
  54. package/src/core/modules/tenant/core-tenant.guard.ts +78 -5
  55. package/src/core/modules/tus/INTEGRATION-CHECKLIST.md +3 -1
  56. package/src/core/modules/tus/README.md +9 -0
  57. package/src/core/modules/user/core-user.service.ts +53 -0
  58. package/src/index.ts +2 -0
@@ -392,6 +392,15 @@ for them.
392
392
  | Header naming a tenant the caller belongs to | the finished file gets `metadata.tenantId` |
393
393
  | Header naming a tenant the caller does NOT belong to | **403**, nothing is stored |
394
394
 
395
+ **The header has to come from the TUS client itself.** Projects usually add `X-Tenant-Id` in an
396
+ interceptor of their generated API client, and `tus-js-client` does not go through that client. Without
397
+ an explicit header the upload is accepted, stays tenant-less and bypasses tenant filtering and any
398
+ per-tenant quota, with no error anywhere. Pass it to the upload, e.g. with nuxt-extensions:
399
+
400
+ ```typescript
401
+ const { addFiles } = useLtTusUpload({ headers: { 'X-Tenant-Id': currentTenantId } });
402
+ ```
403
+
395
404
  The tenant is recorded under `TUS_TENANT_METADATA_KEY` (`ltTenantId`), next to the owner under
396
405
  `TUS_OWNER_METADATA_KEY`. Both keys are framework-owned and always overwritten, so a client cannot
397
406
  put either of them into its own `Upload-Metadata`. `metadata.tenantId` on the finished file is what
@@ -5,6 +5,7 @@ import { sha256 } from 'js-sha256';
5
5
  import { Document, Model } from 'mongoose';
6
6
 
7
7
  import { looksLikeSystemRole, SYSTEM_ROLE_PREFIX } from '../../common/enums/role.enum';
8
+ import { accessDeniedException } from '../../common/exceptions/access-denied.exception';
8
9
  import { resolveAppUrlFromConfig } from '../../common/helpers/cookies.helper';
9
10
  import { maskEmail } from '../../common/helpers/logging.helper';
10
11
  import { assignPlain, isQueryableString, prepareServiceOptionsForCreate } from '../../common/helpers/input.helper';
@@ -673,6 +674,8 @@ export abstract class CoreUserService<
673
674
  const oldUser = (await this.mainDbModel.findById(id).lean().exec()) as null | TUser;
674
675
  const oldEmail = oldUser?.email;
675
676
 
677
+ this.assertCredentialChangeAllowed(oldUser, input, serviceOptions);
678
+
676
679
  // Capture the submitted password for the IAM sync before super.update()
677
680
  // hashes it in place.
678
681
  //
@@ -738,6 +741,56 @@ export abstract class CoreUserService<
738
741
  return updatedUser;
739
742
  }
740
743
 
744
+ /**
745
+ * Refuses a change of another account's e-mail address or password where the configuration promises
746
+ * tenant isolation from platform administrators.
747
+ *
748
+ * With multi-tenancy active and `adminBypass: false`, a platform administrator deliberately has no
749
+ * way into a tenant's data. Setting a tenant user's address or password is a way around that: change
750
+ * the address, request a reset, sign in as that person. The same holds for whoever created the
751
+ * account, because `S_CREATOR` may update it and the e-mail field is open to everyone who may.
752
+ *
753
+ * Untouched by design:
754
+ * - the account changing its own credentials,
755
+ * - profile fields, and an e-mail address sent along unchanged,
756
+ * - work without a signed-in user (invitation, password reset), which is the system's own,
757
+ * - every configuration other than multi-tenancy with `adminBypass: false`.
758
+ *
759
+ * `force` does not lift it: the rule is about WHO acts, and system work is defined by the absence of
760
+ * a current user. Protected so a project can widen or narrow it.
761
+ */
762
+ protected assertCredentialChangeAllowed(
763
+ target: null | TUser,
764
+ input: TUserInput,
765
+ serviceOptions?: ServiceOptions,
766
+ ): void {
767
+ const multiTenancy = ConfigService.configFastButReadOnly?.multiTenancy;
768
+ if (!multiTenancy || multiTenancy.enabled === false || multiTenancy.adminBypass !== false) {
769
+ return;
770
+ }
771
+ const currentUser = serviceOptions?.currentUser;
772
+ if (!currentUser?.id || !target) {
773
+ return;
774
+ }
775
+ // No exemption for administrator TARGETS: an administrator may write `roles`, so "promote the
776
+ // tenant account to ADMIN, then change its address" would open every account in two requests.
777
+ const targetId = String((target as any)._id ?? (target as any).id);
778
+ if (String(currentUser.id) === targetId) {
779
+ return;
780
+ }
781
+
782
+ const submitted = input as { email?: unknown; password?: unknown };
783
+ const normalize = (value: unknown) => (typeof value === 'string' ? value.trim().toLowerCase() : value);
784
+ const changesEmail = submitted?.email !== undefined && normalize(submitted.email) !== normalize(target.email);
785
+ const changesPassword = submitted?.password !== undefined;
786
+ if (changesEmail || changesPassword) {
787
+ throw accessDeniedException(
788
+ currentUser,
789
+ 'The e-mail address and password of a tenant account can only be changed by the account itself',
790
+ );
791
+ }
792
+ }
793
+
741
794
  /**
742
795
  * Delete user with automatic cleanup of IAM auth data
743
796
  *
package/src/index.ts CHANGED
@@ -47,6 +47,7 @@ export * from './core/common/helpers/input.helper';
47
47
  export * from './core/common/helpers/logging.helper';
48
48
  export * from './core/common/helpers/meta.helper';
49
49
  export * from './core/common/helpers/model.helper';
50
+ export * from './core/common/helpers/outbound-url.helper';
50
51
  export * from './core/common/helpers/process-diagnostics.helper';
51
52
  export * from './core/common/helpers/register-enum.helper';
52
53
  export * from './core/common/helpers/scim.helper';
@@ -258,6 +259,7 @@ export * from './core/modules/tenant/core-tenant.decorators';
258
259
  export * from './core/modules/tenant/core-tenant.enums';
259
260
  export * from './core/modules/tenant/core-tenant.guard';
260
261
  export * from './core/modules/tenant/core-role-scope.registry';
262
+ export * from './core/modules/tenant/core-tenant-role.helper';
261
263
  export * from './core/modules/tenant/core-tenant.helpers';
262
264
  export * from './core/modules/tenant/core-tenant.module';
263
265
  export * from './core/modules/tenant/core-tenant.service';