From b6e22b7723279964fb362a7f6e1f84f29d764d4e Mon Sep 17 00:00:00 2001
From: barisusakli <barisusakli@gmail.com>
Date: Tue, 15 Mar 2016 12:13:35 +0200
Subject: [PATCH] closes #4367

---
 public/less/admin/admin.less                  |  4 +--
 public/less/{admin/manage => }/blacklist.less |  1 +
 public/src/admin/manage/ip-blacklist.js       | 23 ++++++-------
 src/controllers/admin/blacklist.js            |  9 ++++-
 src/controllers/globalmods.js                 | 29 ++++++++++++++++
 src/controllers/index.js                      |  4 +--
 src/controllers/posts.js                      | 19 -----------
 src/meta/blacklist.js                         | 34 +++++++++++++------
 src/meta/css.js                               |  1 +
 src/routes/index.js                           |  9 +++--
 src/socket.io/admin.js                        |  6 +---
 src/socket.io/blacklist.js                    | 27 +++++++++++++++
 src/socket.io/index.js                        |  2 +-
 src/views/admin/manage/ip-blacklist.tpl       |  5 +--
 14 files changed, 111 insertions(+), 62 deletions(-)
 rename public/less/{admin/manage => }/blacklist.less (71%)
 create mode 100644 src/controllers/globalmods.js
 delete mode 100644 src/controllers/posts.js
 create mode 100644 src/socket.io/blacklist.js

diff --git a/public/less/admin/admin.less b/public/less/admin/admin.less
index 182b0afccf..e733d8d9bd 100644
--- a/public/less/admin/admin.less
+++ b/public/less/admin/admin.less
@@ -12,7 +12,6 @@
 @import "./manage/tags";
 @import "./manage/groups";
 @import "./manage/users";
-@import "./manage/blacklist";
 @import "./appearance/customise";
 @import "./appearance/themes";
 @import "./extend/plugins";
@@ -22,6 +21,7 @@
 @import "./settings";
 
 @import "../flags";
+@import "../blacklist";
 
 @import "./modules/alerts";
 @import "./modules/selectable";
@@ -239,5 +239,5 @@
 }
 
 [class^="col-"] .mdl-switch__label {
-	padding-right: 15px;	
+	padding-right: 15px;
 }
\ No newline at end of file
diff --git a/public/less/admin/manage/blacklist.less b/public/less/blacklist.less
similarity index 71%
rename from public/less/admin/manage/blacklist.less
rename to public/less/blacklist.less
index 4e0dcb7c27..5f23cd5698 100644
--- a/public/less/admin/manage/blacklist.less
+++ b/public/less/blacklist.less
@@ -2,4 +2,5 @@
 	width: 100%;
 	height: 450px;
 	display: block;
+	border: 1px solid #eee;
 }
\ No newline at end of file
diff --git a/public/src/admin/manage/ip-blacklist.js b/public/src/admin/manage/ip-blacklist.js
index daba34ec3d..9769b3e3bb 100644
--- a/public/src/admin/manage/ip-blacklist.js
+++ b/public/src/admin/manage/ip-blacklist.js
@@ -1,23 +1,22 @@
 'use strict';
-/* globals $, app, socket, templates */
+/* globals $, app, socket, templates, define, bootbox */
 
-define('admin/manage/ip-blacklist', ['settings'], function(Settings) {
+define('admin/manage/ip-blacklist', [], function() {
 
 	var Blacklist = {};
 
 	Blacklist.init = function() {
-		var blacklist = ace.edit("blacklist-rules");
+		var blacklist = $('#blacklist-rules');
 
-		blacklist.on('change', function(e) {
-		    $('#blacklist-rules-holder').val(blacklist.getValue());
-		}); 
-
-		Settings.load('blacklist', $('.blacklist-settings'), function(err, settings) {
-			blacklist.setValue(settings.rules);
+		blacklist.on('keyup', function() {
+		    $('#blacklist-rules-holder').val(blacklist.val());
 		});
 
 		$('[data-action="apply"]').on('click', function() {
-			Settings.save('blacklist', $('.blacklist-settings'), function() {
+			socket.emit('blacklist.save', blacklist.val(), function(err) {
+				if (err) {
+					return app.alertError(err.message);
+				}
 				app.alert({
 					type: 'success',
 					alert_id: 'blacklist-saved',
@@ -27,8 +26,8 @@ define('admin/manage/ip-blacklist', ['settings'], function(Settings) {
 		});
 
 		$('[data-action="test"]').on('click', function() {
-			socket.emit('admin.blacklist.validate', {
-				rules: blacklist.getValue()
+			socket.emit('blacklist.validate', {
+				rules: blacklist.val()
 			}, function(err, data) {
 				templates.parse('admin/partials/blacklist-validate', data, function(html) {
 					bootbox.alert(html);
diff --git a/src/controllers/admin/blacklist.js b/src/controllers/admin/blacklist.js
index 60c3940e44..3561ba0f76 100644
--- a/src/controllers/admin/blacklist.js
+++ b/src/controllers/admin/blacklist.js
@@ -1,9 +1,16 @@
 "use strict";
 
+var meta = require('../../meta');
+
 var blacklistController = {};
 
 blacklistController.get = function(req, res, next) {
-	res.render('admin/manage/ip-blacklist', {});
+	meta.blacklist.get(function(err, rules) {
+		if (err) {
+			return next(err);
+		}
+		res.render('admin/manage/ip-blacklist', {rules: rules});
+	});
 };
 
 module.exports = blacklistController;
diff --git a/src/controllers/globalmods.js b/src/controllers/globalmods.js
new file mode 100644
index 0000000000..3275c7929e
--- /dev/null
+++ b/src/controllers/globalmods.js
@@ -0,0 +1,29 @@
+"use strict";
+
+var user = require('../user');
+var adminFlagsController = require('./admin/flags');
+var adminBlacklistController = require('./admin/blacklist');
+
+var globalModsController = {};
+
+globalModsController.flagged = function(req, res, next) {
+	user.isAdminOrGlobalMod(req.uid, function(err, isAdminOrGlobalMod) {
+		if (err || !isAdminOrGlobalMod) {
+			return next(err);
+		}
+
+		adminFlagsController.get(req, res, next);
+	});
+};
+
+globalModsController.ipBlacklist = function(req, res, next) {
+	user.isAdminOrGlobalMod(req.uid, function(err, isAdminOrGlobalMod) {
+		if (err || !isAdminOrGlobalMod) {
+			return next(err);
+		}
+
+		adminBlacklistController.get(req, res, next);
+	});
+};
+
+module.exports = globalModsController;
diff --git a/src/controllers/index.js b/src/controllers/index.js
index 51c5c316ba..fa35523c2b 100644
--- a/src/controllers/index.js
+++ b/src/controllers/index.js
@@ -12,7 +12,6 @@ var helpers = require('./helpers');
 
 var Controllers = {
 	topics: require('./topics'),
-	posts: require('./posts'),
 	categories: require('./categories'),
 	category: require('./category'),
 	unread: require('./unread'),
@@ -25,7 +24,8 @@ var Controllers = {
 	accounts: require('./accounts'),
 	authentication: require('./authentication'),
 	api: require('./api'),
-	admin: require('./admin')
+	admin: require('./admin'),
+	globalMods: require('./globalmods')
 };
 
 
diff --git a/src/controllers/posts.js b/src/controllers/posts.js
deleted file mode 100644
index 5618069b9b..0000000000
--- a/src/controllers/posts.js
+++ /dev/null
@@ -1,19 +0,0 @@
-"use strict";
-
-var user = require('../user');
-var adminFlagsController = require('./admin/flags');
-
-var postsController = {};
-
-postsController.flagged = function(req, res, next) {
-	user.isAdminOrGlobalMod(req.uid, function(err, isAdminOrGlobalMod) {
-		if (err || !isAdminOrGlobalMod) {
-			return next(err);
-		}
-
-		adminFlagsController.get(req, res, next);
-	});
-};
-
-
-module.exports = postsController;
diff --git a/src/meta/blacklist.js b/src/meta/blacklist.js
index 2029b406f2..b0269a1f0c 100644
--- a/src/meta/blacklist.js
+++ b/src/meta/blacklist.js
@@ -1,10 +1,9 @@
 'use strict';
 
-var ip = require('ip'),
-	winston = require('winston'),
-	async = require('async');
-
-var	meta = module.parent.exports;
+var ip = require('ip');
+var winston = require('winston');
+var async = require('async');
+var db = require('../database');
 
 var Blacklist = {
 		_rules: []
@@ -12,7 +11,7 @@ var Blacklist = {
 
 Blacklist.load = function(callback) {
 	async.waterfall([
-		async.apply(meta.settings.getOne, 'blacklist', 'rules'),
+		async.apply(db.get, 'ip-blacklist-rules'),
 		async.apply(Blacklist.validate)
 	], function(err, rules) {
 		if (err) {
@@ -34,6 +33,19 @@ Blacklist.load = function(callback) {
 	});
 };
 
+Blacklist.save = function(rules, callback) {
+	db.set('ip-blacklist-rules', rules, function(err) {
+		if (err) {
+			return callback(err);
+		}
+		Blacklist.load(callback);
+	});
+};
+
+Blacklist.get = function(callback) {
+	db.get('ip-blacklist-rules', callback);
+};
+
 Blacklist.test = function(clientIp, callback) {
 	if (
 		Blacklist._rules.ipv4.indexOf(clientIp) === -1	// not explicitly specified in ipv4 list
@@ -60,11 +72,11 @@ Blacklist.test = function(clientIp, callback) {
 };
 
 Blacklist.validate = function(rules, callback) {
-	var rules = (rules || '').split('\n'),
-		ipv4 = [],
-		ipv6 = [],
-		cidr = [],
-		invalid = [];
+	rules = (rules || '').split('\n');
+	var ipv4 = [];
+	var ipv6 = [];
+	var cidr = [];
+	var invalid = [];
 
 	var isCidrSubnet = /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$/;
 
diff --git a/src/meta/css.js b/src/meta/css.js
index effd560526..172c2bf0e6 100644
--- a/src/meta/css.js
+++ b/src/meta/css.js
@@ -70,6 +70,7 @@ module.exports = function(Meta) {
 				source += '\n@import (inline) "..' + path.sep + '..' + path.sep + 'public/vendor/jquery/textcomplete/jquery.textcomplete.css";';
 				source += '\n@import (inline) "..' + path.sep + '..' + path.sep + 'public/vendor/colorpicker/colorpicker.css";';
 				source += '\n@import "..' + path.sep + '..' + path.sep + 'public/less/flags.less";';
+				source += '\n@import "..' + path.sep + '..' + path.sep + 'public/less/blacklist.less";';
 				source += '\n@import "..' + path.sep + '..' + path.sep + 'public/less/generics.less";';
 				source += '\n@import "..' + path.sep + '..' + path.sep + 'public/less/mixins.less";';
 
diff --git a/src/routes/index.js b/src/routes/index.js
index e49256f933..58754670d5 100644
--- a/src/routes/index.js
+++ b/src/routes/index.js
@@ -36,8 +36,9 @@ function mainRoutes(app, middleware, controllers) {
 	setupPageRoute(app, '/tos', middleware, [], controllers.termsOfUse);
 }
 
-function postRoutes(app, middleware, controllers) {
-	setupPageRoute(app, '/posts/flags', middleware, [], controllers.posts.flagged);
+function globalModRoutes(app, middleware, controllers) {
+	setupPageRoute(app, '/ip-blacklist', middleware, [], controllers.globalMods.ipBlacklist);
+	setupPageRoute(app, '/posts/flags', middleware, [], controllers.globalMods.flagged);
 }
 
 function topicRoutes(app, middleware, controllers) {
@@ -111,7 +112,7 @@ module.exports = function(app, middleware) {
 
 	mainRoutes(router, middleware, controllers);
 	topicRoutes(router, middleware, controllers);
-	postRoutes(router, middleware, controllers);
+	globalModRoutes(router, middleware, controllers);
 	tagRoutes(router, middleware, controllers);
 	categoryRoutes(router, middleware, controllers);
 	accountRoutes(router, middleware, controllers);
@@ -191,10 +192,8 @@ function handleErrors(app, middleware) {
 			case 'EBADCSRFTOKEN':
 				winston.error(req.path + '\n', err.message);
 				return res.sendStatus(403);
-				break;
 			case 'blacklisted-ip':
 				return res.status(403).type('text/plain').send(err.message);
-				break;
 		}
 
 		if (parseInt(err.status, 10) === 302 && err.path) {
diff --git a/src/socket.io/admin.js b/src/socket.io/admin.js
index f3504a920d..6dd22134ba 100644
--- a/src/socket.io/admin.js
+++ b/src/socket.io/admin.js
@@ -33,8 +33,7 @@ var	async = require('async'),
 		settings: {},
 		email: {},
 		analytics: {},
-		logs: {},
-		blacklist: {}
+		logs: {}
 	};
 
 SocketAdmin.before = function(socket, method, data, next) {
@@ -274,8 +273,5 @@ SocketAdmin.deleteAllEvents = function(socket, data, callback) {
 	events.deleteAll(callback);
 };
 
-SocketAdmin.blacklist.validate = function(socket, data, callback) {
-	meta.blacklist.validate(data.rules, callback);
-};
 
 module.exports = SocketAdmin;
diff --git a/src/socket.io/blacklist.js b/src/socket.io/blacklist.js
new file mode 100644
index 0000000000..f4158dc94b
--- /dev/null
+++ b/src/socket.io/blacklist.js
@@ -0,0 +1,27 @@
+
+'use strict';
+
+var async = require('async');
+var winston = require('winston');
+
+var user = require('../user');
+var meta = require('../meta');
+
+var SocketBlacklist = {};
+
+SocketBlacklist.validate = function(socket, data, callback) {
+	meta.blacklist.validate(data.rules, callback);
+};
+
+SocketBlacklist.save = function(socket, rules, callback) {
+	user.isAdminOrGlobalMod(socket.uid, function(err, isAdminOrGlobalMod) {
+		if (err || !isAdminOrGlobalMod) {
+			return callback(err || new Error('[[error:no-privileges]]'));
+		}
+
+		meta.blacklist.save(rules, callback);
+	});
+};
+
+
+module.exports = SocketBlacklist;
diff --git a/src/socket.io/index.js b/src/socket.io/index.js
index 032c4b9c68..8f6f3c4c5a 100644
--- a/src/socket.io/index.js
+++ b/src/socket.io/index.js
@@ -121,7 +121,7 @@ function onMessage(socket, payload) {
 
 function requireModules() {
 	var modules = ['admin', 'categories', 'groups', 'meta', 'modules',
-		'notifications', 'plugins', 'posts', 'topics', 'user'
+		'notifications', 'plugins', 'posts', 'topics', 'user', 'blacklist'
 	];
 
 	modules.forEach(function(module) {
diff --git a/src/views/admin/manage/ip-blacklist.tpl b/src/views/admin/manage/ip-blacklist.tpl
index 13e43a84d7..8ef308f364 100644
--- a/src/views/admin/manage/ip-blacklist.tpl
+++ b/src/views/admin/manage/ip-blacklist.tpl
@@ -11,10 +11,7 @@
 
 		<div class="row">
 			<div class="col-sm-6">
-				<div id="blacklist-rules"></div>
-				<form class="blacklist-settings">
-					<input type="hidden" id="blacklist-rules-holder" value="" name="rules" />
-				</form>
+				<textarea id="blacklist-rules">{rules}</textarea>
 			</div>
 			<div class="col-sm-6">
 				<div class="panel panel-default">