proteum 2.1.1 → 2.1.2

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.
@@ -25,12 +25,6 @@ import UsersRequestService from './request';
25
25
  - TYPES
26
26
  ----------------------------------*/
27
27
 
28
- /*----------------------------------
29
- - CONFIG
30
- ----------------------------------*/
31
-
32
- const LogPrefix = '[router][auth]';
33
-
34
28
  /*----------------------------------
35
29
  - SERVICE
36
30
  ----------------------------------*/
@@ -59,11 +53,32 @@ export default class AuthenticationRouterService<
59
53
  this.users = this.config.users;
60
54
  }
61
55
 
56
+ private traceRouteAuth(
57
+ request: TRequest,
58
+ route: TAnyRoute,
59
+ details: Record<string, any>,
60
+ minimumCapture: 'summary' | 'resolve' | 'deep' = 'resolve',
61
+ ) {
62
+ this.app.container.Trace.record(
63
+ request.id,
64
+ 'auth.route',
65
+ {
66
+ routePath: route.path || '',
67
+ routeId: route.options.id || '',
68
+ authInput: route.options.auth ?? null,
69
+ tracking: route.options.authTracking ?? null,
70
+ redirectLogged: route.options.redirectLogged ?? null,
71
+ ...details,
72
+ },
73
+ minimumCapture,
74
+ );
75
+ }
76
+
62
77
  public async ready() {
63
78
  // Decode current user
64
79
  this.parent.on('request', async (request: TRequest) => {
65
80
  // TODO: Typings. (context.user ?)
66
- const decoded = await this.users.decode(request.req, true);
81
+ const decoded = await this.users.decode(request.req, true, request.id);
67
82
 
68
83
  request.user = decoded || null;
69
84
  });
@@ -72,10 +87,48 @@ export default class AuthenticationRouterService<
72
87
  this.parent.on('resolved', async (route: TAnyRoute, request: TRequest, response: ServerResponse<TRouter>) => {
73
88
  if (route.options.auth !== undefined) {
74
89
  const tracking = route.options.authTracking ?? null;
90
+ const strategy =
91
+ route.options.auth === false
92
+ ? 'guest-only'
93
+ : route.options.auth === null
94
+ ? 'authenticated'
95
+ : typeof route.options.auth === 'object'
96
+ ? 'conditions'
97
+ : route.options.auth === true
98
+ ? tracking !== null && this.users.config.rules
99
+ ? 'user-via-rules'
100
+ : 'user'
101
+ : tracking !== null && this.users.config.rules
102
+ ? 'role-via-rules'
103
+ : 'role';
104
+
105
+ this.traceRouteAuth(
106
+ request,
107
+ route,
108
+ {
109
+ phase: 'start',
110
+ strategy,
111
+ },
112
+ 'resolve',
113
+ );
75
114
 
76
115
  // Guest-only routes can still redirect authenticated users away.
77
116
  if (route.options.auth === false) {
78
117
  const currentUser = this.users.check(request, false, tracking);
118
+ const redirected = Boolean(route.options.redirectLogged && currentUser);
119
+
120
+ this.traceRouteAuth(
121
+ request,
122
+ route,
123
+ {
124
+ phase: 'result',
125
+ strategy,
126
+ outcome: redirected ? 'redirected' : 'allowed',
127
+ userPresent: currentUser !== null,
128
+ redirectTo: redirected ? route.options.redirectLogged : null,
129
+ },
130
+ 'resolve',
131
+ );
79
132
 
80
133
  if (route.options.redirectLogged && currentUser) response.redirect(route.options.redirectLogged);
81
134
  return;
@@ -83,11 +136,13 @@ export default class AuthenticationRouterService<
83
136
 
84
137
  if (route.options.auth === null) {
85
138
  this.users.check(request, null, tracking);
139
+ this.traceRouteAuth(request, route, { phase: 'result', strategy, outcome: 'allowed' }, 'resolve');
86
140
  return;
87
141
  }
88
142
 
89
143
  if (typeof route.options.auth === 'object') {
90
144
  this.users.check(request, route.options.auth as TAuthCheckConditions, tracking);
145
+ this.traceRouteAuth(request, route, { phase: 'result', strategy, outcome: 'allowed' }, 'resolve');
91
146
  return;
92
147
  }
93
148
 
@@ -95,10 +150,32 @@ export default class AuthenticationRouterService<
95
150
  if (route.options.auth === true) {
96
151
  if (tracking !== null && this.users.config.rules) {
97
152
  this.users.check(request, { role: 'USER' }, tracking);
153
+ this.traceRouteAuth(
154
+ request,
155
+ route,
156
+ {
157
+ phase: 'result',
158
+ strategy,
159
+ outcome: 'allowed',
160
+ requiredRole: 'USER',
161
+ },
162
+ 'resolve',
163
+ );
98
164
  return;
99
165
  }
100
166
 
101
167
  this.users.check(request, true);
168
+ this.traceRouteAuth(
169
+ request,
170
+ route,
171
+ {
172
+ phase: 'result',
173
+ strategy,
174
+ outcome: 'allowed',
175
+ requiredRole: 'USER',
176
+ },
177
+ 'resolve',
178
+ );
102
179
  return;
103
180
  }
104
181
 
@@ -106,10 +183,32 @@ export default class AuthenticationRouterService<
106
183
 
107
184
  if (tracking !== null && this.users.config.rules) {
108
185
  this.users.check(request, { role: requiredRole }, tracking);
186
+ this.traceRouteAuth(
187
+ request,
188
+ route,
189
+ {
190
+ phase: 'result',
191
+ strategy,
192
+ outcome: 'allowed',
193
+ requiredRole,
194
+ },
195
+ 'resolve',
196
+ );
109
197
  return;
110
198
  }
111
199
 
112
200
  this.users.check(request, requiredRole);
201
+ this.traceRouteAuth(
202
+ request,
203
+ route,
204
+ {
205
+ phase: 'result',
206
+ strategy,
207
+ outcome: 'allowed',
208
+ requiredRole,
209
+ },
210
+ 'resolve',
211
+ );
113
212
  }
114
213
  });
115
214
  }
@@ -17,7 +17,6 @@ import helmet from 'helmet'; // Diverses protections
17
17
  import compression from 'compression';
18
18
  import fileUpload from 'express-fileupload';
19
19
  import cookieParser from 'cookie-parser';
20
- import * as csp from 'express-csp-header';
21
20
 
22
21
  // Core
23
22
  import Container from '@server/app/container';
@@ -53,6 +52,27 @@ export type Config = {
53
52
 
54
53
  export type Hooks = {};
55
54
 
55
+ type TContentSecurityPolicyOptions = NonNullable<Parameters<typeof helmet.contentSecurityPolicy>[0]>;
56
+ type TContentSecurityPolicyDirectives = NonNullable<TContentSecurityPolicyOptions['directives']>;
57
+
58
+ const createContentSecurityPolicy = (config: Config['csp']): TContentSecurityPolicyOptions => {
59
+ const directives: TContentSecurityPolicyDirectives = {
60
+ defaultSrc:
61
+ config.default && config.default.length > 0
62
+ ? [...config.default]
63
+ : helmet.contentSecurityPolicy.dangerouslyDisableDefaultSrc,
64
+ scriptSrc: ["'unsafe-inline'", "'self'", "'unsafe-eval'", ...config.scripts],
65
+ };
66
+
67
+ if (config.styles && config.styles.length > 0) directives.styleSrc = [...config.styles];
68
+ if (config.images && config.images.length > 0) directives.imgSrc = [...config.images];
69
+
70
+ return {
71
+ useDefaults: false,
72
+ directives,
73
+ };
74
+ };
75
+
56
76
  /*----------------------------------
57
77
  - FUNCTION
58
78
  ----------------------------------*/
@@ -203,11 +223,7 @@ export default class HttpServer<TRouter extends TServerRouter = TServerRouter> {
203
223
 
204
224
  if (this.config.cors !== undefined) routes.use(cors(this.config.cors));
205
225
 
206
- routes.use(
207
- csp.expressCspHeader({
208
- directives: { 'script-src': [csp.INLINE, csp.SELF, csp.UNSAFE_EVAL, ...this.config.csp.scripts] },
209
- }),
210
- );
226
+ routes.use(helmet.contentSecurityPolicy(createContentSecurityPolicy(this.config.csp)));
211
227
 
212
228
  this.registerDevTraceRoutes(routes);
213
229
  routes.use(routeRequest);