Add username bans, tweak ban interface

This commit is contained in:
calzoneman 2013-05-21 12:17:01 -04:00
parent 505c628eb9
commit f8b0b06caf
9 changed files with 311 additions and 73 deletions

View File

@ -68,6 +68,7 @@ var Channel = function(name) {
html: "" html: ""
}; };
this.ipbans = {}; this.ipbans = {};
this.namebans = {};
this.logins = {}; this.logins = {};
this.logger = new Logger.Logger("chanlogs/" + this.name + ".log"); this.logger = new Logger.Logger("chanlogs/" + this.name + ".log");
this.i = 0; this.i = 0;
@ -80,6 +81,11 @@ var Channel = function(name) {
this.css = ""; this.css = "";
this.js = ""; this.js = "";
this.ipkey = "";
for(var i = 0; i < 15; i++) {
this.ipkey += "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"[parseInt(Math.random() * 65)]
}
Database.loadChannel(this); Database.loadChannel(this);
if(this.registered) { if(this.registered) {
this.loadDump(); this.loadDump();
@ -290,6 +296,102 @@ Channel.prototype.cacheMedia = function(media) {
return false; return false;
} }
Channel.prototype.banName = function(actor, name) {
if(!Rank.hasPermission(actor, "ban")) {
return false;
}
name = name.toLowerCase();
var rank = this.getRank(name);
if(rank < 1) {
actor.socket.emit("errorMsg", {msg: "You can't ban guest names. Use a kick or IP ban."});
return false;
}
if(rank >= actor.rank) {
actor.socket.emit("errorMsg", {msg: "You don't have permission to ban this person."});
return false;
}
this.namebans[name] = actor.name;
for(var i = 0; i < this.users.length; i++) {
if(this.users[i].name.toLowerCase() == name) {
this.kick(this.users[i], "You're banned!");
break;
}
}
this.broadcastBanlist();
this.logger.log(name + " was banned by " + actor.name);
if(!this.registered) {
return false;
}
return Database.addChannelBan(this.name, actor, {
ip: "*",
name: name
});
}
Channel.prototype.unbanName = function(actor, name) {
if(!Rank.hasPermission(actor, "ban")) {
return false;
}
this.namebans[name] = null;
delete this.namebans[name];
this.broadcastBanlist();
this.logger.log(name + " was unbanned by " + actor.name);
return Database.removeNameBan(this.name, name);
}
Channel.prototype.tryIPBan = function(actor, data) {
if(!Rank.hasPermission(actor, "ipban")) {
return false;
}
if(typeof data.id != "string" || data.id.length != 15) {
return false;
}
if(typeof data.name != "string") {
return false;
}
var ip = this.hideIP(data.id);
if(this.getIPRank(ip) >= actor.rank) {
actor.socket.emit("errorMsg", {msg: "You don't have permission to ban this IP"});
return false;
}
if(data.range) {
ip = ip.replace(/(\d+)\.(\d+)\.(\d+)\.(\d+)/, "$1.$2.$3");
for(var ip2 in this.logins) {
if(ip2.indexOf(ip) == 0 && this.getIPRank(ip2) >= actor.rank) {
actor.socket.emit("errorMsg", {msg: "You don't have permission to ban this IP"});
return false;
}
}
}
this.ipbans[ip] = [data.name, actor.name];
this.broadcastBanlist();
this.logger.log(ip + " (" + data.name + ") was banned by " + actor.name);
for(var i = 0; i < this.users.length; i++) {
if(this.users[i].ip.indexOf(ip) == 0) {
this.kick(this.users[i], "Your IP is banned!");
i--;
}
}
if(!this.registered)
return false;
// Update database ban table
return Database.addChannelBan(this.name, actor, {
ip: ip,
name: data.name
});
}
Channel.prototype.banIP = function(actor, receiver) { Channel.prototype.banIP = function(actor, receiver) {
if(!Rank.hasPermission(actor, "ipban")) if(!Rank.hasPermission(actor, "ipban"))
return false; return false;
@ -315,6 +417,10 @@ Channel.prototype.unbanIP = function(actor, ip) {
if(!Rank.hasPermission(actor, "ipban")) if(!Rank.hasPermission(actor, "ipban"))
return false; return false;
if(this.getIPRank(ip) >= actor.rank) {
return false;
}
this.ipbans[ip] = null; this.ipbans[ip] = null;
if(!this.registered) if(!this.registered)
@ -325,6 +431,16 @@ Channel.prototype.unbanIP = function(actor, ip) {
return Database.removeChannelBan(this.name, ip); return Database.removeChannelBan(this.name, ip);
} }
Channel.prototype.tryUnban = function(actor, data) {
if(data.id) {
var ip = this.hideIP(data.id);
this.unbanIP(actor, ip);
}
else if(data.name) {
this.unbanName(actor, data.name);
}
}
Channel.prototype.search = function(query, callback) { Channel.prototype.search = function(query, callback) {
// Search youtube // Search youtube
if(callback) { if(callback) {
@ -369,6 +485,11 @@ Channel.prototype.userJoin = function(user) {
this.kick(user, "You're banned!"); this.kick(user, "You're banned!");
return; return;
} }
if(user.name && user.name.toLowerCase() in this.namebans &&
this.namebans[user.name.toLowerCase()] != null) {
this.kick(user, "You're banned!");
return;
}
// Join the socket pool for this channel // Join the socket pool for this channel
user.socket.join(this.name); user.socket.join(this.name);
@ -470,6 +591,21 @@ Channel.prototype.kick = function(user, reason) {
user.socket.disconnect(true); user.socket.disconnect(true);
} }
Channel.prototype.hideIP = function(ip) {
while(ip.length < 15) {
ip += "X";
}
var chars = new Array(15);
for(var i = 0; i < ip.length; i++) {
chars[i] = String.fromCharCode(ip.charCodeAt(i) ^ this.ipkey.charCodeAt(i));
if(chars[i] == "X") {
chars[i] = "";
break;
}
}
return chars.join("");
}
Channel.prototype.sendRankStuff = function(user) { Channel.prototype.sendRankStuff = function(user) {
if(Rank.hasPermission(user, "ipban")) { if(Rank.hasPermission(user, "ipban")) {
var ents = []; var ents = [];
@ -482,13 +618,28 @@ Channel.prototype.sendRankStuff = function(user) {
else { else {
name = this.ipbans[ip][0]; name = this.ipbans[ip][0];
} }
var id = this.hideIP(ip);
var disp = ip;
if(user.rank < Rank.Siteadmin) {
disp = "(Hidden)";
}
ents.push({ ents.push({
ip: ip, ip: disp,
id: id,
name: name, name: name,
banner: this.ipbans[ip][1] banner: this.ipbans[ip][1]
}); });
} }
} }
for(var name in this.namebans) {
if(this.namebans[name] != null) {
ents.push({
ip: "*",
name: name,
banner: this.namebans[name]
});
}
}
user.socket.emit("banlist", {entries: ents}); user.socket.emit("banlist", {entries: ents});
} }
if(Rank.hasPermission(user, "seenlogins")) { if(Rank.hasPermission(user, "seenlogins")) {
@ -496,11 +647,13 @@ Channel.prototype.sendRankStuff = function(user) {
for(var ip in this.logins) { for(var ip in this.logins) {
var disp = ip; var disp = ip;
if(user.rank < Rank.Siteadmin) { if(user.rank < Rank.Siteadmin) {
disp = "(Masked)"; disp = "(Hidden)";
} }
ents.push({ ents.push({
ip: disp, ip: disp,
name: this.logins[ip].join(",") id: this.hideIP(ip),
names: this.logins[ip],
banned: (ip in this.ipbans && this.ipbans[ip] != null)
}); });
} }
user.socket.emit("seenlogins", {entries: ents}); user.socket.emit("seenlogins", {entries: ents});
@ -591,6 +744,11 @@ Channel.prototype.broadcastNewUser = function(user) {
if(!this.seen(user.ip, user.name)) { if(!this.seen(user.ip, user.name)) {
this.logins[user.ip].push(user.name); this.logins[user.ip].push(user.name);
} }
if(user.name.toLowerCase() in this.namebans &&
this.namebans[user.name.toLowerCase()] != null) {
this.kick(user, "You're banned!");
return;
}
this.sendAll("addUser", { this.sendAll("addUser", {
name: user.name, name: user.name,
rank: user.rank, rank: user.rank,
@ -633,6 +791,7 @@ Channel.prototype.broadcastOpts = function() {
Channel.prototype.broadcastBanlist = function() { Channel.prototype.broadcastBanlist = function() {
var ents = []; var ents = [];
var adminents = [];
for(var ip in this.ipbans) { for(var ip in this.ipbans) {
if(this.ipbans[ip] != null) { if(this.ipbans[ip] != null) {
var name; var name;
@ -642,16 +801,43 @@ Channel.prototype.broadcastBanlist = function() {
else { else {
name = this.ipbans[ip][0]; name = this.ipbans[ip][0];
} }
var id = this.hideIP(ip);
ents.push({ ents.push({
ip: "(Hidden)",
id: id,
name: name,
banner: this.ipbans[ip][1]
});
adminents.push({
ip: ip, ip: ip,
id: id,
name: name, name: name,
banner: this.ipbans[ip][1] banner: this.ipbans[ip][1]
}); });
} }
} }
for(var name in this.namebans) {
if(this.namebans[name] != null) {
ents.push({
ip: "*",
name: name,
banner: this.namebans[name]
});
adminents.push({
ip: "*",
name: name,
banner: this.namebans[name]
});
}
}
for(var i = 0; i < this.users.length; i++) { for(var i = 0; i < this.users.length; i++) {
if(Rank.hasPermission(this.users[i], "ipban")) { if(Rank.hasPermission(this.users[i], "ipban")) {
this.users[i].socket.emit("banlist", {entries: ents}); if(this.users[i].rank >= Rank.Siteadmin) {
this.users[i].socket.emit("banlist", {entries: adminents});
}
else {
this.users[i].socket.emit("banlist", {entries: ents});
}
} }
} }
} }

View File

@ -38,9 +38,6 @@ function handle(chan, user, msg, data) {
else if(msg.indexOf("/ban ") == 0) { else if(msg.indexOf("/ban ") == 0) {
handleBan(chan, user, msg.substring(5).split(" ")); handleBan(chan, user, msg.substring(5).split(" "));
} }
else if(msg.indexOf("/ipban ") == 0) {
handleIpban(chan, user, msg.substring(7).split(" "));
}
else if(msg.indexOf("/unban ") == 0) { else if(msg.indexOf("/unban ") == 0) {
handleUnban(chan, user, msg.substring(7).split(" ")); handleUnban(chan, user, msg.substring(7).split(" "));
} }
@ -89,45 +86,6 @@ function handleBan(chan, user, args) {
chan.kick(kickee, "(banned) " + reason); chan.kick(kickee, "(banned) " + reason);
chan.banIP(user, kickee); chan.banIP(user, kickee);
} }
else if(!kickee && chan.getRank(args[0]) < user.rank) {
for(var ip in chan.logins) {
if(chan.getIPRank(ip) >= user.rank) {
return;
}
if(!chan.seen(ip, args[0])) {
continue;
}
if(args.length >= 2 && args[1] == "range") {
var parts = ip.split(".");
ip = parts[0] + "." + parts[1] + "." + parts[2];
}
chan.logger.log("*** " + user.name + " banned " + ip);
chan.banIP(user, {
ip: ip,
name: args[0]
});
}
}
}
}
function handleIpban(chan, user, args) {
if(Rank.hasPermission(user, "ipban") && args.length > 0) {
var name = "";
for(var ip in chan.logins) {
if(ip.indexOf(args[0]) == 0) {
if(chan.getIPRank(ip) >= user.rank) {
return;
}
var names = chan.logins[ip];
name = names[names.length - 1];
}
}
chan.logger.log("*** " + user.name + " banned " + args[0]);
chan.banIP(user, {
ip: args[0],
name: name
});
} }
} }

View File

@ -202,7 +202,13 @@ exports.loadChannel = function(chan) {
} }
var rows = results.fetchAllSync(); var rows = results.fetchAllSync();
for(var i = 0; i < rows.length; i++) { for(var i = 0; i < rows.length; i++) {
chan.ipbans[rows[i].ip] = [rows[i].name, rows[i].banner]; // Name ban
if(rows[i].ip == "*") {
chan.namebans[rows[i].name] = rows[i].banner;
}
else {
chan.ipbans[rows[i].ip] = [rows[i].name, rows[i].banner];
}
} }
chan.logger.log("*** Loaded channel from database"); chan.logger.log("*** Loaded channel from database");
@ -388,6 +394,19 @@ exports.removeChannelBan = function(channame, ip) {
return results; return results;
} }
exports.removeNameBan = function(channame, name) {
var db = exports.getConnection();
if(!db) {
return false;
}
var query = "DELETE FROM `chan_{1}_bans` WHERE `ip` = '*' AND `name`='{2}'"
.replace("{1}", sqlEscape(channame))
.replace("{2}", sqlEscape(name));
results = db.querySync(query);
db.closeSync();
return results;
}
exports.getChannelRanks = function(channame) { exports.getChannelRanks = function(channame) {
var db = exports.getConnection(); var db = exports.getConnection();
if(!db) { if(!db) {

View File

@ -2,7 +2,7 @@
"author": "Calvin Montgomery", "author": "Calvin Montgomery",
"name": "CyTube", "name": "CyTube",
"description": "Online media synchronizer and chat", "description": "Online media synchronizer and chat",
"version": "1.8.1", "version": "1.8.2",
"repository": { "repository": {
"url": "http://github.com/calzoneman/sync" "url": "http://github.com/calzoneman/sync"
}, },

View File

@ -27,6 +27,7 @@ var permissions = {
assignLeader : exports.Moderator, assignLeader : exports.Moderator,
kick : exports.Moderator, kick : exports.Moderator,
ipban : exports.Moderator, ipban : exports.Moderator,
ban : exports.Moderator,
promote : exports.Moderator, promote : exports.Moderator,
qlock : exports.Moderator, qlock : exports.Moderator,
poll : exports.Moderator, poll : exports.Moderator,

View File

@ -9,7 +9,7 @@ The above copyright notice and this permission notice shall be included in all c
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
const VERSION = "1.8.1"; const VERSION = "1.8.2";
var fs = require("fs"); var fs = require("fs");
var Logger = require("./logger.js"); var Logger = require("./logger.js");

18
user.js
View File

@ -143,6 +143,24 @@ User.prototype.initCallbacks = function() {
} }
}.bind(this)); }.bind(this));
this.socket.on("banName", function(data) {
if(this.channel != null) {
this.channel.banName(this, data.name || "");
}
}.bind(this));
this.socket.on("banIP", function(data) {
if(this.channel != null) {
this.channel.tryIPBan(this, data);
}
}.bind(this));
this.socket.on("unban", function(data) {
if(this.channel != null) {
this.channel.tryUnban(this, data);
}
}.bind(this));
this.socket.on("chatMsg", function(data) { this.socket.on("chatMsg", function(data) {
if(this.channel != null) { if(this.channel != null) {
this.channel.tryChat(this, data); this.channel.tryChat(this, data);

View File

@ -26,6 +26,10 @@ Callbacks = {
$("#messagebuffer").scrollTop($("#messagebuffer").prop("scrollHeight")); $("#messagebuffer").scrollTop($("#messagebuffer").prop("scrollHeight"));
}, },
errorMsg: function(data) {
alert(data.msg);
},
announcement: function(data) { announcement: function(data) {
$("#announcerow").html(""); $("#announcerow").html("");
$("#announcerow").css("display", ""); $("#announcerow").css("display", "");
@ -269,11 +273,12 @@ Callbacks = {
var name = $("<td/>").text(entries[i].name).appendTo(tr); var name = $("<td/>").text(entries[i].name).appendTo(tr);
var banner = $("<td/>").text(entries[i].banner).appendTo(tr); var banner = $("<td/>").text(entries[i].banner).appendTo(tr);
var callback = (function(ip) { return function() { var callback = (function(id, name) { return function() {
socket.emit("chatMsg", { socket.emit("unban", {
msg: "/unban " + ip id: id,
name: name
}); });
} })(entries[i].ip); } })(entries[i].id, entries[i].name);
remove.click(callback); remove.click(callback);
} }
}, },
@ -299,27 +304,69 @@ Callbacks = {
for(var i = 0; i < entries.length; i++) { for(var i = 0; i < entries.length; i++) {
var tr = $("<tr/>").appendTo(tbl); var tr = $("<tr/>").appendTo(tbl);
var bantd = $("<td/>").appendTo(tr); var bantd = $("<td/>").appendTo(tr);
var ban = $("<button/>").addClass("btn btn-mini btn-danger") if(entries[i].banned) {
.text("Ban") bantd.text("Banned");
.appendTo(bantd); tr.addClass("alert alert-error");
var banrange = $("<button/>").addClass("btn btn-mini btn-danger") }
.text("Ban Range") else {
.appendTo(bantd); var ban = $("<button/>").addClass("btn btn-mini btn-danger")
.text("Ban IP")
.appendTo(bantd);
var banrange = $("<button/>").addClass("btn btn-mini btn-danger")
.text("Ban IP Range")
.appendTo(bantd);
var callback = (function(id, name) { return function() {
console.log(id, name);
socket.emit("banIP", {
id: id,
name: name,
range: false
});
return false;
} })(entries[i].id, entries[i].names[0]);
ban.click(callback);
var callback2 = (function(id, name) { return function() {
console.log(id, name);
socket.emit("banIP", {
id: id,
name: name,
range: true
});
return false;
} })(entries[i].id, entries[i].names[0]);
banrange.click(callback2);
}
var ip = $("<td/>").text(entries[i].ip).appendTo(tr); var ip = $("<td/>").text(entries[i].ip).appendTo(tr);
var name = $("<td/>").text(entries[i].name).appendTo(tr); var name = $("<td/>").text(entries[i].names).appendTo(tr);
tr.data("names", entries[i].names);
var callback = (function(name) { return function() { tr.data("banned", entries[i].banned);
socket.emit("chatMsg", { tr.click(function(ev) {
msg: "/ban " + name tbl.find(".name-detail").remove();
}); if(this.data("namesopen")) {
} })(entries[i].name.split(",")[0]); this.data("namesopen", false);
ban.click(callback); return;
var callback2 = (function(name) { return function() { }
socket.emit("chatMsg", { var names = this.data("names") || [];
msg: "/ban " + name + " range" for(var i = names.length-1; i >= 0; i--) {
}); var detail = $("<tr/>").insertAfter(this);
} })(entries[i].name.split(",")[0]); detail.addClass("name-detail");
banrange.click(callback2); if(this.data("banned")) {
detail.addClass("alert alert-error");
}
var buttontd = $("<td/>").appendTo(detail);
$("<button/>").addClass("btn btn-mini btn-danger")
.text("Ban Name")
.appendTo(buttontd)
.click(function() {
socket.emit("banName", {
name: this
});
}.bind(names[i]));
$("<td/>").text("\"").appendTo(detail);
$("<td/>").text(names[i]).appendTo(detail);
}
this.data("namesopen", true);
}.bind(tr));
} }
}, },

View File

@ -132,6 +132,15 @@ function addUserDropdown(entry, name) {
}); });
}); });
var nameban = $("<li />").appendTo(ul);
var a = $("<a />").attr("tabindex", "-1").attr("href", "javascript:void(0);").appendTo(nameban);
a.text("Name Ban");
a.click(function() {
socket.emit("banName", {
name: name
});
});
$("<li />").addClass("divider").appendTo(ul); $("<li />").addClass("divider").appendTo(ul);
var promote = $("<li />").appendTo(ul); var promote = $("<li />").appendTo(ul);