mirror of
https://github.com/luxfi/pics.git
synced 2026-10-30 15:22:15 +00:00
Fetch account twofactor when needed for checks, since its not stored in session try catch, log warning, and fail twofactor checks on error
127 lines
9.9 KiB
JavaScript
127 lines
9.9 KiB
JavaScript
'use strict';
|
|
|
|
const changeBoardSettings = require(__dirname+'/../../models/forms/changeboardsettings.js')
|
|
, { Permissions } = require(__dirname+'/../../lib/permission/permissions.js')
|
|
, { themes, codeThemes } = require(__dirname+'/../../lib/misc/themes.js')
|
|
, { Accounts, Ratelimits } = require(__dirname+'/../../db/')
|
|
, dynamicResponse = require(__dirname+'/../../lib/misc/dynamic.js')
|
|
, config = require(__dirname+'/../../lib/misc/config.js')
|
|
, paramConverter = require(__dirname+'/../../lib/middleware/input/paramconverter.js')
|
|
, i18n = require(__dirname+'/../../lib/locale/locale.js')
|
|
, doTwoFactor = require(__dirname+'/../../lib/misc/dotwofactor.js')
|
|
, { existsBody, checkSchema, lengthBody, numberBody, minmaxBody, numberBodyVariable,
|
|
inArrayBody, arrayInBody } = require(__dirname+'/../../lib/input/schema.js');
|
|
|
|
module.exports = {
|
|
|
|
paramConverter: paramConverter({
|
|
timeFields: ['delete_protection_age'],
|
|
trimFields: ['twofactor', 'tags', 'announcement', 'description', 'name', 'custom_css', 'language'],
|
|
allowedArrays: ['countries'],
|
|
numberFields: ['lock_reset', 'captcha_reset', 'lock_mode', 'message_r9k_mode', 'file_r9k_mode', 'captcha_mode', 'tph_trigger', 'pph_trigger', 'pph_trigger_action',
|
|
'tph_trigger_action', 'bump_limit', 'reply_limit', 'max_files', 'thread_limit', 'max_thread_message_length', 'max_reply_message_length', 'min_thread_message_length',
|
|
'min_reply_message_length', 'delete_protection_count'],
|
|
}),
|
|
|
|
controller: async (req, res, next) => {
|
|
|
|
const { __ } = res.locals;
|
|
|
|
const { globalLimits, rateLimitCost, forceActionTwofactor } = config.get
|
|
, maxThread = (Math.min(globalLimits.fieldLength.message, res.locals.board.settings.maxThreadMessageLength) || globalLimits.fieldLength.message)
|
|
, maxReply = (Math.min(globalLimits.fieldLength.message, res.locals.board.settings.maxReplyMessageLength) || globalLimits.fieldLength.message);
|
|
|
|
const errors = await checkSchema([
|
|
{ result: existsBody(req.body.twofactor) ? lengthBody(req.body.twofactor, 0, 6) : false, expected: false, error: __('Invalid 2FA code') },
|
|
{ result: async () => {
|
|
if (res.locals.user.twofactor && forceActionTwofactor) {
|
|
//2fA (TOTP) validation
|
|
try {
|
|
const twofactorSecret = (await Accounts.findOne(req.session.user)).twofactor;
|
|
const delta = await doTwoFactor(res.locals.user.username, twofactorSecret, req.body.twofactor || '');
|
|
if (delta === null) {
|
|
return false;
|
|
}
|
|
return true;
|
|
} catch (err) {
|
|
console.warn(err);
|
|
return false;
|
|
}
|
|
} else {
|
|
return true; //Force twofactor not enabled
|
|
}
|
|
}, expected: true, error: __('Invalid 2FA Code') },
|
|
{ result: lengthBody(req.body.description, 0, globalLimits.fieldLength.description), expected: false, error: __('Board description must be %s characters or less', globalLimits.fieldLength.description) },
|
|
{ result: lengthBody(req.body.announcements, 0, 5000), expected: false, error: __('Board announcements must be 5000 characters or less') },
|
|
{ result: lengthBody(req.body.tags, 0, 2000), expected: false, error: __('Tags length must be 2000 characters or less') },
|
|
{ result: lengthBody(req.body.custom_css, 0, globalLimits.customCss.max), expected: false, error: __('Custom CSS must be %s characters or less', globalLimits.customCss.max) },
|
|
{ result: arrayInBody(globalLimits.customCss.filters, req.body.custom_css), permission: Permissions.ROOT, expected: false, error: __('Custom CSS strict mode is enabled and does not allow the following: "%s"', globalLimits.customCss.filters.join('", "')) },
|
|
{ result: lengthBody(req.body.name, 1, globalLimits.fieldLength.boardname), expected: false, error: __('Board name must be 1-%s characters', globalLimits.fieldLength.boardname) },
|
|
{ result: lengthBody(req.body.default_name, 0, 50), expected: false, error: __('Anon name must be 50 characters or less') },
|
|
{ result: numberBody(req.body.reply_limit, globalLimits.replyLimit.min, globalLimits.replyLimit.max), expected: true, error: __('Reply Limit must be %s-%s', globalLimits.replyLimit.min, globalLimits.replyLimit.max) },
|
|
{ result: numberBody(req.body.bump_limit, globalLimits.bumpLimit.min, globalLimits.bumpLimit.max), expected: true, error: __('Bump Limit must be %s-%s', globalLimits.bumpLimit.min, globalLimits.bumpLimit.max) },
|
|
{ result: numberBody(req.body.thread_limit, globalLimits.threadLimit.min, globalLimits.threadLimit.max), expected: true, error: __('Threads Limit must be %s-%s', globalLimits.threadLimit.min, globalLimits.threadLimit.max) },
|
|
{ result: numberBody(req.body.max_files, 0, globalLimits.postFiles.max), expected: true, error: __('Max files must be 0-%s', globalLimits.postFiles.max) },
|
|
{ result: numberBody(req.body.min_thread_message_length, 0, globalLimits.fieldLength.message), expected: true, error: __('Min thread message length must be 0-%s', globalLimits.fieldLength.message) },
|
|
{ result: numberBody(req.body.min_reply_message_length, 0, globalLimits.fieldLength.message), expected: true, error: __('Min reply message length must be 0-%s', globalLimits.fieldLength.message) },
|
|
{ result: numberBody(req.body.max_thread_message_length, 0, globalLimits.fieldLength.message), expected: true, error: __('Max thread message length must be 0-%s', globalLimits.fieldLength.message) },
|
|
{ result: numberBody(req.body.max_reply_message_length, 0, globalLimits.fieldLength.message), expected: true, error: __('Max reply message length must be 0-%s', globalLimits.fieldLength.message) },
|
|
{ result: minmaxBody(req.body.min_thread_message_length, req.body.max_thread_message_length), expected: true, error: __('Min and max thread message lengths must not violate eachother') },
|
|
{ result: minmaxBody(req.body.min_reply_message_length, req.body.max_reply_message_length), expected: true, error: __('Min and max reply message lengths must not violate eachother') },
|
|
{ result: numberBodyVariable(req.body.min_thread_message_length, res.locals.board.settings.minThreadMessageLength,
|
|
req.body.min_thread_message_length, maxThread, req.body.max_thread_message_length), expected: true,
|
|
error: __('Min thread message length must be 0-%s and not more than "Max Thread Message Length" (currently %s)', globalLimits.fieldLength.message, res.locals.board.settings.maxThreadMessageLength) },
|
|
{ result: numberBodyVariable(req.body.min_reply_message_length, res.locals.board.settings.minReplyMessageLength,
|
|
req.body.min_reply_message_length, maxReply, req.body.max_reply_message_length), expected: true,
|
|
error: __('Min reply message length must be 0-%s and not more than "Max Reply Message Length" (currently %s)', globalLimits.fieldLength.message, res.locals.board.settings.maxReplyMessageLength) },
|
|
{ result: numberBodyVariable(req.body.max_thread_message_length, res.locals.board.settings.minThreadMessageLength,
|
|
req.body.min_thread_message_length, globalLimits.fieldLength.message, globalLimits.fieldLength.message), expected: true,
|
|
error: __('Max thread message length must be 0-%s and not less than "Min Thread Message Length" (currently %s)', globalLimits.fieldLength.message, res.locals.board.settings.minThreadMessageLength) },
|
|
{ result: numberBodyVariable(req.body.max_reply_message_length, res.locals.board.settings.minReplyMessageLength,
|
|
req.body.min_reply_message_length, globalLimits.fieldLength.message, globalLimits.fieldLength.message), expected: true,
|
|
error: __('Max reply message length must be 0-%s and not less than "Min Reply Message Length" (currently %s)', globalLimits.fieldLength.message, res.locals.board.settings.minReplyMessageLength) },
|
|
{ result: numberBody(req.body.lock_mode, 0, 2), expected: true, error: __('Invalid lock mode') },
|
|
{ result: numberBody(req.body.captcha_mode, 0, 2), expected: true, error: __('Invalid captcha mode') },
|
|
{ result: numberBody(req.body.tph_trigger, 0, 10000), expected: true, error: __('Invalid tph trigger threshold') },
|
|
{ result: numberBody(req.body.tph_trigger_action, 0, 4), expected: true, error: __('Invalid tph trigger action') },
|
|
{ result: numberBody(req.body.pph_trigger, 0, 10000), expected: true, error: __('Invalid pph trigger threshold') },
|
|
{ result: numberBody(req.body.pph_trigger_action, 0, 4), expected: true, error: __('Invalid pph trigger action') },
|
|
{ result: numberBody(req.body.lock_reset, 0, 2), expected: true, error: __('Invalid trigger reset lock') },
|
|
{ result: numberBody(req.body.captcha_reset, 0, 2), expected: true, error: __('Invalid trigger reset captcha') },
|
|
{ result: numberBody(req.body.delete_protection_age, 0), expected: true, error: __('Invalid OP thread age delete protection') },
|
|
{ result: numberBody(req.body.delete_protection_count, 0), expected: true, error: __('Invalid OP thread reply count delete protection') },
|
|
{ result: inArrayBody(req.body.language, i18n.getLocales()), expected: true, error: __('Invalid language') },
|
|
{ result: inArrayBody(req.body.theme, themes), expected: true, error: __('Invalid theme') },
|
|
{ result: inArrayBody(req.body.code_theme, codeThemes), expected: true, error: __('Invalid code theme') },
|
|
], res.locals.permissions);
|
|
|
|
if (errors.length > 0) {
|
|
return dynamicResponse(req, res, 400, 'message', {
|
|
'title': __('Bad request'),
|
|
'errors': errors,
|
|
'redirect': `/${req.params.board}/manage/settings.html`
|
|
});
|
|
}
|
|
|
|
if (!res.locals.permissions.get(Permissions.BYPASS_RATELIMITS)) {
|
|
const ratelimitBoard = await Ratelimits.incrmentQuota(req.params.board, 'settings', rateLimitCost.boardSettings); //2 changes a minute
|
|
const ratelimitIp = res.locals.anonymizer ? 0 : (await Ratelimits.incrmentQuota(res.locals.ip.cloak, 'settings', rateLimitCost.boardSettings));
|
|
if (ratelimitBoard > 100 || ratelimitIp > 100) {
|
|
return dynamicResponse(req, res, 429, 'message', {
|
|
'title': __('Ratelimited'),
|
|
'error': __('You are changing settings too quickly, please wait a minute and try again'),
|
|
'redirect': `/${req.params.board}/manage/settings.html`
|
|
});
|
|
}
|
|
}
|
|
|
|
try {
|
|
await changeBoardSettings(req, res, next);
|
|
} catch (err) {
|
|
return next(err);
|
|
}
|
|
|
|
}
|
|
|
|
};
|