diff --git a/pass/app.js b/pass/app.js index 0778cfb5fe32186aa5a1cec2ff8decd763d8e2b8..a09a9c36c3ac1d0510997aa565b91166a67320aa 100644 --- a/pass/app.js +++ b/pass/app.js @@ -28,11 +28,21 @@ app.use(express.urlencoded({ extended: false, limit: "1mb" })); app.use(cookieParser()); app.use((req, res, next) => { + res.locals.baseDir = ""; let subPath = req.url.match(/^((\/.*)?\/keys)/); - if (subPath) { - res.locals.baseDir = subPath[1]; - } else { - res.locals.baseDir = ""; + let allowed_hosts = [ + "localhost", + "metager.de", + "metager.org" + ]; + if (subPath && "x-forwarded-proto" in req.headers && "x-forwarded-host" in req.headers && "x-forwarded-port" in req.headers) { + if (allowed_hosts.includes(req.hostname) || (process.env.NODE_ENV === "development" && req.hostname.match(/^(localhost|.*\.ngrok\.io|.*\.review\.metager\.de)$/))) { + res.locals.baseDir = req.headers["x-forwarded-proto"] + "://" + req.headers["x-forwarded-host"]; + if (!["80", "443"].includes(req.headers["x-forwarded-port"])) { + res.locals.baseDir += ":" + req.headers["x-forwarded-port"]; + } + res.locals.baseDir += subPath[1]; + } } next(); }); diff --git a/pass/package-lock.json b/pass/package-lock.json index 19c20264624beec1ead705e6c81327ba1ae51b51..5688512068209bb09a9c1a20bbc492a1eb8f1ece 100644 --- a/pass/package-lock.json +++ b/pass/package-lock.json @@ -30,6 +30,7 @@ "multer": "^1.4.5-lts.1", "node-forge": "^1.3.1", "pdfkit": "^0.13.0", + "qr-scanner": "^1.4.2", "qrcode": "^1.5.1", "qrcode-reader": "^1.0.4", "uuid": "^9.0.0" @@ -588,6 +589,11 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.17.tgz", "integrity": "sha512-HJSUJmni4BeDHhfzn6nF0sVmd1SMezP7/4F0Lq+aXzmp2xm9O7WXrUtHW/CHlYVtZUbByEvWidHqRtcJXGF2Ng==" }, + "node_modules/@types/offscreencanvas": { + "version": "2019.7.0", + "resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.7.0.tgz", + "integrity": "sha512-PGcyveRIpL1XIqK8eBsmRBt76eFgtzuPiSTyKHZxnGemp2yzGzWpjYKAfK3wIMiU7eH+851yEpiuP8JZerTmWg==" + }, "node_modules/@types/responselike": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", @@ -5086,6 +5092,14 @@ "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", "optional": true }, + "node_modules/qr-scanner": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/qr-scanner/-/qr-scanner-1.4.2.tgz", + "integrity": "sha512-kV1yQUe2FENvn59tMZW6mOVfpq9mGxGf8l6+EGaXUOd4RBOLg7tRC83OrirM5AtDvZRpdjdlXURsHreAOSPOUw==", + "dependencies": { + "@types/offscreencanvas": "^2019.6.4" + } + }, "node_modules/qrcode": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.1.tgz", @@ -7010,6 +7024,11 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.17.tgz", "integrity": "sha512-HJSUJmni4BeDHhfzn6nF0sVmd1SMezP7/4F0Lq+aXzmp2xm9O7WXrUtHW/CHlYVtZUbByEvWidHqRtcJXGF2Ng==" }, + "@types/offscreencanvas": { + "version": "2019.7.0", + "resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.7.0.tgz", + "integrity": "sha512-PGcyveRIpL1XIqK8eBsmRBt76eFgtzuPiSTyKHZxnGemp2yzGzWpjYKAfK3wIMiU7eH+851yEpiuP8JZerTmWg==" + }, "@types/responselike": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", @@ -10551,6 +10570,14 @@ "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", "optional": true }, + "qr-scanner": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/qr-scanner/-/qr-scanner-1.4.2.tgz", + "integrity": "sha512-kV1yQUe2FENvn59tMZW6mOVfpq9mGxGf8l6+EGaXUOd4RBOLg7tRC83OrirM5AtDvZRpdjdlXURsHreAOSPOUw==", + "requires": { + "@types/offscreencanvas": "^2019.6.4" + } + }, "qrcode": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.1.tgz", diff --git a/pass/package.json b/pass/package.json index c8e17ec1c4c64bd4fc75f9f38aa84da7121c1302..2d05c6de58f876ad0e900da71d9c5aede1f34d85 100644 --- a/pass/package.json +++ b/pass/package.json @@ -29,6 +29,7 @@ "multer": "^1.4.5-lts.1", "node-forge": "^1.3.1", "pdfkit": "^0.13.0", + "qr-scanner": "^1.4.2", "qrcode": "^1.5.1", "qrcode-reader": "^1.0.4", "uuid": "^9.0.0" @@ -36,4 +37,4 @@ "devDependencies": { "nodemon": "^2.0.20" } -} \ No newline at end of file +} diff --git a/pass/routes/authentication.js b/pass/routes/authentication.js index 2ea54f72e8cdfbecf754b11f0bbdfb0841f48ac6..fe974fdc957e2c0306827bef406bb2db43db88e6 100644 --- a/pass/routes/authentication.js +++ b/pass/routes/authentication.js @@ -17,18 +17,20 @@ router.use( // General Optional authorization for every page // It will not require login or start a session by default // Only when a route uses the middleware requireAuth - auth({ - issuerBaseURL: `${config.get("app.openid_auth.url")}`, - baseURL: config.get("app.url"), - clientID: config.get("app.openid_auth.app_id"), - clientSecret: config.get("app.openid_auth.app_secret"), - secret: config.get("app.secret"), - session: { - rollingDuration: auth_session_expiration_seconds, - signSessionStoreCookie: true, - }, - authRequired: false, - }), + (req, res, next) => { + auth({ + issuerBaseURL: `${config.get("app.openid_auth.url")}`, + baseURL: res.locals.baseDir, + clientID: config.get("app.openid_auth.app_id"), + clientSecret: config.get("app.openid_auth.app_secret"), + secret: config.get("app.secret"), + session: { + rollingDuration: auth_session_expiration_seconds, + signSessionStoreCookie: true, + }, + authRequired: false, + })(req, res, next); + }, /** * If the user is already authenticated this middleware will check * if the user inherits the required role in his oidc token to use this app @@ -68,7 +70,7 @@ router.use( } else { let params = req.query; delete params.moderation; - let redirect_url = `${req.path}?${new URLSearchParams( + let redirect_url = `${res.locals.baseDir}${req.path}?${new URLSearchParams( params ).toString()}`; res.redirect(redirect_url); diff --git a/pass/routes/checkout/paypal.js b/pass/routes/checkout/paypal.js index 762b5cde338188869ca2d40fa75be891801a97da..a2e23e8f283542489a4fc0ec9b8c126615433f62 100644 --- a/pass/routes/checkout/paypal.js +++ b/pass/routes/checkout/paypal.js @@ -36,21 +36,21 @@ router.get("/:funding_source", async (req, res) => { } req.data.change_url.funding_source = - "/key/" + + `${res.locals.baseDir}/key/` + encodeURIComponent(req.data.key.key) + "/checkout/" + encodeURIComponent(req.data.checkout.amount) + "#payment"; req.data.change_url.funding_source_not_eligible = - "/key/" + + `${res.locals.baseDir}/key/` + encodeURIComponent(req.data.key.key) + "/checkout/" + encodeURIComponent(req.data.checkout.amount) + "?error=funding_source_not_eligible"; req.data.change_url.order_base_url = - "/key/" + + `${res.locals.baseDir}/key/` + encodeURIComponent(req.data.key.key) + "/checkout/" + encodeURIComponent(req.data.checkout.amount) + @@ -142,7 +142,7 @@ router.post("/:funding_source/order/capture", async (req, res) => { .then(() => loaded_order.chargeKey()) .then(() => { let redirect_url = - "/key/" + req.data.key.key + "/orders/" + loaded_order.getOrderID(); + `${res.locals.baseDir}/key/` + req.data.key.key + "/orders/" + loaded_order.getOrderID(); res.status(200).json({ redirect_url: redirect_url, order: { diff --git a/pass/routes/key.js b/pass/routes/key.js index 5ee3ec5750a0d421953d05ac92217ab251b8eed8..a14acb7ea6793f6756c92303e1e0b7dd51a17fb9 100644 --- a/pass/routes/key.js +++ b/pass/routes/key.js @@ -101,7 +101,7 @@ router.use("/:key", param("key").isUUID(4), async (req, res, next) => { encodeURIComponent(req.params.key); let QRCode = require("qrcode"); - let qr_data_uri = await QRCode.toDataURL(metager_url); + let qr_data_uri = await QRCode.toDataURL(metager_url, { errorCorrectionLevel: 'H', scale: 8 }); req.data = Object.assign(req.data, { created_new: req.query.new === "true" ? true : false, @@ -122,18 +122,19 @@ router.use("/:key", param("key").isUUID(4), async (req, res, next) => { css: [`${res.locals.baseDir}/styles/key/key.css`], }); - if (!req.data.admin && (!req.cookies.key || req.cookies.key !== req.data.key.key)) { - res.cookie("key", req.data.key.key, { - sameSite: "lax" - }); - } - next("route"); }); // Basic account page router.get("/:key", async (req, res) => { - res.render("key", req.data); + if (!req.data.admin && (!req.cookies.key || req.cookies.key !== req.data.key.key)) { + res.cookie("key", req.data.key.key, { + sameSite: "lax" + }); + res.render("key", req.data); + } else { + res.redirect(`${res.locals.baseDir}/logout`); + } }); router.use("/:key/orders", orderRouter); diff --git a/pass/routes/orders/invoice.js b/pass/routes/orders/invoice.js index 3a699f7a9e66489bb9ac396ee83b84149dbae78b..7d5ed3a9dc9dcebee7d9d4ceed79fed89334ee19 100644 --- a/pass/routes/orders/invoice.js +++ b/pass/routes/orders/invoice.js @@ -12,9 +12,8 @@ router.get("/", (req, res) => { email: req.query.email || "", address: req.query.address || "", }, - create_invoice_url: `/key/${ - req.data.key.key - }/orders/${req.data.order.order.getOrderID()}/invoice/create`, + create_invoice_url: `${res.locals.baseDir}/key/${req.data.key.key + }/orders/${req.data.order.order.getOrderID()}/invoice/create`, errors: {}, }; @@ -72,11 +71,10 @@ router.post( address: req.data.order.invoice.params.address, }; - req.data.order.invoice.moderation_url = `${config.get("app.url")}/key/${ - req.data.key.key - }/orders/${req.data.order.order.getOrderID()}/invoice?${new URLSearchParams( - moderation_params - ).toString()}#invoice-form`; + req.data.order.invoice.moderation_url = `${res.locals.baseDir}/key/${req.data.key.key + }/orders/${req.data.order.order.getOrderID()}/invoice?${new URLSearchParams( + moderation_params + ).toString()}#invoice-form`; // Render the message let ejs = require("ejs"), @@ -157,8 +155,7 @@ router.post( .then(() => { // Added receipt. We can redirect to download page res.redirect( - `/key/${ - req.data.key.key + `${res.locals.baseDir}/key/${req.data.key.key }/orders/${req.data.order.order.getOrderID()}/invoice/download` ); }) diff --git a/pass/routes/orders/orders.js b/pass/routes/orders/orders.js index 60b2372518bc6c6ac65dc2c9448cf9a4b3f96036..c8fa13176e58f7c139033515985cd4a39bcc42b7 100644 --- a/pass/routes/orders/orders.js +++ b/pass/routes/orders/orders.js @@ -16,16 +16,15 @@ router.use("/", (req, res, next) => { }); router.get("/", (req, res, next) => { req.data.page = "order"; - req.data.css.push("/styles/orders/orders.css"); + req.data.css.push(`${res.locals.baseDir}/styles/orders/orders.css`); res.render("key", req.data); }); router.post("/", (req, res, next) => { - console.log(req.body["order-id"]); - let matches = req.body["order-id"].match(/^(INV_)?(\d{14})$/); + let matches = req.body["order-id"].match(/^(INV_)?(\d+)$/); if (!matches) { req.data.page = "order"; - req.data.css.push("/styles/orders/orders.css"); + req.data.css.push(`${res.locals.baseDir}/styles/orders/orders.css`); req.data.error = 400; req.data.form = { "order-id": req.body["order-id"], @@ -34,16 +33,16 @@ router.post("/", (req, res, next) => { } else { let order_id = matches[2]; Order.LOAD_ORDER_FROM_ID(order_id) - .then(/** @param {Order} order */ (order) => order.getKeyFromOrderLink()) + .then(/** @param {Order} order */(order) => order.getKeyFromOrderLink()) .then((key) => { if (key !== req.data.key.key) { throw "Order is not connected to this key"; } - res.redirect(`/key/${req.data.key.key}/orders/${order_id}`); + res.redirect(`${res.locals.baseDir}/key/${req.data.key.key}/orders/${order_id}`); }) .catch((reason) => { req.data.page = "order"; - req.data.css.push("/styles/orders/orders.css"); + req.data.css.push(`${res.locals.baseDir}/styles/orders/orders.css`); req.data.error = 404; req.data.form = { "order-id": req.body["order-id"], @@ -67,16 +66,15 @@ router.use("/:order_id", param("order_id").isInt(), (req, res, next) => { .then((order) => { res.cookie("order", order.getOrderID()); req.data.page = "order"; - req.data.css.push("/styles/orders/order.css"); + req.data.css.push(`${res.locals.baseDir}/styles/orders/order.css`); req.data.order = { order: order }; - req.data.links.order_url = `/key/${req.data.key.key}/orders/${req.params.order_id}#order`; - req.data.links.receipt_url = `/key/${req.data.key.key}/orders/${req.params.order_id}/pdf`; - req.data.links.invoice_url = `/key/${req.data.key.key}/orders/${req.params.order_id}/invoice#invoice-form`; - req.data.links.refund_url = `/key/${req.data.key.key}/orders/${req.params.order_id}/refund#refund-form`; + req.data.links.order_url = `${res.locals.baseDir}/key/${req.data.key.key}/orders/${req.params.order_id}#order`; + req.data.links.receipt_url = `${res.locals.baseDir}/key/${req.data.key.key}/orders/${req.params.order_id}/pdf`; + req.data.links.invoice_url = `${res.locals.baseDir}/key/${req.data.key.key}/orders/${req.params.order_id}/invoice#invoice-form`; + req.data.links.refund_url = `${res.locals.baseDir}/key/${req.data.key.key}/orders/${req.params.order_id}/refund#refund-form`; if (req.data.order.order.isReceiptCreated()) { - req.data.order.download_invoice_url = `/key/${ - req.data.key.key - }/orders/${req.data.order.order.getOrderID()}/invoice/download`; + req.data.order.download_invoice_url = `${res.locals.baseDir}/key/${req.data.key.key + }/orders/${req.data.order.order.getOrderID()}/invoice/download`; } next("route"); })