forked from flanchan/doushio
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
221 lines
5.2 KiB
221 lines
5.2 KiB
6 years ago
|
var async = require('async'),
|
||
|
authcommon = require('../admin/common'),
|
||
|
check = require('./msgcheck').check,
|
||
|
common = require('../common'),
|
||
|
config = require('../config'),
|
||
|
db = require('../db'),
|
||
|
hooks = require('../hooks');
|
||
|
|
||
|
var RANGES = require('./state').dbCache.ranges;
|
||
|
|
||
|
function can_access_board(ident, board) {
|
||
|
if (board == 'graveyard' && can_administrate(ident))
|
||
|
return true;
|
||
|
if (board == config.STAFF_BOARD && !can_moderate(ident))
|
||
|
return false;
|
||
|
if (ident.ban || ident.suspension)
|
||
|
return false;
|
||
|
if (!temporal_access_check(ident, board))
|
||
|
return false;
|
||
|
return db.is_board(board);
|
||
|
}
|
||
|
exports.can_access_board = can_access_board;
|
||
|
|
||
|
exports.can_access_thread = function (ident, op) {
|
||
|
var tags = db.tags_of(op);
|
||
|
if (!tags)
|
||
|
return false;
|
||
|
for (var i = 0; i < tags.length; i++)
|
||
|
if (can_access_board(ident, tags[i]))
|
||
|
return tags[i];
|
||
|
return false;
|
||
|
};
|
||
|
|
||
|
function temporal_access_check(ident, board) {
|
||
|
var info = {ident: ident, board: board, access: true};
|
||
|
hooks.trigger_sync('temporalAccessCheck', info);
|
||
|
return info.access;
|
||
|
}
|
||
|
exports.temporal_access_check = temporal_access_check;
|
||
|
|
||
|
exports.can_ever_access_board = function (ident, board) {
|
||
|
if (can_access_board(ident, board))
|
||
|
return true;
|
||
|
if (!temporal_access_check(ident, board))
|
||
|
return true;
|
||
|
return false;
|
||
|
};
|
||
|
|
||
|
function can_moderate(ident) {
|
||
|
return (ident.auth === 'Admin' || ident.auth === 'Moderator');
|
||
|
}
|
||
|
exports.can_moderate = can_moderate;
|
||
|
|
||
|
function can_administrate(ident) {
|
||
|
return ident.auth === 'Admin';
|
||
|
}
|
||
|
exports.can_administrate = can_administrate;
|
||
|
|
||
|
function denote_priv(info) {
|
||
|
if (info.data.priv)
|
||
|
info.header.push(' (priv)');
|
||
|
}
|
||
|
|
||
|
function dead_media_paths(paths) {
|
||
|
paths.src = '../dead/src/';
|
||
|
paths.thumb = '../dead/thumb/';
|
||
|
paths.mid = '../dead/mid/';
|
||
|
}
|
||
|
|
||
|
exports.augment_oneesama = function (oneeSama, opts) {
|
||
|
var ident = opts.ident;
|
||
|
oneeSama.ident = ident;
|
||
|
if (can_moderate(ident))
|
||
|
oneeSama.hook('headerName', authcommon.append_mnemonic);
|
||
|
if (can_administrate(ident)) {
|
||
|
oneeSama.hook('headerName', denote_priv);
|
||
|
oneeSama.hook('headerName', authcommon.denote_hidden);
|
||
|
}
|
||
|
if (can_administrate(ident) && opts.board == 'graveyard')
|
||
|
oneeSama.hook('mediaPaths', dead_media_paths);
|
||
|
};
|
||
|
|
||
|
exports.mod_handler = function (func) {
|
||
|
return function (nums, client) {
|
||
|
if (!can_moderate(client.ident))
|
||
|
return false;
|
||
|
var opts = nums.shift();
|
||
|
if (!check({when: 'string'}, opts) || !check('id...', nums))
|
||
|
return false;
|
||
|
if (!(opts.when in authcommon.delayDurations))
|
||
|
return false;
|
||
|
var delay = authcommon.delayDurations[opts.when];
|
||
|
if (!delay)
|
||
|
func(nums, client);
|
||
|
else
|
||
|
setTimeout(func.bind(null, nums, client), delay*1000);
|
||
|
return true;
|
||
|
};
|
||
|
};
|
||
|
|
||
|
function parse_ip(ip) {
|
||
|
var m = ip.match(/^(\d+)\.(\d+)\.(\d+)\.(\d+)(?:\/(\d+))?$/);
|
||
|
if (!m)
|
||
|
return false;
|
||
|
// damn you signed int32s!
|
||
|
var num = 0;
|
||
|
for (var i = 4, shift = 1; i > 0; i--) {
|
||
|
num += parseInt(m[i], 10) * shift;
|
||
|
shift *= 256;
|
||
|
}
|
||
|
|
||
|
var info = {full: ip, num: num};
|
||
|
if (m[5]) {
|
||
|
var bits = parseInt(m[5], 10);
|
||
|
if (bits > 0 && bits <= 32) {
|
||
|
info.mask = 0x100000000 - Math.pow(2, 32 - bits);
|
||
|
info.num &= info.mask;
|
||
|
}
|
||
|
}
|
||
|
return info;
|
||
|
}
|
||
|
|
||
|
function parse_ranges(ranges) {
|
||
|
if (!ranges)
|
||
|
return [];
|
||
|
ranges = ranges.map(function (o) {
|
||
|
if (typeof o == 'object') {
|
||
|
o.ip = parse_ip(o.ip);
|
||
|
return o;
|
||
|
}
|
||
|
else
|
||
|
return {ip: parse_ip(o)};
|
||
|
});
|
||
|
ranges.sort(function (a, b) { return a.ip.num - b.ip.num; });
|
||
|
return ranges;
|
||
|
}
|
||
|
|
||
|
function range_lookup(ranges, num) {
|
||
|
if (!ranges)
|
||
|
return null;
|
||
|
/* Ideally would have a tree lookup here or something */
|
||
|
var result = null;
|
||
|
for (var i = 0; i < ranges.length; i++) {
|
||
|
var box = ranges[i].ip;
|
||
|
/* sint32 issue here doesn't matter for realistic ranges */
|
||
|
if ((box.mask ? (num & box.mask) : num) === box.num)
|
||
|
result = ranges[i];
|
||
|
/* don't break out of loop */
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
hooks.hook('reloadHot', function (hot, cb) {
|
||
|
var r = global.redis;
|
||
|
async.forEach(authcommon.suspensionKeys, function (key, cb) {
|
||
|
global.redis.smembers('hot:' + key, function (err, ranges) {
|
||
|
if (err)
|
||
|
return cb(err);
|
||
|
if (key == 'suspensions')
|
||
|
ranges = parse_suspensions(ranges);
|
||
|
var up = key.toUpperCase();
|
||
|
hot[up] = (hot[up] || []).concat(ranges || []);
|
||
|
RANGES[key] = parse_ranges(hot[up]);
|
||
|
cb(null);
|
||
|
});
|
||
|
}, cb);
|
||
|
});
|
||
|
|
||
|
function parse_suspensions(suspensions) {
|
||
|
if (!suspensions)
|
||
|
return [];
|
||
|
var parsed = [];
|
||
|
suspensions.forEach(function (s) {
|
||
|
try {
|
||
|
parsed.push(JSON.parse(s));
|
||
|
}
|
||
|
catch (e) {
|
||
|
winston.error("Bad suspension JSON: " + s);
|
||
|
}
|
||
|
});
|
||
|
return parsed;
|
||
|
}
|
||
|
|
||
|
exports.lookup_ident = function (ip, country) {
|
||
|
var ident = {ip, country, readOnly: config.READ_ONLY};
|
||
|
if (country
|
||
|
&& config.RESTRICTED_COUNTRIES
|
||
|
&& config.RESTRICTED_COUNTRIES.indexOf(country) >= 0) {
|
||
|
ident.readOnly = true;
|
||
|
}
|
||
|
var num = parse_ip(ip).num;
|
||
|
var ban = range_lookup(RANGES.bans, num);
|
||
|
if (ban) {
|
||
|
ident.ban = ban.ip.full;
|
||
|
return ident;
|
||
|
}
|
||
|
ban = range_lookup(RANGES.timeouts, num);
|
||
|
if (ban) {
|
||
|
ident.ban = ban.ip.full;
|
||
|
ident.timeout = true;
|
||
|
return ident;
|
||
|
}
|
||
|
var suspension = range_lookup(RANGES.suspensions, num);
|
||
|
if (suspension) {
|
||
|
ident.suspension = suspension;
|
||
|
return ident;
|
||
|
}
|
||
|
|
||
|
var priv = range_lookup(RANGES.boxes, num);
|
||
|
if (priv)
|
||
|
ident.priv = priv.ip.full;
|
||
|
|
||
|
var slow = range_lookup(RANGES.slows, num);
|
||
|
if (slow)
|
||
|
ident.slow = slow;
|
||
|
|
||
|
return ident;
|
||
|
};
|
||
|
|
||
|
|