@naturalcycles/backend-lib 4.0.0 → 4.2.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.
@@ -19,7 +19,7 @@ exports.createAdminMiddleware = createAdminMiddleware;
19
19
  */
20
20
  function requireAdminPermissions(adminService, reqPermissions = [], cfg = {}) {
21
21
  const { loginHtmlPath = '/login.html', urlStartsWith, apiHost, autoLogin = true } = cfg;
22
- return async (req, res, next) => {
22
+ return async function requireAdminPermissionsFn(req, res, next) {
23
23
  if (urlStartsWith && !req.url.startsWith(urlStartsWith))
24
24
  return next();
25
25
  try {
@@ -1,8 +1,12 @@
1
- import { AdminMiddleware, RequireAdminCfg } from './admin.mw';
1
+ import { AdminMiddleware, RequireAdminCfg } from './adminMiddleware';
2
2
  import { BaseAdminService } from './base.admin.service';
3
3
  export interface SecureHeaderMiddlewareCfg extends RequireAdminCfg {
4
4
  adminService: BaseAdminService;
5
- secureHeader: string;
5
+ /**
6
+ * Defaults to `Authorization`
7
+ */
8
+ secureHeaderKey?: string;
9
+ secureHeaderValue: string;
6
10
  }
7
11
  /**
8
12
  * Secures the endpoint by requiring a secret header to be present.
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createSecureHeaderMiddleware = void 0;
4
4
  const js_lib_1 = require("@naturalcycles/js-lib");
5
- const admin_mw_1 = require("./admin.mw");
5
+ const adminMiddleware_1 = require("./adminMiddleware");
6
6
  /**
7
7
  * Secures the endpoint by requiring a secret header to be present.
8
8
  * Throws Error401Admin otherwise.
@@ -12,11 +12,12 @@ function createSecureHeaderMiddleware(cfg) {
12
12
  }
13
13
  exports.createSecureHeaderMiddleware = createSecureHeaderMiddleware;
14
14
  function requireSecureHeaderOrAdmin(cfg, reqPermissions) {
15
- const requireAdmin = (0, admin_mw_1.requireAdminPermissions)(cfg.adminService, reqPermissions, cfg);
15
+ const { secureHeaderKey = 'Authorization' } = cfg;
16
+ const requireAdmin = (0, adminMiddleware_1.requireAdminPermissions)(cfg.adminService, reqPermissions, cfg);
16
17
  return async (req, res, next) => {
17
- const providedHeader = req.get('Authorization');
18
+ const providedHeader = req.get(secureHeaderKey);
18
19
  // pass
19
- if (!cfg.adminService.cfg.authEnabled || providedHeader === cfg.secureHeader)
20
+ if (!cfg.adminService.cfg.authEnabled || providedHeader === cfg.secureHeaderValue)
20
21
  return next();
21
22
  // Header provided - don't check for Admin
22
23
  if (providedHeader) {
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import onFinished = require('on-finished');
2
- import { AdminMiddleware, createAdminMiddleware, loginHtml, RequireAdminCfg } from './admin/admin.mw';
2
+ import { AdminMiddleware, createAdminMiddleware, loginHtml, RequireAdminCfg } from './admin/adminMiddleware';
3
3
  import { AdminInfo, AdminServiceCfg, BaseAdminService } from './admin/base.admin.service';
4
4
  import { FirebaseSharedService } from './admin/firebase.shared.service';
5
- import { createSecureHeaderMiddleware, SecureHeaderMiddlewareCfg } from './admin/secureHeader.mw';
5
+ import { createSecureHeaderMiddleware, SecureHeaderMiddlewareCfg } from './admin/secureHeaderMiddleware';
6
6
  import { BaseEnv } from './env/env.model';
7
7
  import { EnvSharedService, EnvSharedServiceCfg } from './env/env.shared.service';
8
8
  export * from './gae/appEngine.util';
package/dist/index.js CHANGED
@@ -4,15 +4,15 @@ exports.simpleRequestLoggerMiddleware = exports.clearBodyParserTimeout = exports
4
4
  const tslib_1 = require("tslib");
5
5
  const onFinished = require("on-finished");
6
6
  exports.onFinished = onFinished;
7
- const admin_mw_1 = require("./admin/admin.mw");
8
- Object.defineProperty(exports, "createAdminMiddleware", { enumerable: true, get: function () { return admin_mw_1.createAdminMiddleware; } });
9
- Object.defineProperty(exports, "loginHtml", { enumerable: true, get: function () { return admin_mw_1.loginHtml; } });
7
+ const adminMiddleware_1 = require("./admin/adminMiddleware");
8
+ Object.defineProperty(exports, "createAdminMiddleware", { enumerable: true, get: function () { return adminMiddleware_1.createAdminMiddleware; } });
9
+ Object.defineProperty(exports, "loginHtml", { enumerable: true, get: function () { return adminMiddleware_1.loginHtml; } });
10
10
  const base_admin_service_1 = require("./admin/base.admin.service");
11
11
  Object.defineProperty(exports, "BaseAdminService", { enumerable: true, get: function () { return base_admin_service_1.BaseAdminService; } });
12
12
  const firebase_shared_service_1 = require("./admin/firebase.shared.service");
13
13
  Object.defineProperty(exports, "FirebaseSharedService", { enumerable: true, get: function () { return firebase_shared_service_1.FirebaseSharedService; } });
14
- const secureHeader_mw_1 = require("./admin/secureHeader.mw");
15
- Object.defineProperty(exports, "createSecureHeaderMiddleware", { enumerable: true, get: function () { return secureHeader_mw_1.createSecureHeaderMiddleware; } });
14
+ const secureHeaderMiddleware_1 = require("./admin/secureHeaderMiddleware");
15
+ Object.defineProperty(exports, "createSecureHeaderMiddleware", { enumerable: true, get: function () { return secureHeaderMiddleware_1.createSecureHeaderMiddleware; } });
16
16
  const env_shared_service_1 = require("./env/env.shared.service");
17
17
  Object.defineProperty(exports, "EnvSharedService", { enumerable: true, get: function () { return env_shared_service_1.EnvSharedService; } });
18
18
  (0, tslib_1.__exportStar)(require("./gae/appEngine.util"), exports);
@@ -38,8 +38,22 @@ function createDefaultApp(cfg) {
38
38
  app.use((0, simpleRequestLoggerMiddleware_1.simpleRequestLoggerMiddleware)());
39
39
  }
40
40
  // app.use(safeJsonMiddleware()) // optional
41
- app.use(express.json({ limit: '1mb' }));
42
- app.use(express.urlencoded({ limit: '1mb', extended: true }));
41
+ // accepts application/json
42
+ app.use(express.json({
43
+ limit: '1mb',
44
+ ...cfg.bodyParserJsonOptions,
45
+ }));
46
+ app.use(express.urlencoded({
47
+ limit: '1mb',
48
+ extended: true,
49
+ ...cfg.bodyParserUrlEncodedOptions,
50
+ }));
51
+ // accepts application/octet-stream
52
+ app.use(express.raw({
53
+ // inflate: true, // default is `true`
54
+ limit: '1mb',
55
+ ...cfg.bodyParserRawOptions,
56
+ }));
43
57
  app.use(cookieParser());
44
58
  if (!isTest) {
45
59
  // leaks, load lazily
@@ -52,6 +66,7 @@ function createDefaultApp(cfg) {
52
66
  credentials: true,
53
67
  // methods: 'GET,HEAD,PUT,PATCH,POST,DELETE', // default
54
68
  maxAge: 86400,
69
+ ...cfg.corsOptions,
55
70
  }));
56
71
  // app.use(clearBodyParserTimeout()) // removed by default
57
72
  // Static is now disabled by default due to performance
@@ -1,3 +1,5 @@
1
+ import { Options, OptionsJson, OptionsUrlencoded } from 'body-parser';
2
+ import { CorsOptions } from 'cors';
1
3
  import { SentrySharedService } from '../sentry/sentry.shared.service';
2
4
  import { BackendRequestHandler } from './server.model';
3
5
  /**
@@ -23,4 +25,8 @@ export interface DefaultAppCfg {
23
25
  resources?: BackendRequestHandlerCfg[];
24
26
  postHandlers?: BackendRequestHandlerCfg[];
25
27
  sentryService?: SentrySharedService;
28
+ bodyParserJsonOptions?: OptionsJson;
29
+ bodyParserUrlEncodedOptions?: OptionsUrlencoded;
30
+ bodyParserRawOptions?: Options;
31
+ corsOptions?: CorsOptions;
26
32
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@naturalcycles/backend-lib",
3
- "version": "4.0.0",
3
+ "version": "4.2.0",
4
4
  "scripts": {
5
5
  "prepare": "husky install && patch-package",
6
6
  "serve": "APP_ENV=dev nodemon",
@@ -26,13 +26,13 @@
26
26
  "@types/on-finished": "^2.3.1",
27
27
  "cookie-parser": "^1.4.3",
28
28
  "cors": "^2.8.5",
29
- "dotenv": "^10.0.0",
29
+ "dotenv": "^11.0.0",
30
30
  "ejs": "^3.0.1",
31
31
  "express": "^4.16.4",
32
32
  "express-promise-router": "^4.0.0",
33
33
  "firebase-admin": "^10.0.0",
34
34
  "fs-extra": "^10.0.0",
35
- "helmet": "^4.0.0",
35
+ "helmet": "^5.0.0",
36
36
  "js-yaml": "^4.0.0",
37
37
  "on-finished": "^2.3.0",
38
38
  "simple-git": "^2.1.0",
@@ -44,7 +44,7 @@
44
44
  "@sentry/node": "^6.5.1",
45
45
  "@types/ejs": "^3.0.0",
46
46
  "@types/js-yaml": "^4.0.0",
47
- "@types/node": "^16.4.1",
47
+ "@types/node": "^17.0.0",
48
48
  "esbuild-register": "^3.1.2",
49
49
  "fastify": "^3.20.1",
50
50
  "jest": "^27.0.1",
@@ -84,7 +84,7 @@
84
84
  "url": "https://github.com/NaturalCycles/backend-lib"
85
85
  },
86
86
  "engines": {
87
- "node": ">=16.13.0"
87
+ "node": ">=16.10.0"
88
88
  },
89
89
  "type": "commonjs",
90
90
  "description": "Standard library for making Express.js / AppEngine based backend services",
@@ -55,7 +55,7 @@ export function requireAdminPermissions(
55
55
  ): BackendRequestHandler {
56
56
  const { loginHtmlPath = '/login.html', urlStartsWith, apiHost, autoLogin = true } = cfg
57
57
 
58
- return async (req, res, next) => {
58
+ return async function requireAdminPermissionsFn(req, res, next) {
59
59
  if (urlStartsWith && !req.url.startsWith(urlStartsWith)) return next()
60
60
 
61
61
  try {
@@ -1,11 +1,17 @@
1
1
  import { Admin401ErrorData, HttpError } from '@naturalcycles/js-lib'
2
2
  import { BackendRequestHandler } from '../server/server.model'
3
- import { AdminMiddleware, RequireAdminCfg, requireAdminPermissions } from './admin.mw'
3
+ import { AdminMiddleware, RequireAdminCfg, requireAdminPermissions } from './adminMiddleware'
4
4
  import { BaseAdminService } from './base.admin.service'
5
5
 
6
6
  export interface SecureHeaderMiddlewareCfg extends RequireAdminCfg {
7
7
  adminService: BaseAdminService
8
- secureHeader: string
8
+
9
+ /**
10
+ * Defaults to `Authorization`
11
+ */
12
+ secureHeaderKey?: string
13
+
14
+ secureHeaderValue: string
9
15
  }
10
16
 
11
17
  /**
@@ -20,13 +26,15 @@ function requireSecureHeaderOrAdmin(
20
26
  cfg: SecureHeaderMiddlewareCfg,
21
27
  reqPermissions?: string[],
22
28
  ): BackendRequestHandler {
29
+ const { secureHeaderKey = 'Authorization' } = cfg
30
+
23
31
  const requireAdmin = requireAdminPermissions(cfg.adminService, reqPermissions, cfg)
24
32
 
25
33
  return async (req, res, next) => {
26
- const providedHeader = req.get('Authorization')
34
+ const providedHeader = req.get(secureHeaderKey)
27
35
 
28
36
  // pass
29
- if (!cfg.adminService.cfg.authEnabled || providedHeader === cfg.secureHeader) return next()
37
+ if (!cfg.adminService.cfg.authEnabled || providedHeader === cfg.secureHeaderValue) return next()
30
38
 
31
39
  // Header provided - don't check for Admin
32
40
  if (providedHeader) {
package/src/index.ts CHANGED
@@ -4,10 +4,13 @@ import {
4
4
  createAdminMiddleware,
5
5
  loginHtml,
6
6
  RequireAdminCfg,
7
- } from './admin/admin.mw'
7
+ } from './admin/adminMiddleware'
8
8
  import { AdminInfo, AdminServiceCfg, BaseAdminService } from './admin/base.admin.service'
9
9
  import { FirebaseSharedService } from './admin/firebase.shared.service'
10
- import { createSecureHeaderMiddleware, SecureHeaderMiddlewareCfg } from './admin/secureHeader.mw'
10
+ import {
11
+ createSecureHeaderMiddleware,
12
+ SecureHeaderMiddlewareCfg,
13
+ } from './admin/secureHeaderMiddleware'
11
14
  import { BaseEnv } from './env/env.model'
12
15
  import { EnvSharedService, EnvSharedServiceCfg } from './env/env.shared.service'
13
16
  export * from './gae/appEngine.util'
@@ -1,3 +1,5 @@
1
+ import { Options, OptionsJson, OptionsUrlencoded } from 'body-parser'
2
+ import { CorsOptions } from 'cors'
1
3
  import { SentrySharedService } from '../sentry/sentry.shared.service'
2
4
  import { BackendRequestHandler } from './server.model'
3
5
 
@@ -25,5 +27,12 @@ export interface DefaultAppCfg {
25
27
  handlers?: BackendRequestHandlerCfg[]
26
28
  resources?: BackendRequestHandlerCfg[]
27
29
  postHandlers?: BackendRequestHandlerCfg[]
30
+
28
31
  sentryService?: SentrySharedService
32
+
33
+ bodyParserJsonOptions?: OptionsJson
34
+ bodyParserUrlEncodedOptions?: OptionsUrlencoded
35
+ bodyParserRawOptions?: Options
36
+
37
+ corsOptions?: CorsOptions
29
38
  }
@@ -51,9 +51,34 @@ export function createDefaultApp(cfg: DefaultAppCfg): BackendApplication {
51
51
  }
52
52
 
53
53
  // app.use(safeJsonMiddleware()) // optional
54
- app.use(express.json({ limit: '1mb' }))
55
- app.use(express.urlencoded({ limit: '1mb', extended: true }))
54
+
55
+ // accepts application/json
56
+ app.use(
57
+ express.json({
58
+ limit: '1mb',
59
+ ...cfg.bodyParserJsonOptions,
60
+ }),
61
+ )
62
+
63
+ app.use(
64
+ express.urlencoded({
65
+ limit: '1mb',
66
+ extended: true,
67
+ ...cfg.bodyParserUrlEncodedOptions,
68
+ }),
69
+ )
70
+
71
+ // accepts application/octet-stream
72
+ app.use(
73
+ express.raw({
74
+ // inflate: true, // default is `true`
75
+ limit: '1mb',
76
+ ...cfg.bodyParserRawOptions,
77
+ }),
78
+ )
79
+
56
80
  app.use(cookieParser())
81
+
57
82
  if (!isTest) {
58
83
  // leaks, load lazily
59
84
  app.use(
@@ -69,6 +94,7 @@ export function createDefaultApp(cfg: DefaultAppCfg): BackendApplication {
69
94
  credentials: true,
70
95
  // methods: 'GET,HEAD,PUT,PATCH,POST,DELETE', // default
71
96
  maxAge: 86400,
97
+ ...cfg.corsOptions,
72
98
  }),
73
99
  )
74
100