@blazedpath/commons 0.0.5 → 0.0.7
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/blz-config/index.js +8 -8
- package/blz-rds-oracle/index.js +0 -1
- package/blz-security/__test__/AuthorizationKpn.yaml +106 -99
- package/blz-security/__test__/autorization.test.js +54 -54
- package/blz-security/__test__/autorizationKpn.test.js +42 -0
- package/blz-security/__test__/orderManagement.test.js +4 -4
- package/blz-security/authorizationService.js +18 -17
- package/blz-security/lab/ConfigurationAdmin.agent.json +258 -0
- package/blz-security/lab/frontend.json +430 -0
- package/blz-security/lab/index.js +14 -5
- package/blz-security/lab/result.json +430 -0
- package/blz-security/lab/resultOnWeb.json +430 -0
- package/blz-security/lab/rules.json +614 -0
- package/blz-security/lab/securityRules.json +680 -0
- package/blz-security/middleware/HapiServerAzureAd.js +45 -6
- package/blz-security/middleware/HapiServerKeycloak.js +40 -2
- package/blz-security/middleware/HapiServerSimToken.js +39 -0
- package/blz-security/middleware/hapi.js +40 -0
- package/blz-security/middleware/hapiServer.js +41 -8
- package/blz-security/secureUrlService.js +9 -8
- package/blz-security/securityService.js +4 -0
- package/package.json +1 -1
|
@@ -103,7 +103,7 @@ class HapiServerAzureAd {
|
|
|
103
103
|
// To refresh the tokens, Azure uses a silent re authentication
|
|
104
104
|
const silentRereshTokenResponse = await me.authServerConfig.authServer.msalClient.acquireTokenSilent({
|
|
105
105
|
account: tokenInfo.account, // If token exists in yar. Account should always exist
|
|
106
|
-
scopes: me.authServerConfig.authServer.scope.split(" ")?? ["user.read"],
|
|
106
|
+
scopes: me.authServerConfig.authServer.scope.split(" ") ?? ["user.read"],
|
|
107
107
|
});
|
|
108
108
|
const obtainedTokens = {};
|
|
109
109
|
// Check if the silent refresh token was successful
|
|
@@ -166,7 +166,7 @@ class HapiServerAzureAd {
|
|
|
166
166
|
});
|
|
167
167
|
|
|
168
168
|
let userRelog = request.yar.get('userRelog');
|
|
169
|
-
request.yar.set('pkv',pkce.verifier)
|
|
169
|
+
request.yar.set('pkv', pkce.verifier)
|
|
170
170
|
request.yar.commit(h);
|
|
171
171
|
if (userRelog && request.path !== '/') {
|
|
172
172
|
return h.redirect('/');
|
|
@@ -193,7 +193,7 @@ class HapiServerAzureAd {
|
|
|
193
193
|
const tokenResponse = await me.authServerConfig.authServer.msalClient.acquireTokenByCode({
|
|
194
194
|
code: authCode,
|
|
195
195
|
redirectUri: me.getRedirectUri(request, 'auth/callback'),
|
|
196
|
-
scopes: me.authServerConfig.authServer.scope.split(" ")?? ["user.read"],
|
|
196
|
+
scopes: me.authServerConfig.authServer.scope.split(" ") ?? ["user.read"],
|
|
197
197
|
codeVerifier: pkceverifier,
|
|
198
198
|
});
|
|
199
199
|
let obtainedTokens = {
|
|
@@ -253,7 +253,7 @@ class HapiServerAzureAd {
|
|
|
253
253
|
const tokenResponse = await me.authServerConfig.authServer.msalClient.acquireTokenByCode({
|
|
254
254
|
code: authCode,
|
|
255
255
|
redirectUri: me.getRedirectUri(request, 'auth/callback'),
|
|
256
|
-
scopes: me.authServerConfig.authServer.scope.split(" ")?? ["user.read"],
|
|
256
|
+
scopes: me.authServerConfig.authServer.scope.split(" ") ?? ["user.read"],
|
|
257
257
|
codeVerifier: pkceverifier,
|
|
258
258
|
responseMode: 'form_post'
|
|
259
259
|
});
|
|
@@ -352,6 +352,45 @@ class HapiServerAzureAd {
|
|
|
352
352
|
}
|
|
353
353
|
}
|
|
354
354
|
})
|
|
355
|
+
|
|
356
|
+
hapiServer.route({
|
|
357
|
+
method: 'GET',
|
|
358
|
+
path: '/check-authorize',
|
|
359
|
+
handler: async (request, h) => {
|
|
360
|
+
try {
|
|
361
|
+
const resourcePath = request.query.path;
|
|
362
|
+
const action = request.query.action;
|
|
363
|
+
const roles = request.query.roles;
|
|
364
|
+
const domains = request.query.domains;
|
|
365
|
+
let parsedRoles;
|
|
366
|
+
if (Array.isArray(roles)) {
|
|
367
|
+
parsedRoles = roles;
|
|
368
|
+
} else if (typeof roles === 'string') {
|
|
369
|
+
parsedRoles = roles.split(',').map(r => r.trim());
|
|
370
|
+
} else {
|
|
371
|
+
parsedRoles = [];
|
|
372
|
+
}
|
|
373
|
+
let parsedDomains;
|
|
374
|
+
if (Array.isArray(domains)) {
|
|
375
|
+
parsedDomains = domains;
|
|
376
|
+
} else if (typeof domains === 'string') {
|
|
377
|
+
parsedDomains = domains.split(',').map(d => d.trim());
|
|
378
|
+
} else {
|
|
379
|
+
parsedDomains = [];
|
|
380
|
+
}
|
|
381
|
+
const result = await securityService.checkAuthorize(
|
|
382
|
+
resourcePath,
|
|
383
|
+
action,
|
|
384
|
+
parsedRoles,
|
|
385
|
+
parsedDomains
|
|
386
|
+
);
|
|
387
|
+
return h.response(JSON.stringify(result)).takeover()
|
|
388
|
+
} catch (err) {
|
|
389
|
+
return errorResponse(h, err, 401)
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
})
|
|
393
|
+
|
|
355
394
|
// /get-user-info
|
|
356
395
|
hapiServer.route({
|
|
357
396
|
method: 'GET',
|
|
@@ -406,7 +445,7 @@ class HapiServerAzureAd {
|
|
|
406
445
|
checkSessionUrl: me.getBaseUrl(request) + 'check-session'
|
|
407
446
|
})
|
|
408
447
|
return h.response(content)
|
|
409
|
-
|
|
448
|
+
.header('Content-Type', 'text/html')
|
|
410
449
|
} catch (err) {
|
|
411
450
|
return errorResponse(h, err, 500)
|
|
412
451
|
}
|
|
@@ -426,7 +465,7 @@ class HapiServerAzureAd {
|
|
|
426
465
|
// check if token is about to be expired
|
|
427
466
|
tokenIsExpired.expired = await me.tokenAboutToExpire(tokenInfo.token, 0.5);
|
|
428
467
|
if (tokenIsExpired.expired) {
|
|
429
|
-
let params = {
|
|
468
|
+
let params = {
|
|
430
469
|
redirectUri: me.getRedirectUri(request, 'auth/callback'),
|
|
431
470
|
scopes: me.authServerConfig.authServer.scope.split(" "),
|
|
432
471
|
}
|
|
@@ -101,8 +101,7 @@ class HapiServerKeycloak {
|
|
|
101
101
|
if (!authServer.scope || !authServer.scope.split(' ').some((reg) => reg === 'openid')) {
|
|
102
102
|
authServer.scope = `openid ${authServer.scope || ''}`
|
|
103
103
|
authServer.scope.trim();
|
|
104
|
-
}
|
|
105
|
-
this.authServerConfig.authServer.scope ? this.authServerConfig.authServer.scope.trim().replace(/\s+/g, '%20') : 'openid';
|
|
104
|
+
}
|
|
106
105
|
if (authServer.tokenEndpoint && !authServer.tokenEndpoint.match(/https.*/)) {
|
|
107
106
|
hapiServer.states.cookies[this.COOKIE_NAMES.SID].isSecure = false
|
|
108
107
|
hapiServer.states.cookies[this.COOKIE_NAMES.SESSION_STATE].isSecure = false
|
|
@@ -349,6 +348,45 @@ class HapiServerKeycloak {
|
|
|
349
348
|
}
|
|
350
349
|
}
|
|
351
350
|
})
|
|
351
|
+
|
|
352
|
+
hapiServer.route({
|
|
353
|
+
method: 'GET',
|
|
354
|
+
path: '/check-authorize',
|
|
355
|
+
handler: async (request, h) => {
|
|
356
|
+
try {
|
|
357
|
+
const resourcePath = request.query.path;
|
|
358
|
+
const action = request.query.action;
|
|
359
|
+
const roles = request.query.roles;
|
|
360
|
+
const domains = request.query.domains;
|
|
361
|
+
let parsedRoles;
|
|
362
|
+
if (Array.isArray(roles)) {
|
|
363
|
+
parsedRoles = roles;
|
|
364
|
+
} else if (typeof roles === 'string') {
|
|
365
|
+
parsedRoles = roles.split(',').map(r => r.trim());
|
|
366
|
+
} else {
|
|
367
|
+
parsedRoles = [];
|
|
368
|
+
}
|
|
369
|
+
let parsedDomains;
|
|
370
|
+
if (Array.isArray(domains)) {
|
|
371
|
+
parsedDomains = domains;
|
|
372
|
+
} else if (typeof domains === 'string') {
|
|
373
|
+
parsedDomains = domains.split(',').map(d => d.trim());
|
|
374
|
+
} else {
|
|
375
|
+
parsedDomains = [];
|
|
376
|
+
}
|
|
377
|
+
const result = await securityService.checkAuthorize(
|
|
378
|
+
resourcePath,
|
|
379
|
+
action,
|
|
380
|
+
parsedRoles,
|
|
381
|
+
parsedDomains
|
|
382
|
+
);
|
|
383
|
+
return h.response(JSON.stringify(result)).takeover()
|
|
384
|
+
} catch (err) {
|
|
385
|
+
return errorResponse(h, err, 401)
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
})
|
|
389
|
+
|
|
352
390
|
// /get-user-info
|
|
353
391
|
hapiServer.route({
|
|
354
392
|
method: 'GET',
|
|
@@ -123,6 +123,45 @@ class HapiServerSimToken {
|
|
|
123
123
|
.code(200)
|
|
124
124
|
}
|
|
125
125
|
})
|
|
126
|
+
|
|
127
|
+
hapiServer.route({
|
|
128
|
+
method: 'GET',
|
|
129
|
+
path: '/check-authorize',
|
|
130
|
+
handler: async (request, h) => {
|
|
131
|
+
try {
|
|
132
|
+
const resourcePath = request.query.path;
|
|
133
|
+
const action = request.query.action;
|
|
134
|
+
const roles = request.query.roles;
|
|
135
|
+
const domains = request.query.domains;
|
|
136
|
+
let parsedRoles;
|
|
137
|
+
if (Array.isArray(roles)) {
|
|
138
|
+
parsedRoles = roles;
|
|
139
|
+
} else if (typeof roles === 'string') {
|
|
140
|
+
parsedRoles = roles.split(',').map(r => r.trim());
|
|
141
|
+
} else {
|
|
142
|
+
parsedRoles = [];
|
|
143
|
+
}
|
|
144
|
+
let parsedDomains;
|
|
145
|
+
if (Array.isArray(domains)) {
|
|
146
|
+
parsedDomains = domains;
|
|
147
|
+
} else if (typeof domains === 'string') {
|
|
148
|
+
parsedDomains = domains.split(',').map(d => d.trim());
|
|
149
|
+
} else {
|
|
150
|
+
parsedDomains = [];
|
|
151
|
+
}
|
|
152
|
+
const result = await securityService.checkAuthorize(
|
|
153
|
+
resourcePath,
|
|
154
|
+
action,
|
|
155
|
+
parsedRoles,
|
|
156
|
+
parsedDomains
|
|
157
|
+
);
|
|
158
|
+
return h.response(JSON.stringify(result)).takeover()
|
|
159
|
+
} catch (err) {
|
|
160
|
+
return errorResponse(h, err, 401)
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
})
|
|
164
|
+
|
|
126
165
|
// /get-user-info
|
|
127
166
|
hapiServer.route({
|
|
128
167
|
path: '/get-user-info',
|
|
@@ -267,6 +267,46 @@ class Hapi {
|
|
|
267
267
|
}
|
|
268
268
|
})
|
|
269
269
|
|
|
270
|
+
context.route({
|
|
271
|
+
method: 'GET',
|
|
272
|
+
path: '/check-authorize',
|
|
273
|
+
handler: async (request, h) => {
|
|
274
|
+
try {
|
|
275
|
+
const resourcePath = request.query.path;
|
|
276
|
+
const action = request.query.action;
|
|
277
|
+
const roles = request.query.roles;
|
|
278
|
+
const domains = request.query.domains;
|
|
279
|
+
let parsedRoles;
|
|
280
|
+
if (Array.isArray(roles)) {
|
|
281
|
+
parsedRoles = roles;
|
|
282
|
+
} else if (typeof roles === 'string') {
|
|
283
|
+
parsedRoles = roles.split(',').map(r => r.trim());
|
|
284
|
+
} else {
|
|
285
|
+
parsedRoles = [];
|
|
286
|
+
}
|
|
287
|
+
let parsedDomains;
|
|
288
|
+
if (Array.isArray(domains)) {
|
|
289
|
+
parsedDomains = domains;
|
|
290
|
+
} else if (typeof domains === 'string') {
|
|
291
|
+
parsedDomains = domains.split(',').map(d => d.trim());
|
|
292
|
+
} else {
|
|
293
|
+
parsedDomains = [];
|
|
294
|
+
}
|
|
295
|
+
const result = await securityService.checkAuthorize(
|
|
296
|
+
resourcePath,
|
|
297
|
+
action,
|
|
298
|
+
parsedRoles,
|
|
299
|
+
parsedDomains
|
|
300
|
+
);
|
|
301
|
+
return h.response(JSON.stringify(result)).takeover()
|
|
302
|
+
} catch (err) {
|
|
303
|
+
return errorResponse(h, err, 401)
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
})
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
|
|
270
310
|
context.route({
|
|
271
311
|
method: 'GET',
|
|
272
312
|
path: '/get-user-info',
|
|
@@ -205,13 +205,7 @@ class HapiServer {
|
|
|
205
205
|
// By this point, token refresh was already attempted in onPreAuth event, so it redirects to login on unauthorized
|
|
206
206
|
if (response.isBoom && response.output.statusCode === 401 && !request.path.startsWith('/auth/callback') && !authError) {
|
|
207
207
|
if (this.authServerConfig.authServer.provider=== 'ad-azure') {
|
|
208
|
-
return h.redirect('/login').takeover();
|
|
209
|
-
const authUrl = await this.authServerConfig.authServer.msalClient.getAuthCodeUrl({
|
|
210
|
-
redirectUri: me.getBaseUrl(request) + 'auth/callback',
|
|
211
|
-
scopes: ['user.read'],
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
return h.redirect(authUrl);
|
|
208
|
+
return h.redirect('/login').takeover();
|
|
215
209
|
}
|
|
216
210
|
// Create the url query string parameters. with a random code verifier, store in yar and get the codeChallenge
|
|
217
211
|
const codeVerifier = crypto.randomBytes(32).toString('base64url');
|
|
@@ -312,7 +306,7 @@ class HapiServer {
|
|
|
312
306
|
},
|
|
313
307
|
}
|
|
314
308
|
);
|
|
315
|
-
if (!tokenResponse.statusText
|
|
309
|
+
if (!tokenResponse.statusText =='OK') {
|
|
316
310
|
throw new Error('Failed to exchange code for tokens');
|
|
317
311
|
}
|
|
318
312
|
obtainedTokens.tokenType = 'Bearer';
|
|
@@ -396,6 +390,45 @@ class HapiServer {
|
|
|
396
390
|
}
|
|
397
391
|
}
|
|
398
392
|
})
|
|
393
|
+
|
|
394
|
+
context.route({
|
|
395
|
+
method: 'GET',
|
|
396
|
+
path: '/check-authorize',
|
|
397
|
+
handler: async (request, h) => {
|
|
398
|
+
try {
|
|
399
|
+
const resourcePath = request.query.path;
|
|
400
|
+
const action = request.query.action;
|
|
401
|
+
const roles = request.query.roles;
|
|
402
|
+
const domains = request.query.domains;
|
|
403
|
+
let parsedRoles;
|
|
404
|
+
if (Array.isArray(roles)) {
|
|
405
|
+
parsedRoles = roles;
|
|
406
|
+
} else if (typeof roles === 'string') {
|
|
407
|
+
parsedRoles = roles.split(',').map(r => r.trim());
|
|
408
|
+
} else {
|
|
409
|
+
parsedRoles = [];
|
|
410
|
+
}
|
|
411
|
+
let parsedDomains;
|
|
412
|
+
if (Array.isArray(domains)) {
|
|
413
|
+
parsedDomains = domains;
|
|
414
|
+
} else if (typeof domains === 'string') {
|
|
415
|
+
parsedDomains = domains.split(',').map(d => d.trim());
|
|
416
|
+
} else {
|
|
417
|
+
parsedDomains = [];
|
|
418
|
+
}
|
|
419
|
+
const result = await securityService.checkAuthorize(
|
|
420
|
+
resourcePath,
|
|
421
|
+
action,
|
|
422
|
+
parsedRoles,
|
|
423
|
+
parsedDomains
|
|
424
|
+
);
|
|
425
|
+
return h.response(JSON.stringify(result)).takeover()
|
|
426
|
+
} catch (err) {
|
|
427
|
+
return errorResponse(h, err, 401)
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
})
|
|
431
|
+
|
|
399
432
|
// /get-user-info
|
|
400
433
|
hapiServer.route({
|
|
401
434
|
method: 'GET',
|
|
@@ -9,8 +9,8 @@ module.exports = class SecureUrlService {
|
|
|
9
9
|
validate(url, token, _session_key, timeoutMs) {
|
|
10
10
|
const path = decodeURIComponent(url.split('?')[0]);
|
|
11
11
|
if (!token) {
|
|
12
|
-
this.logger.error(`Token parameter '
|
|
13
|
-
throw new Exception("Token parameter '
|
|
12
|
+
this.logger.error(`Token parameter 'sut' is missing in the URL. path:${path}`)
|
|
13
|
+
throw new Exception("Token parameter 'sut' is missing in the URL.", 'SecureUrlError', 404);
|
|
14
14
|
}
|
|
15
15
|
const session_key = isBase64(_session_key)?atob(_session_key):_session_key
|
|
16
16
|
const key = `${session_key}${btoa(path)}`;
|
|
@@ -26,13 +26,14 @@ module.exports = class SecureUrlService {
|
|
|
26
26
|
} catch (e) {
|
|
27
27
|
this.logger.error(`Malformed token content. path:${path} error: ${e.message}`)
|
|
28
28
|
throw new Exception("Malformed token content.", 'SecureUrlError', 400);
|
|
29
|
-
}
|
|
30
|
-
const
|
|
31
|
-
const
|
|
32
|
-
const
|
|
29
|
+
}
|
|
30
|
+
const now = Date.now();
|
|
31
|
+
const diff = Math.abs(now - requestTime);
|
|
32
|
+
const limit = parseInt(timeoutMs, 10);
|
|
33
|
+
const isValid = diff <= limit;
|
|
33
34
|
if (!isValid) {
|
|
34
|
-
this.logger.error(`The token has expired. path:${path}
|
|
35
|
-
throw new Exception("The token has expired.", 'SecureUrlError',
|
|
35
|
+
this.logger.error(`The token has expired. path:${path} requestTime:${requestTime} now: ${now} limit:${limit} diff:${diff}`)
|
|
36
|
+
throw new Exception("The token has expired.", 'SecureUrlError', 410);
|
|
36
37
|
}
|
|
37
38
|
}
|
|
38
39
|
|
|
@@ -406,4 +406,8 @@ module.exports = class SecurityService {
|
|
|
406
406
|
logUnProtected() {
|
|
407
407
|
this.logger.info('unprotected: ' + JSON.stringify(this.unProtected))
|
|
408
408
|
}
|
|
409
|
+
|
|
410
|
+
async checkAuthorize (path, action, roles, domains) {
|
|
411
|
+
return this.authorizationService.checkAuthorize(path, action, roles, domains)
|
|
412
|
+
}
|
|
409
413
|
}
|