@nocobase/server 2.2.0-beta.13 → 2.2.0-beta.14
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/lib/application.js +1 -0
- package/lib/gateway/index.js +32 -3
- package/lib/gateway/static-file-security.js +10 -1
- package/lib/helper.js +27 -11
- package/package.json +17 -17
package/lib/application.js
CHANGED
|
@@ -944,6 +944,7 @@ const _Application = class _Application extends import_koa.default {
|
|
|
944
944
|
}
|
|
945
945
|
});
|
|
946
946
|
this._dataSourceManager.use(this._authManager.middleware(), { tag: "auth", before: "default" });
|
|
947
|
+
this._dataSourceManager.use(import_auth.csrfMiddleware, { tag: "csrf", after: "auth", before: "default" });
|
|
947
948
|
this._dataSourceManager.use(import_validate_filter_params.default, { tag: "validate-filter-params", before: ["auth"] });
|
|
948
949
|
this._dataSourceManager.use(import_middlewares.parseVariables, {
|
|
949
950
|
group: "parseVariables",
|
package/lib/gateway/index.js
CHANGED
|
@@ -78,6 +78,17 @@ function normalizeBasePath(path = "") {
|
|
|
78
78
|
return normalized || "/";
|
|
79
79
|
}
|
|
80
80
|
__name(normalizeBasePath, "normalizeBasePath");
|
|
81
|
+
function getFilesPathPrefixes(appPublicPath = "/") {
|
|
82
|
+
const normalizedPublicPath = normalizeBasePath(appPublicPath);
|
|
83
|
+
const canonicalPrefix = `${normalizedPublicPath === "/" ? "" : normalizedPublicPath}/files/`;
|
|
84
|
+
return canonicalPrefix === "/files/" ? ["/files/"] : [canonicalPrefix, "/files/"];
|
|
85
|
+
}
|
|
86
|
+
__name(getFilesPathPrefixes, "getFilesPathPrefixes");
|
|
87
|
+
function getFileAccessRestPath(pathname, appPublicPath = "/") {
|
|
88
|
+
const prefix = getFilesPathPrefixes(appPublicPath).find((prefix2) => pathname.startsWith(prefix2));
|
|
89
|
+
return prefix ? pathname.slice(prefix.length) : null;
|
|
90
|
+
}
|
|
91
|
+
__name(getFileAccessRestPath, "getFileAccessRestPath");
|
|
81
92
|
function getSocketPath() {
|
|
82
93
|
const socketPath = import_node_process.default.env.SOCKET_PATH;
|
|
83
94
|
if (socketPath) {
|
|
@@ -202,6 +213,17 @@ const _Gateway = class _Gateway extends import_events.EventEmitter {
|
|
|
202
213
|
req.url = rewrittenUrl;
|
|
203
214
|
}
|
|
204
215
|
}
|
|
216
|
+
const fileAccessRestPath = parsedUrl.pathname ? getFileAccessRestPath(parsedUrl.pathname, import_node_process.default.env.APP_PUBLIC_PATH || "/") : null;
|
|
217
|
+
if (fileAccessRestPath) {
|
|
218
|
+
const restPath = fileAccessRestPath;
|
|
219
|
+
const [pathAppName] = restPath.split("/");
|
|
220
|
+
if (pathAppName) {
|
|
221
|
+
try {
|
|
222
|
+
ctx.resolvedAppName = decodeURIComponent(pathAppName);
|
|
223
|
+
} catch (error) {
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
205
227
|
await next();
|
|
206
228
|
},
|
|
207
229
|
{
|
|
@@ -337,7 +359,7 @@ const _Gateway = class _Gateway extends import_events.EventEmitter {
|
|
|
337
359
|
return (0, import_utils3.injectRuntimeScript)(html, this.getV2RuntimeConfigScript());
|
|
338
360
|
}
|
|
339
361
|
async requestHandler(req, res) {
|
|
340
|
-
const { pathname } = (0, import_url.parse)(req.url);
|
|
362
|
+
const { pathname, search } = (0, import_url.parse)(req.url);
|
|
341
363
|
const { PLUGIN_STATICS_PATH } = import_node_process.default.env;
|
|
342
364
|
const APP_PUBLIC_PATH = this.getAppPublicPath();
|
|
343
365
|
if (pathname.endsWith("/__umi/api/bundle-status")) {
|
|
@@ -345,6 +367,12 @@ const _Gateway = class _Gateway extends import_events.EventEmitter {
|
|
|
345
367
|
res.end("ok");
|
|
346
368
|
return;
|
|
347
369
|
}
|
|
370
|
+
if (APP_PUBLIC_PATH !== "/" && pathname.startsWith("/files/")) {
|
|
371
|
+
res.statusCode = 302;
|
|
372
|
+
res.setHeader("Location", `${APP_PUBLIC_PATH.replace(/\/$/, "")}${pathname}${search || ""}`);
|
|
373
|
+
res.end();
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
348
376
|
const supervisor = import_app_supervisor.AppSupervisor.getInstance();
|
|
349
377
|
let handleApp = "main";
|
|
350
378
|
try {
|
|
@@ -361,7 +389,7 @@ const _Gateway = class _Gateway extends import_events.EventEmitter {
|
|
|
361
389
|
return;
|
|
362
390
|
}
|
|
363
391
|
}
|
|
364
|
-
const headers = (0, import_static_file_security.getStorageUploadSecurityHeaders)(pathname);
|
|
392
|
+
const headers = (0, import_static_file_security.getStorageUploadSecurityHeaders)(`${pathname}${search || ""}`);
|
|
365
393
|
for (const [key, value] of Object.entries(headers)) {
|
|
366
394
|
res.setHeader(key, value);
|
|
367
395
|
}
|
|
@@ -407,7 +435,8 @@ const _Gateway = class _Gateway extends import_events.EventEmitter {
|
|
|
407
435
|
]
|
|
408
436
|
});
|
|
409
437
|
}
|
|
410
|
-
|
|
438
|
+
const isFilesRequest = Boolean(getFileAccessRestPath(pathname, APP_PUBLIC_PATH));
|
|
439
|
+
if (!pathname.startsWith(import_node_process.default.env.API_BASE_PATH) && !isFilesRequest) {
|
|
411
440
|
if (this.isV2Request(pathname)) {
|
|
412
441
|
if (handleApp !== "main") {
|
|
413
442
|
const isProxy = await this.proxyRequestToSubApp(supervisor, handleApp, req, res);
|
|
@@ -47,6 +47,15 @@ function stripQueryAndHash(pathname = "") {
|
|
|
47
47
|
return pathname.split("?")[0].split("#")[0];
|
|
48
48
|
}
|
|
49
49
|
__name(stripQueryAndHash, "stripQueryAndHash");
|
|
50
|
+
function shouldDownload(pathname = "") {
|
|
51
|
+
var _a;
|
|
52
|
+
const query = (_a = pathname.split("?")[1]) == null ? void 0 : _a.split("#")[0];
|
|
53
|
+
if (!query) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
return new URLSearchParams(query).get("download") === "1";
|
|
57
|
+
}
|
|
58
|
+
__name(shouldDownload, "shouldDownload");
|
|
50
59
|
function hasActiveContentExtension(pathname = "") {
|
|
51
60
|
const ext = import_node_path.default.extname(stripQueryAndHash(pathname)).toLowerCase();
|
|
52
61
|
return ACTIVE_CONTENT_EXTENSIONS.has(ext);
|
|
@@ -56,7 +65,7 @@ function getStorageUploadSecurityHeaders(pathname = "") {
|
|
|
56
65
|
const headers = {
|
|
57
66
|
"X-Content-Type-Options": "nosniff"
|
|
58
67
|
};
|
|
59
|
-
if (hasActiveContentExtension(pathname)) {
|
|
68
|
+
if (hasActiveContentExtension(pathname) || shouldDownload(pathname)) {
|
|
60
69
|
headers["Content-Disposition"] = "attachment";
|
|
61
70
|
}
|
|
62
71
|
return headers;
|
package/lib/helper.js
CHANGED
|
@@ -76,23 +76,28 @@ function createResourcer(options) {
|
|
|
76
76
|
return new import_resourcer.Resourcer({ ...options.resourcer });
|
|
77
77
|
}
|
|
78
78
|
__name(createResourcer, "createResourcer");
|
|
79
|
+
function isWhitelistedCorsOrigin(ctx) {
|
|
80
|
+
const origin = ctx.get("origin");
|
|
81
|
+
const whitelist = (0, import_utils.getCorsWhitelist)();
|
|
82
|
+
if (!origin) {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
if (!whitelist) {
|
|
86
|
+
return (0, import_utils.isTrustedOrigin)(ctx, origin);
|
|
87
|
+
}
|
|
88
|
+
return whitelist.has(origin);
|
|
89
|
+
}
|
|
90
|
+
__name(isWhitelistedCorsOrigin, "isWhitelistedCorsOrigin");
|
|
79
91
|
function resolveCorsOrigin(ctx) {
|
|
80
92
|
const origin = ctx.get("origin");
|
|
81
93
|
const disallowNoOrigin = process.env.CORS_DISALLOW_NO_ORIGIN === "true";
|
|
82
|
-
const whitelistString = process.env.CORS_ORIGIN_WHITELIST;
|
|
83
94
|
if (!origin && disallowNoOrigin) {
|
|
84
95
|
return false;
|
|
85
96
|
}
|
|
86
|
-
if (
|
|
97
|
+
if (isWhitelistedCorsOrigin(ctx)) {
|
|
87
98
|
return origin;
|
|
88
99
|
}
|
|
89
|
-
|
|
90
|
-
whitelistString.split(",").map((item) => item.trim()).filter(Boolean)
|
|
91
|
-
);
|
|
92
|
-
if (whitelist.has(origin)) {
|
|
93
|
-
return origin;
|
|
94
|
-
}
|
|
95
|
-
return false;
|
|
100
|
+
return (0, import_utils.getCorsWhitelist)() ? false : origin;
|
|
96
101
|
}
|
|
97
102
|
__name(resolveCorsOrigin, "resolveCorsOrigin");
|
|
98
103
|
function registerMiddlewares(app, options) {
|
|
@@ -108,6 +113,7 @@ function registerMiddlewares(app, options) {
|
|
|
108
113
|
app.use((0, import_logger.requestLogger)(app.name, app.requestLogger, (_a = options.logger) == null ? void 0 : _a.request), { tag: "logger" });
|
|
109
114
|
app.use(
|
|
110
115
|
(0, import_cors.default)({
|
|
116
|
+
credentials: isWhitelistedCorsOrigin,
|
|
111
117
|
exposeHeaders: ["content-disposition"],
|
|
112
118
|
origin: resolveCorsOrigin,
|
|
113
119
|
...options.cors
|
|
@@ -134,8 +140,18 @@ function registerMiddlewares(app, options) {
|
|
|
134
140
|
}
|
|
135
141
|
app.use(/* @__PURE__ */ __name(async function getBearerToken(ctx, next) {
|
|
136
142
|
ctx.getBearerToken = () => {
|
|
137
|
-
const
|
|
138
|
-
|
|
143
|
+
const authorization = ctx.get("Authorization");
|
|
144
|
+
if (authorization) {
|
|
145
|
+
ctx.state.pendingAuthTokenSource = "authorization";
|
|
146
|
+
return authorization.replace(/^Bearer\s+/gi, "");
|
|
147
|
+
}
|
|
148
|
+
if (ctx.query.token) {
|
|
149
|
+
ctx.state.pendingAuthTokenSource = "query";
|
|
150
|
+
return ctx.query.token;
|
|
151
|
+
}
|
|
152
|
+
const cookieToken = ctx.cookies.get((0, import_utils.getAuthCookieName)("authToken", app.name));
|
|
153
|
+
ctx.state.pendingAuthTokenSource = cookieToken ? "cookie" : void 0;
|
|
154
|
+
return cookieToken;
|
|
139
155
|
};
|
|
140
156
|
await next();
|
|
141
157
|
}, "getBearerToken"));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nocobase/server",
|
|
3
|
-
"version": "2.2.0-beta.
|
|
3
|
+
"version": "2.2.0-beta.14",
|
|
4
4
|
"main": "lib/index.js",
|
|
5
5
|
"types": "./lib/index.d.ts",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -10,21 +10,21 @@
|
|
|
10
10
|
"@koa/cors": "^5.0.0",
|
|
11
11
|
"@koa/multer": "^3.1.0",
|
|
12
12
|
"@koa/router": "^13.1.0",
|
|
13
|
-
"@nocobase/acl": "2.2.0-beta.
|
|
14
|
-
"@nocobase/actions": "2.2.0-beta.
|
|
15
|
-
"@nocobase/ai": "2.2.0-beta.
|
|
16
|
-
"@nocobase/auth": "2.2.0-beta.
|
|
17
|
-
"@nocobase/cache": "2.2.0-beta.
|
|
18
|
-
"@nocobase/data-source-manager": "2.2.0-beta.
|
|
19
|
-
"@nocobase/database": "2.2.0-beta.
|
|
20
|
-
"@nocobase/evaluators": "2.2.0-beta.
|
|
21
|
-
"@nocobase/lock-manager": "2.2.0-beta.
|
|
22
|
-
"@nocobase/logger": "2.2.0-beta.
|
|
23
|
-
"@nocobase/resourcer": "2.2.0-beta.
|
|
24
|
-
"@nocobase/sdk": "2.2.0-beta.
|
|
25
|
-
"@nocobase/snowflake-id": "2.2.0-beta.
|
|
26
|
-
"@nocobase/telemetry": "2.2.0-beta.
|
|
27
|
-
"@nocobase/utils": "2.2.0-beta.
|
|
13
|
+
"@nocobase/acl": "2.2.0-beta.14",
|
|
14
|
+
"@nocobase/actions": "2.2.0-beta.14",
|
|
15
|
+
"@nocobase/ai": "2.2.0-beta.14",
|
|
16
|
+
"@nocobase/auth": "2.2.0-beta.14",
|
|
17
|
+
"@nocobase/cache": "2.2.0-beta.14",
|
|
18
|
+
"@nocobase/data-source-manager": "2.2.0-beta.14",
|
|
19
|
+
"@nocobase/database": "2.2.0-beta.14",
|
|
20
|
+
"@nocobase/evaluators": "2.2.0-beta.14",
|
|
21
|
+
"@nocobase/lock-manager": "2.2.0-beta.14",
|
|
22
|
+
"@nocobase/logger": "2.2.0-beta.14",
|
|
23
|
+
"@nocobase/resourcer": "2.2.0-beta.14",
|
|
24
|
+
"@nocobase/sdk": "2.2.0-beta.14",
|
|
25
|
+
"@nocobase/snowflake-id": "2.2.0-beta.14",
|
|
26
|
+
"@nocobase/telemetry": "2.2.0-beta.14",
|
|
27
|
+
"@nocobase/utils": "2.2.0-beta.14",
|
|
28
28
|
"@types/decompress": "4.2.7",
|
|
29
29
|
"@types/ini": "^1.3.31",
|
|
30
30
|
"@types/koa-send": "^4.1.3",
|
|
@@ -61,5 +61,5 @@
|
|
|
61
61
|
"@types/serve-handler": "^6.1.1",
|
|
62
62
|
"@types/ws": "^8.5.5"
|
|
63
63
|
},
|
|
64
|
-
"gitHead": "
|
|
64
|
+
"gitHead": "1ef021f3ce0a84469e40f181a19551a50b6f850c"
|
|
65
65
|
}
|