@lenne.tech/nest-server 10.5.0 → 10.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/dist/core/common/decorators/restricted.decorator.d.ts +5 -1
- package/dist/core/common/decorators/restricted.decorator.js +20 -3
- package/dist/core/common/decorators/restricted.decorator.js.map +1 -1
- package/dist/core/common/helpers/common.helper.js +2 -3
- package/dist/core/common/helpers/common.helper.js.map +1 -1
- package/dist/core/common/helpers/config.helper.js +1 -2
- package/dist/core/common/helpers/config.helper.js.map +1 -1
- package/dist/core/common/helpers/context.helper.d.ts +0 -1
- package/dist/core/common/helpers/context.helper.js +1 -2
- package/dist/core/common/helpers/context.helper.js.map +1 -1
- package/dist/core/common/helpers/db.helper.d.ts +0 -24
- package/dist/core/common/helpers/db.helper.js +20 -18
- package/dist/core/common/helpers/db.helper.js.map +1 -1
- package/dist/core/common/helpers/decorator.helper.d.ts +0 -24
- package/dist/core/common/helpers/decorator.helper.js +2 -3
- package/dist/core/common/helpers/decorator.helper.js.map +1 -1
- package/dist/core/common/helpers/file.helper.js +3 -4
- package/dist/core/common/helpers/file.helper.js.map +1 -1
- package/dist/core/common/helpers/filter.helper.d.ts +0 -24
- package/dist/core/common/helpers/filter.helper.js +6 -6
- package/dist/core/common/helpers/filter.helper.js.map +1 -1
- package/dist/core/common/helpers/graphql.helper.d.ts +2 -2
- package/dist/core/common/helpers/graphql.helper.js +12 -13
- package/dist/core/common/helpers/graphql.helper.js.map +1 -1
- package/dist/core/common/helpers/input.helper.d.ts +2 -0
- package/dist/core/common/helpers/input.helper.js +45 -38
- package/dist/core/common/helpers/input.helper.js.map +1 -1
- package/dist/core/common/helpers/model.helper.js +8 -8
- package/dist/core/common/helpers/model.helper.js.map +1 -1
- package/dist/core/common/helpers/service.helper.js +3 -4
- package/dist/core/common/helpers/service.helper.js.map +1 -1
- package/dist/core/common/helpers/table.helper.js +1 -2
- package/dist/core/common/helpers/table.helper.js.map +1 -1
- package/dist/core/common/interceptors/check-response.interceptor.d.ts +1 -0
- package/dist/core/common/interceptors/check-response.interceptor.js +8 -1
- package/dist/core/common/interceptors/check-response.interceptor.js.map +1 -1
- package/dist/core/common/interceptors/check-security.interceptor.d.ts +7 -0
- package/dist/core/common/interceptors/check-security.interceptor.js +30 -2
- package/dist/core/common/interceptors/check-security.interceptor.js.map +1 -1
- package/dist/core/common/interfaces/server-options.interface.d.ts +6 -2
- package/dist/core/common/interfaces/service-options.interface.d.ts +0 -24
- package/dist/core/common/models/core-persistence.model.d.ts +0 -26
- package/dist/core/common/models/core-persistence.model.js +1 -23
- package/dist/core/common/models/core-persistence.model.js.map +1 -1
- package/dist/core/common/plugins/mongoose-id.plugin.js +1 -2
- package/dist/core/common/plugins/mongoose-id.plugin.js.map +1 -1
- package/dist/core/common/services/crud.service.d.ts +0 -24
- package/dist/core/common/services/model-doc.service.d.ts +0 -24
- package/dist/core/common/services/module.service.d.ts +0 -24
- package/dist/core/common/types/field-selection.type.d.ts +0 -24
- package/dist/core/common/types/ids.type.d.ts +0 -24
- package/dist/core/common/types/populate-config.type.d.ts +0 -24
- package/dist/core/common/types/string-or-object-id.type.d.ts +0 -24
- package/dist/core/modules/auth/strategies/jwt-refresh.strategy.d.ts +0 -1
- package/dist/core/modules/file/core-file-info.model.d.ts +0 -24
- package/dist/core/modules/file/core-file.service.d.ts +0 -26
- package/dist/core/modules/file/interfaces/file-upload.interface.d.ts +0 -1
- package/dist/core/modules/user/core-user.model.d.ts +0 -24
- package/dist/core/modules/user/core-user.model.js +1 -2
- package/dist/core/modules/user/core-user.model.js.map +1 -1
- package/dist/core/modules/user/core-user.service.d.ts +0 -24
- package/dist/server/common/models/persistence.model.d.ts +0 -24
- package/dist/server/modules/file/file-info.model.d.ts +0 -25
- package/dist/server/modules/file/file.controller.d.ts +0 -1
- package/dist/server/modules/file/file.service.d.ts +0 -24
- package/dist/server/modules/user/avatar.controller.d.ts +0 -1
- package/dist/server/modules/user/user.model.d.ts +1 -24
- package/dist/server/modules/user/user.model.js +8 -2
- package/dist/server/modules/user/user.model.js.map +1 -1
- package/dist/server/modules/user/user.resolver.d.ts +1 -1
- package/dist/server/modules/user/user.resolver.js +1 -1
- package/dist/server/modules/user/user.resolver.js.map +1 -1
- package/dist/server/modules/user/user.service.d.ts +0 -25
- package/dist/test/test.helper.d.ts +0 -2
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +28 -28
- package/src/core/common/decorators/restricted.decorator.ts +32 -6
- package/src/core/common/helpers/db.helper.ts +5 -1
- package/src/core/common/helpers/input.helper.ts +16 -4
- package/src/core/common/interceptors/check-response.interceptor.ts +13 -1
- package/src/core/common/interceptors/check-security.interceptor.ts +37 -2
- package/src/core/common/interfaces/server-options.interface.ts +26 -3
- package/src/core/common/models/core-persistence.model.ts +1 -25
- package/src/core/modules/user/core-user.model.ts +2 -3
- package/src/server/modules/user/user.model.ts +10 -3
- package/src/server/modules/user/user.resolver.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lenne.tech/nest-server",
|
|
3
|
-
"version": "10.
|
|
3
|
+
"version": "10.7.0",
|
|
4
4
|
"description": "Modern, fast, powerful Node.js web framework in TypeScript based on Nest with a GraphQL API and a connection to MongoDB (or other databases).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"node",
|
|
@@ -66,14 +66,14 @@
|
|
|
66
66
|
"@getbrevo/brevo": "1.0.1",
|
|
67
67
|
"@lenne.tech/mongoose-gridfs": "1.4.2",
|
|
68
68
|
"@lenne.tech/multer-gridfs-storage": "5.0.6",
|
|
69
|
-
"@nestjs/apollo": "12.2.
|
|
70
|
-
"@nestjs/common": "10.4.
|
|
71
|
-
"@nestjs/core": "10.4.
|
|
72
|
-
"@nestjs/graphql": "12.2.
|
|
69
|
+
"@nestjs/apollo": "12.2.2",
|
|
70
|
+
"@nestjs/common": "10.4.13",
|
|
71
|
+
"@nestjs/core": "10.4.13",
|
|
72
|
+
"@nestjs/graphql": "12.2.2",
|
|
73
73
|
"@nestjs/jwt": "10.2.0",
|
|
74
74
|
"@nestjs/mongoose": "10.1.0",
|
|
75
75
|
"@nestjs/passport": "10.0.3",
|
|
76
|
-
"@nestjs/platform-express": "10.4.
|
|
76
|
+
"@nestjs/platform-express": "10.4.13",
|
|
77
77
|
"@nestjs/schedule": "4.1.1",
|
|
78
78
|
"@nestjs/terminus": "10.2.3",
|
|
79
79
|
"apollo-server-core": "3.13.0",
|
|
@@ -81,22 +81,22 @@
|
|
|
81
81
|
"bcrypt": "5.1.1",
|
|
82
82
|
"class-transformer": "0.5.1",
|
|
83
83
|
"class-validator": "0.14.1",
|
|
84
|
-
"compression": "1.7.
|
|
84
|
+
"compression": "1.7.5",
|
|
85
85
|
"cookie-parser": "1.4.7",
|
|
86
86
|
"ejs": "3.1.10",
|
|
87
87
|
"graphql": "16.9.0",
|
|
88
88
|
"graphql-query-complexity": "1.0.0",
|
|
89
|
-
"graphql-subscriptions": "
|
|
89
|
+
"graphql-subscriptions": "3.0.0",
|
|
90
90
|
"graphql-upload": "15.0.2",
|
|
91
91
|
"js-sha256": "0.11.0",
|
|
92
92
|
"json-to-graphql-query": "2.3.0",
|
|
93
|
-
"light-my-request": "6.
|
|
93
|
+
"light-my-request": "6.3.0",
|
|
94
94
|
"lodash": "4.17.21",
|
|
95
|
-
"mongodb": "6.
|
|
96
|
-
"mongoose": "7.8.
|
|
95
|
+
"mongodb": "6.11.0",
|
|
96
|
+
"mongoose": "7.8.3",
|
|
97
97
|
"multer": "1.4.5-lts.1",
|
|
98
98
|
"node-mailjet": "6.0.6",
|
|
99
|
-
"nodemailer": "6.9.
|
|
99
|
+
"nodemailer": "6.9.16",
|
|
100
100
|
"nodemon": "3.1.7",
|
|
101
101
|
"passport": "0.7.0",
|
|
102
102
|
"passport-jwt": "4.0.1",
|
|
@@ -110,22 +110,22 @@
|
|
|
110
110
|
"@babel/plugin-proposal-private-methods": "7.18.6",
|
|
111
111
|
"@compodoc/compodoc": "1.1.26",
|
|
112
112
|
"@lenne.tech/eslint-config-ts": "0.0.16",
|
|
113
|
-
"@nestjs/cli": "10.4.
|
|
114
|
-
"@nestjs/schematics": "10.2.
|
|
115
|
-
"@nestjs/testing": "10.4.
|
|
116
|
-
"@swc/cli": "0.
|
|
117
|
-
"@swc/core": "1.
|
|
118
|
-
"@swc/jest": "0.2.
|
|
113
|
+
"@nestjs/cli": "10.4.8",
|
|
114
|
+
"@nestjs/schematics": "10.2.3",
|
|
115
|
+
"@nestjs/testing": "10.4.13",
|
|
116
|
+
"@swc/cli": "0.5.2",
|
|
117
|
+
"@swc/core": "1.10.0",
|
|
118
|
+
"@swc/jest": "0.2.37",
|
|
119
119
|
"@types/compression": "1.7.5",
|
|
120
|
-
"@types/cookie-parser": "1.4.
|
|
120
|
+
"@types/cookie-parser": "1.4.8",
|
|
121
121
|
"@types/ejs": "3.1.5",
|
|
122
122
|
"@types/express": "4.17.21",
|
|
123
123
|
"@types/jest": "29.5.14",
|
|
124
|
-
"@types/lodash": "4.17.
|
|
124
|
+
"@types/lodash": "4.17.13",
|
|
125
125
|
"@types/multer": "1.4.12",
|
|
126
|
-
"@types/node": "
|
|
127
|
-
"@types/nodemailer": "6.4.
|
|
128
|
-
"@types/passport": "1.0.
|
|
126
|
+
"@types/node": "22.10.1",
|
|
127
|
+
"@types/nodemailer": "6.4.17",
|
|
128
|
+
"@types/passport": "1.0.17",
|
|
129
129
|
"@types/supertest": "6.0.2",
|
|
130
130
|
"@typescript-eslint/eslint-plugin": "6.21.0",
|
|
131
131
|
"@typescript-eslint/parser": "6.21.0",
|
|
@@ -139,19 +139,19 @@
|
|
|
139
139
|
"grunt-contrib-clean": "2.0.1",
|
|
140
140
|
"grunt-contrib-watch": "1.1.0",
|
|
141
141
|
"grunt-sync": "0.8.2",
|
|
142
|
-
"husky": "9.1.
|
|
142
|
+
"husky": "9.1.7",
|
|
143
143
|
"jest": "29.7.0",
|
|
144
144
|
"npm-watch": "0.13.0",
|
|
145
|
-
"pm2": "5.4.
|
|
146
|
-
"prettier": "3.
|
|
145
|
+
"pm2": "5.4.3",
|
|
146
|
+
"prettier": "3.4.2",
|
|
147
147
|
"pretty-quick": "4.0.0",
|
|
148
148
|
"supertest": "7.0.0",
|
|
149
149
|
"ts-jest": "29.2.5",
|
|
150
150
|
"ts-loader": "9.5.1",
|
|
151
|
-
"ts-morph": "
|
|
151
|
+
"ts-morph": "24.0.0",
|
|
152
152
|
"ts-node": "10.9.2",
|
|
153
153
|
"tsconfig-paths": "4.2.0",
|
|
154
|
-
"typescript": "5.
|
|
154
|
+
"typescript": "5.7.2",
|
|
155
155
|
"yalc": "1.0.0-pre.53"
|
|
156
156
|
},
|
|
157
157
|
"overrides": {
|
|
@@ -3,7 +3,7 @@ import 'reflect-metadata';
|
|
|
3
3
|
|
|
4
4
|
import { ProcessType } from '../enums/process-type.enum';
|
|
5
5
|
import { RoleEnum } from '../enums/role.enum';
|
|
6
|
-
import { getIncludedIds } from '../helpers/db.helper';
|
|
6
|
+
import { equalIds, getIncludedIds } from '../helpers/db.helper';
|
|
7
7
|
import { RequireAtLeastOne } from '../types/required-at-least-one.type';
|
|
8
8
|
|
|
9
9
|
import _ = require('lodash');
|
|
@@ -22,6 +22,7 @@ export type RestrictedType = (
|
|
|
22
22
|
'memberOf' | 'roles'
|
|
23
23
|
>
|
|
24
24
|
| string
|
|
25
|
+
| string[]
|
|
25
26
|
)[];
|
|
26
27
|
|
|
27
28
|
/**
|
|
@@ -63,11 +64,15 @@ export const checkRestricted = (
|
|
|
63
64
|
data: any,
|
|
64
65
|
user: { hasRole: (roles: string[]) => boolean; id: any },
|
|
65
66
|
options: {
|
|
67
|
+
allowCreatorOfParent?: boolean;
|
|
66
68
|
checkObjectItself?: boolean;
|
|
67
69
|
dbObject?: any;
|
|
68
70
|
debug?: boolean;
|
|
71
|
+
ignoreFunctions?: boolean;
|
|
69
72
|
ignoreUndefined?: boolean;
|
|
73
|
+
isCreatorOfParent?: boolean;
|
|
70
74
|
mergeRoles?: boolean;
|
|
75
|
+
noteCheckedObjects?: boolean;
|
|
71
76
|
processType?: ProcessType;
|
|
72
77
|
removeUndefinedFromResultArray?: boolean;
|
|
73
78
|
throwError?: boolean;
|
|
@@ -78,16 +83,20 @@ export const checkRestricted = (
|
|
|
78
83
|
// For Input: throwError = true
|
|
79
84
|
// For Output: throwError = false
|
|
80
85
|
const config = {
|
|
86
|
+
allowCreatorOfParent: true,
|
|
81
87
|
checkObjectItself: false,
|
|
88
|
+
ignoreFunctions: true,
|
|
82
89
|
ignoreUndefined: true,
|
|
90
|
+
isCreatorOfParent: false,
|
|
83
91
|
mergeRoles: true,
|
|
92
|
+
noteCheckedObjects: true,
|
|
84
93
|
removeUndefinedFromResultArray: true,
|
|
85
94
|
throwError: true,
|
|
86
95
|
...options,
|
|
87
96
|
};
|
|
88
97
|
|
|
89
98
|
// Primitives
|
|
90
|
-
if (!data || typeof data !== 'object') {
|
|
99
|
+
if (!data || typeof data !== 'object' || (config.noteCheckedObjects && data._objectAlreadyCheckedForRestrictions)) {
|
|
91
100
|
return data;
|
|
92
101
|
}
|
|
93
102
|
|
|
@@ -109,6 +118,10 @@ export const checkRestricted = (
|
|
|
109
118
|
|
|
110
119
|
// Check function
|
|
111
120
|
const validateRestricted = (restricted) => {
|
|
121
|
+
if (config.noteCheckedObjects && data?._objectAlreadyCheckedForRestrictions) {
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
|
|
112
125
|
// Check restrictions
|
|
113
126
|
if (!restricted?.length) {
|
|
114
127
|
return true;
|
|
@@ -116,7 +129,7 @@ export const checkRestricted = (
|
|
|
116
129
|
|
|
117
130
|
let valid = false;
|
|
118
131
|
|
|
119
|
-
// Get roles
|
|
132
|
+
// Get roles restricted element
|
|
120
133
|
const roles: string[] = [];
|
|
121
134
|
restricted.forEach((item) => {
|
|
122
135
|
if (typeof item === 'string') {
|
|
@@ -130,6 +143,8 @@ export const checkRestricted = (
|
|
|
130
143
|
} else {
|
|
131
144
|
roles.push(item.roles);
|
|
132
145
|
}
|
|
146
|
+
} else if (Array.isArray(item)) {
|
|
147
|
+
roles.push(...item);
|
|
133
148
|
}
|
|
134
149
|
});
|
|
135
150
|
|
|
@@ -145,8 +160,10 @@ export const checkRestricted = (
|
|
|
145
160
|
roles.includes(RoleEnum.S_EVERYONE)
|
|
146
161
|
|| user?.hasRole?.(roles)
|
|
147
162
|
|| (user?.id && roles.includes(RoleEnum.S_USER))
|
|
148
|
-
|| (roles.includes(RoleEnum.S_SELF) &&
|
|
149
|
-
|| (roles.includes(RoleEnum.S_CREATOR)
|
|
163
|
+
|| (roles.includes(RoleEnum.S_SELF) && equalIds(data, user))
|
|
164
|
+
|| (roles.includes(RoleEnum.S_CREATOR)
|
|
165
|
+
&& (('createdBy' in data && equalIds(data.createdBy, user))
|
|
166
|
+
|| (config.allowCreatorOfParent && !('createdBy' in data) && config.isCreatorOfParent)))
|
|
150
167
|
) {
|
|
151
168
|
valid = true;
|
|
152
169
|
}
|
|
@@ -200,7 +217,7 @@ export const checkRestricted = (
|
|
|
200
217
|
return valid;
|
|
201
218
|
};
|
|
202
219
|
|
|
203
|
-
// Check object
|
|
220
|
+
// Check data object
|
|
204
221
|
const objectRestrictions = getRestricted(data.constructor) || [];
|
|
205
222
|
if (config.checkObjectItself) {
|
|
206
223
|
const objectIsValid = validateRestricted(objectRestrictions);
|
|
@@ -218,6 +235,11 @@ export const checkRestricted = (
|
|
|
218
235
|
|
|
219
236
|
// Check properties of object
|
|
220
237
|
for (const propertyKey of Object.keys(data)) {
|
|
238
|
+
// Ignore functions
|
|
239
|
+
if (typeof data[propertyKey] === 'function' && config.ignoreFunctions) {
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
|
|
221
243
|
// Check undefined
|
|
222
244
|
if (data[propertyKey] === undefined && config.ignoreUndefined) {
|
|
223
245
|
continue;
|
|
@@ -230,6 +252,10 @@ export const checkRestricted = (
|
|
|
230
252
|
|
|
231
253
|
// Check rights
|
|
232
254
|
if (valid) {
|
|
255
|
+
// Check if data is user or user is creator of data (for nested plain objects)
|
|
256
|
+
config.isCreatorOfParent
|
|
257
|
+
= equalIds(data, user) || ('createdBy' in data ? equalIds(data.createdBy, user) : config.isCreatorOfParent);
|
|
258
|
+
|
|
233
259
|
// Check deep
|
|
234
260
|
data[propertyKey] = checkRestricted(data[propertyKey], user, config, processedObjects);
|
|
235
261
|
} else {
|
|
@@ -212,7 +212,9 @@ export async function check(
|
|
|
212
212
|
value: any,
|
|
213
213
|
user: { hasRole: (roles: string[]) => boolean; id: any },
|
|
214
214
|
options?: {
|
|
215
|
+
allowCreatorOfParent?: boolean;
|
|
215
216
|
dbObject?: any;
|
|
217
|
+
isCreatorOfParent?: boolean;
|
|
216
218
|
metatype?: any;
|
|
217
219
|
processType?: ProcessType;
|
|
218
220
|
roles?: string | string[];
|
|
@@ -221,6 +223,8 @@ export async function check(
|
|
|
221
223
|
},
|
|
222
224
|
): Promise<any> {
|
|
223
225
|
const config = {
|
|
226
|
+
allowCreatorOfParent: true,
|
|
227
|
+
isCreatorOfParent: false,
|
|
224
228
|
throwError: true,
|
|
225
229
|
...options,
|
|
226
230
|
validatorOptions: {
|
|
@@ -254,7 +258,12 @@ export async function check(
|
|
|
254
258
|
// check if the user is herself / himself
|
|
255
259
|
|| (roles.includes(RoleEnum.S_SELF) && equalIds(config.dbObject, user))
|
|
256
260
|
// check if the user is the creator
|
|
257
|
-
|| (roles.includes(RoleEnum.S_CREATOR)
|
|
261
|
+
|| (roles.includes(RoleEnum.S_CREATOR)
|
|
262
|
+
&& ((config.dbObject && 'createdBy' in config.dbObject && equalIds(config.dbObject.createdBy, user))
|
|
263
|
+
|| (config.allowCreatorOfParent
|
|
264
|
+
&& config.dbObject
|
|
265
|
+
&& !('createdBy' in config.dbObject)
|
|
266
|
+
&& config.isCreatorOfParent)))
|
|
258
267
|
) {
|
|
259
268
|
valid = true;
|
|
260
269
|
}
|
|
@@ -263,6 +272,9 @@ export async function check(
|
|
|
263
272
|
}
|
|
264
273
|
}
|
|
265
274
|
|
|
275
|
+
// Object check is done
|
|
276
|
+
delete config.roles;
|
|
277
|
+
|
|
266
278
|
// Return value if it is only a basic type
|
|
267
279
|
if (!value || typeof value !== 'object') {
|
|
268
280
|
return value;
|
|
@@ -278,12 +290,12 @@ export async function check(
|
|
|
278
290
|
|
|
279
291
|
const metatype = config.metatype;
|
|
280
292
|
if (metatype) {
|
|
281
|
-
//
|
|
293
|
+
// If metatype is a basic type, additional checks are not possible
|
|
282
294
|
if (isBasicType(metatype)) {
|
|
283
295
|
return value;
|
|
284
296
|
}
|
|
285
297
|
|
|
286
|
-
// Convert to metatype
|
|
298
|
+
// Convert to metatype, validate rights
|
|
287
299
|
if (!(value instanceof metatype)) {
|
|
288
300
|
if ((metatype as any)?.map) {
|
|
289
301
|
value = (metatype as any)?.map(value);
|
|
@@ -293,7 +305,7 @@ export async function check(
|
|
|
293
305
|
}
|
|
294
306
|
}
|
|
295
307
|
|
|
296
|
-
// Validate
|
|
308
|
+
// Validate values (like isEmail, isNumber, etc.)
|
|
297
309
|
const errors = await validate(value, config.validatorOptions);
|
|
298
310
|
if (errors.length > 0 && config.throwError) {
|
|
299
311
|
throw new BadRequestException('Validation failed');
|
|
@@ -16,6 +16,7 @@ export class CheckResponseInterceptor implements NestInterceptor {
|
|
|
16
16
|
debug: false,
|
|
17
17
|
ignoreUndefined: true,
|
|
18
18
|
mergeRoles: true,
|
|
19
|
+
noteCheckedObjects: true,
|
|
19
20
|
removeUndefinedFromResultArray: true,
|
|
20
21
|
throwError: false,
|
|
21
22
|
};
|
|
@@ -38,7 +39,18 @@ export class CheckResponseInterceptor implements NestInterceptor {
|
|
|
38
39
|
return next.handle().pipe(
|
|
39
40
|
map((data) => {
|
|
40
41
|
// Prepare response data for current user
|
|
41
|
-
|
|
42
|
+
const start = Date.now();
|
|
43
|
+
const result = checkRestricted(data, currentUser, this.config);
|
|
44
|
+
if (
|
|
45
|
+
this.config.debug
|
|
46
|
+
&& Date.now() - start >= (typeof this.config.debug === 'number' ? this.config.debug : 100)
|
|
47
|
+
) {
|
|
48
|
+
console.warn(
|
|
49
|
+
`Duration for CheckResponseInterceptor is too long: ${Date.now() - start}ms`,
|
|
50
|
+
Array.isArray(data) ? `${data[0].constructor.name}[]: ${data.length}` : data?.constructor?.name,
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
return result;
|
|
42
54
|
}),
|
|
43
55
|
);
|
|
44
56
|
}
|
|
@@ -5,13 +5,29 @@ import { map } from 'rxjs/operators';
|
|
|
5
5
|
import { getContextData } from '../helpers/context.helper';
|
|
6
6
|
import { getStringIds } from '../helpers/db.helper';
|
|
7
7
|
import { processDeep } from '../helpers/input.helper';
|
|
8
|
+
import { ConfigService } from '../services/config.service';
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Verification of all outgoing data via securityCheck
|
|
11
12
|
*/
|
|
12
13
|
@Injectable()
|
|
13
14
|
export class CheckSecurityInterceptor implements NestInterceptor {
|
|
15
|
+
config = {
|
|
16
|
+
debug: false,
|
|
17
|
+
noteCheckedObjects: true,
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
constructor(private readonly configService: ConfigService) {
|
|
21
|
+
const configuration = this.configService.getFastButReadOnly('security.checkSecurityInterceptor');
|
|
22
|
+
if (typeof configuration === 'object') {
|
|
23
|
+
this.config = { ...this.config, ...configuration };
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
14
27
|
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
|
|
28
|
+
// Start time
|
|
29
|
+
const start = Date.now();
|
|
30
|
+
|
|
15
31
|
// Get current user
|
|
16
32
|
const user = getContextData(context)?.currentUser || null;
|
|
17
33
|
|
|
@@ -25,7 +41,17 @@ export class CheckSecurityInterceptor implements NestInterceptor {
|
|
|
25
41
|
}
|
|
26
42
|
}
|
|
27
43
|
|
|
28
|
-
|
|
44
|
+
// Data from next for check
|
|
45
|
+
let objectData: any;
|
|
46
|
+
|
|
47
|
+
const check = (data: any) => {
|
|
48
|
+
objectData = data;
|
|
49
|
+
|
|
50
|
+
// Check if data already checked
|
|
51
|
+
if (this.config.noteCheckedObjects && data?._objectAlreadyCheckedForRestrictions) {
|
|
52
|
+
return data;
|
|
53
|
+
}
|
|
54
|
+
|
|
29
55
|
// Check data
|
|
30
56
|
if (data && typeof data === 'object' && typeof data.securityCheck === 'function') {
|
|
31
57
|
const dataJson = JSON.stringify(data);
|
|
@@ -73,6 +99,15 @@ export class CheckSecurityInterceptor implements NestInterceptor {
|
|
|
73
99
|
};
|
|
74
100
|
|
|
75
101
|
// Check response
|
|
76
|
-
|
|
102
|
+
const result = next.handle().pipe(map(check));
|
|
103
|
+
if (this.config.debug && Date.now() - start >= (typeof this.config.debug === 'number' ? this.config.debug : 100)) {
|
|
104
|
+
console.warn(
|
|
105
|
+
`Duration for CheckResponseInterceptor is too long: ${Date.now() - start}ms`,
|
|
106
|
+
Array.isArray(objectData)
|
|
107
|
+
? `${objectData[0].constructor.name}[]: ${objectData.length}`
|
|
108
|
+
: objectData?.constructor?.name,
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
return result;
|
|
77
112
|
}
|
|
78
113
|
}
|
|
@@ -421,10 +421,11 @@ export interface IServerOptions {
|
|
|
421
421
|
checkObjectItself?: boolean;
|
|
422
422
|
|
|
423
423
|
/**
|
|
424
|
-
* Whether to log if a restricted field is found
|
|
424
|
+
* Whether to log if a restricted field is found or process is slow
|
|
425
|
+
* boolean or number (time in ms)
|
|
425
426
|
* default = false
|
|
426
427
|
*/
|
|
427
|
-
debug?: boolean;
|
|
428
|
+
debug?: boolean | number;
|
|
428
429
|
|
|
429
430
|
/**
|
|
430
431
|
* Whether to ignore fields with undefined values
|
|
@@ -438,6 +439,13 @@ export interface IServerOptions {
|
|
|
438
439
|
*/
|
|
439
440
|
mergeRoles?: boolean;
|
|
440
441
|
|
|
442
|
+
/**
|
|
443
|
+
* Whether objects that have already been checked should be ignored
|
|
444
|
+
* Objects with truly property `_objectAlreadyCheckedForRestrictions` will be ignored
|
|
445
|
+
* default = true
|
|
446
|
+
*/
|
|
447
|
+
noteCheckedObjects?: boolean;
|
|
448
|
+
|
|
441
449
|
/**
|
|
442
450
|
* Remove undefined values from result array
|
|
443
451
|
* default = true
|
|
@@ -457,7 +465,22 @@ export interface IServerOptions {
|
|
|
457
465
|
* See @lenne.tech/nest-server/src/core/common/interceptors/check-security.interceptor.ts
|
|
458
466
|
* default = true
|
|
459
467
|
*/
|
|
460
|
-
checkSecurityInterceptor?:
|
|
468
|
+
checkSecurityInterceptor?:
|
|
469
|
+
| {
|
|
470
|
+
/**
|
|
471
|
+
* Whether to log if a process is slow
|
|
472
|
+
* boolean or number (time in ms)
|
|
473
|
+
* default = false
|
|
474
|
+
*/
|
|
475
|
+
debug?: boolean | number;
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* Whether objects with truly property `_objectAlreadyCheckedForRestrictions` will be ignored
|
|
479
|
+
* default = true
|
|
480
|
+
*/
|
|
481
|
+
noteCheckedObjects?: boolean;
|
|
482
|
+
}
|
|
483
|
+
| boolean;
|
|
461
484
|
|
|
462
485
|
/**
|
|
463
486
|
* Map incoming plain objects to meta-type and validate
|
|
@@ -57,28 +57,6 @@ export abstract class CorePersistenceModel extends CoreModel {
|
|
|
57
57
|
@Prop({ onCreate: () => new Date() })
|
|
58
58
|
createdAt: Date = undefined;
|
|
59
59
|
|
|
60
|
-
/**
|
|
61
|
-
* Labels of the object
|
|
62
|
-
*/
|
|
63
|
-
@Restricted(RoleEnum.S_EVERYONE)
|
|
64
|
-
@Field(type => [String], {
|
|
65
|
-
description: 'Labels of the object',
|
|
66
|
-
nullable: true,
|
|
67
|
-
})
|
|
68
|
-
@Prop([String])
|
|
69
|
-
labels: string[] = undefined;
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Tags for the object
|
|
73
|
-
*/
|
|
74
|
-
@Restricted(RoleEnum.S_EVERYONE)
|
|
75
|
-
@Field(type => [String], {
|
|
76
|
-
description: 'Tags for the object',
|
|
77
|
-
nullable: true,
|
|
78
|
-
})
|
|
79
|
-
@Prop([String])
|
|
80
|
-
tags: string[] = undefined;
|
|
81
|
-
|
|
82
60
|
/**
|
|
83
61
|
* Updated date is set automatically by mongoose
|
|
84
62
|
*/
|
|
@@ -97,9 +75,7 @@ export abstract class CorePersistenceModel extends CoreModel {
|
|
|
97
75
|
override init() {
|
|
98
76
|
super.init();
|
|
99
77
|
this.createdAt = this.createdAt === undefined ? new Date() : this.createdAt;
|
|
100
|
-
this.
|
|
101
|
-
this.tags = this.tags === undefined ? [] : this.tags;
|
|
102
|
-
this.updatedAt = this.tags === undefined ? this.createdAt : this.updatedAt;
|
|
78
|
+
this.updatedAt = this.updatedAt === undefined ? this.createdAt : this.updatedAt;
|
|
103
79
|
return this;
|
|
104
80
|
}
|
|
105
81
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Field, ObjectType } from '@nestjs/graphql';
|
|
2
2
|
import { Schema as MongooseSchema, Prop, raw } from '@nestjs/mongoose';
|
|
3
|
-
import {
|
|
3
|
+
import { IsOptional } from 'class-validator';
|
|
4
4
|
import { Document } from 'mongoose';
|
|
5
5
|
|
|
6
6
|
import { Restricted } from '../../common/decorators/restricted.decorator';
|
|
@@ -26,8 +26,7 @@ export abstract class CoreUserModel extends CorePersistenceModel {
|
|
|
26
26
|
*/
|
|
27
27
|
@Restricted(RoleEnum.S_EVERYONE)
|
|
28
28
|
@Field({ description: 'Email of the user', nullable: true })
|
|
29
|
-
@
|
|
30
|
-
@Prop({ lowercase: true, trim: true, unique: true })
|
|
29
|
+
@Prop({ index: true, lowercase: true, trim: true })
|
|
31
30
|
email: string = undefined;
|
|
32
31
|
|
|
33
32
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Field, ObjectType } from '@nestjs/graphql';
|
|
2
2
|
import { Schema as MongooseSchema, Prop, SchemaFactory } from '@nestjs/mongoose';
|
|
3
|
-
import { IsOptional } from 'class-validator';
|
|
3
|
+
import { IsEmail, IsOptional } from 'class-validator';
|
|
4
4
|
import { Document, Schema } from 'mongoose';
|
|
5
5
|
|
|
6
6
|
import { Restricted } from '../../../core/common/decorators/restricted.decorator';
|
|
@@ -42,6 +42,15 @@ export class User extends CoreUserModel implements PersistenceModel {
|
|
|
42
42
|
@Prop({ ref: 'User', type: Schema.Types.ObjectId })
|
|
43
43
|
createdBy: string = undefined;
|
|
44
44
|
|
|
45
|
+
/**
|
|
46
|
+
* E-Mail address of the user
|
|
47
|
+
*/
|
|
48
|
+
@Restricted(RoleEnum.S_EVERYONE)
|
|
49
|
+
@Field({ description: 'Email of the user', nullable: true })
|
|
50
|
+
@IsEmail()
|
|
51
|
+
@Prop({ lowercase: true, trim: true, unique: true })
|
|
52
|
+
override email: string = undefined;
|
|
53
|
+
|
|
45
54
|
/**
|
|
46
55
|
* Roles of the user
|
|
47
56
|
*/
|
|
@@ -105,8 +114,6 @@ export class User extends CoreUserModel implements PersistenceModel {
|
|
|
105
114
|
// PersistenceModel and CorePersistenceModel
|
|
106
115
|
this.createdAt = null;
|
|
107
116
|
this.createdBy = null;
|
|
108
|
-
this.labels = null;
|
|
109
|
-
this.tags = null;
|
|
110
117
|
this.updatedAt = null;
|
|
111
118
|
this.updatedBy = null;
|
|
112
119
|
}
|