@lenne.tech/nest-server 9.1.0 → 9.2.1
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/config.env.js +41 -2
- package/dist/config.env.js.map +1 -1
- package/dist/core/common/filters/http-exception-log.filter.d.ts +4 -0
- package/dist/core/common/filters/http-exception-log.filter.js +30 -0
- package/dist/core/common/filters/http-exception-log.filter.js.map +1 -0
- package/dist/core/common/interceptors/check-security.interceptor.d.ts +5 -0
- package/dist/core/common/interceptors/check-security.interceptor.js +46 -0
- package/dist/core/common/interceptors/check-security.interceptor.js.map +1 -0
- package/dist/core/common/interfaces/server-options.interface.d.ts +16 -6
- package/dist/core/common/models/core-model.model.d.ts +1 -0
- package/dist/core/common/models/core-model.model.js +3 -0
- package/dist/core/common/models/core-model.model.js.map +1 -1
- package/dist/core/common/plugins/complexity.plugin.d.ts +9 -0
- package/dist/core/common/plugins/complexity.plugin.js +47 -0
- package/dist/core/common/plugins/complexity.plugin.js.map +1 -0
- package/dist/core/common/plugins/mongoose-id.plugin.d.ts +1 -2
- package/dist/core/common/plugins/mongoose-id.plugin.js +7 -2
- package/dist/core/common/plugins/mongoose-id.plugin.js.map +1 -1
- package/dist/core/common/services/config.service.d.ts +4 -4
- package/dist/core/common/services/config.service.js.map +1 -1
- package/dist/core/common/services/module.service.js +2 -2
- package/dist/core/common/services/module.service.js.map +1 -1
- package/dist/core/modules/auth/core-auth.model.d.ts +4 -1
- package/dist/core/modules/auth/core-auth.model.js +12 -1
- package/dist/core/modules/auth/core-auth.model.js.map +1 -1
- package/dist/core/modules/auth/core-auth.module.d.ts +3 -1
- package/dist/core/modules/auth/core-auth.module.js +7 -2
- package/dist/core/modules/auth/core-auth.module.js.map +1 -1
- package/dist/core/modules/auth/core-auth.resolver.d.ts +22 -2
- package/dist/core/modules/auth/core-auth.resolver.js +77 -9
- package/dist/core/modules/auth/core-auth.resolver.js.map +1 -1
- package/dist/core/modules/auth/guards/auth.guard.d.ts +1 -1
- package/dist/core/modules/auth/guards/auth.guard.js +9 -4
- package/dist/core/modules/auth/guards/auth.guard.js.map +1 -1
- package/dist/core/modules/auth/guards/refresh-token.guard.d.ts +4 -0
- package/dist/core/modules/auth/guards/refresh-token.guard.js +18 -0
- package/dist/core/modules/auth/guards/refresh-token.guard.js.map +1 -0
- package/dist/core/modules/auth/guards/roles.guard.js.map +1 -1
- package/dist/core/modules/auth/inputs/core-auth-sign-in.input.d.ts +1 -0
- package/dist/core/modules/auth/inputs/core-auth-sign-in.input.js +5 -0
- package/dist/core/modules/auth/inputs/core-auth-sign-in.input.js.map +1 -1
- package/dist/core/modules/auth/inputs/core-auth-sign-up.input.d.ts +1 -0
- package/dist/core/modules/auth/inputs/core-auth-sign-up.input.js +5 -0
- package/dist/core/modules/auth/inputs/core-auth-sign-up.input.js.map +1 -1
- package/dist/core/modules/auth/interfaces/core-auth-user.interface.d.ts +3 -0
- package/dist/core/modules/auth/interfaces/jwt-payload.interface.d.ts +1 -1
- package/dist/core/modules/auth/services/core-auth-user.service.d.ts +3 -0
- package/dist/core/modules/auth/services/core-auth-user.service.js.map +1 -1
- package/dist/core/modules/auth/services/core-auth.service.d.ts +23 -5
- package/dist/core/modules/auth/services/core-auth.service.js +121 -13
- package/dist/core/modules/auth/services/core-auth.service.js.map +1 -1
- package/dist/core/modules/auth/strategies/jwt-refresh.strategy.d.ts +12 -0
- package/dist/core/modules/auth/strategies/jwt-refresh.strategy.js +61 -0
- package/dist/core/modules/auth/strategies/jwt-refresh.strategy.js.map +1 -0
- package/dist/core/modules/auth/{jwt.strategy.d.ts → strategies/jwt.strategy.d.ts} +4 -3
- package/dist/core/modules/auth/{jwt.strategy.js → strategies/jwt.strategy.js} +12 -5
- package/dist/core/modules/auth/strategies/jwt.strategy.js.map +1 -0
- package/dist/core/modules/file/core-file.controller.d.ts +2 -2
- package/dist/core/modules/file/core-file.controller.js +2 -2
- package/dist/core/modules/file/core-file.controller.js.map +1 -1
- package/dist/core/modules/user/core-user.model.d.ts +2 -0
- package/dist/core/modules/user/core-user.model.js +12 -0
- package/dist/core/modules/user/core-user.model.js.map +1 -1
- package/dist/core.module.js +12 -2
- package/dist/core.module.js.map +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/main.js +23 -0
- package/dist/main.js.map +1 -1
- package/dist/server/modules/auth/auth.model.js.map +1 -1
- package/dist/server/modules/auth/auth.resolver.d.ts +13 -5
- package/dist/server/modules/auth/auth.resolver.js +21 -12
- package/dist/server/modules/auth/auth.resolver.js.map +1 -1
- package/dist/server/modules/auth/auth.service.d.ts +2 -1
- package/dist/server/modules/auth/auth.service.js +7 -48
- package/dist/server/modules/auth/auth.service.js.map +1 -1
- package/dist/server/modules/file/file.module.js +3 -3
- package/dist/server/modules/file/file.module.js.map +1 -1
- package/dist/server/modules/user/user.model.d.ts +1 -0
- package/dist/server/modules/user/user.model.js +19 -0
- package/dist/server/modules/user/user.model.js.map +1 -1
- package/dist/server/server.module.js +12 -1
- package/dist/server/server.module.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +32 -27
- package/src/config.env.ts +41 -2
- package/src/core/common/filters/http-exception-log.filter.ts +27 -0
- package/src/core/common/interceptors/check-security.interceptor.ts +52 -0
- package/src/core/common/interfaces/server-options.interface.ts +67 -30
- package/src/core/common/models/core-model.model.ts +7 -0
- package/src/core/common/plugins/complexity.plugin.ts +31 -0
- package/src/core/common/plugins/mongoose-id.plugin.js +4 -2
- package/src/core/common/services/config.service.ts +4 -4
- package/src/core/common/services/module.service.ts +2 -2
- package/src/core/modules/auth/core-auth.model.ts +15 -2
- package/src/core/modules/auth/core-auth.module.ts +8 -2
- package/src/core/modules/auth/core-auth.resolver.ts +93 -10
- package/src/core/modules/auth/guards/auth.guard.ts +12 -5
- package/src/core/modules/auth/guards/refresh-token.guard.ts +5 -0
- package/src/core/modules/auth/guards/roles.guard.ts +1 -1
- package/src/core/modules/auth/inputs/core-auth-sign-in.input.ts +3 -0
- package/src/core/modules/auth/inputs/core-auth-sign-up.input.ts +3 -0
- package/src/core/modules/auth/interfaces/core-auth-user.interface.ts +15 -0
- package/src/core/modules/auth/interfaces/jwt-payload.interface.ts +1 -1
- package/src/core/modules/auth/services/core-auth-user.service.ts +15 -0
- package/src/core/modules/auth/services/core-auth.service.ts +216 -18
- package/src/core/modules/auth/strategies/jwt-refresh.strategy.ts +56 -0
- package/src/core/modules/auth/{jwt.strategy.ts → strategies/jwt.strategy.ts} +16 -5
- package/src/core/modules/file/core-file.controller.ts +2 -2
- package/src/core/modules/user/core-user.model.ts +17 -2
- package/src/core.module.ts +14 -2
- package/src/index.ts +6 -1
- package/src/main.ts +29 -0
- package/src/server/modules/auth/auth.model.ts +1 -1
- package/src/server/modules/auth/auth.resolver.ts +26 -8
- package/src/server/modules/auth/auth.service.ts +20 -61
- package/src/server/modules/file/file.module.ts +3 -3
- package/src/server/modules/user/user.model.ts +29 -0
- package/src/server/server.module.ts +12 -1
- package/dist/core/modules/auth/jwt.strategy.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lenne.tech/nest-server",
|
|
3
|
-
"version": "9.1
|
|
3
|
+
"version": "9.2.1",
|
|
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",
|
|
@@ -60,78 +60,83 @@
|
|
|
60
60
|
"node": ">= 16.13.0"
|
|
61
61
|
},
|
|
62
62
|
"dependencies": {
|
|
63
|
-
"@apollo/gateway": "2.2
|
|
64
|
-
"@nestjs/apollo": "10.
|
|
65
|
-
"@nestjs/common": "9.
|
|
66
|
-
"@nestjs/core": "9.
|
|
67
|
-
"@nestjs/graphql": "10.
|
|
68
|
-
"@nestjs/jwt": "10.0.
|
|
63
|
+
"@apollo/gateway": "2.3.2",
|
|
64
|
+
"@nestjs/apollo": "10.2.0",
|
|
65
|
+
"@nestjs/common": "9.3.9",
|
|
66
|
+
"@nestjs/core": "9.3.9",
|
|
67
|
+
"@nestjs/graphql": "10.2.0",
|
|
68
|
+
"@nestjs/jwt": "10.0.2",
|
|
69
69
|
"@nestjs/mongoose": "9.2.1",
|
|
70
|
-
"@nestjs/passport": "9.0.
|
|
71
|
-
"@nestjs/platform-express": "9.
|
|
72
|
-
"@nestjs/schedule": "2.
|
|
70
|
+
"@nestjs/passport": "9.0.3",
|
|
71
|
+
"@nestjs/platform-express": "9.3.9",
|
|
72
|
+
"@nestjs/schedule": "2.2.0",
|
|
73
73
|
"apollo-server-core": "3.11.1",
|
|
74
74
|
"apollo-server-express": "3.11.1",
|
|
75
75
|
"bcrypt": "5.1.0",
|
|
76
76
|
"class-transformer": "0.5.1",
|
|
77
77
|
"class-validator": "0.14.0",
|
|
78
|
+
"compression": "1.7.4",
|
|
79
|
+
"cookie-parser": "1.4.6",
|
|
78
80
|
"ejs": "3.1.8",
|
|
79
81
|
"graphql": "16.6.0",
|
|
82
|
+
"graphql-query-complexity": "0.12.0",
|
|
80
83
|
"graphql-subscriptions": "2.0.0",
|
|
81
84
|
"graphql-upload": "15.0.2",
|
|
82
85
|
"js-sha256": "0.9.0",
|
|
83
|
-
"json-to-graphql-query": "2.2.
|
|
86
|
+
"json-to-graphql-query": "2.2.5",
|
|
84
87
|
"light-my-request": "5.8.0",
|
|
85
88
|
"lodash": "4.17.21",
|
|
86
|
-
"mongodb": "4.
|
|
87
|
-
"mongoose": "6.
|
|
89
|
+
"mongodb": "4.14.0",
|
|
90
|
+
"mongoose": "6.9.1",
|
|
88
91
|
"mongoose-gridfs": "1.3.0",
|
|
89
|
-
"multer-gridfs-storage": "5.0.2",
|
|
90
92
|
"multer": "1.4.5-lts.1",
|
|
91
|
-
"
|
|
92
|
-
"
|
|
93
|
+
"multer-gridfs-storage": "5.0.2",
|
|
94
|
+
"node-mailjet": "6.0.2",
|
|
95
|
+
"nodemailer": "6.9.1",
|
|
93
96
|
"nodemon": "2.0.20",
|
|
94
97
|
"passport": "0.6.0",
|
|
95
98
|
"passport-jwt": "4.0.1",
|
|
96
99
|
"reflect-metadata": "0.1.13",
|
|
97
100
|
"rfdc": "1.3.0",
|
|
98
|
-
"rimraf": "4.1.
|
|
101
|
+
"rimraf": "4.1.2",
|
|
99
102
|
"rxjs": "7.8.0"
|
|
100
103
|
},
|
|
101
104
|
"devDependencies": {
|
|
102
|
-
"@nestjs/testing": "9.
|
|
105
|
+
"@nestjs/testing": "9.3.9",
|
|
106
|
+
"@types/compression": "1.7.2",
|
|
107
|
+
"@types/cookie-parser": "1.4.3",
|
|
103
108
|
"@types/cron": "2.0.0",
|
|
104
109
|
"@types/ejs": "3.1.1",
|
|
105
|
-
"@types/jest": "29.
|
|
110
|
+
"@types/jest": "29.4.0",
|
|
106
111
|
"@types/lodash": "4.14.191",
|
|
107
112
|
"@types/multer": "1.4.7",
|
|
108
|
-
"@types/node": "18.
|
|
113
|
+
"@types/node": "18.13.0",
|
|
109
114
|
"@types/nodemailer": "6.4.7",
|
|
110
115
|
"@types/passport": "1.0.11",
|
|
111
116
|
"@types/supertest": "2.0.12",
|
|
112
|
-
"@typescript-eslint/eslint-plugin": "5.
|
|
113
|
-
"@typescript-eslint/parser": "5.
|
|
117
|
+
"@typescript-eslint/eslint-plugin": "5.52.0",
|
|
118
|
+
"@typescript-eslint/parser": "5.52.0",
|
|
114
119
|
"coffeescript": "2.7.0",
|
|
115
|
-
"eslint": "8.
|
|
120
|
+
"eslint": "8.34.0",
|
|
116
121
|
"eslint-config-prettier": "8.6.0",
|
|
117
122
|
"find-file-up": "2.0.1",
|
|
118
|
-
"grunt": "1.
|
|
123
|
+
"grunt": "1.6.1",
|
|
119
124
|
"grunt-bg-shell": "2.3.3",
|
|
120
125
|
"grunt-contrib-clean": "2.0.1",
|
|
121
126
|
"grunt-contrib-watch": "1.1.0",
|
|
122
127
|
"grunt-sync": "0.8.2",
|
|
123
128
|
"husky": "8.0.3",
|
|
124
|
-
"jest": "29.
|
|
129
|
+
"jest": "29.4.2",
|
|
125
130
|
"npm-watch": "0.11.0",
|
|
126
131
|
"pm2": "5.2.2",
|
|
127
|
-
"prettier": "2.8.
|
|
132
|
+
"prettier": "2.8.4",
|
|
128
133
|
"pretty-quick": "3.1.3",
|
|
129
134
|
"supertest": "6.3.3",
|
|
130
135
|
"ts-jest": "29.0.5",
|
|
131
136
|
"ts-morph": "17.0.1",
|
|
132
137
|
"ts-node": "10.9.1",
|
|
133
138
|
"tsconfig-paths": "4.1.2",
|
|
134
|
-
"typescript": "4.9.
|
|
139
|
+
"typescript": "4.9.5",
|
|
135
140
|
"yalc": "1.0.0-pre.53"
|
|
136
141
|
},
|
|
137
142
|
"overrides": {
|
package/src/config.env.ts
CHANGED
|
@@ -12,6 +12,8 @@ const config: { [env: string]: IServerOptions } = {
|
|
|
12
12
|
// ===========================================================================
|
|
13
13
|
local: {
|
|
14
14
|
automaticObjectIdFiltering: true,
|
|
15
|
+
compression: true,
|
|
16
|
+
cookies: false,
|
|
15
17
|
cronJobs: {
|
|
16
18
|
sayHello: {
|
|
17
19
|
cronTime: CronExpression.EVERY_10_SECONDS,
|
|
@@ -52,16 +54,27 @@ const config: { [env: string]: IServerOptions } = {
|
|
|
52
54
|
debug: true,
|
|
53
55
|
introspection: true,
|
|
54
56
|
},
|
|
57
|
+
maxComplexity: 20,
|
|
55
58
|
},
|
|
56
59
|
jwt: {
|
|
57
|
-
secret: '
|
|
60
|
+
secret: 'SECRET_OR_PRIVATE_KEY_LOCAL',
|
|
61
|
+
signInOptions: {
|
|
62
|
+
expiresIn: '15m',
|
|
63
|
+
},
|
|
64
|
+
refresh: {
|
|
65
|
+
secret: 'SECRET_OR_PRIVATE_KEY_LOCAL_REFRESH',
|
|
66
|
+
signInOptions: {
|
|
67
|
+
expiresIn: '7d',
|
|
68
|
+
},
|
|
69
|
+
},
|
|
58
70
|
},
|
|
59
71
|
loadLocalConfig: false,
|
|
72
|
+
logExceptions: true,
|
|
60
73
|
mongoose: {
|
|
61
74
|
collation: {
|
|
62
75
|
locale: 'de',
|
|
63
76
|
},
|
|
64
|
-
uri: 'mongodb://127.0.0.1/nest-server-
|
|
77
|
+
uri: 'mongodb://127.0.0.1/nest-server-local',
|
|
65
78
|
},
|
|
66
79
|
port: 3000,
|
|
67
80
|
sha256: true,
|
|
@@ -80,6 +93,8 @@ const config: { [env: string]: IServerOptions } = {
|
|
|
80
93
|
// ===========================================================================
|
|
81
94
|
development: {
|
|
82
95
|
automaticObjectIdFiltering: true,
|
|
96
|
+
compression: true,
|
|
97
|
+
cookies: false,
|
|
83
98
|
email: {
|
|
84
99
|
smtp: {
|
|
85
100
|
auth: {
|
|
@@ -111,11 +126,22 @@ const config: { [env: string]: IServerOptions } = {
|
|
|
111
126
|
debug: true,
|
|
112
127
|
introspection: true,
|
|
113
128
|
},
|
|
129
|
+
maxComplexity: 20,
|
|
114
130
|
},
|
|
115
131
|
jwt: {
|
|
116
132
|
secret: 'SECRET_OR_PRIVATE_KEY_DEV',
|
|
133
|
+
signInOptions: {
|
|
134
|
+
expiresIn: '15m',
|
|
135
|
+
},
|
|
136
|
+
refresh: {
|
|
137
|
+
secret: 'SECRET_OR_PRIVATE_KEY_DEV_REFRESH',
|
|
138
|
+
signInOptions: {
|
|
139
|
+
expiresIn: '7d',
|
|
140
|
+
},
|
|
141
|
+
},
|
|
117
142
|
},
|
|
118
143
|
loadLocalConfig: false,
|
|
144
|
+
logExceptions: true,
|
|
119
145
|
mongoose: {
|
|
120
146
|
collation: {
|
|
121
147
|
locale: 'de',
|
|
@@ -139,6 +165,8 @@ const config: { [env: string]: IServerOptions } = {
|
|
|
139
165
|
// ===========================================================================
|
|
140
166
|
production: {
|
|
141
167
|
automaticObjectIdFiltering: true,
|
|
168
|
+
compression: true,
|
|
169
|
+
cookies: false,
|
|
142
170
|
email: {
|
|
143
171
|
smtp: {
|
|
144
172
|
auth: {
|
|
@@ -170,11 +198,22 @@ const config: { [env: string]: IServerOptions } = {
|
|
|
170
198
|
debug: false,
|
|
171
199
|
introspection: true,
|
|
172
200
|
},
|
|
201
|
+
maxComplexity: 20,
|
|
173
202
|
},
|
|
174
203
|
jwt: {
|
|
175
204
|
secret: 'SECRET_OR_PRIVATE_KEY_PROD',
|
|
205
|
+
signInOptions: {
|
|
206
|
+
expiresIn: '15m',
|
|
207
|
+
},
|
|
208
|
+
refresh: {
|
|
209
|
+
secret: 'SECRET_OR_PRIVATE_KEY_PROD_REFRESH',
|
|
210
|
+
signInOptions: {
|
|
211
|
+
expiresIn: '7d',
|
|
212
|
+
},
|
|
213
|
+
},
|
|
176
214
|
},
|
|
177
215
|
loadLocalConfig: false,
|
|
216
|
+
logExceptions: true,
|
|
178
217
|
mongoose: {
|
|
179
218
|
collation: {
|
|
180
219
|
locale: 'de',
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { ArgumentsHost, Catch, ContextType, ExceptionFilter, HttpException } from '@nestjs/common';
|
|
2
|
+
import { Response } from 'express';
|
|
3
|
+
|
|
4
|
+
@Catch(HttpException)
|
|
5
|
+
export class HttpExceptionLogFilter implements ExceptionFilter {
|
|
6
|
+
/**
|
|
7
|
+
* Log exception
|
|
8
|
+
*/
|
|
9
|
+
catch(exception: HttpException, host: ArgumentsHost) {
|
|
10
|
+
// Log
|
|
11
|
+
console.debug(exception.stack);
|
|
12
|
+
|
|
13
|
+
// GraphQL
|
|
14
|
+
const type = host.getType() as ContextType | 'graphql';
|
|
15
|
+
if (type === 'graphql') {
|
|
16
|
+
return exception;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// REST
|
|
20
|
+
const ctx = host.switchToHttp();
|
|
21
|
+
const res = ctx.getResponse<Response>();
|
|
22
|
+
const status = exception.getStatus();
|
|
23
|
+
res.status(status).json({
|
|
24
|
+
...exception,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
|
|
2
|
+
import { Observable } from 'rxjs';
|
|
3
|
+
import { map } from 'rxjs/operators';
|
|
4
|
+
import { getContextData } from '../helpers/context.helper';
|
|
5
|
+
import { processDeep } from '../helpers/input.helper';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Verification of all outgoing data via securityCheck
|
|
9
|
+
*/
|
|
10
|
+
@Injectable()
|
|
11
|
+
export class CheckSecurityInterceptor implements NestInterceptor {
|
|
12
|
+
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
|
|
13
|
+
// Get current user
|
|
14
|
+
const user = getContextData(context)?.currentUser;
|
|
15
|
+
|
|
16
|
+
// Set force mode for sign in and sign up
|
|
17
|
+
let force = false;
|
|
18
|
+
if (!user) {
|
|
19
|
+
// Here the name is used and not the class itself, because the concrete class is located in the respective project.
|
|
20
|
+
// In case of an override it is better to use the concrete class directly (context.getClass() instead of context.getClasss()?.name).
|
|
21
|
+
if (context.getClass()?.name === 'AuthResolver') {
|
|
22
|
+
force = true;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Check response
|
|
27
|
+
return next.handle().pipe(
|
|
28
|
+
map((data) => {
|
|
29
|
+
// Check data
|
|
30
|
+
if (data && typeof data === 'object' && typeof data.securityCheck === 'function') {
|
|
31
|
+
return data.securityCheck(user, force);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Check if data is writeable (e.g. objects from direct access to json files via http are not writable)
|
|
35
|
+
if (data && typeof data === 'object') {
|
|
36
|
+
const writeable = !Object.keys(data).find((key) => !Object.getOwnPropertyDescriptor(data, key).writable);
|
|
37
|
+
if (!writeable) {
|
|
38
|
+
return data;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Check deep
|
|
43
|
+
return processDeep(data, (item) => {
|
|
44
|
+
if (!item || typeof item !== 'object' || typeof item.securityCheck !== 'function') {
|
|
45
|
+
return item;
|
|
46
|
+
}
|
|
47
|
+
return item.securityCheck(user, force);
|
|
48
|
+
});
|
|
49
|
+
})
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -1,15 +1,57 @@
|
|
|
1
1
|
import { ApolloDriverConfig } from '@nestjs/apollo';
|
|
2
2
|
import { GqlModuleAsyncOptions } from '@nestjs/graphql';
|
|
3
3
|
import { JwtModuleOptions } from '@nestjs/jwt';
|
|
4
|
+
import { JwtSignOptions } from '@nestjs/jwt/dist/interfaces';
|
|
4
5
|
import { MongooseModuleOptions } from '@nestjs/mongoose';
|
|
5
6
|
import { ServeStaticOptions } from '@nestjs/platform-express/interfaces/serve-static-options.interface';
|
|
6
7
|
import { CronExpression } from '@nestjs/schedule';
|
|
8
|
+
import compression from 'compression';
|
|
7
9
|
import { CollationOptions } from 'mongodb';
|
|
8
10
|
import * as SMTPTransport from 'nodemailer/lib/smtp-transport';
|
|
9
11
|
import { Falsy } from '../types/falsy.type';
|
|
10
12
|
import { CronJobConfig } from './cron-job-config.interface';
|
|
11
13
|
import { MailjetOptions } from './mailjet-options.interface';
|
|
12
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Interface for JWT configuration (main and refresh)
|
|
17
|
+
*/
|
|
18
|
+
export interface IJwt {
|
|
19
|
+
/**
|
|
20
|
+
* Private key
|
|
21
|
+
*/
|
|
22
|
+
privateKey?: string;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Public key
|
|
26
|
+
*/
|
|
27
|
+
publicKey?: string;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Secret to encrypt the JWT
|
|
31
|
+
*/
|
|
32
|
+
secret?: string;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* JWT Provider
|
|
36
|
+
* See https://github.com/mikenicholson/passport-jwt/blob/master/README.md#configure-strategy
|
|
37
|
+
*/
|
|
38
|
+
secretOrKeyProvider?: (
|
|
39
|
+
request: Record<string, any>,
|
|
40
|
+
rawJwtToken: string,
|
|
41
|
+
done: (err: any, secret: string) => any
|
|
42
|
+
) => any;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Alias of secret (for backwards compatibility)
|
|
46
|
+
*/
|
|
47
|
+
secretOrPrivateKey?: string;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* SignIn Options like expiresIn
|
|
51
|
+
*/
|
|
52
|
+
signInOptions?: JwtSignOptions;
|
|
53
|
+
}
|
|
54
|
+
|
|
13
55
|
/**
|
|
14
56
|
* Options for the server
|
|
15
57
|
*/
|
|
@@ -21,6 +63,18 @@ export interface IServerOptions {
|
|
|
21
63
|
*/
|
|
22
64
|
automaticObjectIdFiltering?: boolean;
|
|
23
65
|
|
|
66
|
+
/**
|
|
67
|
+
* Whether to use the compression middleware package to enable gzip compression.
|
|
68
|
+
* See: https://docs.nestjs.com/techniques/compression
|
|
69
|
+
*/
|
|
70
|
+
compression?: boolean | compression.CompressionOptions;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Whether to use cookies for authentication handling
|
|
74
|
+
* See: https://docs.nestjs.com/techniques/cookies
|
|
75
|
+
*/
|
|
76
|
+
cookies?: boolean;
|
|
77
|
+
|
|
24
78
|
/**
|
|
25
79
|
* Cron jobs configuration object with the name of the cron job function as key
|
|
26
80
|
* and the cron expression or config as value
|
|
@@ -105,6 +159,11 @@ export interface IServerOptions {
|
|
|
105
159
|
*/
|
|
106
160
|
enableSubscriptionAuth?: boolean;
|
|
107
161
|
|
|
162
|
+
/**
|
|
163
|
+
* Maximum complexity of GraphQL requests
|
|
164
|
+
*/
|
|
165
|
+
maxComplexity?: number;
|
|
166
|
+
|
|
108
167
|
/**
|
|
109
168
|
* Module options (forRootAsync)
|
|
110
169
|
*/
|
|
@@ -123,36 +182,9 @@ export interface IServerOptions {
|
|
|
123
182
|
* Configuration of JavaScript Web Token (JWT) module
|
|
124
183
|
*/
|
|
125
184
|
jwt?: {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
privateKey?: string;
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Public key
|
|
133
|
-
*/
|
|
134
|
-
publicKey?: string;
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Secret to encrypt the JWT
|
|
138
|
-
*/
|
|
139
|
-
secret?: string;
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* JWT Provider
|
|
143
|
-
* See https://github.com/mikenicholson/passport-jwt/blob/master/README.md#configure-strategy
|
|
144
|
-
*/
|
|
145
|
-
secretOrKeyProvider?: (
|
|
146
|
-
request: Record<string, any>,
|
|
147
|
-
rawJwtToken: string,
|
|
148
|
-
done: (err: any, secret: string) => any
|
|
149
|
-
) => any;
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Alias of secret (for backwards compatibility)
|
|
153
|
-
*/
|
|
154
|
-
secretOrPrivateKey?: string;
|
|
155
|
-
} & JwtModuleOptions;
|
|
185
|
+
refresh?: IJwt;
|
|
186
|
+
} & IJwt &
|
|
187
|
+
JwtModuleOptions;
|
|
156
188
|
|
|
157
189
|
/**
|
|
158
190
|
* Load local configuration
|
|
@@ -162,6 +194,11 @@ export interface IServerOptions {
|
|
|
162
194
|
*/
|
|
163
195
|
loadLocalConfig?: boolean | string;
|
|
164
196
|
|
|
197
|
+
/**
|
|
198
|
+
* Log exceptions (for better debugging)
|
|
199
|
+
*/
|
|
200
|
+
logExceptions?: boolean;
|
|
201
|
+
|
|
165
202
|
/**
|
|
166
203
|
* Configuration for Mongoose
|
|
167
204
|
*/
|
|
@@ -125,4 +125,11 @@ export abstract class CoreModel {
|
|
|
125
125
|
};
|
|
126
126
|
return this.map(data, config);
|
|
127
127
|
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Verification of the user's rights to access the properties of this object
|
|
131
|
+
*/
|
|
132
|
+
securityCheck(user: any, force?: boolean): this {
|
|
133
|
+
return this;
|
|
134
|
+
}
|
|
128
135
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { GraphQLSchemaHost } from '@nestjs/graphql';
|
|
2
|
+
import { Plugin } from '@nestjs/apollo';
|
|
3
|
+
import { ApolloServerPlugin, GraphQLRequestListener } from 'apollo-server-plugin-base';
|
|
4
|
+
import { GraphQLError } from 'graphql';
|
|
5
|
+
import { fieldExtensionsEstimator, getComplexity, simpleEstimator } from 'graphql-query-complexity';
|
|
6
|
+
import { ConfigService } from '../services/config.service';
|
|
7
|
+
|
|
8
|
+
@Plugin()
|
|
9
|
+
export class ComplexityPlugin implements ApolloServerPlugin {
|
|
10
|
+
constructor(private gqlSchemaHost: GraphQLSchemaHost, private configService: ConfigService) {}
|
|
11
|
+
|
|
12
|
+
async requestDidStart(): Promise<GraphQLRequestListener> {
|
|
13
|
+
const maxComplexity: number = this.configService.getFastButReadOnly('graphQl.maxComplexity');
|
|
14
|
+
const { schema } = this.gqlSchemaHost;
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
async didResolveOperation({ request, document }) {
|
|
18
|
+
const complexity = getComplexity({
|
|
19
|
+
schema,
|
|
20
|
+
operationName: request.operationName,
|
|
21
|
+
query: document,
|
|
22
|
+
variables: request.variables,
|
|
23
|
+
estimators: [fieldExtensionsEstimator(), simpleEstimator({ defaultComplexity: 1 })],
|
|
24
|
+
});
|
|
25
|
+
if (maxComplexity !== undefined && complexity > maxComplexity) {
|
|
26
|
+
throw new GraphQLError(`Query is too complex: ${complexity}. Maximum allowed complexity: ${maxComplexity}`);
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
export function mongooseIdPlugin(schema, options) {
|
|
2
2
|
schema.post(['find', 'findOne', 'save', 'deleteOne'], function (docs) {
|
|
3
3
|
if (!Array.isArray(docs)) {
|
|
4
4
|
docs = [docs];
|
|
@@ -10,4 +10,6 @@ module.exports = function mongooseIdPlugin(schema, options) {
|
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
12
|
});
|
|
13
|
-
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
module.exports = mongooseIdPlugin;
|
|
@@ -129,28 +129,28 @@ export class ConfigService {
|
|
|
129
129
|
/**
|
|
130
130
|
* Get readable and writable deep-cloned property from configuration
|
|
131
131
|
*/
|
|
132
|
-
get(key: string, defaultValue: any = undefined) {
|
|
132
|
+
get<T = any>(key: string, defaultValue: any = undefined): T {
|
|
133
133
|
return ConfigService.get(key, defaultValue);
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
/**
|
|
137
137
|
* Get readable and writable deep-cloned property from configuration
|
|
138
138
|
*/
|
|
139
|
-
static get(key: string, defaultValue: any = undefined) {
|
|
139
|
+
static get<T = any>(key: string, defaultValue: any = undefined): T {
|
|
140
140
|
return clone(_.get(ConfigService._configSubject$.getValue(), key, defaultValue), { circles: false });
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
/**
|
|
144
144
|
* Get faster but read-ony deep-frozen property from configuration
|
|
145
145
|
*/
|
|
146
|
-
getFastButReadOnly(key: string, defaultValue: any = undefined) {
|
|
146
|
+
getFastButReadOnly<T = any>(key: string, defaultValue: any = undefined): T {
|
|
147
147
|
return ConfigService.getFastButReadOnly(key, defaultValue);
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
/**
|
|
151
151
|
* Get faster but read-ony deep-frozen property from configuration
|
|
152
152
|
*/
|
|
153
|
-
static getFastButReadOnly(key: string, defaultValue: any = undefined) {
|
|
153
|
+
static getFastButReadOnly<T = any>(key: string, defaultValue: any = undefined): T {
|
|
154
154
|
return _.get(ConfigService._frozenConfigSubject$.getValue(), key, defaultValue);
|
|
155
155
|
}
|
|
156
156
|
|
|
@@ -109,10 +109,10 @@ export abstract class ModuleService<T extends CoreModel = any> {
|
|
|
109
109
|
// Note force configuration
|
|
110
110
|
if (config.force) {
|
|
111
111
|
config.checkRights = false;
|
|
112
|
-
if (typeof config.prepareInput === 'object') {
|
|
112
|
+
if (config.prepareInput && typeof config.prepareInput === 'object') {
|
|
113
113
|
config.prepareInput.checkRoles = false;
|
|
114
114
|
}
|
|
115
|
-
if (typeof config.prepareOutput === 'object') {
|
|
115
|
+
if (config.prepareOutput && typeof config.prepareOutput === 'object') {
|
|
116
116
|
config.prepareOutput.removeSecrets = false;
|
|
117
117
|
}
|
|
118
118
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Field, ObjectType } from '@nestjs/graphql';
|
|
2
2
|
import { CoreModel } from '../../common/models/core-model.model';
|
|
3
|
+
import { CoreUserModel } from '../user/core-user.model';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* CoreAuth model for the response after the sign in
|
|
@@ -13,8 +14,20 @@ export class CoreAuthModel extends CoreModel {
|
|
|
13
14
|
/**
|
|
14
15
|
* JavaScript Web Token (JWT)
|
|
15
16
|
*/
|
|
16
|
-
@Field({ description: 'JavaScript Web Token (JWT)' })
|
|
17
|
-
token
|
|
17
|
+
@Field({ description: 'JavaScript Web Token (JWT)', nullable: true })
|
|
18
|
+
token?: string = undefined;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Refresh token
|
|
22
|
+
*/
|
|
23
|
+
@Field({ description: 'Refresh token', nullable: true })
|
|
24
|
+
refreshToken?: string = undefined;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Current user
|
|
28
|
+
*/
|
|
29
|
+
@Field({ description: 'Current user' })
|
|
30
|
+
user: CoreUserModel = undefined;
|
|
18
31
|
|
|
19
32
|
// ===================================================================================================================
|
|
20
33
|
// Methods
|
|
@@ -3,7 +3,8 @@ import { APP_GUARD } from '@nestjs/core';
|
|
|
3
3
|
import { JwtModule, JwtModuleOptions } from '@nestjs/jwt';
|
|
4
4
|
import { PassportModule } from '@nestjs/passport';
|
|
5
5
|
import { RolesGuard } from './guards/roles.guard';
|
|
6
|
-
import {
|
|
6
|
+
import { JwtRefreshStrategy } from './strategies/jwt-refresh.strategy';
|
|
7
|
+
import { JwtStrategy } from './strategies/jwt.strategy';
|
|
7
8
|
import { CoreAuthUserService } from './services/core-auth-user.service';
|
|
8
9
|
import { CoreAuthService } from './services/core-auth.service';
|
|
9
10
|
import { PubSub } from 'graphql-subscriptions';
|
|
@@ -23,6 +24,7 @@ export class CoreAuthModule {
|
|
|
23
24
|
options: JwtModuleOptions & {
|
|
24
25
|
authService?: Type<CoreAuthService>;
|
|
25
26
|
jwtStrategy?: Type<JwtStrategy>;
|
|
27
|
+
jwtRefreshStrategy?: Type<JwtRefreshStrategy>;
|
|
26
28
|
imports?: Array<Type<any> | DynamicModule | Promise<DynamicModule> | ForwardReference>;
|
|
27
29
|
providers?: Provider[];
|
|
28
30
|
}
|
|
@@ -56,6 +58,10 @@ export class CoreAuthModule {
|
|
|
56
58
|
provide: JwtStrategy,
|
|
57
59
|
useClass: options.jwtStrategy || JwtStrategy,
|
|
58
60
|
},
|
|
61
|
+
{
|
|
62
|
+
provide: JwtRefreshStrategy,
|
|
63
|
+
useClass: options.jwtRefreshStrategy || JwtRefreshStrategy,
|
|
64
|
+
},
|
|
59
65
|
];
|
|
60
66
|
if (Array.isArray(options?.providers)) {
|
|
61
67
|
providers = imports.concat(options.providers);
|
|
@@ -66,7 +72,7 @@ export class CoreAuthModule {
|
|
|
66
72
|
module: CoreAuthModule,
|
|
67
73
|
imports: imports,
|
|
68
74
|
providers: providers,
|
|
69
|
-
exports: [CoreAuthService, JwtModule, JwtStrategy, PassportModule, UserModule],
|
|
75
|
+
exports: [CoreAuthService, JwtModule, JwtStrategy, JwtRefreshStrategy, PassportModule, UserModule],
|
|
70
76
|
};
|
|
71
77
|
}
|
|
72
78
|
}
|