mirror of https://github.com/calzoneman/sync.git
Begin the long process of refactoring the database
This commit is contained in:
parent
353205b63a
commit
a821498298
|
@ -7,3 +7,4 @@ node_modules
|
|||
*.crt
|
||||
*.cert
|
||||
*.key
|
||||
torlist
|
||||
|
|
|
@ -57,7 +57,7 @@ module.exports = {
|
|||
});
|
||||
|
||||
user.socket.on("acp-lookup-user", function(name) {
|
||||
db.searchUser(name, function (err, res) {
|
||||
db.users.search(name, ["name", "email", "global_rank"], function (err, res) {
|
||||
res = res || [];
|
||||
user.socket.emit("acp-userdata", res);
|
||||
});
|
||||
|
@ -71,7 +71,7 @@ module.exports = {
|
|||
});
|
||||
|
||||
user.socket.on("acp-reset-password", function(data) {
|
||||
db.getGlobalRank(data.name, function (err, rank) {
|
||||
db.users.getGlobalRank(data.name, function (err, rank) {
|
||||
if(err || rank >= user.global_rank)
|
||||
return;
|
||||
|
||||
|
@ -98,11 +98,11 @@ module.exports = {
|
|||
if(data.rank < 1 || data.rank >= user.global_rank)
|
||||
return;
|
||||
|
||||
db.getGlobalRank(data.name, function (err, rank) {
|
||||
db.users.getGlobalRank(data.name, function (err, rank) {
|
||||
if(err || rank >= user.global_rank)
|
||||
return;
|
||||
|
||||
db.setGlobalRank(data.name, data.rank,
|
||||
db.users.setGlobalRank(data.name, data.rank,
|
||||
function (err, res) {
|
||||
ActionLog.record(user.ip, user.name, "acp-set-rank",
|
||||
data);
|
||||
|
|
53
lib/api.js
53
lib/api.js
|
@ -87,7 +87,7 @@ module.exports = function (Server) {
|
|||
if (pw !== needPassword) {
|
||||
var uname = req.cookies.cytube_uname;
|
||||
var session = req.cookies.cytube_session;
|
||||
Server.db.userLoginSession(uname, session, function (err, row) {
|
||||
Server.db.users.verifyAuth(uname + ":" + session, function (err, row) {
|
||||
if (err) {
|
||||
res.status(403);
|
||||
res.type("application/json");
|
||||
|
@ -135,7 +135,7 @@ module.exports = function (Server) {
|
|||
if(filter !== "public") {
|
||||
var name = query.name || "";
|
||||
var session = query.session || "";
|
||||
db.userLoginSession(name, session, function (err, row) {
|
||||
db.users.verifyAuth(name + ":" + session, function (err, row) {
|
||||
if(err) {
|
||||
if(err !== "Invalid session" &&
|
||||
err !== "Session expired") {
|
||||
|
@ -197,7 +197,7 @@ module.exports = function (Server) {
|
|||
return;
|
||||
}
|
||||
|
||||
db.userLogin(name, pw, session, function (err, row) {
|
||||
var callback = function (err, row) {
|
||||
if(err) {
|
||||
if(err !== "Session expired")
|
||||
ActionLog.record(getIP(req), name, "login-failure", err);
|
||||
|
@ -215,9 +215,15 @@ module.exports = function (Server) {
|
|||
res.jsonp({
|
||||
success: true,
|
||||
name: name,
|
||||
session: row.session_hash
|
||||
session: row.hash
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
if (session) {
|
||||
db.users.verifyAuth(name + ":" + session, callback);
|
||||
} else {
|
||||
db.users.verifyLogin(name, pw, callback);
|
||||
}
|
||||
});
|
||||
|
||||
/* register an account */
|
||||
|
@ -268,21 +274,8 @@ module.exports = function (Server) {
|
|||
return;
|
||||
}
|
||||
|
||||
|
||||
if(!$util.isValidUserName(name)) {
|
||||
ActionLog.record(ip, name, "register-failure",
|
||||
"Invalid name");
|
||||
res.jsonp({
|
||||
success: false,
|
||||
error: "Invalid username. Valid usernames must be " +
|
||||
"1-20 characters long and consist only of " +
|
||||
"alphanumeric characters and underscores (_)"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// db.registerUser checks if the name is taken already
|
||||
db.registerUser(name, pw, function (err, session) {
|
||||
db.users.register(name, pw, "", req.ip, function (err, session) {
|
||||
if(err) {
|
||||
res.jsonp({
|
||||
success: false,
|
||||
|
@ -328,7 +321,7 @@ module.exports = function (Server) {
|
|||
return;
|
||||
}
|
||||
|
||||
db.userLoginPassword(name, oldpw, function (err, row) {
|
||||
db.users.verifyLogin(name, oldpw, function (err, row) {
|
||||
if(err) {
|
||||
res.jsonp({
|
||||
success: false,
|
||||
|
@ -337,7 +330,7 @@ module.exports = function (Server) {
|
|||
return;
|
||||
}
|
||||
|
||||
db.setUserPassword(name, newpw, function (err, row) {
|
||||
db.users.setPassword(name, newpw, function (err, row) {
|
||||
if(err) {
|
||||
res.jsonp({
|
||||
success: false,
|
||||
|
@ -514,7 +507,7 @@ module.exports = function (Server) {
|
|||
text = text.substring(0, 255);
|
||||
}
|
||||
|
||||
db.userLoginSession(name, session, function (err, row) {
|
||||
db.verifyAuth(name + ":" + session, function (err, row) {
|
||||
if(err) {
|
||||
res.jsonp({
|
||||
success: false,
|
||||
|
@ -590,7 +583,7 @@ module.exports = function (Server) {
|
|||
return;
|
||||
}
|
||||
|
||||
db.userLoginPassword(name, pw, function (err, row) {
|
||||
db.users.verifyLogin(name, pw, function (err, row) {
|
||||
if(err) {
|
||||
res.jsonp({
|
||||
success: false,
|
||||
|
@ -599,7 +592,7 @@ module.exports = function (Server) {
|
|||
return;
|
||||
}
|
||||
|
||||
db.setUserEmail(name, email, function (err, dbres) {
|
||||
db.users.setEmail(name, email, function (err, dbres) {
|
||||
if(err) {
|
||||
res.jsonp({
|
||||
success: false,
|
||||
|
@ -611,7 +604,7 @@ module.exports = function (Server) {
|
|||
ActionLog.record(getIP(req), name, "email-update", email);
|
||||
res.jsonp({
|
||||
success: true,
|
||||
session: row.session_hash
|
||||
session: row.hash
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -633,7 +626,7 @@ module.exports = function (Server) {
|
|||
return;
|
||||
}
|
||||
|
||||
db.userLoginSession(name, session, function (err, row) {
|
||||
db.users.verifyAuth(name + ":" + session, function (err, row) {
|
||||
if(err) {
|
||||
res.jsonp({
|
||||
success: false,
|
||||
|
@ -682,7 +675,7 @@ module.exports = function (Server) {
|
|||
return;
|
||||
}
|
||||
|
||||
db.userLoginSession(name, session, function (err, row) {
|
||||
db.verifyAuth(name + ":" + session, function (err, row) {
|
||||
if(err) {
|
||||
if(err !== "Invalid session" &&
|
||||
err !== "Session expired") {
|
||||
|
@ -743,7 +736,7 @@ module.exports = function (Server) {
|
|||
return;
|
||||
}
|
||||
|
||||
db.userLoginSession(name, session, function (err, row) {
|
||||
db.users.verifyAuth(name + ":" + session, function (err, row) {
|
||||
if(err) {
|
||||
if(err !== "Invalid session" &&
|
||||
err !== "Session expired") {
|
||||
|
@ -780,7 +773,7 @@ module.exports = function (Server) {
|
|||
return;
|
||||
}
|
||||
|
||||
db.userLoginSession(name, session, function (err, row) {
|
||||
db.users.verifyAuth(name + ":" + session, function (err, row) {
|
||||
if(err) {
|
||||
if(err !== "Invalid session" &&
|
||||
err !== "Session expired") {
|
||||
|
@ -817,7 +810,7 @@ module.exports = function (Server) {
|
|||
return;
|
||||
}
|
||||
|
||||
db.userLoginSession(name, session, function (err, row) {
|
||||
db.users.verifyAuth(name + ":" + session, function (err, row) {
|
||||
if(err) {
|
||||
if(err !== "Invalid session" &&
|
||||
err !== "Session expired") {
|
||||
|
|
|
@ -26,6 +26,8 @@ var Database = function (cfg) {
|
|||
});
|
||||
|
||||
self.global_ipbans = {};
|
||||
self.users = require("./database/accounts")(self);
|
||||
self.users.init();
|
||||
};
|
||||
|
||||
Database.prototype.query = function (query, sub, callback) {
|
||||
|
|
|
@ -0,0 +1,439 @@
|
|||
//var db = require("../database");
|
||||
var $util = require("../utilities");
|
||||
var bcrypt = require("bcrypt");
|
||||
|
||||
var registrationLock = {};
|
||||
var blackHole = function () { };
|
||||
|
||||
module.exports = function (db) {
|
||||
return {
|
||||
/**
|
||||
* Initialize the accounts table
|
||||
*/
|
||||
init: function () {
|
||||
db.query("CREATE TABLE IF NOT EXISTS `users` (" +
|
||||
"`id` INT NOT NULL AUTO_INCREMENT," +
|
||||
"`name` VARCHAR(20) NOT NULL," +
|
||||
"`password` VARCHAR(64) NOT NULL," +
|
||||
"`global_rank` INT NOT NULL," +
|
||||
"`email` VARCHAR(255) NOT NULL," +
|
||||
"`ip` VARCHAR(39) NOT NULL," +
|
||||
"`time` BIGINT NOT NULL, " +
|
||||
"PRIMARY KEY(`id`), INDEX(`name`)) " +
|
||||
"CHARACTER SET utf8");
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if a username is taken
|
||||
*/
|
||||
isUsernameTaken: function (name, callback) {
|
||||
db.query("SELECT name FROM `users` WHERE name=?", [name],
|
||||
function (err, rows) {
|
||||
if (err) {
|
||||
callback(err, true);
|
||||
return;
|
||||
}
|
||||
callback(null, rows.length > 0);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Search for a user by name
|
||||
*/
|
||||
search: function (name, fields, callback) {
|
||||
/* This bit allows it to accept varargs
|
||||
Function can be called as (name, callback) or
|
||||
(name, fields, callback)
|
||||
*/
|
||||
if (typeof callback !== "function") {
|
||||
if (typeof fields === "function") {
|
||||
callback = fields;
|
||||
fields = ["name"];
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Don't allow search to return password hashes
|
||||
if (fields.indexOf("password") !== -1) {
|
||||
fields.splice(fields.indexOf("password"));
|
||||
}
|
||||
|
||||
db.query("SELECT " + fields.join(",") + " FROM `users` WHERE name LIKE ?",
|
||||
["%"+name+"%"],
|
||||
function (err, rows) {
|
||||
if (err) {
|
||||
callback(err, true);
|
||||
return;
|
||||
}
|
||||
callback(null, rows);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Registers a new user account
|
||||
*/
|
||||
register: function (name, pw, email, ip, callback) {
|
||||
// Start off with a boatload of error checking
|
||||
if (typeof callback !== "function") {
|
||||
callback = blackHole;
|
||||
}
|
||||
|
||||
if (typeof name !== "string" || typeof pw !== "string") {
|
||||
callback(new Error("You must provide a nonempty username and password"), null);
|
||||
return;
|
||||
}
|
||||
var lname = name.toLowerCase();
|
||||
|
||||
if (registrationLock[lname]) {
|
||||
callback(new Error("There is already a registration in progress for "+name),
|
||||
null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$util.isValidUserName(name)) {
|
||||
callback(new Error("Invalid username. Usernames may consist of 1-20 " +
|
||||
"characters a-z, A-Z, 0-9, -, _, and accented letters."),
|
||||
null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof email !== "string") {
|
||||
email = "";
|
||||
}
|
||||
|
||||
if (typeof ip !== "string") {
|
||||
ip = "";
|
||||
}
|
||||
|
||||
// From this point forward, actual registration happens
|
||||
// registrationLock prevents concurrent database activity
|
||||
// on the same user account
|
||||
registrationLock[lname] = true;
|
||||
|
||||
this.isUsernameTaken(name, function (err, taken) {
|
||||
if (err) {
|
||||
delete registrationLock[lname];
|
||||
callback(err, null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (taken) {
|
||||
delete registrationLock[lname];
|
||||
callback(new Error("Username is already registered"), null);
|
||||
return;
|
||||
}
|
||||
|
||||
bcrypt.hash(pw, 10, function (err, hash) {
|
||||
if (err) {
|
||||
delete registrationLock[lname];
|
||||
callback(err, null);
|
||||
return;
|
||||
}
|
||||
|
||||
db.query("INSERT INTO `users` " +
|
||||
"(`name`, `password`, `global_rank`, `email`, `ip`, `time`)" +
|
||||
" VALUES " +
|
||||
"(?, ?, ?, ?, ?, ?)",
|
||||
[name, hash, 1, email, ip, Date.now()],
|
||||
function (err, res) {
|
||||
delete registrationLock[lname];
|
||||
if (err) {
|
||||
callback(err, null);
|
||||
} else {
|
||||
callback(null, {
|
||||
name: name,
|
||||
hash: hash
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Verify a username/password pair
|
||||
*/
|
||||
verifyLogin: function (name, pw, callback) {
|
||||
if (typeof callback !== "function") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof name !== "string" || typeof pw !== "string") {
|
||||
callback(new Error("Invalid username/password combination"), null);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Passwords are capped at 100 characters to prevent a potential
|
||||
denial of service vector through causing the server to hash
|
||||
ridiculously long strings.
|
||||
*/
|
||||
pw = pw.substring(0, 100);
|
||||
|
||||
/* Note: rather than hash the password and then query based on name and
|
||||
password, I query by name, then use bcrypt.compare() to check that
|
||||
the hashes match.
|
||||
*/
|
||||
|
||||
db.query("SELECT name,password,global_rank FROM `users` WHERE name=?",
|
||||
[name],
|
||||
function (err, rows) {
|
||||
if (err) {
|
||||
callback(err, null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (rows.length === 0) {
|
||||
callback(new Error("User does not exist"), null);
|
||||
return;
|
||||
}
|
||||
|
||||
bcrypt.compare(pw, rows[0].password, function (err, match) {
|
||||
if (err) {
|
||||
callback(err, null);
|
||||
} else if (!match) {
|
||||
callback(new Error("Invalid username/password combination"), null);
|
||||
} else {
|
||||
callback(null, {
|
||||
name: rows[0].name,
|
||||
hash: rows[0].password,
|
||||
global_rank: rows[0].global_rank
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Verify an auth string of the form name:hash
|
||||
*/
|
||||
verifyAuth: function (auth, callback) {
|
||||
if (typeof callback !== "function") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof auth !== "string") {
|
||||
callback(new Error("Invalid auth string"), null);
|
||||
return;
|
||||
}
|
||||
|
||||
var split = auth.split(":");
|
||||
if (split.length !== 2) {
|
||||
callback(new Error("Invalid auth string"), null);
|
||||
return;
|
||||
}
|
||||
|
||||
var name = split[0];
|
||||
var hash = split[1];
|
||||
db.query("SELECT name,password,global_rank FROM `users` WHERE " +
|
||||
"name=? and password=?", [name, hash],
|
||||
function (err, rows) {
|
||||
if (err) {
|
||||
callback(err, null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (rows.length === 0) {
|
||||
callback(new Error("Auth string does not match an existing user"), null);
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null, {
|
||||
name: rows[0].name,
|
||||
hash: rows[0].password,
|
||||
global_rank: rows[0].global_rank
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Change a user's password
|
||||
*/
|
||||
setPassword: function (name, pw, callback) {
|
||||
if (typeof callback !== "function") {
|
||||
callback = blackHole;
|
||||
}
|
||||
|
||||
if (typeof name !== "string" || typeof pw !== "string") {
|
||||
callback(new Error("Invalid username/password combination"), null);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Passwords are capped at 100 characters to prevent a potential
|
||||
denial of service vector through causing the server to hash
|
||||
ridiculously long strings.
|
||||
*/
|
||||
pw = pw.substring(0, 100);
|
||||
|
||||
bcrypt.hash(pw, 10, function (err, hash) {
|
||||
if (err) {
|
||||
callback(err, null);
|
||||
return;
|
||||
}
|
||||
|
||||
db.query("UPDATE `users` SET password=? WHERE name=?",
|
||||
[hash, name],
|
||||
function (err, result) {
|
||||
callback(err, err ? null : true);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Lookup a user's global rank
|
||||
*/
|
||||
getGlobalRank: function (name, callback) {
|
||||
if (typeof callback !== "function") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof name !== "string") {
|
||||
callback(new Error("Invalid username"), null);
|
||||
return;
|
||||
}
|
||||
|
||||
db.query("SELECT global_rank FROM `users` WHERE name=?", [name],
|
||||
function (err, rows) {
|
||||
if (err) {
|
||||
callback(err, null);
|
||||
} else if (rows.length === 0) {
|
||||
callback(new Error("User does not exist"), null);
|
||||
} else {
|
||||
callback(null, rows[0].global_rank);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates a user's global rank
|
||||
*/
|
||||
setGlobalRank: function (name, rank, callback) {
|
||||
if (typeof callback !== "function") {
|
||||
callback = blackHole;
|
||||
}
|
||||
|
||||
if (typeof name !== "string") {
|
||||
callback(new Error("Invalid username"), null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof rank !== "number") {
|
||||
callback(new Error("Invalid rank"), null);
|
||||
return;
|
||||
}
|
||||
|
||||
db.query("UPDATE `users` SET global_rank=? WHERE name=?", [rank, name],
|
||||
function (err, result) {
|
||||
callback(err, err ? null : true);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Lookup multiple users' global rank in one query
|
||||
*/
|
||||
getGlobalRanks: function (names, callback) {
|
||||
if (typeof callback !== "function") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(names instanceof Array)) {
|
||||
callback(new Error("Expected array of names, got " + typeof names), null);
|
||||
return;
|
||||
}
|
||||
|
||||
var list = "(" + names.map(function () { return "?";}).join(",") + ")";
|
||||
|
||||
db.query("SELECT global_rank FROM `users` WHERE name IN " + list, names,
|
||||
function (err, rows) {
|
||||
if (err) {
|
||||
callback(err, null);
|
||||
} else if (rows.length === 0) {
|
||||
callback(null, []);
|
||||
} else {
|
||||
callback(null, rows.map(function (x) { return x.global_rank; }));
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Lookup a user's email
|
||||
*/
|
||||
getEmail: function (name, callback) {
|
||||
if (typeof callback !== "function") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof name !== "string") {
|
||||
callback(new Error("Invalid username"), null);
|
||||
return;
|
||||
}
|
||||
|
||||
db.query("SELECT email FROM `users` WHERE name=?", [name],
|
||||
function (err, rows) {
|
||||
if (err) {
|
||||
callback(err, null);
|
||||
} else if (rows.length === 0) {
|
||||
callback(new Error("User does not exist"), null);
|
||||
} else {
|
||||
callback(null, rows[0].email);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates a user's email
|
||||
*/
|
||||
setEmail: function (name, email, callback) {
|
||||
if (typeof callback !== "function") {
|
||||
callback = blackHole;
|
||||
}
|
||||
|
||||
if (typeof name !== "string") {
|
||||
callback(new Error("Invalid username"), null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof email !== "string") {
|
||||
callback(new Error("Invalid email"), null);
|
||||
return;
|
||||
}
|
||||
|
||||
db.query("UPDATE `users` SET email=? WHERE name=?", [email, name],
|
||||
function (err, result) {
|
||||
callback(err, err ? null : true);
|
||||
});
|
||||
},
|
||||
|
||||
getProfile: function (name, callback) {
|
||||
if (typeof callback !== "function") {
|
||||
return;
|
||||
}
|
||||
|
||||
callback(new Error("getProfile is not implemented"), null);
|
||||
},
|
||||
|
||||
setProfile: function (name, callback) {
|
||||
if (typeof callback !== "function") {
|
||||
return;
|
||||
}
|
||||
|
||||
callback(new Error("setProfile is not implemented"), null);
|
||||
},
|
||||
|
||||
generatePasswordReset: function (ip, name, email, callback) {
|
||||
if (typeof callback !== "function") {
|
||||
return;
|
||||
}
|
||||
|
||||
callback(new Error("generatePasswordReset is not implemented"), null);
|
||||
},
|
||||
|
||||
recoverPassword: function (hash, callback) {
|
||||
if (typeof callback !== "function") {
|
||||
return;
|
||||
}
|
||||
|
||||
callback(new Error("recoverPassword is not implemented"), null);
|
||||
},
|
||||
};
|
||||
};
|
|
@ -38,7 +38,7 @@ module.exports = {
|
|||
},
|
||||
|
||||
isValidUserName: function (name) {
|
||||
return name.match(/^[\w-_]{1,20}$/);
|
||||
return name.match(/^[-\w\u00c0-\u00ff]{1,20}$/);
|
||||
},
|
||||
|
||||
randomSalt: function (length) {
|
||||
|
|
|
@ -134,9 +134,9 @@ $("#registerbtn").click(function() {
|
|||
var pwc = $("#regpwconfirm").val();
|
||||
|
||||
var err = false;
|
||||
if(!name.match(/^[a-z0-9_]{1,20}$/i)) {
|
||||
if(!name.match(/^[-\w\u00c0-\u00ff]{1,20}$/i)) {
|
||||
$("<div/>").addClass("alert alert-error")
|
||||
.text("Usernames must be 1-20 characters long and contain only a-z, 0-9, and underscores")
|
||||
.text("Usernames must be 1-20 characters long and contain only a-z, A-Z, 0-9, -, _, and accented letters.")
|
||||
.insertAfter($("#regusername").parent().parent());
|
||||
err = true;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue