@lenne.tech/nest-server 11.34.1 → 11.35.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/.claude/rules/architecture.md +3 -1
- package/.claude/rules/configurable-features.md +3 -3
- package/.claude/rules/role-system.md +281 -0
- package/CLAUDE.md +11 -1
- package/FRAMEWORK-API.md +7 -3
- package/dist/core/common/decorators/nested-type.registry.d.ts +2 -0
- package/dist/core/common/decorators/nested-type.registry.js +17 -0
- package/dist/core/common/decorators/nested-type.registry.js.map +1 -0
- package/dist/core/common/decorators/restricted.decorator.d.ts +2 -1
- package/dist/core/common/decorators/restricted.decorator.js +52 -16
- package/dist/core/common/decorators/restricted.decorator.js.map +1 -1
- package/dist/core/common/decorators/restrictions-checked.marker.d.ts +4 -0
- package/dist/core/common/decorators/restrictions-checked.marker.js +30 -0
- package/dist/core/common/decorators/restrictions-checked.marker.js.map +1 -0
- package/dist/core/common/decorators/unified-field.decorator.d.ts +1 -1
- package/dist/core/common/decorators/unified-field.decorator.js +6 -3
- package/dist/core/common/decorators/unified-field.decorator.js.map +1 -1
- package/dist/core/common/enums/role.enum.d.ts +8 -0
- package/dist/core/common/enums/role.enum.js +28 -1
- package/dist/core/common/enums/role.enum.js.map +1 -1
- package/dist/core/common/helpers/execution-context-request.helper.d.ts +7 -0
- package/dist/core/common/helpers/execution-context-request.helper.js +31 -0
- package/dist/core/common/helpers/execution-context-request.helper.js.map +1 -0
- package/dist/core/common/helpers/graphql-ws-context.helper.d.ts +15 -0
- package/dist/core/common/helpers/graphql-ws-context.helper.js +96 -0
- package/dist/core/common/helpers/graphql-ws-context.helper.js.map +1 -0
- package/dist/core/common/helpers/gridfs.helper.js +1 -1
- package/dist/core/common/helpers/gridfs.helper.js.map +1 -1
- package/dist/core/common/interceptors/check-security.interceptor.js +2 -1
- package/dist/core/common/interceptors/check-security.interceptor.js.map +1 -1
- package/dist/core/common/interceptors/response-model.interceptor.js +3 -2
- package/dist/core/common/interceptors/response-model.interceptor.js.map +1 -1
- package/dist/core/common/interfaces/server-options.interface.d.ts +5 -0
- package/dist/core/common/plugins/mongoose-system-role.plugin.d.ts +1 -0
- package/dist/core/common/plugins/mongoose-system-role.plugin.js +106 -0
- package/dist/core/common/plugins/mongoose-system-role.plugin.js.map +1 -0
- package/dist/core/common/plugins/mongoose-tenant.plugin.js +134 -9
- package/dist/core/common/plugins/mongoose-tenant.plugin.js.map +1 -1
- package/dist/core/common/services/core-s3.service.js +4 -1
- package/dist/core/common/services/core-s3.service.js.map +1 -1
- package/dist/core/common/services/core-tenant-context.registry.d.ts +10 -0
- package/dist/core/common/services/core-tenant-context.registry.js +12 -0
- package/dist/core/common/services/core-tenant-context.registry.js.map +1 -0
- package/dist/core/modules/auth/guards/roles.guard.d.ts +1 -1
- package/dist/core/modules/auth/guards/roles.guard.js +2 -2
- package/dist/core/modules/auth/guards/roles.guard.js.map +1 -1
- package/dist/core/modules/better-auth/better-auth-roles.guard.js +2 -10
- package/dist/core/modules/better-auth/better-auth-roles.guard.js.map +1 -1
- package/dist/core/modules/file/core-file.service.d.ts +3 -1
- package/dist/core/modules/file/core-file.service.js +62 -7
- package/dist/core/modules/file/core-file.service.js.map +1 -1
- package/dist/core/modules/file/file-access.helper.d.ts +18 -0
- package/dist/core/modules/file/file-access.helper.js +46 -0
- package/dist/core/modules/file/file-access.helper.js.map +1 -0
- package/dist/core/modules/file/file-metadata.helper.js +1 -1
- package/dist/core/modules/file/file-metadata.helper.js.map +1 -1
- package/dist/core/modules/file/file-roles.config.d.ts +10 -0
- package/dist/core/modules/file/file-roles.config.js +78 -0
- package/dist/core/modules/file/file-roles.config.js.map +1 -0
- package/dist/core/modules/file/file-roles.helper.d.ts +2 -2
- package/dist/core/modules/file/file-roles.helper.js +8 -20
- package/dist/core/modules/file/file-roles.helper.js.map +1 -1
- package/dist/core/modules/tenant/core-role-scope.registry.d.ts +28 -0
- package/dist/core/modules/tenant/core-role-scope.registry.js +93 -0
- package/dist/core/modules/tenant/core-role-scope.registry.js.map +1 -0
- package/dist/core/modules/tenant/core-tenant.guard.d.ts +5 -0
- package/dist/core/modules/tenant/core-tenant.guard.js +37 -11
- package/dist/core/modules/tenant/core-tenant.guard.js.map +1 -1
- package/dist/core/modules/tenant/core-tenant.helpers.d.ts +9 -1
- package/dist/core/modules/tenant/core-tenant.helpers.js +65 -4
- package/dist/core/modules/tenant/core-tenant.helpers.js.map +1 -1
- package/dist/core/modules/tenant/core-tenant.module.js +5 -0
- package/dist/core/modules/tenant/core-tenant.module.js.map +1 -1
- package/dist/core/modules/tenant/core-tenant.service.d.ts +1 -0
- package/dist/core/modules/tenant/core-tenant.service.js +25 -0
- package/dist/core/modules/tenant/core-tenant.service.js.map +1 -1
- package/dist/core/modules/tus/core-tus.service.d.ts +2 -0
- package/dist/core/modules/tus/core-tus.service.js +34 -4
- package/dist/core/modules/tus/core-tus.service.js.map +1 -1
- package/dist/core/modules/tus/index.d.ts +1 -0
- package/dist/core/modules/tus/index.js +1 -0
- package/dist/core/modules/tus/index.js.map +1 -1
- package/dist/core/modules/tus/tus.constants.d.ts +1 -0
- package/dist/core/modules/tus/tus.constants.js +2 -1
- package/dist/core/modules/tus/tus.constants.js.map +1 -1
- package/dist/core/modules/user/core-user.service.js +5 -0
- package/dist/core/modules/user/core-user.service.js.map +1 -1
- package/dist/core/modules/user/inputs/core-user.input.js +8 -0
- package/dist/core/modules/user/inputs/core-user.input.js.map +1 -1
- package/dist/core.module.js +10 -0
- package/dist/core.module.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/server/modules/file/file.service.js +4 -1
- package/dist/server/modules/file/file.service.js.map +1 -1
- package/dist/test/test.helper.d.ts +1 -0
- package/dist/test/test.helper.js +26 -6
- package/dist/test/test.helper.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/docs/REQUEST-LIFECYCLE.md +60 -1
- package/migration-guides/11.34.x-to-11.35.x.md +832 -0
- package/package.json +1 -1
- package/src/core/common/decorators/nested-type.registry.ts +42 -0
- package/src/core/common/decorators/restricted.decorator.ts +149 -21
- package/src/core/common/decorators/restrictions-checked.marker.ts +87 -0
- package/src/core/common/decorators/unified-field.decorator.ts +4 -3
- package/src/core/common/enums/role.enum.ts +116 -0
- package/src/core/common/helpers/execution-context-request.helper.ts +102 -0
- package/src/core/common/helpers/graphql-ws-context.helper.ts +192 -0
- package/src/core/common/helpers/gridfs.helper.ts +18 -2
- package/src/core/common/interceptors/check-security.interceptor.ts +4 -1
- package/src/core/common/interceptors/response-model.interceptor.ts +3 -2
- package/src/core/common/interfaces/server-options.interface.ts +139 -7
- package/src/core/common/plugins/mongoose-system-role.plugin.ts +193 -0
- package/src/core/common/plugins/mongoose-tenant.plugin.ts +244 -12
- package/src/core/common/services/core-s3.service.ts +9 -1
- package/src/core/common/services/core-tenant-context.registry.ts +50 -0
- package/src/core/modules/auth/guards/roles.guard.ts +7 -5
- package/src/core/modules/better-auth/better-auth-roles.guard.ts +7 -13
- package/src/core/modules/file/INTEGRATION-CHECKLIST.md +95 -1
- package/src/core/modules/file/README.md +61 -0
- package/src/core/modules/file/core-file.service.ts +166 -9
- package/src/core/modules/file/file-access.helper.ts +149 -0
- package/src/core/modules/file/file-metadata.helper.ts +13 -5
- package/src/core/modules/file/file-roles.config.ts +202 -0
- package/src/core/modules/file/file-roles.helper.ts +11 -40
- package/src/core/modules/tenant/README.md +20 -1
- package/src/core/modules/tenant/core-role-scope.registry.ts +195 -0
- package/src/core/modules/tenant/core-tenant.guard.ts +92 -12
- package/src/core/modules/tenant/core-tenant.helpers.ts +139 -7
- package/src/core/modules/tenant/core-tenant.module.ts +12 -0
- package/src/core/modules/tenant/core-tenant.service.ts +56 -0
- package/src/core/modules/tus/README.md +46 -7
- package/src/core/modules/tus/core-tus.service.ts +94 -5
- package/src/core/modules/tus/index.ts +1 -0
- package/src/core/modules/tus/tus.constants.ts +17 -1
- package/src/core/modules/user/core-user.service.ts +12 -0
- package/src/core/modules/user/inputs/core-user.input.ts +28 -2
- package/src/core.module.ts +62 -1
- package/src/index.ts +3 -0
- package/src/server/modules/file/file.service.ts +45 -2
- package/src/test/test.helper.ts +54 -11
|
@@ -205,6 +205,7 @@ JWT-based authentication for existing projects:
|
|
|
205
205
|
| **Apollo Server** | Full GraphQL server with schema-first or code-first |
|
|
206
206
|
| **Custom Scalars** | `Date`, `DateTime` (timestamp), `JSON`, `Any` |
|
|
207
207
|
| **Subscriptions** | WebSocket support via `graphql-ws` with auth. The `PUB_SUB` provider is built from a factory: `CoreRedisPubSub` when `redis` is configured (delivery is then cluster-wide), the in-memory `PubSub` otherwise (delivery only to clients connected to the publishing replica). **Constraint once Redis is in play: every published payload must be JSON-serializable** — it crosses the wire as JSON, so `Date`, class instances, `Map`/`Set` and `undefined` do not survive the round trip. An in-process `PubSub` never had this constraint, so a payload that worked on one replica can silently lose fields on a cluster. Publish plain objects and ISO strings |
|
|
208
|
+
| **Subscription request context** | Since 11.35.0 `CoreModule` installs a context-aware `execute` / `subscribe` pair (inside `subscriptions`, where `ApolloDriver` forwards it) on all three GraphQL driver builders, so every WebSocket operation runs inside a `RequestContext`. Before that a WS operation had **none** — no Express middleware runs on an upgrade, and `CoreTenantGuard.getRequest()` finds no `req` on a subscription context — and `mongooseTenantPlugin` reads "no context" as "system operation, no filter", so a tenant-scoped read while delivering a subscription message returned EVERY tenant's rows. The tenant comes from the handshake's tenant header **validated against an active membership**, else from the subscriber's memberships; an unresolvable tenant leaves the safety net to refuse the read. The async iterator is wrapped too, not only the subscribe call: graphql-js runs `resolve` / `filter` / field resolvers inside its `next()`. See `.claude/rules/role-system.md` → "Tenant context on non-HTTP transports" |
|
|
208
209
|
| **Complexity Analysis** | Query cost calculation to prevent DoS attacks |
|
|
209
210
|
| **Enum Registration** | `registerEnum()` helper for GraphQL enum types |
|
|
210
211
|
| **Upload Support** | `graphqlUploadExpress()` for multipart file uploads |
|
|
@@ -1217,6 +1218,64 @@ export class User extends CorePersistenceModel {
|
|
|
1217
1218
|
}
|
|
1218
1219
|
```
|
|
1219
1220
|
|
|
1221
|
+
#### Nested data: only a DECLARED type is enforced (11.35.0+)
|
|
1222
|
+
|
|
1223
|
+
The check recurses, but it can only find metadata for a nested value whose class it knows. An embedded
|
|
1224
|
+
subdocument read out of MongoDB is a **plain object** (`CoreModel.map()` is a shallow `Object.assign`;
|
|
1225
|
+
`prepareOutput()` and `ResponseModelInterceptor` map the top level only), and `Object` carries no
|
|
1226
|
+
`@Restricted` metadata. Before 11.35.0 that meant every nested restriction silently evaluated to "no
|
|
1227
|
+
restrictions at all"; since 11.35.0 the nested type registry `@UnifiedField` fills is consulted, so a
|
|
1228
|
+
plain nested value is matched against the class its parent DECLARED — at every level, and for every
|
|
1229
|
+
item of a declared array.
|
|
1230
|
+
|
|
1231
|
+
```typescript
|
|
1232
|
+
@UnifiedField({ type: () => Insurance }) // enforced
|
|
1233
|
+
insurance?: Insurance;
|
|
1234
|
+
|
|
1235
|
+
@UnifiedField({ isArray: true, type: () => Insurance }) // enforced, every item
|
|
1236
|
+
insurances?: Insurance[];
|
|
1237
|
+
|
|
1238
|
+
@UnifiedField({ isAny: true }) // NOT reached — nothing declares the type
|
|
1239
|
+
extra?: any;
|
|
1240
|
+
```
|
|
1241
|
+
|
|
1242
|
+
An undeclared nested type stays unchecked on purpose: such a value is just as legitimately free-form
|
|
1243
|
+
JSON, a `Map` or a scalar, and failing closed would strip far more than it protects. **If a nested
|
|
1244
|
+
field must be protected, declare its type.** A class-level `@Restricted` on a nested type strips the
|
|
1245
|
+
contents and leaves an empty container (the `checkObjectItself: false` default).
|
|
1246
|
+
|
|
1247
|
+
### File access — two layers, and why neither is optional
|
|
1248
|
+
|
|
1249
|
+
Files are the one resource whose authorization does NOT go through `CrudService.process()`, so the
|
|
1250
|
+
decorator model above does not reach them. GridFS is reached through the native MongoDB driver and S3
|
|
1251
|
+
through its own SDK, which means `mongooseTenantPlugin` never runs on a file store — and that is why
|
|
1252
|
+
`CoreFileController` and `CoreFileResolver` carry `@SkipTenantCheck()`.
|
|
1253
|
+
|
|
1254
|
+
| Layer | What it answers | Where |
|
|
1255
|
+
|-------|-----------------|-------|
|
|
1256
|
+
| `file.downloadRoles` / `uploadRoles` / `deleteRoles` | "may this caller reach the route at all" | applied onto the base-class members at boot by `applyFileRoles()`, read by the role guards |
|
|
1257
|
+
| `CoreFileService.checkRights()` | "…but only THIS file" | the service, once per operation, with `checkInputType` naming the path (`'id'`, `'filename'`, `'filterArgs'`, `'file'`, `'files'`) |
|
|
1258
|
+
|
|
1259
|
+
**The first layer cannot express the second.** The role names resolve against `user.roles` — a global
|
|
1260
|
+
attribute — never against `membership.role`, so no configuration can say "only their own tenant's
|
|
1261
|
+
files". That sentence needs data, and the data lives in the file's `metadata`.
|
|
1262
|
+
|
|
1263
|
+
**File ids are not secrets.** An ObjectId is 4 bytes of timestamp + 5 bytes of randomness generated once
|
|
1264
|
+
PER PROCESS + a 3-byte incrementing counter, so a caller who holds one valid id (their own upload) knows
|
|
1265
|
+
the random part and a counter reference point; neighbouring files sit on neighbouring values. The file
|
|
1266
|
+
routes are also not rate-limited by the framework. So a widened role gate without a per-file rule is
|
|
1267
|
+
practically enumerable, not theoretically.
|
|
1268
|
+
|
|
1269
|
+
Since 11.35.0 the second layer is a declaration rather than code — `file.access`, one value per project
|
|
1270
|
+
class (`'public'`, `'authenticated'`, `'owner'`, `'tenant'`, or `'custom'` to write your own, the
|
|
1271
|
+
default). `'owner'` / `'tenant'` also STAMP the metadata they decide on as the service writes. When the
|
|
1272
|
+
gate is widened past `ADMIN` and neither `file.access` nor an override declares a policy, the service
|
|
1273
|
+
warns at boot — the difference between a decision and an omission is the only thing a boot check can
|
|
1274
|
+
usefully detect.
|
|
1275
|
+
|
|
1276
|
+
Full model, per-driver behaviour and the audit checklist: `src/core/modules/file/README.md` § Access
|
|
1277
|
+
control and `src/core/modules/file/INTEGRATION-CHECKLIST.md`.
|
|
1278
|
+
|
|
1220
1279
|
### @UnifiedField() — Schema & Validation
|
|
1221
1280
|
|
|
1222
1281
|
Single decorator that replaces `@Field()`, `@ApiProperty()`, `@IsOptional()`, and more:
|
|
@@ -1459,7 +1518,7 @@ const allOrders = await RequestContext.runWithBypassTenantGuard(async () => {
|
|
|
1459
1518
|
|
|
1460
1519
|
// Exclude specific schemas from tenant filtering
|
|
1461
1520
|
multiTenancy: {
|
|
1462
|
-
excludeSchemas: [
|
|
1521
|
+
excludeSchemas: [], // model names, not collection names — this turns isolation OFF per model
|
|
1463
1522
|
}
|
|
1464
1523
|
```
|
|
1465
1524
|
|