@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.
- package/dist/admin/{admin.mw.d.ts → adminMiddleware.d.ts} +0 -0
- package/dist/admin/{admin.mw.js → adminMiddleware.js} +1 -1
- package/dist/admin/{secureHeader.mw.d.ts → secureHeaderMiddleware.d.ts} +6 -2
- package/dist/admin/{secureHeader.mw.js → secureHeaderMiddleware.js} +5 -4
- package/dist/index.d.ts +2 -2
- package/dist/index.js +5 -5
- package/dist/server/createDefaultApp.js +17 -2
- package/dist/server/createDefaultApp.model.d.ts +6 -0
- package/package.json +5 -5
- package/src/admin/{admin.mw.ts → adminMiddleware.ts} +1 -1
- package/src/admin/{secureHeader.mw.ts → secureHeaderMiddleware.ts} +12 -4
- package/src/index.ts +5 -2
- package/src/server/createDefaultApp.model.ts +9 -0
- package/src/server/createDefaultApp.ts +28 -2
|
File without changes
|
|
@@ -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 './
|
|
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
|
-
|
|
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
|
|
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
|
|
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(
|
|
18
|
+
const providedHeader = req.get(secureHeaderKey);
|
|
18
19
|
// pass
|
|
19
|
-
if (!cfg.adminService.cfg.authEnabled || providedHeader === cfg.
|
|
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/
|
|
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/
|
|
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
|
|
8
|
-
Object.defineProperty(exports, "createAdminMiddleware", { enumerable: true, get: function () { return
|
|
9
|
-
Object.defineProperty(exports, "loginHtml", { enumerable: true, get: function () { return
|
|
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
|
|
15
|
-
Object.defineProperty(exports, "createSecureHeaderMiddleware", { enumerable: true, get: function () { return
|
|
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
|
-
|
|
42
|
-
app.use(express.
|
|
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.
|
|
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": "^
|
|
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": "^
|
|
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": "^
|
|
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.
|
|
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 './
|
|
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
|
-
|
|
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(
|
|
34
|
+
const providedHeader = req.get(secureHeaderKey)
|
|
27
35
|
|
|
28
36
|
// pass
|
|
29
|
-
if (!cfg.adminService.cfg.authEnabled || providedHeader === cfg.
|
|
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/
|
|
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 {
|
|
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
|
-
|
|
55
|
-
|
|
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
|
|