329 lines
8.8 KiB
JavaScript
329 lines
8.8 KiB
JavaScript
/**
|
|
* web/auth.js - Webserver functions for user authentication and registration
|
|
*
|
|
* @author Calvin Montgomery <cyzon@cyzon.us>
|
|
*/
|
|
|
|
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.host;
|
|
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);
|
|
}
|
|
};
|