action handler improvement. also fixed board-specific IP deleted not working for global staff

merge-requests/208/head
fatchan 5 years ago
parent 497a07c8c3
commit f1707bc5a7
  1. 64
      models/forms/actionhandler.js

@ -22,25 +22,22 @@ const { Posts, Boards, Modlogs } = require(__dirname+'/../../db/')
module.exports = async (req, res, next) => { module.exports = async (req, res, next) => {
//get the ids //if user isnt staff, and they put an action that requires password, e.g. delete/spoiler, then filter posts to only matching password
const postMongoIds = res.locals.posts.map(post => Mongo.ObjectId(post._id));
let passwordPostMongoIds = [];
let passwordPosts = [];
if (res.locals.permLevel >= 4 && res.locals.actions.numPasswords > 0) { if (res.locals.permLevel >= 4 && res.locals.actions.numPasswords > 0) {
const passwordPosts = [];
if (req.body.password && req.body.password.length > 0) { if (req.body.password && req.body.password.length > 0) {
//hash their input and make it a buffer //hash their input and make it a buffer
const inputPasswordHash = createHash('sha256').update(postPasswordSecret + req.body.password).digest('base64'); const inputPasswordHash = createHash('sha256').update(postPasswordSecret + req.body.password).digest('base64');
const inputPasswordBuffer = Buffer.from(inputPasswordHash); const inputPasswordBuffer = Buffer.from(inputPasswordHash);
passwordPosts = res.locals.posts.filter(post => { passwordPosts = res.locals.posts.filter(post => {
if (post.password != null) { if (post.password != null) { //null password doesnt matter for timing attack, it cant be deleted by non-staff
const postBuffer = Buffer.from(post.password); const postBuffer = Buffer.from(post.password);
if (timingSafeEqual(inputPasswordBuffer, postBuffer) === true) { //returns true and passes filter if passwod matched. constant time compare
passwordPostMongoIds.push(Mongo.ObjectId(post._id)); return timingSafeEqual(inputPasswordBuffer, postBuffer);
return true;
}
} }
}); });
} }
//no posts matched password, reject
if (passwordPosts.length === 0) { if (passwordPosts.length === 0) {
return res.status(403).render('message', { return res.status(403).render('message', {
'title': 'Forbidden', 'title': 'Forbidden',
@ -48,9 +45,7 @@ module.exports = async (req, res, next) => {
'redirect': `/${req.params.board ? req.params.board+'/' : 'globalmanage.html'}` 'redirect': `/${req.params.board ? req.params.board+'/' : 'globalmanage.html'}`
}); });
} }
} else { res.locals.posts = passwordPosts
passwordPosts = res.locals.posts;
passwordPostMongoIds = postMongoIds;
} }
//get a map of boards to threads affected //get a map of boards to threads affected
@ -85,7 +80,6 @@ module.exports = async (req, res, next) => {
const messages = []; const messages = [];
const modlogActions = [] const modlogActions = []
const combinedQuery = {}; const combinedQuery = {};
const passwordCombinedQuery = {};
let aggregateNeeded = false; let aggregateNeeded = false;
try { try {
// if getting global banned, board ban doesnt matter // if getting global banned, board ban doesnt matter
@ -115,7 +109,7 @@ module.exports = async (req, res, next) => {
const { message } = await deletePostsFiles(res.locals.posts, false); //delete files, not just unlink const { message } = await deletePostsFiles(res.locals.posts, false); //delete files, not just unlink
messages.push(message); messages.push(message);
} }
const { action, message } = await deletePosts(passwordPosts, req.body.delete_ip_global ? null : req.params.board); const { action, message } = await deletePosts(res.locals.posts, req.body.delete_ip_global ? null : req.params.board);
messages.push(message); messages.push(message);
if (action) { if (action) {
modlogActions.push(req.body.delete || req.body.delete_ip_board || req.body.delete_ip_global); modlogActions.push(req.body.delete || req.body.delete_ip_board || req.body.delete_ip_global);
@ -124,18 +118,18 @@ module.exports = async (req, res, next) => {
} else { } else {
// if it was getting deleted, we cant do any of these // if it was getting deleted, we cant do any of these
if (req.body.unlink_file || req.body.delete_file) { if (req.body.unlink_file || req.body.delete_file) {
const { message, action, query } = await deletePostsFiles(passwordPosts, req.body.unlink_file); const { message, action, query } = await deletePostsFiles(res.locals.posts, req.body.unlink_file);
if (action) { if (action) {
modlogActions.push(req.body.unlink_file || req.body.delete_file); modlogActions.push(req.body.unlink_file || req.body.delete_file);
aggregateNeeded = true; aggregateNeeded = true;
passwordCombinedQuery[action] = { ...passwordCombinedQuery[action], ...query} combinedQuery[action] = { ...combinedQuery[action], ...query}
} }
messages.push(message); messages.push(message);
} else if (req.body.spoiler) { } else if (req.body.spoiler) {
const { message, action, query } = spoilerPosts(passwordPosts); const { message, action, query } = spoilerPosts(res.locals.posts);
if (action) { if (action) {
modlogActions.push(req.body.spoiler); modlogActions.push(req.body.spoiler);
passwordCombinedQuery[action] = { ...passwordCombinedQuery[action], ...query} combinedQuery[action] = { ...combinedQuery[action], ...query}
} }
messages.push(message); messages.push(message);
} }
@ -203,34 +197,12 @@ module.exports = async (req, res, next) => {
messages.push(message); messages.push(message);
} }
} }
const actionBulkWrites = [];
if (Object.keys(combinedQuery).length > 0) { if (Object.keys(combinedQuery).length > 0) {
actionBulkWrites.push({ await Posts.db.updateMany({
'updateMany': { '_id': {
'filter': { '$in': res.locals.posts.map(p => Mongo.ObjectId(p._id))
'_id': {
'$in': postMongoIds
}
},
'update': combinedQuery
}
});
}
if (Object.keys(passwordCombinedQuery).length > 0) {
actionBulkWrites.push({
'updateMany': {
'filter': {
'_id': {
'$in': passwordPostMongoIds
}
},
'update': passwordCombinedQuery
} }
}); }, combinedQuery);
}
//execute actions now
if (actionBulkWrites.length > 0) {
await Posts.db.bulkWrite(actionBulkWrites);
} }
//if there are actions that can cause some rebuilding //if there are actions that can cause some rebuilding
@ -240,8 +212,8 @@ module.exports = async (req, res, next) => {
if (modlogActions.length > 0) { if (modlogActions.length > 0) {
const modlog = {}; const modlog = {};
const logDate = new Date(); //all events current date const logDate = new Date(); //all events current date
for (let i = 0; i < passwordPosts.length; i++) { for (let i = 0; i < res.locals.posts.length; i++) {
const post = passwordPosts[i]; const post = res.locals.posts[i];
if (!modlog[post.board]) { if (!modlog[post.board]) {
//per board actions, all actions combined to one event //per board actions, all actions combined to one event
const logUser = res.locals.permLevel < 4 ? req.session.user.username : 'Unregistered User' const logUser = res.locals.permLevel < 4 ? req.session.user.username : 'Unregistered User'

Loading…
Cancel
Save