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.
- package/README.md +25 -6
- package/agents/framework/AGENTS.md +14 -1
- package/agents/project/AGENTS.md +3 -0
- package/cli/commands/create.ts +5 -0
- package/cli/commands/dev.ts +2 -1
- package/cli/commands/init.ts +2 -94
- package/cli/index.ts +1 -4
- package/cli/presentation/commands.ts +45 -9
- package/cli/presentation/devSession.ts +17 -3
- package/cli/presentation/proteum_logo_400x400_square_icon.txt +400 -0
- package/cli/runtime/commands.ts +61 -3
- package/cli/scaffold/index.ts +720 -0
- package/cli/scaffold/templates.ts +344 -0
- package/cli/scaffold/types.ts +26 -0
- package/client/dev/profiler/index.tsx +1230 -230
- package/common/dev/profiler.ts +1 -0
- package/common/dev/requestTrace.ts +6 -0
- package/docs/dev-commands.md +7 -0
- package/docs/diagnostics.md +88 -0
- package/docs/request-tracing.md +10 -0
- package/eslint.js +11 -6
- package/package.json +3 -2
- package/server/app/index.ts +2 -2
- package/server/index.ts +0 -1
- package/server/services/auth/index.ts +525 -61
- package/server/services/auth/router/index.ts +106 -7
- package/server/services/router/http/index.ts +22 -6
|
@@ -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);
|