@apolitical/server 2.8.2-rc.1 → 2.9.0-rc.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/CHANGELOG.md +6 -0
- package/package.json +19 -21
- package/src/helpers/jwt/passport.helper.js +1 -11
- package/src/helpers/logger.helper.js +0 -3
- package/src/loaders/documentation.loader.js +0 -4
- package/src/loaders/express.loader.js +0 -4
- package/src/loaders/fallback.loader.js +1 -5
- package/src/loaders/logger.loader.js +0 -3
- package/src/loaders/middlewares.loader.js +0 -4
- package/src/loaders/probes.loader.js +1 -4
- package/src/middlewares/authentication.middleware.js +1 -4
- package/src/middlewares/authorisation.middleware.js +1 -4
- package/src/middlewares/error.middleware.js +0 -2
- package/src/middlewares/jwt/apolitical.middleware.js +1 -5
- package/src/services/express.service.js +2 -11
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [2.9.0] - 2023-01-10
|
|
9
|
+
### Changed
|
|
10
|
+
- Bump dependencies to keep the project up-to-date
|
|
11
|
+
### Removed
|
|
12
|
+
- Unnecessary use of `.debug` logs
|
|
13
|
+
|
|
8
14
|
## [2.8.2] - 2022-11-17
|
|
9
15
|
### Removed
|
|
10
16
|
- ETag header on static files to avoid cache mismatching
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@apolitical/server",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.9.0-rc.1",
|
|
4
4
|
"description": "Node.js module to encapsulate Apolitical's express server setup",
|
|
5
5
|
"author": "Apolitical Group Limited <engineering@apolitical.co>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -24,39 +24,37 @@
|
|
|
24
24
|
"Node Modules"
|
|
25
25
|
],
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@apolitical/logger": "2.0
|
|
27
|
+
"@apolitical/logger": "2.1.0",
|
|
28
28
|
"@cloudnative/health-connect": "2.1.0",
|
|
29
|
-
"awilix": "
|
|
30
|
-
"body-parser": "1.20.
|
|
29
|
+
"awilix": "8.0.0",
|
|
30
|
+
"body-parser": "1.20.1",
|
|
31
31
|
"compression": "1.7.4",
|
|
32
32
|
"cookie-parser": "1.4.6",
|
|
33
33
|
"cors": "2.8.5",
|
|
34
|
-
"dotenv": "16.0.
|
|
35
|
-
"express": "4.18.
|
|
36
|
-
"express-jwt": "
|
|
34
|
+
"dotenv": "16.0.3",
|
|
35
|
+
"express": "4.18.2",
|
|
36
|
+
"express-jwt": "8.3.0",
|
|
37
37
|
"http-status-codes": "2.2.0",
|
|
38
38
|
"http-terminator": "3.2.0",
|
|
39
|
-
"jsrsasign": "10.
|
|
40
|
-
"jwks-rsa": "
|
|
39
|
+
"jsrsasign": "10.6.1",
|
|
40
|
+
"jwks-rsa": "3.0.0",
|
|
41
41
|
"jwt-decode": "3.1.2",
|
|
42
|
-
"lru-cache": "7.
|
|
42
|
+
"lru-cache": "7.14.1",
|
|
43
43
|
"morgan": "1.10.0",
|
|
44
44
|
"passport": "0.6.0",
|
|
45
|
-
"passport-jwt": "4.0.
|
|
45
|
+
"passport-jwt": "4.0.1",
|
|
46
46
|
"prerender-node": "3.5.0",
|
|
47
47
|
"qs": "6.11.0",
|
|
48
|
-
"swagger-ui-express": "4.
|
|
48
|
+
"swagger-ui-express": "4.6.0"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
|
-
"@apolitical/eslint-config": "2.0
|
|
52
|
-
"@apolitical/testing": "1.0.
|
|
53
|
-
"audit-ci": "6.
|
|
54
|
-
"husky": "8.0.
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"mock-jwks": "1.0.3",
|
|
59
|
-
"nock": "13.2.4"
|
|
51
|
+
"@apolitical/eslint-config": "2.1.0",
|
|
52
|
+
"@apolitical/testing": "2.1.0-rc.1",
|
|
53
|
+
"audit-ci": "6.6.0",
|
|
54
|
+
"husky": "8.0.3",
|
|
55
|
+
"lint-staged": "13.1.0",
|
|
56
|
+
"mock-jwks": "1.0.9",
|
|
57
|
+
"nock": "13.2.9"
|
|
60
58
|
},
|
|
61
59
|
"engines": {
|
|
62
60
|
"node": ">=16.13.0"
|
|
@@ -1,35 +1,25 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
module.exports = ({ passport, passportJwt,
|
|
3
|
+
module.exports = ({ passport, passportJwt, config }) => {
|
|
4
4
|
const { COOKIE_KEY } = config.JWT.APOLITICAL;
|
|
5
5
|
|
|
6
6
|
const jwtOptions = {
|
|
7
7
|
jwtFromRequest(req) {
|
|
8
|
-
const childLogger = logger.where(__filename, 'jwtFromRequest');
|
|
9
|
-
childLogger.debug('Started');
|
|
10
8
|
let result = null;
|
|
11
9
|
if (req && req.cookies && req.cookies[COOKIE_KEY]) {
|
|
12
10
|
result = req.cookies[COOKIE_KEY];
|
|
13
|
-
} else {
|
|
14
|
-
childLogger.debug('Cannot find Apolitical token (cookie)');
|
|
15
11
|
}
|
|
16
|
-
childLogger.debug('Finished');
|
|
17
12
|
return result;
|
|
18
13
|
},
|
|
19
14
|
};
|
|
20
15
|
|
|
21
16
|
function jwtCallback(jwtPayload, done) {
|
|
22
|
-
const childLogger = logger.where(__filename, 'jwtCallback');
|
|
23
|
-
childLogger.debug('Started');
|
|
24
17
|
if (jwtPayload.sub !== 'login') {
|
|
25
|
-
childLogger.debug(`Invalid JWT: incorrect sub ${jwtPayload}`);
|
|
26
18
|
return done(null, false, { message: jwtPayload });
|
|
27
19
|
}
|
|
28
20
|
if (!jwtPayload.slug) {
|
|
29
|
-
childLogger.debug(`Invalid JWT: missing slug ${jwtPayload}`);
|
|
30
21
|
return done(null, false, { message: jwtPayload });
|
|
31
22
|
}
|
|
32
|
-
childLogger.debug(`User ${jwtPayload.slug} has a valid JWT`);
|
|
33
23
|
return done(null, jwtPayload);
|
|
34
24
|
}
|
|
35
25
|
|
|
@@ -22,12 +22,9 @@ module.exports = ({ morgan, config, logger }) => {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
function buildMiddleware(labels) {
|
|
25
|
-
const childLogger = logger.where(__filename, 'load');
|
|
26
|
-
childLogger.debug('Started');
|
|
27
25
|
// Setup Morgan with custom format and Apolitical Logger stream
|
|
28
26
|
morgan.token(USER_SLUG, getUserSlug);
|
|
29
27
|
const morganMiddleware = morgan(buildCustomFormat, logger.where(__filename, 'morganMiddleware', labels));
|
|
30
|
-
childLogger.debug('Finished');
|
|
31
28
|
return morganMiddleware;
|
|
32
29
|
}
|
|
33
30
|
|
|
@@ -3,15 +3,12 @@
|
|
|
3
3
|
module.exports = ({
|
|
4
4
|
httpStatusCodes: { MOVED_PERMANENTLY },
|
|
5
5
|
config,
|
|
6
|
-
logger,
|
|
7
6
|
swaggerUi: { serve, setup },
|
|
8
7
|
authorisationMiddleware,
|
|
9
8
|
}) => {
|
|
10
9
|
const { DOCUMENTATION } = config.ENDPOINTS;
|
|
11
10
|
|
|
12
11
|
return function load(app, { swaggerDocument }) {
|
|
13
|
-
const childLogger = logger.where(__filename, 'load');
|
|
14
|
-
childLogger.debug('Started');
|
|
15
12
|
// Load document endpoint
|
|
16
13
|
if (swaggerDocument) {
|
|
17
14
|
// Redirection to documentation
|
|
@@ -19,6 +16,5 @@ module.exports = ({
|
|
|
19
16
|
// Documentation express setup
|
|
20
17
|
app.use(DOCUMENTATION, authorisationMiddleware(), serve, setup(swaggerDocument));
|
|
21
18
|
}
|
|
22
|
-
childLogger.debug('Finished');
|
|
23
19
|
};
|
|
24
20
|
};
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
module.exports = ({
|
|
4
|
-
logger,
|
|
5
4
|
documentationLoader,
|
|
6
5
|
loggerLoader,
|
|
7
6
|
errorMiddleware,
|
|
@@ -11,8 +10,6 @@ module.exports = ({
|
|
|
11
10
|
}) => {
|
|
12
11
|
// Main (express) loader
|
|
13
12
|
return async function load(app, opts) {
|
|
14
|
-
const childLogger = logger.where(__filename, 'load');
|
|
15
|
-
childLogger.debug('Started');
|
|
16
13
|
// Enable strict routing (treat "/foo" and "/foo/" as different)
|
|
17
14
|
if (opts.strictRouting) {
|
|
18
15
|
app.enable('strict routing');
|
|
@@ -35,6 +32,5 @@ module.exports = ({
|
|
|
35
32
|
if (opts.handleErrors) {
|
|
36
33
|
app.use(errorMiddleware(opts));
|
|
37
34
|
}
|
|
38
|
-
childLogger.debug('Finished');
|
|
39
35
|
};
|
|
40
36
|
};
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
module.exports = (
|
|
3
|
+
module.exports = () => {
|
|
4
4
|
return function load(app, { fallbackController, staticFiles }) {
|
|
5
|
-
const childLogger = logger.where(__filename, 'load');
|
|
6
|
-
childLogger.debug('Started');
|
|
7
|
-
|
|
8
5
|
if (staticFiles) {
|
|
9
6
|
const { baseUrl, indexFilePath } = staticFiles;
|
|
10
7
|
// Load fallback or static content
|
|
@@ -16,6 +13,5 @@ module.exports = ({ logger }) => {
|
|
|
16
13
|
});
|
|
17
14
|
}
|
|
18
15
|
}
|
|
19
|
-
childLogger.debug('Finished');
|
|
20
16
|
};
|
|
21
17
|
};
|
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
module.exports = ({ apoliticalLogger, config, logger, loggerHelper }) => {
|
|
4
4
|
return async function load(app, { labels }) {
|
|
5
|
-
const childLogger = logger.where(__filename, 'load');
|
|
6
|
-
childLogger.debug('Started');
|
|
7
5
|
if (config.NODE_ENV === 'production') {
|
|
8
6
|
// Setup Google Cloud with Apolitical Logger
|
|
9
7
|
const winstonMiddleware = await apoliticalLogger.loggerMiddleware(
|
|
@@ -15,6 +13,5 @@ module.exports = ({ apoliticalLogger, config, logger, loggerHelper }) => {
|
|
|
15
13
|
const morganMiddleware = loggerHelper.buildMiddleware(labels);
|
|
16
14
|
app.use(morganMiddleware);
|
|
17
15
|
}
|
|
18
|
-
childLogger.debug('Finished');
|
|
19
16
|
};
|
|
20
17
|
};
|
|
@@ -8,7 +8,6 @@ module.exports = ({
|
|
|
8
8
|
cookieParser,
|
|
9
9
|
prerender,
|
|
10
10
|
config,
|
|
11
|
-
logger,
|
|
12
11
|
}) => {
|
|
13
12
|
const {
|
|
14
13
|
BODY_PARSER_OPTIONS: { JSON_OPTIONS, URL_ENCODED_OPTIONS },
|
|
@@ -17,8 +16,6 @@ module.exports = ({
|
|
|
17
16
|
} = config.SERVER;
|
|
18
17
|
|
|
19
18
|
return function load(app, { corsOptions, prerenderToken, staticFiles }) {
|
|
20
|
-
const childLogger = logger.where(__filename, 'load');
|
|
21
|
-
childLogger.debug('Started');
|
|
22
19
|
// Load useful middlewares
|
|
23
20
|
app.use(cookieParser());
|
|
24
21
|
app.use(json(JSON_OPTIONS));
|
|
@@ -45,6 +42,5 @@ module.exports = ({
|
|
|
45
42
|
),
|
|
46
43
|
);
|
|
47
44
|
}
|
|
48
|
-
childLogger.debug('Finished');
|
|
49
45
|
};
|
|
50
46
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
module.exports = ({ config,
|
|
3
|
+
module.exports = ({ config, healthService }) => {
|
|
4
4
|
const { HEALTH, LIVENESS, READINESS } = config.ENDPOINTS.PROBES;
|
|
5
5
|
const { API, UI } = config.SERVER.PROBES_OPTIONS;
|
|
6
6
|
|
|
@@ -19,8 +19,6 @@ module.exports = ({ config, logger, healthService }) => {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
return function load(app, { probes }) {
|
|
22
|
-
const childLogger = logger.where(__filename, 'load');
|
|
23
|
-
childLogger.debug('Started');
|
|
24
22
|
if (probes) {
|
|
25
23
|
const { readinessCheck, livenessCheck } = probes;
|
|
26
24
|
// Overwrite checks when defined
|
|
@@ -37,6 +35,5 @@ module.exports = ({ config, logger, healthService }) => {
|
|
|
37
35
|
app.get(`${probesPath}${READINESS}`, healthService.readinessEndpoint());
|
|
38
36
|
app.get(`${probesPath}${LIVENESS}`, healthService.livenessEndpoint());
|
|
39
37
|
}
|
|
40
|
-
childLogger.debug('Finished');
|
|
41
38
|
};
|
|
42
39
|
};
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
module.exports = ({ jwtApoliticalMiddleware,
|
|
3
|
+
module.exports = ({ jwtApoliticalMiddleware, serverError: { Forbidden }, config }) => {
|
|
4
4
|
const { COOKIE_KEY } = config.JWT.APOLITICAL;
|
|
5
5
|
// Define external options
|
|
6
6
|
return ({ allowLoggedOut = false } = {}) => {
|
|
7
7
|
// Return middleware handler
|
|
8
8
|
return async function handler(req, res, next) {
|
|
9
|
-
const childLogger = logger.where(__filename, 'handler');
|
|
10
|
-
childLogger.debug('Started');
|
|
11
9
|
// Verifies Apolitical JWT token
|
|
12
10
|
const user = await jwtApoliticalMiddleware(req, res);
|
|
13
11
|
// If logged out users are not allowed and there is no user object throws forbidden error
|
|
@@ -18,7 +16,6 @@ module.exports = ({ jwtApoliticalMiddleware, logger, serverError: { Forbidden },
|
|
|
18
16
|
Object.assign(req, { user });
|
|
19
17
|
// Assigns auth token to the request object
|
|
20
18
|
Object.assign(req, { authToken: req.cookies[COOKIE_KEY] });
|
|
21
|
-
childLogger.debug('Finished');
|
|
22
19
|
return next();
|
|
23
20
|
};
|
|
24
21
|
};
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
module.exports = ({
|
|
3
|
+
module.exports = ({ serverError: { Forbidden }, config }) => {
|
|
4
4
|
const { ADMIN_ROLE, MYSELF_SLUG } = config.MIDDLEWARES.PERMISSIONS;
|
|
5
5
|
// Define external options
|
|
6
6
|
return ({ myselfSource = null, myselfTarget = null, allowNonAdmin = false } = {}) => {
|
|
7
7
|
// Return middleware handler
|
|
8
8
|
return function handler(req, res, next) {
|
|
9
|
-
const childLogger = logger.where(__filename, 'handler');
|
|
10
|
-
childLogger.debug('Started');
|
|
11
9
|
let isNotAdmin = false;
|
|
12
10
|
let isNotMyselfSlug = false;
|
|
13
11
|
// Check user role (from JWT)
|
|
@@ -34,7 +32,6 @@ module.exports = ({ logger, serverError: { Forbidden }, config }) => {
|
|
|
34
32
|
if (myselfSource && myselfTarget && req.params[myselfSource] === MYSELF_SLUG) {
|
|
35
33
|
Object.assign(req.params, { [myselfSource]: req.user[myselfTarget] });
|
|
36
34
|
}
|
|
37
|
-
childLogger.debug('Finished');
|
|
38
35
|
return next();
|
|
39
36
|
};
|
|
40
37
|
};
|
|
@@ -13,8 +13,6 @@ module.exports =
|
|
|
13
13
|
({ labels, redirectURL, fallbackController }) => {
|
|
14
14
|
// eslint-disable-next-line no-unused-vars
|
|
15
15
|
return function handler(err, req, res, next) {
|
|
16
|
-
const childLogger = logger.where(__filename, 'handler');
|
|
17
|
-
childLogger.debug('Started');
|
|
18
16
|
// Initialise variables
|
|
19
17
|
let code = INTERNAL_SERVER_ERROR;
|
|
20
18
|
let message = 'Unexpected error';
|
|
@@ -1,17 +1,13 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
module.exports = ({ passport, config
|
|
3
|
+
module.exports = ({ passport, config }) => {
|
|
4
4
|
const { NAME, SESSION } = config.JWT.APOLITICAL;
|
|
5
5
|
|
|
6
6
|
return function handler(req, res) {
|
|
7
|
-
const childLogger = logger.where(__filename, 'handler');
|
|
8
|
-
childLogger.debug('Started');
|
|
9
|
-
|
|
10
7
|
// Passport JWT authentication (Apolitical)
|
|
11
8
|
return new Promise((resolve) => {
|
|
12
9
|
passport.authenticate(NAME, SESSION, (err, user) => {
|
|
13
10
|
if (err) {
|
|
14
|
-
childLogger.warn('Cannot parse token from cookie', err.message);
|
|
15
11
|
return resolve();
|
|
16
12
|
}
|
|
17
13
|
return resolve(user);
|
|
@@ -5,43 +5,34 @@ module.exports = ({ express, httpTerminator, logger, expressLoader }) => {
|
|
|
5
5
|
let terminator = null; // I need your clothes, your boots and your motorcycle
|
|
6
6
|
|
|
7
7
|
function expressRunner(app, port) {
|
|
8
|
-
const childLogger = logger.where(__filename, 'expressRunner');
|
|
9
|
-
childLogger.debug('Started');
|
|
10
8
|
return new Promise((resolve, reject) => {
|
|
11
9
|
if (!port) {
|
|
12
|
-
childLogger.warn('Missing port option');
|
|
13
10
|
return reject(new Error('Cannot start server without port option'));
|
|
14
11
|
}
|
|
15
12
|
const server = app.listen(port, () => {
|
|
16
|
-
|
|
13
|
+
const runnerLogger = logger.where(__filename, 'expressRunner');
|
|
14
|
+
runnerLogger.info(`Server listening on ${port}`);
|
|
17
15
|
return resolve();
|
|
18
16
|
});
|
|
19
17
|
terminator = httpTerminator.createHttpTerminator({ server });
|
|
20
|
-
childLogger.debug('Finished');
|
|
21
18
|
});
|
|
22
19
|
}
|
|
23
20
|
|
|
24
21
|
async function startup(opts) {
|
|
25
|
-
const childLogger = logger.where(__filename, 'startup');
|
|
26
|
-
childLogger.debug('Started');
|
|
27
22
|
// Create, load and run the express app
|
|
28
23
|
if (!app) {
|
|
29
24
|
app = express();
|
|
30
25
|
await expressLoader(app, opts);
|
|
31
26
|
await expressRunner(app, opts.port);
|
|
32
27
|
}
|
|
33
|
-
childLogger.debug('Finished');
|
|
34
28
|
return app;
|
|
35
29
|
}
|
|
36
30
|
|
|
37
31
|
async function shutdown() {
|
|
38
|
-
const childLogger = logger.where(__filename, 'shutdown');
|
|
39
|
-
childLogger.debug('Started');
|
|
40
32
|
// Terminate process and clean-up variables
|
|
41
33
|
await terminator.terminate();
|
|
42
34
|
app = null;
|
|
43
35
|
terminator = null;
|
|
44
|
-
childLogger.debug('Finished');
|
|
45
36
|
}
|
|
46
37
|
|
|
47
38
|
return { startup, shutdown };
|