mirror of
https://github.com/NodeBB/NodeBB.git
synced 2026-05-05 17:46:56 +02:00
Merge remote-tracking branch 'refs/remotes/origin/master' into develop
# Conflicts: # package.json # src/views/admin/manage/ip-blacklist.tpl
This commit is contained in:
@@ -211,3 +211,9 @@ Analytics.getErrorAnalytics = function (callback) {
|
||||
}, callback);
|
||||
};
|
||||
|
||||
Analytics.getBlacklistAnalytics = function (callback) {
|
||||
async.parallel({
|
||||
daily: async.apply(Analytics.getDailyStatsForSet, 'analytics:blacklist', Date.now(), 7),
|
||||
hourly: async.apply(Analytics.getHourlyStatsForSet, 'analytics:blacklist', Date.now(), 24),
|
||||
}, callback);
|
||||
};
|
||||
|
||||
@@ -2,19 +2,22 @@
|
||||
|
||||
var async = require('async');
|
||||
var meta = require('../../meta');
|
||||
var analytics = require('../../analytics');
|
||||
|
||||
var blacklistController = module.exports;
|
||||
|
||||
blacklistController.get = function (req, res, next) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
meta.blacklist.get(next);
|
||||
},
|
||||
function (rules) {
|
||||
res.render('admin/manage/ip-blacklist', {
|
||||
rules: rules,
|
||||
title: '[[pages:ip-blacklist]]',
|
||||
});
|
||||
},
|
||||
], next);
|
||||
// Analytics.getBlacklistAnalytics
|
||||
async.parallel({
|
||||
rules: async.apply(meta.blacklist.get),
|
||||
analytics: async.apply(analytics.getBlacklistAnalytics),
|
||||
}, function (err, data) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
res.render('admin/manage/ip-blacklist', Object.assign(data, {
|
||||
title: '[[pages:ip-blacklist]]',
|
||||
}));
|
||||
});
|
||||
};
|
||||
|
||||
@@ -288,7 +288,6 @@ authenticationController.doLogin = function (req, uid, callback) {
|
||||
};
|
||||
|
||||
authenticationController.onSuccessfulLogin = function (req, uid, callback) {
|
||||
callback = callback || function () {};
|
||||
var uuid = utils.generateUUID();
|
||||
req.session.meta = {};
|
||||
|
||||
@@ -308,6 +307,7 @@ authenticationController.onSuccessfulLogin = function (req, uid, callback) {
|
||||
});
|
||||
|
||||
async.waterfall([
|
||||
async.apply(meta.blacklist.test, req.ip),
|
||||
function (next) {
|
||||
async.parallel([
|
||||
function (next) {
|
||||
@@ -330,7 +330,17 @@ authenticationController.onSuccessfulLogin = function (req, uid, callback) {
|
||||
plugins.fireHook('action:user.loggedIn', { uid: uid, req: req });
|
||||
next();
|
||||
},
|
||||
], callback);
|
||||
], function (err) {
|
||||
if (err) {
|
||||
req.session.destroy();
|
||||
}
|
||||
|
||||
if (typeof callback === 'function') {
|
||||
callback(err);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
authenticationController.localLogin = function (req, username, password, next) {
|
||||
|
||||
@@ -11,7 +11,7 @@ var meta = require('../meta');
|
||||
var helpers = require('./helpers');
|
||||
var pagination = require('../pagination');
|
||||
|
||||
var recentController = {};
|
||||
var recentController = module.exports;
|
||||
|
||||
var validFilter = { '': true, new: true, watched: true };
|
||||
|
||||
@@ -47,49 +47,44 @@ recentController.get = function (req, res, next) {
|
||||
|
||||
topics.getRecentTopics(cid, req.uid, start, stop, filter, next);
|
||||
},
|
||||
], function (err, data) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
function (data) {
|
||||
data.categories = categoryData.categories;
|
||||
data.selectedCategory = categoryData.selectedCategory;
|
||||
data.nextStart = stop + 1;
|
||||
data.set = 'topics:recent';
|
||||
data['feeds:disableRSS'] = parseInt(meta.config['feeds:disableRSS'], 10) === 1;
|
||||
data.rssFeedUrl = nconf.get('relative_path') + '/recent.rss';
|
||||
data.title = '[[pages:recent]]';
|
||||
data.filters = [{
|
||||
name: '[[unread:all-topics]]',
|
||||
url: 'recent',
|
||||
selected: filter === '',
|
||||
filter: '',
|
||||
}, {
|
||||
name: '[[unread:new-topics]]',
|
||||
url: 'recent/new',
|
||||
selected: filter === 'new',
|
||||
filter: 'new',
|
||||
}, {
|
||||
name: '[[unread:watched-topics]]',
|
||||
url: 'recent/watched',
|
||||
selected: filter === 'watched',
|
||||
filter: 'watched',
|
||||
}];
|
||||
|
||||
data.categories = categoryData.categories;
|
||||
data.selectedCategory = categoryData.selectedCategory;
|
||||
data.nextStart = stop + 1;
|
||||
data.set = 'topics:recent';
|
||||
data['feeds:disableRSS'] = parseInt(meta.config['feeds:disableRSS'], 10) === 1;
|
||||
data.rssFeedUrl = nconf.get('relative_path') + '/recent.rss';
|
||||
data.title = '[[pages:recent]]';
|
||||
data.filters = [{
|
||||
name: '[[unread:all-topics]]',
|
||||
url: 'recent',
|
||||
selected: filter === '',
|
||||
filter: '',
|
||||
}, {
|
||||
name: '[[unread:new-topics]]',
|
||||
url: 'recent/new',
|
||||
selected: filter === 'new',
|
||||
filter: 'new',
|
||||
}, {
|
||||
name: '[[unread:watched-topics]]',
|
||||
url: 'recent/watched',
|
||||
selected: filter === 'watched',
|
||||
filter: 'watched',
|
||||
}];
|
||||
data.selectedFilter = data.filters.find(function (filter) {
|
||||
return filter && filter.selected;
|
||||
});
|
||||
|
||||
data.selectedFilter = data.filters.find(function (filter) {
|
||||
return filter && filter.selected;
|
||||
});
|
||||
var pageCount = Math.max(1, Math.ceil(data.topicCount / settings.topicsPerPage));
|
||||
data.pagination = pagination.create(page, pageCount, req.query);
|
||||
|
||||
var pageCount = Math.max(1, Math.ceil(data.topicCount / settings.topicsPerPage));
|
||||
data.pagination = pagination.create(page, pageCount, req.query);
|
||||
if (req.path.startsWith('/api/recent') || req.path.startsWith('/recent')) {
|
||||
data.breadcrumbs = helpers.buildBreadcrumbs([{ text: '[[recent:title]]' }]);
|
||||
}
|
||||
|
||||
if (req.path.startsWith('/api/recent') || req.path.startsWith('/recent')) {
|
||||
data.breadcrumbs = helpers.buildBreadcrumbs([{ text: '[[recent:title]]' }]);
|
||||
}
|
||||
|
||||
data.querystring = cid ? ('?cid=' + validator.escape(String(cid))) : '';
|
||||
res.render('recent', data);
|
||||
});
|
||||
data.querystring = cid ? ('?cid=' + validator.escape(String(cid))) : '';
|
||||
res.render('recent', data);
|
||||
},
|
||||
], next);
|
||||
};
|
||||
|
||||
module.exports = recentController;
|
||||
|
||||
@@ -41,11 +41,11 @@ module.exports = function (db, module) {
|
||||
var query = { _key: { $in: keys } };
|
||||
|
||||
if (min !== '-inf') {
|
||||
query.score = { $gte: min };
|
||||
query.score = { $gte: parseFloat(min) };
|
||||
}
|
||||
if (max !== '+inf') {
|
||||
query.score = query.score || {};
|
||||
query.score.$lte = max;
|
||||
query.score.$lte = parseFloat(max);
|
||||
}
|
||||
|
||||
db.collection('objects').remove(query, function (err) {
|
||||
|
||||
@@ -14,9 +14,9 @@ module.exports = function (Messaging) {
|
||||
return next();
|
||||
}
|
||||
var keys = uids.map(function (uid) {
|
||||
return 'uid:' + uid + ':chat:room:' + roomId + 'mids';
|
||||
return 'uid:' + uid + ':chat:room:' + roomId + ':mids';
|
||||
});
|
||||
db.sortedSetsRemove(keys, roomId, next);
|
||||
db.sortedSetsRemove(keys, mid, next);
|
||||
},
|
||||
function (next) {
|
||||
db.delete('message:' + mid, next);
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
'use strict';
|
||||
|
||||
var ip = require('ip');
|
||||
var ipRangeCheck = require('ip-range-check');
|
||||
var ipaddr = require('ipaddr.js');
|
||||
var winston = require('winston');
|
||||
var async = require('async');
|
||||
|
||||
var db = require('../database');
|
||||
var pubsub = require('../pubsub');
|
||||
var plugins = require('../plugins');
|
||||
var analytics = require('../analytics');
|
||||
|
||||
var Blacklist = {
|
||||
_rules: [],
|
||||
@@ -54,23 +55,38 @@ Blacklist.get = function (callback) {
|
||||
};
|
||||
|
||||
Blacklist.test = function (clientIp, callback) {
|
||||
// Some handy test addresses
|
||||
// clientIp = '2001:db8:85a3:0:0:8a2e:370:7334'; // IPv6
|
||||
// clientIp = '127.0.15.1'; // IPv4
|
||||
var addr = ipaddr.parse(clientIp);
|
||||
|
||||
if (
|
||||
Blacklist._rules.ipv4.indexOf(clientIp) === -1 && // not explicitly specified in ipv4 list
|
||||
Blacklist._rules.ipv6.indexOf(clientIp) === -1 && // not explicitly specified in ipv6 list
|
||||
!Blacklist._rules.cidr.some(function (subnet) {
|
||||
return ip.cidrSubnet(subnet).contains(clientIp);
|
||||
}) && // not in a blacklisted IPv4 cidr range
|
||||
!ipRangeCheck(clientIp, Blacklist._rules.cidr6) // not in a blacklisted IPv6 cidr range
|
||||
return addr.match(ipaddr.parseCIDR(subnet));
|
||||
// return ip.cidrSubnet(subnet).contains(clientIp);
|
||||
}) // not in a blacklisted IPv4 or IPv6 cidr range
|
||||
) {
|
||||
if (typeof callback === 'function') {
|
||||
setImmediate(callback);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
plugins.fireHook('filter:blacklist.test', { // To return test failure, pass back an error in callback
|
||||
ip: clientIp,
|
||||
}, function (err) {
|
||||
if (err) {
|
||||
analytics.increment('blacklist');
|
||||
}
|
||||
|
||||
if (typeof callback === 'function') {
|
||||
callback(err);
|
||||
} else {
|
||||
return !!err;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
var err = new Error('[[error:blacklisted-ip]]');
|
||||
err.code = 'blacklisted-ip';
|
||||
|
||||
analytics.increment('blacklist');
|
||||
|
||||
if (typeof callback === 'function') {
|
||||
setImmediate(callback, err);
|
||||
} else {
|
||||
@@ -84,11 +100,8 @@ Blacklist.validate = function (rules, callback) {
|
||||
var ipv4 = [];
|
||||
var ipv6 = [];
|
||||
var cidr = [];
|
||||
var cidr6 = [];
|
||||
var invalid = [];
|
||||
|
||||
var isIPv4CidrSubnet = /^(([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]))$/;
|
||||
var isIPv6CidrSubnet = /^s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:)))(%.+)?s*(\/([0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8]))?$/;
|
||||
var inlineCommentMatch = /#.*$/;
|
||||
var whitelist = ['127.0.0.1', '::1', '::ffff:0:127.0.0.1'];
|
||||
|
||||
@@ -101,29 +114,39 @@ Blacklist.validate = function (rules, callback) {
|
||||
|
||||
// Filter out invalid rules
|
||||
rules = rules.filter(function (rule) {
|
||||
if (whitelist.indexOf(rule) !== -1) {
|
||||
var addr;
|
||||
var isRange = false;
|
||||
try {
|
||||
addr = ipaddr.parse(rule);
|
||||
} catch (e) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
try {
|
||||
addr = ipaddr.parseCIDR(rule);
|
||||
isRange = true;
|
||||
} catch (e) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
if (!addr || whitelist.indexOf(rule) !== -1) {
|
||||
invalid.push(rule);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ip.isV4Format(rule)) {
|
||||
ipv4.push(rule);
|
||||
return true;
|
||||
}
|
||||
if (ip.isV6Format(rule)) {
|
||||
ipv6.push(rule);
|
||||
return true;
|
||||
}
|
||||
if (isIPv4CidrSubnet.test(rule)) {
|
||||
if (!isRange) {
|
||||
if (addr.kind() === 'ipv4' && ipaddr.IPv4.isValid(rule)) {
|
||||
ipv4.push(rule);
|
||||
return true;
|
||||
}
|
||||
if (addr.kind() === 'ipv6' && ipaddr.IPv6.isValid(rule)) {
|
||||
ipv6.push(rule);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
cidr.push(rule);
|
||||
return true;
|
||||
}
|
||||
if (isIPv6CidrSubnet.test(rule)) {
|
||||
cidr.push(rule);
|
||||
return true;
|
||||
}
|
||||
|
||||
invalid.push(rule);
|
||||
return false;
|
||||
});
|
||||
|
||||
@@ -132,7 +155,6 @@ Blacklist.validate = function (rules, callback) {
|
||||
ipv4: ipv4,
|
||||
ipv6: ipv6,
|
||||
cidr: cidr,
|
||||
cidr6: cidr6,
|
||||
valid: rules,
|
||||
invalid: invalid,
|
||||
});
|
||||
|
||||
@@ -46,6 +46,7 @@ module.exports = function (middleware) {
|
||||
res.locals.config = res.locals.config || {};
|
||||
var templateValues = {
|
||||
title: meta.config.title || '',
|
||||
'title:url': meta.config['title:url'] || '',
|
||||
description: meta.config.description || '',
|
||||
'cache-buster': meta.config['cache-buster'] || '',
|
||||
'brand:logo': meta.config['brand:logo'] || '',
|
||||
|
||||
@@ -9,7 +9,26 @@
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<textarea class="form-control" id="blacklist-rules">{rules}</textarea>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
<div><canvas id="blacklist:hourly" height="250"></canvas></div>
|
||||
</div>
|
||||
<div class="panel-footer"><small>[[admin/manage/ip-blacklist:analytics.blacklist-hourly]]</small></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
<div><canvas id="blacklist:daily" height="250"></canvas></div>
|
||||
</div>
|
||||
<div class="panel-footer"><small>[[admin/manage/ip-blacklist:analytics.blacklist-daily]]</small></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<textarea id="blacklist-rules">{rules}</textarea>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="panel panel-default">
|
||||
|
||||
@@ -9,6 +9,12 @@
|
||||
<label>[[admin/settings/general:title]]</label>
|
||||
<input class="form-control" type="text" placeholder="[[admin/settings/general:title.name]]" data-field="title" />
|
||||
|
||||
<label for="title:url">[[admin/settings/general:title.url]]</label>
|
||||
<input id ="title:url" type="text" class="form-control" placeholder="[[admin/settings/general:title.url-placeholder]]" data-field="title:url" />
|
||||
<p class="help-block">
|
||||
[[admin/settings/general:title.url-help]]
|
||||
</p>
|
||||
|
||||
<div class="checkbox">
|
||||
<label for="showSiteTitle" class="mdl-switch mdl-js-switch mdl-js-ripple-effect">
|
||||
<input type="checkbox" class="mdl-switch__input" id="showSiteTitle" data-field="showSiteTitle" name="showSiteTitle" />
|
||||
|
||||
Reference in New Issue
Block a user