diff --git a/config.template.yaml b/config.template.yaml index 32e8f3f0..e9854ae7 100644 --- a/config.template.yaml +++ b/config.template.yaml @@ -18,6 +18,10 @@ http: # Specifies the root domain for cookies. If you have multiple domains # e.g. a.example.com and b.example.com, the root domain is example.com root-domain: 'localhost' + # Specify alternate domains/hosts that are allowed to set the login cookie + # Leave out the http:// + alt-domains: + - '127.0.0.1' # Use express-minify to minify CSS and Javascript minify: false # Static content cache (in seconds) diff --git a/lib/config.js b/lib/config.js index 737a0aff..21e5ebfb 100644 --- a/lib/config.js +++ b/lib/config.js @@ -27,6 +27,7 @@ var defaults = { port: 8080, domain: "http://localhost", "root-domain": "localhost", + "alt-domains": ["127.0.0.1"], minify: false, "cache-ttl": 0 }, @@ -143,7 +144,7 @@ exports.load = function (file) { function preprocessConfig(cfg) { // Root domain should start with a . for cookies var root = cfg.http["root-domain"]; - root = "." + root.replace(/^\.*/, ""); + root = root.replace(/^\.*/, ""); cfg.http["root-domain"] = root; // Setup nodemailer diff --git a/lib/web/auth.js b/lib/web/auth.js index 179511f2..5e5b3256 100644 --- a/lib/web/auth.js +++ b/lib/web/auth.js @@ -14,6 +14,7 @@ var Logger = require("../logger"); var $util = require("../utilities"); var db = require("../database"); var Config = require("../config"); +var url = require("url"); /** * Processes a login request. Sets a cookie upon successful authentication @@ -40,14 +41,16 @@ function handleLogin(req, res) { loginError: err }); } else { - cookieall(res, "auth", user.name + ":" + user.hash, { + var auth = user.name + ":" + user.hash; + res.cookie("auth", auth, { + domain: Config.get("http.root-domain"), expires: new Date(Date.now() + 7*24*60*60*1000), httpOnly: true }); - cookieall(res, "rank", user.global_rank, { + res.cookie("rank", user.global_rank, { + domain: "." + Config.get("http.root-domain"), expires: new Date(Date.now() + 7*24*60*60*1000), - httpOnly: true }); // Try to find an appropriate redirect @@ -60,6 +63,25 @@ function handleLogin(req, res) { ref = ""; } + // Redirect to shim cookie layer if the host doesn't match + try { + var data = url.parse(ref); + if (data.host.indexOf(Config.get("http.root-domain")) === -1) { + var host = data.host.replace(/:\d+$/, ""); + if (Config.get("http.alt-domains").indexOf(host) === -1) { + Logger.syslog.log("WARNING: Attempted login from non-approved "+ + "domain " + host); + } else { + var dest = "/shimcookie?auth=" + encodeURIComponent(auth) + + "&rank=" + encodeURIComponent(user.global_rank) + + "&redirect=" + encodeURIComponent(ref); + res.redirect(data.protocol + "//" + data.host + dest); + return; + } + } + } catch (e) { + } + if (ref.match(/login|logout/)) { ref = ""; } @@ -76,6 +98,63 @@ function handleLogin(req, res) { }); } +function handleShimCookie(req, res) { + var auth = req.query.auth; + var rank = req.query.rank; + var redirect = req.query.redirect; + if (typeof auth !== "string" || typeof redirect !== "string" || + typeof rank !== "string") { + res.send(400); + return; + } + + res.cookie("auth", auth, { + expires: new Date(Date.now() + 7*24*60*60*1000), + httpOnly: true + }); + + res.cookie("rank", rank, { + expires: new Date(Date.now() + 7*24*60*60*1000), + }); + + if (redirect.match(/login|logout/)) { + redirect = ""; + } + + if (redirect) { + res.redirect(redirect); + } else { + sendJade(res, "login", { + loggedIn: true, + loginName: auth.split(":")[0] + }); + } +} + +function handleShimLogout(req, res) { + var redirect = req.query.redirect; + if (typeof redirect !== "string") { + res.send(400); + return; + } + + res.clearCookie("auth"); + res.clearCookie("rank"); + res.clearCookie("auth", { domain: "." + Config.get("http.root-domain") }); + res.clearCookie("rank", { domain: "." + Config.get("http.root-domain") }); + + + if (redirect.match(/login|logout/)) { + redirect = ""; + } + + if (redirect) { + res.redirect(redirect); + } else { + sendJade(res, "logout", {}); + } +} + /** * Handles a GET request for /login */ @@ -106,17 +185,28 @@ function handleLoginPage(req, res) { */ function handleLogout(req, res) { res.clearCookie("auth"); - res.clearCookie("auth", { domain: Config.get("http.root-domain") }); + res.clearCookie("rank"); // Try to find an appropriate redirect var ref = req.header("referrer"); if (!ref) { - ref = req.body.redirect; + ref = req.query.redirect; } if (typeof ref !== "string") { ref = ""; } + var host = req.host; + if (host.indexOf(Config.get("http.root-domain")) !== -1) { + res.clearCookie("auth", { domain: Config.get("http.root-domain") }); + res.clearCookie("rank", { domain: Config.get("http.root-domain") }); + } else { + var dest = Config.get("http.full-address") + "/shimlogout?redirect=" + + encodeURIComponent(ref); + res.redirect(dest); + return; + } + if (ref.match(/login|logout/)) { ref = ""; } @@ -227,5 +317,7 @@ module.exports = { app.get("/logout", handleLogout); app.get("/register", handleRegisterPage); app.post("/register", handleRegister); + app.get("/shimcookie", handleShimCookie); + app.get("/shimlogout", handleShimLogout); } }; diff --git a/lib/web/webserver.js b/lib/web/webserver.js index 6e9f4635..79ac44ae 100644 --- a/lib/web/webserver.js +++ b/lib/web/webserver.js @@ -62,12 +62,6 @@ function logRequest(req, status) { ].join(" ")); } -function cookieall(res, name, val, opts) { - res.cookie(name, val, opts); - opts.domain = Config.get("http.root-domain"); - res.cookie(name, val, opts); -} - /** * Redirects a request to HTTPS if the server supports it */ @@ -296,6 +290,4 @@ module.exports = { redirectHttps: redirectHttps, redirectHttp: redirectHttp, - - cookieall: cookieall };