From 62c8f722ec28bceabff089b00b9fe6a1aea7e53d Mon Sep 17 00:00:00 2001 From: Luigi Pinca Date: Fri, 30 May 2014 21:29:27 +0200 Subject: [PATCH] added ability for privileged users to ban players --- app.js | 4 +- lib/middleware/ban-handler.js | 30 +++++++++ .../{errorHandler.js => error-handler.js} | 0 lib/rooms.js | 45 +++++++++----- lib/sparks.js | 27 +++++++- lib/utils.js | 9 +++ package.json | 1 + public/js/app.js | 62 ++++++++++++++----- views/banned.jade | 16 +++++ 9 files changed, 160 insertions(+), 34 deletions(-) create mode 100644 lib/middleware/ban-handler.js rename lib/middleware/{errorHandler.js => error-handler.js} (100%) create mode 100644 views/banned.jade diff --git a/app.js b/app.js index bdeb7d3..9657b03 100644 --- a/app.js +++ b/app.js @@ -2,7 +2,8 @@ * Module dependencies. */ -var errorHandler = require('./lib/middleware/errorHandler') +var banHandler = require('./lib/middleware/ban-handler') + , errorHandler = require('./lib/middleware/error-handler') , express = require('express') , favicon = require('serve-favicon') , http = require('http') @@ -29,6 +30,7 @@ var app = express() app.set('view engine', 'jade'); app.use('/static', express.static(pub, {maxAge: 2419200000})); // 4 weeks = 2419200000 ms app.use(favicon(pub + '/img/favicon.ico', {maxAge: 2419200000})); +app.use(banHandler); app.use(urlencoded()); app.use(cookieParser); app.use(session({ diff --git a/lib/middleware/ban-handler.js b/lib/middleware/ban-handler.js new file mode 100644 index 0000000..7f608a4 --- /dev/null +++ b/lib/middleware/ban-handler.js @@ -0,0 +1,30 @@ +/** + * Module dependencies. + */ + +var db = require('../redis-clients').users + , forwarded = require('forwarded-for') + , utils = require('../utils'); + +/** + * Expose a middleware to filter banned IPs. + */ + +module.exports = function(req, res, next) { + var address = forwarded(req, req.headers); + + db.ttl(['ban:' + address.ip], function(err, ttl) { + if (err) { + return next(err); + } + + if (ttl < 0) { + return next(); + } + + res.render('banned', { + slogan: utils.randomSlogan(), + ttl: Math.round(ttl / 60) + }); + }); +}; diff --git a/lib/middleware/errorHandler.js b/lib/middleware/error-handler.js similarity index 100% rename from lib/middleware/errorHandler.js rename to lib/middleware/error-handler.js diff --git a/lib/rooms.js b/lib/rooms.js index fcde1f7..846900f 100644 --- a/lib/rooms.js +++ b/lib/rooms.js @@ -308,7 +308,7 @@ Room.prototype.onGuess = function(spark, guess) { */ Room.prototype.onIgnore = function(who, executor, callback) { - // Check if the player to be ignored is in the room + // Check if the player is in the room if (this.usersData[who]) { sparks[who].send('chatmsg', executor + ' is ignoring you.', 'binb', who); return callback(true, who); @@ -317,32 +317,47 @@ Room.prototype.onIgnore = function(who, executor, callback) { }; /** - * Kick a player. + * Kick and optionally ban a player. */ -Room.prototype.onKick = function(who, why, executor, callback) { +Room.prototype.onKick = function(who, why, executor, duration, callback) { var room = this; + if (typeof duration === 'function') { + callback = duration; + duration = 0; + } + usersdb.hget(['user:' + executor, 'role'], function(err, role) { if (err) { console.error(err.message); return callback(true); } - // Check role - if (role > 0) { - if (room.usersData[who]) { - if (why) { - why = ' (' + why + ')'; - } - var notice = 'you have been kicked by ' + executor + why + '.' - , recipient = sparks[who]; - recipient.send('chatmsg', notice, 'binb', who); - recipient.end(); + // Check if the sender can kick other players + if ((role || 0) < 1) { + return callback(false); + } + + // Check if the target player is in the room + if (room.usersData[who]) { + var notice = 'you have been kicked by ' + executor + + (why && ' (' + why + ')') + '.'; + var target = sparks[who]; + + if (duration) { + usersdb.setex(['ban:' + target.address.ip, duration, who], function(err) { + if (err) { + console.error(err.message); + } + }); } - return callback(true); + + target.send('chatmsg', notice, 'binb', who); + target.end(); } - callback(false); + + callback(true); }); }; diff --git a/lib/sparks.js b/lib/sparks.js index a342354..a78229d 100644 --- a/lib/sparks.js +++ b/lib/sparks.js @@ -3,6 +3,8 @@ */ var config = require('../config') + , db = require('../lib/redis-clients').users + , forwarded = require('forwarded-for') , fs = require('fs') , minify = require('uglify-js').minify , Primus = require('primus') @@ -13,6 +15,7 @@ var config = require('../config') , sessionstore , sparks = Object.create(null) // Sparks of all rooms , utils = require('./utils') + , banDuration = utils.banDuration , isFunction = utils.isFunction , isString = utils.isString; @@ -85,8 +88,18 @@ var authorize = function(req, authorized) { console.error(err.message); return authorized(err); } - req.user = session.user; - authorized(); + var address = forwarded(req, req.headers); + db.exists(['ban:' + address.ip], function(err, exists) { + if (err) { + console.error(err.message); + return authorized(err); + } + if (exists) { + return authorized(new Error('banned IP address')); + } + req.user = session.user; + authorized(); + }); }); }; @@ -135,6 +148,16 @@ var connection = function(spark) { var joinRoom = function(room, spark) { room = rooms[room]; + spark.on('ban', function(who, why, duration, callback) { + if ( + isString(who) && + isString(why) && + isString(duration) && + isFunction(callback) + ) { + room.onKick(who, why, spark.nickname, banDuration(duration), callback); + } + }); spark.on('chatmsg', function(msg, to) { if (isString(msg)) { room.onChatMessage(msg, spark, to); diff --git a/lib/utils.js b/lib/utils.js index a180ca1..40eaefb 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -1,3 +1,12 @@ +/** + * Convert the duration of a ban from minutes to seconds and return the value. + * Default duration is 15 minutes. + */ + +exports.banDuration = function(str) { + return /^[1-9][0-9]*$/.test(str) ? str * 60 : 900; +}; + /** * Helper function used to build leaderboards. * Rearrange database results in an object. diff --git a/package.json b/package.json index d6e35d2..0cab662 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "cookie-parser": "1.1.x", "express": "4.3.x", "express-session": "1.2.x", + "forwarded-for": "0.0.x", "jade": "1.3.x", "nodemailer": "0.6.x", "primus": "2.2.x", diff --git a/public/js/app.js b/public/js/app.js index 32f6e1b..298818b 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -551,20 +551,6 @@ }); }; - // Kick a player - var kickPlayer = function(args, outcome) { - outcome.append('you are not allowed to kick a player.'); - if (!subscriber) { - return addChatEntry(outcome); - } - var why = args[1] || ''; - primus.send('kick', args[0], why, function(success) { - if (!success) { - addChatEntry(outcome); - } - }); - }; - var loadTrack = function(previewUrl) { jplayer.jPlayer('mute'); jplayer.jPlayer('setMedia', {m4a: previewUrl}); @@ -643,6 +629,44 @@ addFeedback('What is this song?'); }; + // Return a function that will kick or ban a player + var punishPlayer = function(punishment) { + return function(tokens, outcome) { + outcome.append('you are not allowed to ' + punishment + ' a player.'); + if (!subscriber) { + return addChatEntry(outcome); + } + + var args = [punishment, tokens[0]]; + + if (punishment === 'kick') { + args.push(tokens[1] || ''); + } + else if (!tokens[1]) { + args.push('', ''); + } + else if (!tokens[2]) { + if (/^[1-9][0-9]*$/.test(tokens[1])) { + args.push('', tokens[1]); + } + else { + args.push(tokens[1], ''); + } + } + else { + args.push(tokens[1], tokens[2]); + } + + args.push(function(success) { + if (!success) { + addChatEntry(outcome); + } + }); + + primus.send.apply(primus, args); + }; + }; + // Return a function that will add a random text from the given set, with the given style var randomFeedback = function(set, style) { var card = set.length; @@ -959,6 +983,12 @@ }; var slashcommands = { + ban: { + checkrecipient: true, + fn: punishPlayer('ban'), + minargs: 1, + usage: 'usage: /ban <player name> [message] [duration]' + }, clear: { fn: function() { DOM.chat.empty(); @@ -973,7 +1003,7 @@ }, kick: { checkrecipient: true, - fn: kickPlayer, + fn: punishPlayer('kick'), minargs: 1, usage: 'usage: /kick <player name> [message]' }, @@ -1004,11 +1034,11 @@ volume: 1 }); primus.on('alreadyinaroom', alreadyInARoom); + primus.on('close', disconnect); primus.on('invalidnickname', invalidNickName); primus.on('ready', ready); primus.on('updateoverview', updateRoomsOverview); primus.send('getoverview', roomsOverview); }); - primus.on('close', disconnect); })(); diff --git a/views/banned.jade b/views/banned.jade new file mode 100644 index 0000000..6adde56 --- /dev/null +++ b/views/banned.jade @@ -0,0 +1,16 @@ +extends layout + +block title + title binb :: banned + +block sections + section + .row + .span12.offset2 + .alert.alert-error.alert-block + h4.alert-heading Error! + | Yuo have been banned. + br + | Your ban will expire in #{ttl} minutes. + +block scripts -- 2.54.0