/** * web/auth.js - Webserver functions for user authentication and registration * * @author Calvin Montgomery */ var jade = require("jade"); var fs = require("fs"); var path = require("path"); var webserver = require("./webserver"); var cookieall = webserver.cookieall; var sendJade = require("./jade").sendJade; 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 */ function handleLogin(req, res) { var name = req.body.name; var password = req.body.password; if (typeof name !== "string" || typeof password !== "string") { res.send(400); return; } password = password.substring(0, 100); db.users.verifyLogin(name, password, function (err, user) { if (err) { if (err === "Invalid username/password combination") { Logger.eventlog.log("[loginfail] Login failed (bad password): " + name + "@" + webserver.ipForRequest(req)); } sendJade(res, "login", { loggedIn: false, loginError: err }); } else { var auth = user.name + ":" + user.hash; res.cookie("auth", auth, { expires: new Date(Date.now() + 7*24*60*60*1000), httpOnly: true }); res.cookie("auth", auth, { domain: Config.get("http.root-domain-dotted"), expires: new Date(Date.now() + 7*24*60*60*1000), httpOnly: true }); res.cookie("rank", user.global_rank, { domain: Config.get("http.root-domain-dotted"), expires: new Date(Date.now() + 7*24*60*60*1000), }); // Try to find an appropriate redirect var ref = req.header("referrer"); if (!ref) { ref = req.body.redirect; } if (typeof ref !== "string") { 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 = ""; } if (ref) { res.redirect(ref); } else { sendJade(res, "login", { loggedIn: true, loginName: user.name }); } } }); } 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-dotted") }); res.clearCookie("rank", { domain: Config.get("http.root-domain-dotted") }); if (redirect.match(/login|logout/)) { redirect = ""; } if (redirect) { res.redirect(redirect); } else { sendJade(res, "logout", {}); } } /** * Handles a GET request for /login */ function handleLoginPage(req, res) { if (webserver.redirectHttps(req, res)) { return; } if (req.cookies.auth) { var split = req.cookies.auth.split(":"); if (split.length === 2) { sendJade(res, "login", { wasAlreadyLoggedIn: true, loggedIn: true, loginName: split[0] }); return; } } sendJade(res, "login", { loggedIn: false, redirect: req.header("Referrer") }); } /** * Handles a request for /logout. Clears auth cookie */ function handleLogout(req, res) { res.clearCookie("auth"); res.clearCookie("rank"); // Try to find an appropriate redirect var ref = req.header("referrer"); if (!ref) { ref = req.query.redirect; } if (typeof ref !== "string") { ref = ""; } var host = req.hostname; if (host.indexOf(Config.get("http.root-domain")) !== -1) { res.clearCookie("auth", { domain: Config.get("http.root-domain-dotted") }); res.clearCookie("rank", { domain: Config.get("http.root-domain-dotted") }); } else { var dest = Config.get("http.full-address") + "/shimlogout?redirect=" + encodeURIComponent(ref); res.redirect(dest); return; } if (ref.match(/login|logout/)) { ref = ""; } if (ref) { res.redirect(ref); } else { sendJade(res, "logout", {}); } } /** * Handles a GET request for /register */ function handleRegisterPage(req, res) { if (webserver.redirectHttps(req, res)) { return; } if (req.cookies.auth) { var split = req.cookies.auth.split(":"); if (split.length === 2) { sendJade(res, "register", { loggedIn: true, loginName: split[0] }); return; } } sendJade(res, "register", { registered: false, registerError: false }); } /** * Processes a registration request. */ function handleRegister(req, res) { var name = req.body.name; var password = req.body.password; var email = req.body.email; if (typeof email !== "string") { email = ""; } var ip = webserver.ipForRequest(req); if (typeof name !== "string" || typeof password !== "string") { res.send(400); return; } if (name.length === 0) { sendJade(res, "register", { registerError: "Username must not be empty" }); return; } if (name.match(Config.get("reserved-names.usernames"))) { sendJade(res, "register", { registerError: "That username is reserved" }); return; } if (password.length === 0) { sendJade(res, "register", { registerError: "Password must not be empty" }); return; } password = password.substring(0, 100); if (email.length > 0 && !$util.isValidEmail(email)) { sendJade(res, "register", { registerError: "Invalid email address" }); return; } db.users.register(name, password, email, ip, function (err) { if (err) { sendJade(res, "register", { registerError: err }); } else { Logger.eventlog.log("[register] " + ip + " registered account: " + name + (email.length > 0 ? " <" + email + ">" : "")); sendJade(res, "register", { registered: true, registerName: name, redirect: req.body.redirect }); } }); } module.exports = { /** * Initializes auth callbacks */ init: function (app) { app.get("/login", handleLoginPage); app.post("/login", handleLogin); app.get("/logout", handleLogout); app.get("/register", handleRegisterPage); app.post("/register", handleRegister); app.get("/shimcookie", handleShimCookie); app.get("/shimlogout", handleShimLogout); } };