@apolitical/server 2.0.0-beta.0 → 2.0.0

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 CHANGED
@@ -5,6 +5,17 @@ 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.0.0] - 2022-01-20
9
+ ### Added
10
+ - Authorisation middleware
11
+ - New logging middleware for production (based on Google Winston)
12
+ ### Changed
13
+ - Updated dependencies
14
+ - Upgraded Node.js version
15
+ - Revamped GitLab pipelines
16
+ ### Removed
17
+ - Unused logger helper
18
+
8
19
  ## [1.5.2] - 2021-10-18
9
20
  ### Changed
10
21
  - Allow cors options to be passed in as params on start
package/README.md CHANGED
@@ -75,7 +75,7 @@ For usage examples, see [example.spec.js](tests/../test/integration/example.spec
75
75
 
76
76
  ### JWT authentication
77
77
 
78
- The __Apolitical Server__ has integrated JSON Web Token (JWT) functionality. The JWT authentication layer is intended to be used to secure endpoints without sessions.
78
+ The __Apolitical Server__ has integrated JSON Web Token (JWT) functionality. The JWT authentication layer is intended to be used to secure endpoints from requests without a valid session token.
79
79
 
80
80
  ```js
81
81
  // The JWT strategy session secret
@@ -85,7 +85,7 @@ function appLoader(app) {
85
85
  // Setup the Apolitical JWT strategy
86
86
  jwt.apolitical.setup(SESSION_SECRET);
87
87
  // Use the authentication middleware to reject unauthorised requests
88
- app.use(middlewares.auth());
88
+ app.use(middlewares.authentication());
89
89
  // The example endpoint is now protected
90
90
  app.get('/example', exampleController);
91
91
  }
@@ -102,6 +102,29 @@ The JWT authentication must be defined with two steps:
102
102
 
103
103
  For usage examples, see [jwt.apolitical.spec.js](tests/../test/integration/jwt.apolitical.spec.js) test cases.
104
104
 
105
+ ### JWT authorisation
106
+
107
+ The __Apolitical Server__ has integrated JSON Web Token (JWT) functionality. The JWT authorisation layer is intended to be used to protect endpoints from request with session token without the right permissions.
108
+
109
+ ```js
110
+ // The JWT strategy session secret
111
+ const SESSION_SECRET = 'hello';
112
+
113
+ function appLoader(app) {
114
+ // Setup the Apolitical JWT strategy
115
+ jwt.apolitical.setup(SESSION_SECRET);
116
+ // Use the authentication middleware to reject unauthorised requests
117
+ app.use(middlewares.authentication());
118
+ // The example endpoint can only be accessed by admin users
119
+ app.get('/example', middlewares.authorisation(), exampleController);
120
+ }
121
+
122
+ // Start the server
123
+ start({ appLoader, port: 3000 });
124
+ ```
125
+
126
+ As before, the Apolitical token will be validated by the `authentication` middleware, and then, the `authorisation` middleware will check that the `role` property on the session token to determine the permissions.
127
+
105
128
  ### Error handling
106
129
 
107
130
  The __Apolitical Server__ comes with a default error handler so you don’t need to write your own to get started. It’s important to ensure that your server catches all errors that occur while running your business logic. For more info see [express error handling](https://expressjs.com/en/guide/error-handling.html).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apolitical/server",
3
- "version": "2.0.0-beta.0",
3
+ "version": "2.0.0",
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,7 +24,7 @@
24
24
  "Node Modules"
25
25
  ],
26
26
  "dependencies": {
27
- "@apolitical/logger": "2.0.0-beta.4",
27
+ "@apolitical/logger": "2.0.0",
28
28
  "@cloudnative/health-connect": "2.1.0",
29
29
  "awilix": "6.0.0",
30
30
  "body-parser": "1.19.1",
@@ -45,13 +45,13 @@
45
45
  "swagger-ui-express": "4.3.0"
46
46
  },
47
47
  "devDependencies": {
48
- "@apolitical/eslint-config": "1.0.0-beta.0",
49
- "@apolitical/testing": "0.0.4",
48
+ "@apolitical/eslint-config": "1.0.1",
49
+ "@apolitical/testing": "1.0.1",
50
50
  "audit-ci": "5.1.2",
51
51
  "husky": "7.0.4",
52
52
  "jest": "27.4.7",
53
53
  "jest-junit": "13.0.0",
54
- "lint-staged": "12.1.7",
54
+ "lint-staged": "12.2.1",
55
55
  "mock-jwks": "1.0.1",
56
56
  "nock": "13.2.2"
57
57
  },
package/src/container.js CHANGED
@@ -30,7 +30,6 @@ const serverError = require('./errors/server.error');
30
30
  const jwtDecodeHelper = require('./helpers/jwt/decode.helper');
31
31
  const jwtEncodeHelper = require('./helpers/jwt/encode.helper');
32
32
  const jwtPassportHelper = require('./helpers/jwt/passport.helper');
33
- const loggerHelper = require('./helpers/logger.helper');
34
33
  // Loaders
35
34
  const documentationLoader = require('./loaders/documentation.loader');
36
35
  const loggerLoader = require('./loaders/logger.loader');
@@ -38,11 +37,11 @@ const middlewaresLoader = require('./loaders/middlewares.loader');
38
37
  const probesLoader = require('./loaders/probes.loader');
39
38
  const staticLoader = require('./loaders/static.loader');
40
39
  // Middleware
41
- const authMiddleware = require('./middlewares/auth.middleware');
40
+ const authenticationMiddleware = require('./middlewares/authentication.middleware');
41
+ const authorisationMiddleware = require('./middlewares/authorisation.middleware');
42
42
  const jwtApoliticalMiddleware = require('./middlewares/jwt/apolitical.middleware');
43
43
  const jwtAuth0Middleware = require('./middlewares/jwt/auth0.middleware');
44
44
  const errorMiddleware = require('./middlewares/error.middleware');
45
- const permissionsMiddleware = require('./middlewares/permissions.middleware');
46
45
  // Services
47
46
  const jwtService = require('./services/jwt.service');
48
47
  const serverService = require('./services/server.service');
@@ -79,7 +78,6 @@ container.register({
79
78
  jwtDecodeHelper: asFunction(jwtDecodeHelper).singleton(),
80
79
  jwtEncodeHelper: asFunction(jwtEncodeHelper).singleton(),
81
80
  jwtPassportHelper: asFunction(jwtPassportHelper).singleton(),
82
- loggerHelper: asFunction(loggerHelper).singleton(),
83
81
  // Loaders
84
82
  documentationLoader: asFunction(documentationLoader).singleton(),
85
83
  loggerLoader: asFunction(loggerLoader).singleton(),
@@ -87,11 +85,11 @@ container.register({
87
85
  probesLoader: asFunction(probesLoader).singleton(),
88
86
  staticLoader: asFunction(staticLoader).singleton(),
89
87
  // Middlewares
90
- authMiddleware: asFunction(authMiddleware).singleton(),
88
+ authenticationMiddleware: asFunction(authenticationMiddleware).singleton(),
89
+ authorisationMiddleware: asFunction(authorisationMiddleware).singleton(),
91
90
  jwtApoliticalMiddleware: asFunction(jwtApoliticalMiddleware).singleton(),
92
91
  jwtAuth0Middleware: asFunction(jwtAuth0Middleware).singleton(),
93
92
  errorMiddleware: asFunction(errorMiddleware).singleton(),
94
- permissionsMiddleware: asFunction(permissionsMiddleware).singleton(),
95
93
  // Services
96
94
  jwtService: asFunction(jwtService).singleton(),
97
95
  serverService: asFunction(serverService).singleton(),
@@ -11,7 +11,7 @@ module.exports = ({ passport, passportJwt, logger, config }) => {
11
11
  if (req && req.cookies && req.cookies[COOKIE_KEY]) {
12
12
  result = req.cookies[COOKIE_KEY];
13
13
  } else {
14
- childLogger.debug('No Apolitical auth cookie');
14
+ childLogger.debug('Cannot find Apolitical token (cookie)');
15
15
  }
16
16
  childLogger.debug('Finished');
17
17
  return result;
@@ -5,7 +5,7 @@ module.exports = ({
5
5
  config,
6
6
  logger,
7
7
  swaggerUi: { serve, setup },
8
- permissionsMiddleware,
8
+ authorisationMiddleware,
9
9
  }) => {
10
10
  const { DOCUMENTATION } = config.ENDPOINTS;
11
11
 
@@ -17,7 +17,7 @@ module.exports = ({
17
17
  // Redirection to documentation
18
18
  app.use(`${DOCUMENTATION}index.html`, (req, res) => res.redirect(MOVED_PERMANENTLY, DOCUMENTATION));
19
19
  // Documentation express setup
20
- app.use(DOCUMENTATION, permissionsMiddleware(), serve, setup(swaggerDocument));
20
+ app.use(DOCUMENTATION, authorisationMiddleware(), serve, setup(swaggerDocument));
21
21
  }
22
22
  childLogger.debug('Finished');
23
23
  };
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- module.exports = ({ apoliticalLogger, morgan, config, logger, loggerHelper }) => {
3
+ module.exports = ({ apoliticalLogger, morgan, config, logger }) => {
4
4
  const {
5
5
  LOGGED_OUT_SLUG,
6
6
  TOKENS: { USER_SLUG },
@@ -26,24 +26,15 @@ module.exports = ({ apoliticalLogger, morgan, config, logger, loggerHelper }) =>
26
26
  childLogger.debug('Started');
27
27
  // Setup Morgan with custom format and Apolitical Logger stream
28
28
  morgan.token(USER_SLUG, getUserSlug);
29
- const morganMiddleware = morgan(
30
- buildCustomFormat,
31
- loggerHelper.build({
32
- filename: __filename,
33
- method: 'morganMiddleware',
34
- labels,
35
- }),
36
- );
29
+ const morganMiddleware = morgan(buildCustomFormat, logger.where(__filename, 'morganMiddleware', labels));
37
30
  app.use(morganMiddleware);
38
31
  // Setup Google Cloud with Apolitical Logger
39
- const googleMiddleware = await apoliticalLogger.loggerMiddleware(
40
- loggerHelper.build({
41
- filename: __filename,
42
- method: 'googleMiddleware',
43
- labels,
44
- }),
45
- );
46
- app.use(googleMiddleware);
32
+ if (config.NODE_ENV === 'production') {
33
+ const winstonMiddleware = await apoliticalLogger.loggerMiddleware(
34
+ logger.where(__filename, 'winstonMiddleware', labels),
35
+ );
36
+ app.use(winstonMiddleware);
37
+ }
47
38
  childLogger.debug('Finished');
48
39
  };
49
40
  };
@@ -5,7 +5,7 @@ module.exports = ({ logger, serverError: { Forbidden }, config }) => {
5
5
  // Define external options
6
6
  return ({ myselfSource = null, myselfTarget = null, allowNonAdmin = false } = {}) => {
7
7
  // Return middleware handler
8
- return async function handler(req, res, next) {
8
+ return function handler(req, res, next) {
9
9
  const childLogger = logger.where(__filename, 'handler');
10
10
  childLogger.debug('Started');
11
11
  let isNotMyselfSlug = false;
@@ -7,16 +7,11 @@ module.exports =
7
7
  JWT: { AUTH0 },
8
8
  },
9
9
  logger,
10
- loggerHelper,
11
10
  serverError: { GeneralError },
12
11
  }) =>
13
12
  ({ labels, redirectURL }) => {
14
13
  // Apolitical Logger (with service metadata)
15
- const errorLogger = loggerHelper.build({
16
- filename: __filename,
17
- method: 'errorMiddleware',
18
- labels,
19
- });
14
+ const errorLogger = logger.where(__filename, 'errorMiddleware', labels);
20
15
 
21
16
  // eslint-disable-next-line no-unused-vars
22
17
  return function handler(err, req, res, next) {
@@ -6,7 +6,8 @@ module.exports = ({
6
6
  logger,
7
7
  documentationLoader,
8
8
  loggerLoader,
9
- authMiddleware,
9
+ authenticationMiddleware,
10
+ authorisationMiddleware,
10
11
  errorMiddleware,
11
12
  middlewaresLoader,
12
13
  probesLoader,
@@ -74,7 +75,8 @@ module.exports = ({
74
75
  jwt: jwtService,
75
76
  errors: serverError,
76
77
  middlewares: {
77
- auth: authMiddleware,
78
+ authentication: authenticationMiddleware,
79
+ authorisation: authorisationMiddleware,
78
80
  },
79
81
  };
80
82
  };
@@ -1,12 +0,0 @@
1
- 'use strict';
2
-
3
- module.exports = ({ apoliticalLogger, config: { LOG_LEVEL } }) => ({
4
- build({ filename, method, labels }) {
5
- const options = { logLevel: LOG_LEVEL };
6
- if (labels) {
7
- Object.assign(options, { labels });
8
- }
9
- const logger = apoliticalLogger.createLogger(options);
10
- return logger.where(filename, method);
11
- },
12
- });