Work on banlist

This commit is contained in:
calzoneman 2014-01-11 23:55:52 -06:00
parent 501a22556a
commit 637ece4044
4 changed files with 273 additions and 184 deletions

View File

@ -97,8 +97,6 @@ function Channel(name) {
html: "" // Filtered MOTD text (XSS removed; \n replaced by <br>)
};
self.filters = DEFAULT_FILTERS;
self.ipbans = {};
self.namebans = {};
self.logger = new Logger.Logger(path.join(__dirname, "../chanlogs",
self.uniqueName + ".log"));
self.css = ""; // Up to 20KB of inline CSS
@ -442,19 +440,9 @@ Channel.prototype.getIPRank = function (ip, callback) {
*/
Channel.prototype.join = function (user, password) {
var self = this;
self.whenReady(function () {
if (self.opts.password !== false && user.rank < 2) {
if (password !== self.opts.password) {
user.socket.emit("needPassword", typeof password === "undefined");
return;
}
}
user.socket.emit("cancelNeedPassword");
var range = user.ip.replace(/(\d+)\.(\d+)\.(\d+)\.(\d+)/, "$1.$2.$3");
if (user.ip in self.ipbans || range in self.ipbans ||
user.name.toLowerCase() in self.namebans) {
user.kick("You're banned!");
var afterIPBanCheck = function () {
if (self.dead) {
return;
}
@ -467,27 +455,12 @@ Channel.prototype.join = function (user, password) {
self.sendUsercount(self.users);
user.whenLoggedIn(function () {
var lname = user.name.toLowerCase();
for (var i = 0; i < self.users.length; i++) {
if (self.users[i].name.toLowerCase() === lname && self.users[i] !== user) {
self.users[i].kick("Duplicate login");
}
}
self.getRank(user.name, function (err, rank) {
if (self.dead) {
return;
}
if (err) {
user.rank = user.global_rank;
db.channels.isNameBanned(self.name, user.name, function (err, banned) {
if (!err && banned) {
user.kick("You're banned!");
} else {
user.rank = Math.max(rank, user.global_rank);
afterLogin();
}
user.socket.emit("rank", user.rank);
self.sendUserJoin(self.users, user);
self.sendUserlist([user]);
});
});
@ -505,6 +478,50 @@ Channel.prototype.join = function (user, password) {
self.logger.log("+++ " + user.ip + " joined");
Logger.syslog.log(user.ip + " joined channel " + self.name);
};
var afterLogin = function () {
if (self.dead) {
return;
}
var lname = user.name.toLowerCase();
for (var i = 0; i < self.users.length; i++) {
if (self.users[i].name.toLowerCase() === lname && self.users[i] !== user) {
self.users[i].kick("Duplicate login");
}
}
self.getRank(user.name, function (err, rank) {
if (err) {
user.rank = user.global_rank;
} else {
user.rank = Math.max(rank, user.global_rank);
}
user.socket.emit("rank", user.rank);
self.sendUserJoin(self.users, user);
self.sendUserlist([user]);
});
};
self.whenReady(function () {
if (self.opts.password !== false && user.rank < 2) {
if (password !== self.opts.password) {
user.socket.emit("needPassword", typeof password === "undefined");
return;
}
}
user.socket.emit("cancelNeedPassword");
db.channels.isIPBanned(self.name, user.ip, function (err, banned) {
if (!err && banned) {
user.kick("You're banned!");
return;
} else {
afterIPBanCheck();
}
});
});
};
@ -622,6 +639,13 @@ Channel.prototype.handleNameBan = function (actor, name, reason) {
return false;
}
if (!self.registered) {
actor.socket.emit("errorMsg", {
msg: "Banning is only supported in registered channels"
});
return;
}
name = name.toLowerCase();
if (name == actor.name.toLowerCase()) {
actor.socket.emit("costanza", {
@ -655,12 +679,6 @@ Channel.prototype.handleNameBan = function (actor, name, reason) {
}
reason = reason.substring(0, 255);
self.namebans[name] = {
ip: "*",
name: name,
bannedby: actor.name,
reason: reason
};
// If in the channel already, kick the banned user
for (var i = 0; i < self.users.length; i++) {
@ -672,13 +690,22 @@ Channel.prototype.handleNameBan = function (actor, name, reason) {
self.logger.log("*** " + actor.name + " namebanned " + name);
self.sendModMessage(actor.name + " banned " + name, self.permissions.ban);
if (!self.registered) {
return;
}
db.channels.isNameBanned(self.name, name, function (err, banned) {
if (!err && banned) {
actor.socket.emit("errorMsg", {
msg: name + " is already banned"
});
return;
}
// channel, ip, name, reason, actor
db.channels.ban(self.name, "*", name, reason, actor.name);
// TODO send banlist?
if (self.dead) {
return;
}
// channel, ip, name, reason, actor
db.channels.ban(self.name, "*", name, reason, actor.name);
// TODO send banlist?
});
});
};
@ -690,18 +717,65 @@ Channel.prototype.handleUnbanName = function (actor, name) {
return;
}
delete this.namebans[name];
this.logger.log("*** " + actor.name + " un-namebanned " + name);
this.sendModMessage(actor.name + " unbanned " + name, this.permissions.ban);
if (!this.registered) {
return;
}
this.logger.log("*** " + actor.name + " un-namebanned " + name);
this.sendModMessage(actor.name + " unbanned " + name, this.permissions.ban);
db.channels.unbanName(this.name, name);
// TODO send banlist?
};
/**
* Removes a ban by ID
*/
Channel.prototype.handleUnban = function (actor, data) {
var self = this;
if (!this.hasPermission(actor, "ban")) {
return;
}
if (typeof data.id !== "number") {
data.id = parseInt(data.id);
if (isNaN(data.id)) {
return;
}
}
data.actor = actor.name;
if (!self.registered) {
return;
}
db.channels.unbanId(self.name, data.id, function (err, res) {
if (err) {
actor.socket.emit("errorMsg", {
msg: err
});
return;
}
self.sendUnban(self.users, data);
});
};
/**
* Sends an unban packet
*/
Channel.prototype.sendUnban = function (users, data) {
var self = this;
users.forEach(function (u) {
if (self.hasPermission(u, "ban")) {
u.socket.emit("banlistRemove", data);
}
});
self.logger.log("*** " + data.actor + " unbanned " + data.name);
self.sendModMessage(data.actor + " unbanned " + data.name, self.permissions.ban);
};
/**
* Bans all IP addresses associated with a username
*/
@ -715,6 +789,13 @@ Channel.prototype.handleBanAllIP = function (actor, name, reason, range) {
return;
}
if (!self.registered) {
actor.socket.emit("errorMsg", {
msg: "Banning is not supported for unregistered rooms"
});
return;
}
name = name.toLowerCase();
if (name === actor.name.toLowerCase()) {
actor.socket.emit("costanza", {
@ -774,13 +855,6 @@ Channel.prototype.banIP = function (actor, ip, name, reason, range) {
return;
}
self.ipbans[ip] = {
ip: ip,
name: name,
bannedby: actor.name,
reason: reason
};
self.logger.log("*** " + actor.name + " banned " + ip + " (" + name + ")");
self.sendModMessage(actor.name + " banned " + ip + " (" + name + ")", self.permissions.ban);
// If in the channel already, kick the banned user
@ -795,30 +869,25 @@ Channel.prototype.banIP = function (actor, ip, name, reason, range) {
return;
}
// channel, ip, name, reason, ban actor
db.channels.ban(self.name, ip, name, reason, actor.name);
db.channels.isIPBanned(self.name, ip, function (err, banned) {
if (!err && banned) {
var disp = actor.global_rank >= 255 ? ip : util.maskIP(ip);
actor.socket.emit("errorMsg", {
msg: disp + " is alraedy banned"
});
return;
}
if (self.dead) {
return;
}
// channel, ip, name, reason, ban actor
db.channels.ban(self.name, ip, name, reason, actor.name);
});
});
};
/**
* Removes an IP ban
*/
Channel.prototype.handleUnbanIP = function (actor, ip) {
if (!this.hasPermission(actor, "ban")) {
return;
}
var record = this.ipbans[ip];
delete this.ipbans[ip];
this.logger.log("*** " + actor.name + " unbanned " + ip + " (" + record.name + ")");
this.sendModMessage(actor.name + " unbanned " + util.maskIP(ip) + " (" + record.name + ")", this.permissions.ban);
if (!this.registered) {
return;
}
db.channels.unbanIP(this.name, ip);
};
/**
* Sends the banlist
@ -828,46 +897,41 @@ Channel.prototype.sendBanlist = function (users) {
var bans = [];
var unmaskedbans = [];
for (var ip in self.ipbans) {
bans.push({
ip: util.maskIP(ip),
name: self.ipbans[ip].name,
reason: self.ipbans[ip].reason,
bannedby: self.ipbans[ip].bannedby
});
unmaskedbans.push({
ip: ip,
name: self.ipbans[ip].name,
reason: self.ipbans[ip].reason,
bannedby: self.ipbans[ip].bannedby
});
}
for (var name in self.namebans) {
bans.push({
ip: "*",
name: name,
reason: self.namebans[name].reason,
bannedby: self.namebans[name].bannedby
});
unmaskedbans.push({
ip: "*",
name: name,
reason: self.namebans[name].reason,
bannedby: self.namebans[name].bannedby
});
}
users.forEach(function (u) {
if (!self.hasPermission(u, "ban")) {
db.channels.listBans(self.name, function (err, banlist) {
if (err) {
return;
}
if (u.rank >= 255) {
u.socket.emit("banlist", unmaskedbans);
} else {
u.socket.emit("banlist", bans);
console.log(banlist);
for (var i = 0; i < banlist.length; i++) {
bans.push({
id: banlist[i].id,
ip: banlist[i].ip === "*" ? "*" : util.maskIP(banlist[i].ip),
name: banlist[i].name,
reason: banlist[i].reason,
bannedby: banlist[i].bannedby
});
unmaskedbans.push({
id: banlist[i].id,
ip: banlist[i].ip,
name: banlist[i].name,
reason: banlist[i].reason,
bannedby: banlist[i].bannedby
});
}
users.forEach(function (u) {
if (!self.hasPermission(u, "ban")) {
return;
}
if (u.rank >= 255) {
u.socket.emit("banlist", unmaskedbans);
} else {
u.socket.emit("banlist", bans);
}
});
});
};
@ -1122,11 +1186,6 @@ Channel.prototype.sendUserJoin = function (users, user) {
user.meta.aliases = aliases;
if (user.name.toLowerCase() in self.namebans) {
user.kick("You're banned!");
return;
}
if (self.isShadowMuted(user.name)) {
user.meta.muted = true;
user.meta.shadowmuted = true;

View File

@ -27,11 +27,12 @@ function createLibraryTable(name, callback) {
function createBansTable(name, callback) {
db.query("CREATE TABLE `chan_" + name + "_bans` (" +
"`id` INT NOT NULL AUTO_INCREMENT," +
"`ip` VARCHAR(39) NOT NULL," +
"`name` VARCHAR(20) NOT NULL," +
"`bannedby` VARCHAR(20) NOT NULL," +
"`reason` VARCHAR(255) NOT NULL," +
"PRIMARY KEY (`ip`, `name`))" +
"PRIMARY KEY (`id`), UNIQUE (`name`, `ip`))" +
"CHARACTER SET utf8", callback);
}
@ -311,31 +312,8 @@ module.exports = {
chan.name = res[0].name;
chan.canonical_name = chan.name.toLowerCase();
chan.registered = true;
// Load bans
db.query("SELECT * FROM `chan_" + chan.name + "_bans`", function (err, rows) {
if (chan.dead) {
callback("Channel is dead", null);
return;
}
if (err) {
callback(err, null);
return;
}
for (var i = 0; i < rows.length; i++) {
var r = rows[i];
if (r.ip === "*") {
chan.namebans[r.name] = r;
} else {
chan.ipbans[r.ip] = r;
}
}
chan.logger.log("*** Loaded channel from database");
callback(null, true);
});
chan.logger.log("*** Loaded channel from database");
callback(null, true);
});
},
@ -557,6 +535,60 @@ module.exports = {
"VALUES (?, ?, ?, ?)", [ip, name, note, bannedby], callback);
},
/**
* Check if an IP address or range is banned
*/
isIPBanned: function (chan, ip, callback) {
if (typeof callback !== "function") {
return;
}
if (!valid(chan)) {
callback("Invalid channel name", null);
return;
}
db.query("SELECT * FROM `chan_" + chan + "_bans` WHERE ip LIKE ?", [ip+"%"],
function (err, rows) {
callback(err, err ? false : rows.length > 0);
});
},
/**
* Check if a username is banned
*/
isNameBanned: function (chan, name, callback) {
if (typeof callback !== "function") {
return;
}
if (!valid(chan)) {
callback("Invalid channel name", null);
return;
}
db.query("SELECT * FROM `chan_" + chan + "_bans` WHERE name=?", [name],
function (err, rows) {
callback(err, err ? false : rows.length > 0);
});
},
/**
* Lists all bans
*/
listBans: function (chan, callback) {
if (typeof callback !== "function") {
return;
}
if (!valid(chan)) {
callback("Invalid channel name", null);
return;
}
db.query("SELECT * FROM `chan_" + chan + "_bans` WHERE 1", callback);
},
/**
* Removes a ban from the banlist
*/
@ -589,5 +621,22 @@ module.exports = {
db.query("DELETE FROM `chan_" + chan + "_bans` WHERE ip=?",
[ip], callback);
},
/**
* Removes a ban from the banlist
*/
unbanId: function (chan, id, callback) {
if (typeof callback !== "function") {
callback = blackHole;
}
if (!valid(chan)) {
callback("Invalid channel name", null);
return;
}
db.query("DELETE FROM `chan_" + chan + "_bans` WHERE id=?",
[id], callback);
}
};

View File

@ -227,6 +227,10 @@ User.prototype.initChannelCallbacks = function () {
self.channel.handleSetRank(self, data);
});
wrapTypecheck("unban", function (data) {
self.channel.handleUnban(self, data);
});
wrapTypecheck("chatMsg", function (data) {
if (typeof data.msg !== "string") {
return;

View File

@ -169,40 +169,11 @@ function addUserDropdown(entry) {
$("<strong/>").text(name).appendTo(menu);
$("<br/>").appendTo(menu);
/* rank selector (admin+ only)
to prevent odd behaviour, this selector is only visible
when the selected user has a normal rank (e.g. not a guest
or a non-moderator leader
*/
if(CLIENT.rank >= 3 && CLIENT.rank > rank && rank > 0 && rank != 1.5) {
var sel = $("<select/>")
.addClass("form-control")
.appendTo(menu);
$("<option/>").attr("value", "1").text("Regular User")
.appendTo(sel);
$("<option/>").attr("value", "2").text("Moderator")
.appendTo(sel);
if(CLIENT.rank > 3) {
$("<option/>").attr("value", "3").text("Channel Admin")
.appendTo(sel);
if(rank > 3) {
$("<option/>").attr("value", ""+rank)
.text("Current Rank (" + rank + ")")
.appendTo(sel);
}
}
sel.change(function () {
socket.emit("setChannelRank", {
user: name,
rank: parseInt(sel.val())
});
});
sel.val(""+rank);
}
var btngroup = $("<div/>").addClass("btn-group-vertical").appendTo(menu);
/* ignore button */
var ignore = $("<button/>").addClass("btn btn-xs btn-default btn-block")
.appendTo(menu)
var ignore = $("<button/>").addClass("btn btn-xs btn-default")
.appendTo(btngroup)
.click(function () {
if(IGNORED.indexOf(name) == -1) {
ignore.text("Unignore User");
@ -220,8 +191,8 @@ function addUserDropdown(entry) {
/* gib/remove leader (moderator+ only) */
if(CLIENT.rank >= 2) {
var ldr = $("<button/>").addClass("btn btn-xs btn-default btn-block")
.appendTo(menu);
var ldr = $("<button/>").addClass("btn btn-xs btn-default")
.appendTo(btngroup);
if(leader) {
ldr.text("Remove Leader");
ldr.click(function () {
@ -241,34 +212,34 @@ function addUserDropdown(entry) {
/* kick button */
if(hasPermission("kick")) {
$("<button/>").addClass("btn btn-xs btn-default btn-block")
$("<button/>").addClass("btn btn-xs btn-default")
.text("Kick")
.click(function () {
socket.emit("chatMsg", {
msg: "/kick " + name
});
})
.appendTo(menu);
.appendTo(btngroup);
}
/* ban buttons */
if(hasPermission("ban")) {
$("<button/>").addClass("btn btn-xs btn-default btn-block")
$("<button/>").addClass("btn btn-xs btn-default")
.text("Name Ban")
.click(function () {
socket.emit("chatMsg", {
msg: "/ban " + name
});
})
.appendTo(menu);
$("<button/>").addClass("btn btn-xs btn-default btn-block")
.appendTo(btngroup);
$("<button/>").addClass("btn btn-xs btn-default")
.text("IP Ban")
.click(function () {
socket.emit("chatMsg", {
msg: "/ipban " + name
});
})
.appendTo(menu);
.appendTo(btngroup);
}
entry.contextmenu(function(ev) {
@ -1798,6 +1769,12 @@ function formatCSBanlist() {
}
var unban = $("<button/>").addClass("btn btn-xs btn-danger")
.appendTo($("<td/>").appendTo(tr));
unban.click(function () {
socket.emit("unban", {
id: entry.id,
name: entry.name
});
});
$("<span/>").addClass("glyphicon glyphicon-remove-circle").appendTo(unban);
$("<td/>").text(entry.ip).appendTo(tr);
$("<td/>").text(entry.name).appendTo(tr);