diff --git a/packages/node_modules/@node-red/editor-api/lib/index.js b/packages/node_modules/@node-red/editor-api/lib/index.js index 9264550b3d..2da8f99d81 100644 --- a/packages/node_modules/@node-red/editor-api/lib/index.js +++ b/packages/node_modules/@node-red/editor-api/lib/index.js @@ -34,6 +34,39 @@ var adminApp; var server; var editor; +function normaliseHttpNodeRoot(root) { + if (typeof root !== "string" || root === "" || root === "/") { + return null; + } + if (root[0] !== "/") { + root = "/" + root; + } + if (root.length > 1 && root[root.length - 1] === "/") { + root = root.substring(0, root.length - 1); + } + return root; +} + +function requestMatchesRoot(req, root) { + var requestPath = (req.originalUrl || "").split("?")[0]; + return requestPath === root || requestPath.indexOf(root + "/") === 0; +} + +function shouldDeferToHttpNodeCors(settings, req, httpNodeRoot) { + return settings.httpNodeCors && req.method === "OPTIONS" && httpNodeRoot && requestMatchesRoot(req,httpNodeRoot); +} + +function createCorsMiddleware(corsHandler, settings, httpNodeRoot) { + return function corsMiddleware(req,res,next) { + // Let httpNodeCors answer preflight requests for HTTP In nodes. + if (shouldDeferToHttpNodeCors(settings, req, httpNodeRoot)) { + next(); + } else { + corsHandler(req,res,next); + } + }; +} + /** * Initialise the module. * @param {Object} settings The runtime settings @@ -52,7 +85,8 @@ function init(settings,_server,storage,runtimeAPI) { origin: "*", methods: "GET,PUT,POST,DELETE" }); - adminApp.use(corsHandler); + var httpNodeRoot = normaliseHttpNodeRoot(settings.httpNodeRoot); + adminApp.use(createCorsMiddleware(corsHandler, settings, httpNodeRoot)); if (settings.httpAdminMiddleware) { if (typeof settings.httpAdminMiddleware === "function" || Array.isArray(settings.httpAdminMiddleware)) { @@ -93,7 +127,7 @@ function init(settings,_server,storage,runtimeAPI) { if (settings.httpAdminCors) { var corsHandler = cors(settings.httpAdminCors); - adminApp.use(corsHandler); + adminApp.use(createCorsMiddleware(corsHandler, settings, httpNodeRoot)); } var adminApiApp = require("./admin").init(settings, runtimeAPI); diff --git a/test/unit/@node-red/editor-api/lib/index_spec.js b/test/unit/@node-red/editor-api/lib/index_spec.js index 37bb82c13d..45e44bb38d 100644 --- a/test/unit/@node-red/editor-api/lib/index_spec.js +++ b/test/unit/@node-red/editor-api/lib/index_spec.js @@ -18,6 +18,7 @@ var should = require("should"); var sinon = require("sinon"); var request = require("supertest"); var express = require("express"); +var cors = require("cors"); var NR_TEST_UTILS = require("nr-test-utils"); const auth = require("basic-auth"); @@ -154,6 +155,31 @@ describe("api/index", function() { should(middlewareFound).be.length(2); done(); }) + + it('lets http node cors handle node preflight requests mounted below the editor', function(done){ + const httpAdminCors = { + origin: "*", + methods: "GET,PUT,POST" + }; + const httpNodeCors = { + origin: "*", + methods: "GET,PUT,POST,DELETE,PATCH" + }; + const httpNode = express(); + httpNode.options("*", cors(httpNodeCors)); + + api.init({ httpAdminRoot: true, httpNodeRoot: "/api", httpAdminCors, httpNodeCors }, {}, {}, {}); + + const app = express(); + app.use("/", api.httpAdmin); + app.use("/api", httpNode); + + request(app) + .options("/api/route") + .expect("access-control-allow-methods", "GET,PUT,POST,DELETE,PATCH") + .expect(204) + .end(done); + }) }); describe('editor start', function () {