From a2431b7dcbc72c05101dcdbe631ff84a823aeb51 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Mon, 27 Nov 2017 17:30:46 +0100 Subject: Refractor validators --- server/middlewares/async.ts | 7 +- server/middlewares/validators/account.ts | 15 +- .../middlewares/validators/activitypub/activity.ts | 6 +- .../validators/activitypub/signature.ts | 17 +- server/middlewares/validators/follows.ts | 32 ++- server/middlewares/validators/oembed.ts | 59 +++-- server/middlewares/validators/pagination.ts | 9 +- server/middlewares/validators/sort.ts | 6 +- server/middlewares/validators/users.ts | 198 +++++++--------- server/middlewares/validators/utils.ts | 14 +- server/middlewares/validators/video-blacklist.ts | 63 +++--- server/middlewares/validators/video-channels.ts | 129 ++++++----- server/middlewares/validators/videos.ts | 252 ++++++++++----------- server/middlewares/validators/webfinger.ts | 42 ++-- 14 files changed, 398 insertions(+), 451 deletions(-) (limited to 'server/middlewares') diff --git a/server/middlewares/async.ts b/server/middlewares/async.ts index 9692f9be7..534891899 100644 --- a/server/middlewares/async.ts +++ b/server/middlewares/async.ts @@ -1,9 +1,12 @@ -import { Request, Response, NextFunction, RequestHandler } from 'express' import { eachSeries } from 'async' +import { NextFunction, Request, RequestHandler, Response } from 'express' // Syntactic sugar to avoid try/catch in express controllers // Thanks: https://medium.com/@Abazhenov/using-async-await-in-express-with-node-8-b8af872c0016 -function asyncMiddleware (fun: RequestHandler | RequestHandler[]) { + +export type RequestPromiseHandler = (req: Request, res: Response, next: NextFunction) => Promise + +function asyncMiddleware (fun: RequestPromiseHandler | RequestPromiseHandler[]) { return (req: Request, res: Response, next: NextFunction) => { if (Array.isArray(fun) === true) { return eachSeries(fun as RequestHandler[], (f, cb) => { diff --git a/server/middlewares/validators/account.ts b/server/middlewares/validators/account.ts index 47ed6a7bb..70f4e4d3b 100644 --- a/server/middlewares/validators/account.ts +++ b/server/middlewares/validators/account.ts @@ -1,18 +1,19 @@ import * as express from 'express' import { param } from 'express-validator/check' -import { logger } from '../../helpers' -import { checkLocalAccountNameExists, isAccountNameValid } from '../../helpers/custom-validators/accounts' -import { checkErrors } from './utils' +import { logger, isLocalAccountNameExist } from '../../helpers' +import { isAccountNameValid } from '../../helpers/custom-validators/accounts' +import { areValidationErrors } from './utils' const localAccountValidator = [ param('name').custom(isAccountNameValid).withMessage('Should have a valid account name'), - (req: express.Request, res: express.Response, next: express.NextFunction) => { + async (req: express.Request, res: express.Response, next: express.NextFunction) => { logger.debug('Checking localAccountValidator parameters', { parameters: req.params }) - checkErrors(req, res, () => { - checkLocalAccountNameExists(req.params.name, res, next) - }) + if (areValidationErrors(req, res)) return + if (!await isLocalAccountNameExist(req.params.name, res)) return + + return next() } ] diff --git a/server/middlewares/validators/activitypub/activity.ts b/server/middlewares/validators/activitypub/activity.ts index 0de8b2d85..8aa82298c 100644 --- a/server/middlewares/validators/activitypub/activity.ts +++ b/server/middlewares/validators/activitypub/activity.ts @@ -1,7 +1,7 @@ import * as express from 'express' import { body } from 'express-validator/check' import { isRootActivityValid, logger } from '../../../helpers' -import { checkErrors } from '../utils' +import { areValidationErrors } from '../utils' const activityPubValidator = [ body('').custom((value, { req }) => isRootActivityValid(req.body)), @@ -9,7 +9,9 @@ const activityPubValidator = [ (req: express.Request, res: express.Response, next: express.NextFunction) => { logger.debug('Checking activity pub parameters', { parameters: req.body }) - checkErrors(req, res, next) + if (areValidationErrors(req, res)) return + + return next() } ] diff --git a/server/middlewares/validators/activitypub/signature.ts b/server/middlewares/validators/activitypub/signature.ts index 0ce15c1f6..360685512 100644 --- a/server/middlewares/validators/activitypub/signature.ts +++ b/server/middlewares/validators/activitypub/signature.ts @@ -1,14 +1,7 @@ -import { body } from 'express-validator/check' import * as express from 'express' - -import { - logger, - isDateValid, - isSignatureTypeValid, - isSignatureCreatorValid, - isSignatureValueValid -} from '../../../helpers' -import { checkErrors } from '../utils' +import { body } from 'express-validator/check' +import { isDateValid, isSignatureCreatorValid, isSignatureTypeValid, isSignatureValueValid, logger } from '../../../helpers' +import { areValidationErrors } from '../utils' const signatureValidator = [ body('signature.type').custom(isSignatureTypeValid).withMessage('Should have a valid signature type'), @@ -19,7 +12,9 @@ const signatureValidator = [ (req: express.Request, res: express.Response, next: express.NextFunction) => { logger.debug('Checking activitypub signature parameter', { parameters: { signature: req.body.signature } }) - checkErrors(req, res, next) + if (areValidationErrors(req, res)) return + + return next() } ] diff --git a/server/middlewares/validators/follows.ts b/server/middlewares/validators/follows.ts index ddc4c1de1..605872ecf 100644 --- a/server/middlewares/validators/follows.ts +++ b/server/middlewares/validators/follows.ts @@ -4,7 +4,7 @@ import { isTestInstance } from '../../helpers/core-utils' import { isEachUniqueHostValid } from '../../helpers/custom-validators/servers' import { logger } from '../../helpers/logger' import { CONFIG, database as db } from '../../initializers' -import { checkErrors } from './utils' +import { areValidationErrors } from './utils' import { getServerAccount } from '../../helpers/utils' import { isIdOrUUIDValid } from '../../helpers/custom-validators/misc' @@ -23,34 +23,30 @@ const followValidator = [ logger.debug('Checking follow parameters', { parameters: req.body }) - checkErrors(req, res, next) + if (areValidationErrors(req, res)) return + + return next() } ] const removeFollowingValidator = [ param('accountId').custom(isIdOrUUIDValid).withMessage('Should have a valid account id'), - (req: express.Request, res: express.Response, next: express.NextFunction) => { + async (req: express.Request, res: express.Response, next: express.NextFunction) => { logger.debug('Checking unfollow parameters', { parameters: req.params }) - checkErrors(req, res, async () => { - try { - const serverAccount = await getServerAccount() - const follow = await db.AccountFollow.loadByAccountAndTarget(serverAccount.id, req.params.accountId) + if (areValidationErrors(req, res)) return - if (!follow) { - return res.status(404) - .end() - } + const serverAccount = await getServerAccount() + const follow = await db.AccountFollow.loadByAccountAndTarget(serverAccount.id, req.params.accountId) - res.locals.follow = follow + if (!follow) { + return res.status(404) + .end() + } - return next() - } catch (err) { - logger.error('Error in remove following validator.', err) - return res.sendStatus(500) - } - }) + res.locals.follow = follow + return next() } ] diff --git a/server/middlewares/validators/oembed.ts b/server/middlewares/validators/oembed.ts index f8e34d2d4..31f06dc65 100644 --- a/server/middlewares/validators/oembed.ts +++ b/server/middlewares/validators/oembed.ts @@ -1,15 +1,10 @@ -import { query } from 'express-validator/check' import * as express from 'express' +import { query } from 'express-validator/check' import { join } from 'path' - -import { checkErrors } from './utils' +import { isIdOrUUIDValid, isTestInstance, logger } from '../../helpers' import { CONFIG } from '../../initializers' -import { - logger, - isTestInstance, - checkVideoExists, - isIdOrUUIDValid -} from '../../helpers' +import { areValidationErrors } from './utils' +import { isVideoExist } from '../../helpers/custom-validators/videos' const urlShouldStartWith = CONFIG.WEBSERVER.SCHEME + '://' + join(CONFIG.WEBSERVER.HOST, 'videos', 'watch') + '/' const videoWatchRegex = new RegExp('([^/]+)$') @@ -29,33 +24,35 @@ const oembedValidator = [ query('maxheight').optional().isInt().withMessage('Should have a valid max height'), query('format').optional().isIn([ 'xml', 'json' ]).withMessage('Should have a valid format'), - (req: express.Request, res: express.Response, next: express.NextFunction) => { + async (req: express.Request, res: express.Response, next: express.NextFunction) => { logger.debug('Checking oembed parameters', { parameters: req.query }) - checkErrors(req, res, () => { - if (req.query.format !== undefined && req.query.format !== 'json') { - return res.status(501) - .json({ error: 'Requested format is not implemented on server.' }) - .end() - } + if (areValidationErrors(req, res)) return + + if (req.query.format !== undefined && req.query.format !== 'json') { + return res.status(501) + .json({ error: 'Requested format is not implemented on server.' }) + .end() + } + + const startIsOk = req.query.url.startsWith(urlShouldStartWith) + const matches = videoWatchRegex.exec(req.query.url) + if (startIsOk === false || matches === null) { + return res.status(400) + .json({ error: 'Invalid url.' }) + .end() + } - const startIsOk = req.query.url.startsWith(urlShouldStartWith) - const matches = videoWatchRegex.exec(req.query.url) - if (startIsOk === false || matches === null) { - return res.status(400) - .json({ error: 'Invalid url.' }) - .end() - } + const videoId = matches[1] + if (isIdOrUUIDValid(videoId) === false) { + return res.status(400) + .json({ error: 'Invalid video id.' }) + .end() + } - const videoId = matches[1] - if (isIdOrUUIDValid(videoId) === false) { - return res.status(400) - .json({ error: 'Invalid video id.' }) - .end() - } + if (!await isVideoExist(videoId, res)) return - checkVideoExists(videoId, res, next) - }) + return next() } ] diff --git a/server/middlewares/validators/pagination.ts b/server/middlewares/validators/pagination.ts index a5a542cdf..0895b4eb8 100644 --- a/server/middlewares/validators/pagination.ts +++ b/server/middlewares/validators/pagination.ts @@ -1,8 +1,7 @@ -import { query } from 'express-validator/check' import * as express from 'express' - -import { checkErrors } from './utils' +import { query } from 'express-validator/check' import { logger } from '../../helpers' +import { areValidationErrors } from './utils' const paginationValidator = [ query('start').optional().isInt().withMessage('Should have a number start'), @@ -11,7 +10,9 @@ const paginationValidator = [ (req: express.Request, res: express.Response, next: express.NextFunction) => { logger.debug('Checking pagination parameters', { parameters: req.query }) - checkErrors(req, res, next) + if (areValidationErrors(req, res)) return + + return next() } ] diff --git a/server/middlewares/validators/sort.ts b/server/middlewares/validators/sort.ts index 6fea41bb8..636f68885 100644 --- a/server/middlewares/validators/sort.ts +++ b/server/middlewares/validators/sort.ts @@ -1,9 +1,9 @@ import { query } from 'express-validator/check' import * as express from 'express' -import { checkErrors } from './utils' import { logger } from '../../helpers' import { SORTABLE_COLUMNS } from '../../initializers' +import { areValidationErrors } from './utils' // Initialize constants here for better performances const SORTABLE_USERS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.USERS) @@ -43,7 +43,9 @@ function checkSort (sortableColumns: string[]) { (req: express.Request, res: express.Response, next: express.NextFunction) => { logger.debug('Checking sort parameters', { parameters: req.query }) - checkErrors(req, res, next) + if (areValidationErrors(req, res)) return + + return next() } ] } diff --git a/server/middlewares/validators/users.ts b/server/middlewares/validators/users.ts index 6b845f62b..ac7435b7d 100644 --- a/server/middlewares/validators/users.ts +++ b/server/middlewares/validators/users.ts @@ -1,22 +1,19 @@ -import { body, param } from 'express-validator/check' -import 'express-validator' import * as express from 'express' -import * as Promise from 'bluebird' -import * as validator from 'validator' - -import { database as db } from '../../initializers/database' -import { checkErrors } from './utils' +import 'express-validator' +import { body, param } from 'express-validator/check' import { + isIdOrUUIDValid, isSignupAllowed, - logger, - isUserUsernameValid, + isUserDisplayNSFWValid, isUserPasswordValid, + isUserRoleValid, + isUserUsernameValid, isUserVideoQuotaValid, - isUserDisplayNSFWValid, - isIdOrUUIDValid, - isUserRoleValid + logger } from '../../helpers' -import { UserInstance, VideoInstance } from '../../models' +import { isVideoExist } from '../../helpers/custom-validators/videos' +import { database as db } from '../../initializers/database' +import { areValidationErrors } from './utils' const usersAddValidator = [ body('username').custom(isUserUsernameValid).withMessage('Should have a valid username (lowercase alphanumeric characters)'), @@ -25,12 +22,13 @@ const usersAddValidator = [ body('videoQuota').custom(isUserVideoQuotaValid).withMessage('Should have a valid user quota'), body('role').custom(isUserRoleValid).withMessage('Should have a valid role'), - (req: express.Request, res: express.Response, next: express.NextFunction) => { + async (req: express.Request, res: express.Response, next: express.NextFunction) => { logger.debug('Checking usersAdd parameters', { parameters: req.body }) - checkErrors(req, res, () => { - checkUserDoesNotAlreadyExist(req.body.username, req.body.email, res, next) - }) + if (areValidationErrors(req, res)) return + if (!await checkUserNameOrEmailDoesNotAlreadyExist(req.body.username, req.body.email, res)) return + + return next() } ] @@ -39,37 +37,33 @@ const usersRegisterValidator = [ body('password').custom(isUserPasswordValid).withMessage('Should have a valid password'), body('email').isEmail().withMessage('Should have a valid email'), - (req: express.Request, res: express.Response, next: express.NextFunction) => { + async (req: express.Request, res: express.Response, next: express.NextFunction) => { logger.debug('Checking usersRegister parameters', { parameters: req.body }) - checkErrors(req, res, () => { - checkUserDoesNotAlreadyExist(req.body.username, req.body.email, res, next) - }) + if (areValidationErrors(req, res)) return + if (!await checkUserNameOrEmailDoesNotAlreadyExist(req.body.username, req.body.email, res)) return + + return next() } ] const usersRemoveValidator = [ param('id').isInt().not().isEmpty().withMessage('Should have a valid id'), - (req: express.Request, res: express.Response, next: express.NextFunction) => { + async (req: express.Request, res: express.Response, next: express.NextFunction) => { logger.debug('Checking usersRemove parameters', { parameters: req.params }) - checkErrors(req, res, () => { - checkUserExists(req.params.id, res, (err, user) => { - if (err) { - logger.error('Error in usersRemoveValidator.', err) - return res.sendStatus(500) - } - - if (user.username === 'root') { - return res.status(400) - .send({ error: 'Cannot remove the root user' }) - .end() - } - - return next() - }) - }) + if (areValidationErrors(req, res)) return + if (!await checkUserIdExist(req.params.id, res)) return + + const user = res.locals.user + if (user.username === 'root') { + return res.status(400) + .send({ error: 'Cannot remove the root user' }) + .end() + } + + return next() } ] @@ -79,12 +73,13 @@ const usersUpdateValidator = [ body('videoQuota').optional().custom(isUserVideoQuotaValid).withMessage('Should have a valid user quota'), body('role').optional().custom(isUserRoleValid).withMessage('Should have a valid role'), - (req: express.Request, res: express.Response, next: express.NextFunction) => { + async (req: express.Request, res: express.Response, next: express.NextFunction) => { logger.debug('Checking usersUpdate parameters', { parameters: req.body }) - checkErrors(req, res, () => { - checkUserExists(req.params.id, res, next) - }) + if (areValidationErrors(req, res)) return + if (!await checkUserIdExist(req.params.id, res)) return + + return next() } ] @@ -97,64 +92,48 @@ const usersUpdateMeValidator = [ // TODO: Add old password verification logger.debug('Checking usersUpdateMe parameters', { parameters: req.body }) - checkErrors(req, res, next) + if (areValidationErrors(req, res)) return + + return next() } ] const usersGetValidator = [ param('id').isInt().not().isEmpty().withMessage('Should have a valid id'), - (req: express.Request, res: express.Response, next: express.NextFunction) => { - checkErrors(req, res, () => { - checkUserExists(req.params.id, res, next) - }) + async (req: express.Request, res: express.Response, next: express.NextFunction) => { + logger.debug('Checking usersGet parameters', { parameters: req.body }) + + if (areValidationErrors(req, res)) return + if (!await checkUserIdExist(req.params.id, res)) return + + return next() } ] const usersVideoRatingValidator = [ param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid video id'), - (req: express.Request, res: express.Response, next: express.NextFunction) => { + async (req: express.Request, res: express.Response, next: express.NextFunction) => { logger.debug('Checking usersVideoRating parameters', { parameters: req.params }) - checkErrors(req, res, () => { - let videoPromise: Promise - - if (validator.isUUID(req.params.videoId)) { - videoPromise = db.Video.loadByUUID(req.params.videoId) - } else { - videoPromise = db.Video.load(req.params.videoId) - } - - videoPromise - .then(video => { - if (!video) { - return res.status(404) - .json({ error: 'Video not found' }) - .end() - } - - return next() - }) - .catch(err => { - logger.error('Error in user request validator.', err) - return res.sendStatus(500) - }) - }) + if (areValidationErrors(req, res)) return + if (!await isVideoExist(req.params.videoId, res)) return + + return next() } ] const ensureUserRegistrationAllowed = [ - (req: express.Request, res: express.Response, next: express.NextFunction) => { - isSignupAllowed().then(allowed => { - if (allowed === false) { - return res.status(403) - .send({ error: 'User registration is not enabled or user limit is reached.' }) - .end() - } - - return next() - }) + async (req: express.Request, res: express.Response, next: express.NextFunction) => { + const allowed = await isSignupAllowed() + if (allowed === false) { + return res.status(403) + .send({ error: 'User registration is not enabled or user limit is reached.' }) + .end() + } + + return next() } ] @@ -173,37 +152,30 @@ export { // --------------------------------------------------------------------------- -function checkUserExists (id: number, res: express.Response, callback: (err: Error, user: UserInstance) => void) { - db.User.loadById(id) - .then(user => { - if (!user) { - return res.status(404) - .send({ error: 'User not found' }) - .end() - } - - res.locals.user = user - return callback(null, user) - }) - .catch(err => { - logger.error('Error in user request validator.', err) - return res.sendStatus(500) - }) +async function checkUserIdExist (id: number, res: express.Response) { + const user = await db.User.loadById(id) + + if (!user) { + res.status(404) + .send({ error: 'User not found' }) + .end() + + return false + } + + res.locals.user = user + return true } -function checkUserDoesNotAlreadyExist (username: string, email: string, res: express.Response, callback: () => void) { - db.User.loadByUsernameOrEmail(username, email) - .then(user => { - if (user) { - return res.status(409) - .send({ error: 'User with this username of email already exists.' }) - .end() - } - - return callback() - }) - .catch(err => { - logger.error('Error in usersAdd request validator.', err) - return res.sendStatus(500) - }) +async function checkUserNameOrEmailDoesNotAlreadyExist (username: string, email: string, res: express.Response) { + const user = await db.User.loadByUsernameOrEmail(username, email) + + if (user) { + res.status(409) + .send({ error: 'User with this username of email already exists.' }) + .end() + return false + } + + return true } diff --git a/server/middlewares/validators/utils.ts b/server/middlewares/validators/utils.ts index 77a1a0d4b..ca80acf29 100644 --- a/server/middlewares/validators/utils.ts +++ b/server/middlewares/validators/utils.ts @@ -1,19 +1,8 @@ -import { validationResult } from 'express-validator/check' import * as express from 'express' +import { validationResult } from 'express-validator/check' import { logger } from '../../helpers' -function checkErrors (req: express.Request, res: express.Response, next: express.NextFunction) { - const errors = validationResult(req) - - if (!errors.isEmpty()) { - logger.warn('Incorrect request parameters', { path: req.originalUrl, err: errors.mapped() }) - return res.status(400).json({ errors: errors.mapped() }) - } - - return next() -} - function areValidationErrors (req: express.Request, res: express.Response) { const errors = validationResult(req) @@ -30,6 +19,5 @@ function areValidationErrors (req: express.Request, res: express.Response) { // --------------------------------------------------------------------------- export { - checkErrors, areValidationErrors } diff --git a/server/middlewares/validators/video-blacklist.ts b/server/middlewares/validators/video-blacklist.ts index 3c8c31519..f1cc04950 100644 --- a/server/middlewares/validators/video-blacklist.ts +++ b/server/middlewares/validators/video-blacklist.ts @@ -1,35 +1,36 @@ -import { param } from 'express-validator/check' import * as express from 'express' - +import { param } from 'express-validator/check' +import { isIdOrUUIDValid, logger } from '../../helpers' +import { isVideoExist } from '../../helpers/custom-validators/videos' import { database as db } from '../../initializers/database' -import { checkErrors } from './utils' -import { logger, isIdOrUUIDValid, checkVideoExists } from '../../helpers' +import { VideoInstance } from '../../models/video/video-interface' +import { areValidationErrors } from './utils' const videosBlacklistRemoveValidator = [ param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'), - (req: express.Request, res: express.Response, next: express.NextFunction) => { + async (req: express.Request, res: express.Response, next: express.NextFunction) => { logger.debug('Checking blacklistRemove parameters.', { parameters: req.params }) - checkErrors(req, res, () => { - checkVideoExists(req.params.videoId, res, () => { - checkVideoIsBlacklisted(req, res, next) - }) - }) + if (areValidationErrors(req, res)) return + if (!await isVideoExist(req.params.videoId, res)) return + if (!await checkVideoIsBlacklisted(res.locals.video, res)) return + + return next() } ] const videosBlacklistAddValidator = [ param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'), - (req: express.Request, res: express.Response, next: express.NextFunction) => { + async (req: express.Request, res: express.Response, next: express.NextFunction) => { logger.debug('Checking videosBlacklist parameters', { parameters: req.params }) - checkErrors(req, res, () => { - checkVideoExists(req.params.videoId, res, () => { - checkVideoIsBlacklistable(req, res, next) - }) - }) + if (areValidationErrors(req, res)) return + if (!await isVideoExist(req.params.videoId, res)) return + if (!checkVideoIsBlacklistable(res.locals.video, res)) return + + return next() } ] @@ -41,27 +42,27 @@ export { } // --------------------------------------------------------------------------- -function checkVideoIsBlacklistable (req: express.Request, res: express.Response, callback: () => void) { - if (res.locals.video.isOwned() === true) { - return res.status(403) +function checkVideoIsBlacklistable (video: VideoInstance, res: express.Response) { + if (video.isOwned() === true) { + res.status(403) .json({ error: 'Cannot blacklist a local video' }) .end() + + return false } - callback() + return true } -function checkVideoIsBlacklisted (req: express.Request, res: express.Response, callback: () => void) { - db.BlacklistedVideo.loadByVideoId(res.locals.video.id) - .then(blacklistedVideo => { - if (!blacklistedVideo) return res.status(404).send('Blacklisted video not found') +async function checkVideoIsBlacklisted (video: VideoInstance, res: express.Response) { + const blacklistedVideo = await db.BlacklistedVideo.loadByVideoId(video.id) + if (!blacklistedVideo) { + res.status(404) + .send('Blacklisted video not found') - res.locals.blacklistedVideo = blacklistedVideo + return false + } - callback() - }) - .catch(err => { - logger.error('Error in blacklistRemove request validator', { error: err }) - return res.sendStatus(500) - }) + res.locals.blacklistedVideo = blacklistedVideo + return true } diff --git a/server/middlewares/validators/video-channels.ts b/server/middlewares/validators/video-channels.ts index f30fbf0dc..4683c91e1 100644 --- a/server/middlewares/validators/video-channels.ts +++ b/server/middlewares/validators/video-channels.ts @@ -1,29 +1,30 @@ import * as express from 'express' import { body, param } from 'express-validator/check' import { UserRight } from '../../../shared' -import { checkAccountIdExists } from '../../helpers/custom-validators/accounts' import { isIdValid } from '../../helpers/custom-validators/misc' import { - checkVideoChannelExists, isVideoChannelDescriptionValid, - isVideoChannelExistsPromise, + isVideoChannelExist, isVideoChannelNameValid } from '../../helpers/custom-validators/video-channels' import { isIdOrUUIDValid } from '../../helpers/index' import { logger } from '../../helpers/logger' import { database as db } from '../../initializers' import { UserInstance } from '../../models' -import { areValidationErrors, checkErrors } from './utils' +import { areValidationErrors } from './utils' +import { isAccountIdExist } from '../../helpers/custom-validators/accounts' +import { VideoChannelInstance } from '../../models/video/video-channel-interface' const listVideoAccountChannelsValidator = [ param('accountId').custom(isIdOrUUIDValid).withMessage('Should have a valid account id'), - (req: express.Request, res: express.Response, next: express.NextFunction) => { + async (req: express.Request, res: express.Response, next: express.NextFunction) => { logger.debug('Checking listVideoAccountChannelsValidator parameters', { parameters: req.body }) - checkErrors(req, res, () => { - checkAccountIdExists(req.params.accountId, res, next) - }) + if (areValidationErrors(req, res)) return + if (!await isAccountIdExist(req.params.accountId, res)) return + + return next() } ] @@ -34,7 +35,9 @@ const videoChannelsAddValidator = [ (req: express.Request, res: express.Response, next: express.NextFunction) => { logger.debug('Checking videoChannelsAdd parameters', { parameters: req.body }) - checkErrors(req, res, next) + if (areValidationErrors(req, res)) return + + return next() } ] @@ -43,56 +46,56 @@ const videoChannelsUpdateValidator = [ body('name').optional().custom(isVideoChannelNameValid).withMessage('Should have a valid name'), body('description').optional().custom(isVideoChannelDescriptionValid).withMessage('Should have a valid description'), - (req: express.Request, res: express.Response, next: express.NextFunction) => { + async (req: express.Request, res: express.Response, next: express.NextFunction) => { logger.debug('Checking videoChannelsUpdate parameters', { parameters: req.body }) - checkErrors(req, res, () => { - checkVideoChannelExists(req.params.id, res, () => { - // We need to make additional checks - if (res.locals.videoChannel.isOwned() === false) { - return res.status(403) - .json({ error: 'Cannot update video channel of another server' }) - .end() - } - - if (res.locals.videoChannel.Account.userId !== res.locals.oauth.token.User.id) { - return res.status(403) - .json({ error: 'Cannot update video channel of another user' }) - .end() - } - - next() - }) - }) + if (areValidationErrors(req, res)) return + if (!await isVideoChannelExist(req.params.id, res)) return + + // We need to make additional checks + if (res.locals.videoChannel.isOwned() === false) { + return res.status(403) + .json({ error: 'Cannot update video channel of another server' }) + .end() + } + + if (res.locals.videoChannel.Account.userId !== res.locals.oauth.token.User.id) { + return res.status(403) + .json({ error: 'Cannot update video channel of another user' }) + .end() + } + + return next() } ] const videoChannelsRemoveValidator = [ param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), - (req: express.Request, res: express.Response, next: express.NextFunction) => { + async (req: express.Request, res: express.Response, next: express.NextFunction) => { logger.debug('Checking videoChannelsRemove parameters', { parameters: req.params }) - checkErrors(req, res, () => { - checkVideoChannelExists(req.params.id, res, () => { - // Check if the user who did the request is able to delete the video - checkUserCanDeleteVideoChannel(res, () => { - checkVideoChannelIsNotTheLastOne(res, next) - }) - }) - }) + if (areValidationErrors(req, res)) return + if (!await isVideoChannelExist(req.params.id, res)) return + + // Check if the user who did the request is able to delete the video + if (!checkUserCanDeleteVideoChannel(res.locals.user, res.locals.videoChannel, res)) return + if (!await checkVideoChannelIsNotTheLastOne(res)) return + + return next() } ] const videoChannelsGetValidator = [ param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), - (req: express.Request, res: express.Response, next: express.NextFunction) => { + async (req: express.Request, res: express.Response, next: express.NextFunction) => { logger.debug('Checking videoChannelsGet parameters', { parameters: req.params }) - checkErrors(req, res, () => { - checkVideoChannelExists(req.params.id, res, next) - }) + if (areValidationErrors(req, res)) return + if (!await isVideoChannelExist(req.params.id, res)) return + + return next() } ] @@ -104,7 +107,7 @@ const videoChannelsShareValidator = [ logger.debug('Checking videoChannelShare parameters', { parameters: req.params }) if (areValidationErrors(req, res)) return - if (!await isVideoChannelExistsPromise(req.params.id, res)) return + if (!await isVideoChannelExist(req.params.id, res)) return const share = await db.VideoChannelShare.load(res.locals.video.id, req.params.accountId) if (!share) { @@ -131,38 +134,40 @@ export { // --------------------------------------------------------------------------- -function checkUserCanDeleteVideoChannel (res: express.Response, callback: () => void) { - const user: UserInstance = res.locals.oauth.token.User - +function checkUserCanDeleteVideoChannel (user: UserInstance, videoChannel: VideoChannelInstance, res: express.Response) { // Retrieve the user who did the request - if (res.locals.videoChannel.isOwned() === false) { - return res.status(403) + if (videoChannel.isOwned() === false) { + res.status(403) .json({ error: 'Cannot remove video channel of another server.' }) .end() + + return false } // Check if the user can delete the video channel // The user can delete it if s/he is an admin // Or if s/he is the video channel's account - if (user.hasRight(UserRight.REMOVE_ANY_VIDEO_CHANNEL) === false && res.locals.videoChannel.Account.userId !== user.id) { - return res.status(403) + if (user.hasRight(UserRight.REMOVE_ANY_VIDEO_CHANNEL) === false && videoChannel.Account.userId !== user.id) { + res.status(403) .json({ error: 'Cannot remove video channel of another user' }) .end() + + return false } - // If we reach this comment, we can delete the video - callback() + return true } -function checkVideoChannelIsNotTheLastOne (res: express.Response, callback: () => void) { - db.VideoChannel.countByAccount(res.locals.oauth.token.User.Account.id) - .then(count => { - if (count <= 1) { - return res.status(409) - .json({ error: 'Cannot remove the last channel of this user' }) - .end() - } - - callback() - }) +async function checkVideoChannelIsNotTheLastOne (res: express.Response) { + const count = await db.VideoChannel.countByAccount(res.locals.oauth.token.User.Account.id) + + if (count <= 1) { + res.status(409) + .json({ error: 'Cannot remove the last channel of this user' }) + .end() + + return false + } + + return true } diff --git a/server/middlewares/validators/videos.ts b/server/middlewares/validators/videos.ts index 5ffc85210..3cbf98312 100644 --- a/server/middlewares/validators/videos.ts +++ b/server/middlewares/validators/videos.ts @@ -3,11 +3,11 @@ import { body, param, query } from 'express-validator/check' import { UserRight, VideoPrivacy } from '../../../shared' import { isIdOrUUIDValid, isIdValid } from '../../helpers/custom-validators/misc' import { - checkVideoExists, isVideoAbuseReasonValid, isVideoCategoryValid, isVideoDescriptionValid, isVideoDurationValid, + isVideoExist, isVideoFile, isVideoLanguageValid, isVideoLicenceValid, @@ -20,12 +20,11 @@ import { import { getDurationFromVideoFile } from '../../helpers/ffmpeg-utils' import { logger } from '../../helpers/logger' import { CONSTRAINTS_FIELDS, SEARCHABLE_COLUMNS } from '../../initializers' - import { database as db } from '../../initializers/database' import { UserInstance } from '../../models/account/user-interface' +import { VideoInstance } from '../../models/video/video-interface' import { authenticate } from '../oauth' -import { areValidationErrors, checkErrors } from './utils' -import { isVideoExistsPromise } from '../../helpers/index' +import { areValidationErrors } from './utils' const videosAddValidator = [ body('videofile').custom((value, { req }) => isVideoFile(req.files)).withMessage( @@ -42,68 +41,58 @@ const videosAddValidator = [ body('privacy').custom(isVideoPrivacyValid).withMessage('Should have correct video privacy'), body('tags').optional().custom(isVideoTagsValid).withMessage('Should have correct tags'), - (req: express.Request, res: express.Response, next: express.NextFunction) => { + async (req: express.Request, res: express.Response, next: express.NextFunction) => { logger.debug('Checking videosAdd parameters', { parameters: req.body, files: req.files }) - checkErrors(req, res, () => { - const videoFile: Express.Multer.File = req.files['videofile'][0] - const user = res.locals.oauth.token.User + if (areValidationErrors(req, res)) return + + const videoFile: Express.Multer.File = req.files['videofile'][0] + const user = res.locals.oauth.token.User - return db.VideoChannel.loadByIdAndAccount(req.body.channelId, user.Account.id) - .then(videoChannel => { - if (!videoChannel) { - res.status(400) - .json({ error: 'Unknown video video channel for this account.' }) - .end() + const videoChannel = await db.VideoChannel.loadByIdAndAccount(req.body.channelId, user.Account.id) + if (!videoChannel) { + res.status(400) + .json({ error: 'Unknown video video channel for this account.' }) + .end() - return undefined - } - - res.locals.videoChannel = videoChannel - - return user.isAbleToUploadVideo(videoFile) - }) - .then(isAble => { - if (isAble === false) { - res.status(403) - .json({ error: 'The user video quota is exceeded with this video.' }) - .end() - - return undefined - } - - return getDurationFromVideoFile(videoFile.path) - .catch(err => { - logger.error('Invalid input file in videosAddValidator.', err) - res.status(400) - .json({ error: 'Invalid input file.' }) - .end() - - return undefined - }) - }) - .then(duration => { - // Previous test failed, abort - if (duration === undefined) return undefined - - if (!isVideoDurationValid('' + duration)) { - return res.status(400) - .json({ - error: 'Duration of the video file is too big (max: ' + CONSTRAINTS_FIELDS.VIDEOS.DURATION.max + 's).' - }) - .end() - } - - videoFile['duration'] = duration - next() - }) - .catch(err => { - logger.error('Error in video add validator', err) - res.sendStatus(500) - - return undefined - }) - }) + return + } + + res.locals.videoChannel = videoChannel + + const isAble = await user.isAbleToUploadVideo(videoFile) + if (isAble === false) { + res.status(403) + .json({ error: 'The user video quota is exceeded with this video.' }) + .end() + + return + } + + let duration: number + + try { + duration = await getDurationFromVideoFile(videoFile.path) + } catch (err) { + logger.error('Invalid input file in videosAddValidator.', err) + res.status(400) + .json({ error: 'Invalid input file.' }) + .end() + + return + } + + if (!isVideoDurationValid('' + duration)) { + return res.status(400) + .json({ + error: 'Duration of the video file is too big (max: ' + CONSTRAINTS_FIELDS.VIDEOS.DURATION.max + 's).' + }) + .end() + } + + videoFile['duration'] = duration + + return next() } ] @@ -118,61 +107,59 @@ const videosUpdateValidator = [ body('description').optional().custom(isVideoDescriptionValid).withMessage('Should have a valid description'), body('tags').optional().custom(isVideoTagsValid).withMessage('Should have correct tags'), - (req: express.Request, res: express.Response, next: express.NextFunction) => { + async (req: express.Request, res: express.Response, next: express.NextFunction) => { logger.debug('Checking videosUpdate parameters', { parameters: req.body }) - checkErrors(req, res, () => { - checkVideoExists(req.params.id, res, () => { - const video = res.locals.video - - // We need to make additional checks - if (video.isOwned() === false) { - return res.status(403) - .json({ error: 'Cannot update video of another server' }) - .end() - } - - if (video.VideoChannel.Account.userId !== res.locals.oauth.token.User.id) { - return res.status(403) - .json({ error: 'Cannot update video of another user' }) - .end() - } - - if (video.privacy !== VideoPrivacy.PRIVATE && req.body.privacy === VideoPrivacy.PRIVATE) { - return res.status(409) - .json({ error: 'Cannot set "private" a video that was not private anymore.' }) - .end() - } - - next() - }) - }) + if (areValidationErrors(req, res)) return + if (!await isVideoExist(req.params.id, res)) return + + const video = res.locals.video + + // We need to make additional checks + if (video.isOwned() === false) { + return res.status(403) + .json({ error: 'Cannot update video of another server' }) + .end() + } + + if (video.VideoChannel.Account.userId !== res.locals.oauth.token.User.id) { + return res.status(403) + .json({ error: 'Cannot update video of another user' }) + .end() + } + + if (video.privacy !== VideoPrivacy.PRIVATE && req.body.privacy === VideoPrivacy.PRIVATE) { + return res.status(409) + .json({ error: 'Cannot set "private" a video that was not private anymore.' }) + .end() + } + + return next() } ] const videosGetValidator = [ param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), - (req: express.Request, res: express.Response, next: express.NextFunction) => { + async (req: express.Request, res: express.Response, next: express.NextFunction) => { logger.debug('Checking videosGet parameters', { parameters: req.params }) - checkErrors(req, res, () => { - checkVideoExists(req.params.id, res, () => { - const video = res.locals.video + if (areValidationErrors(req, res)) return + if (!await isVideoExist(req.params.id, res)) return - // Video is not private, anyone can access it - if (video.privacy !== VideoPrivacy.PRIVATE) return next() + const video = res.locals.video - authenticate(req, res, () => { - if (video.VideoChannel.Account.userId !== res.locals.oauth.token.User.id) { - return res.status(403) - .json({ error: 'Cannot get this private video of another user' }) - .end() - } + // Video is not private, anyone can access it + if (video.privacy !== VideoPrivacy.PRIVATE) return next() - next() - }) - }) + authenticate(req, res, () => { + if (video.VideoChannel.Account.userId !== res.locals.oauth.token.User.id) { + return res.status(403) + .json({ error: 'Cannot get this private video of another user' }) + .end() + } + + return next() }) } ] @@ -180,17 +167,16 @@ const videosGetValidator = [ const videosRemoveValidator = [ param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), - (req: express.Request, res: express.Response, next: express.NextFunction) => { + async (req: express.Request, res: express.Response, next: express.NextFunction) => { logger.debug('Checking videosRemove parameters', { parameters: req.params }) - checkErrors(req, res, () => { - checkVideoExists(req.params.id, res, () => { - // Check if the user who did the request is able to delete the video - checkUserCanDeleteVideo(res.locals.oauth.token.User, res, () => { - next() - }) - }) - }) + if (areValidationErrors(req, res)) return + if (!await isVideoExist(req.params.id, res)) return + + // Check if the user who did the request is able to delete the video + if (!checkUserCanDeleteVideo(res.locals.oauth.token.User, res.locals.video, res)) return + + return next() } ] @@ -201,7 +187,9 @@ const videosSearchValidator = [ (req: express.Request, res: express.Response, next: express.NextFunction) => { logger.debug('Checking videosSearch parameters', { parameters: req.params }) - checkErrors(req, res, next) + if (areValidationErrors(req, res)) return + + return next() } ] @@ -209,12 +197,13 @@ const videoAbuseReportValidator = [ param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), body('reason').custom(isVideoAbuseReasonValid).withMessage('Should have a valid reason'), - (req: express.Request, res: express.Response, next: express.NextFunction) => { + async (req: express.Request, res: express.Response, next: express.NextFunction) => { logger.debug('Checking videoAbuseReport parameters', { parameters: req.body }) - checkErrors(req, res, () => { - checkVideoExists(req.params.id, res, next) - }) + if (areValidationErrors(req, res)) return + if (!await isVideoExist(req.params.id, res)) return + + return next() } ] @@ -222,12 +211,13 @@ const videoRateValidator = [ param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), body('rating').custom(isVideoRatingTypeValid).withMessage('Should have a valid rate type'), - (req: express.Request, res: express.Response, next: express.NextFunction) => { + async (req: express.Request, res: express.Response, next: express.NextFunction) => { logger.debug('Checking videoRate parameters', { parameters: req.body }) - checkErrors(req, res, () => { - checkVideoExists(req.params.id, res, next) - }) + if (areValidationErrors(req, res)) return + if (!await isVideoExist(req.params.id, res)) return + + return next() } ] @@ -239,7 +229,7 @@ const videosShareValidator = [ logger.debug('Checking videoShare parameters', { parameters: req.params }) if (areValidationErrors(req, res)) return - if (!await isVideoExistsPromise(req.params.id, res)) return + if (!await isVideoExist(req.params.id, res)) return const share = await db.VideoShare.load(req.params.accountId, res.locals.video.id) if (!share) { @@ -248,7 +238,6 @@ const videosShareValidator = [ } res.locals.videoShare = share - return next() } ] @@ -270,24 +259,25 @@ export { // --------------------------------------------------------------------------- -function checkUserCanDeleteVideo (user: UserInstance, res: express.Response, callback: () => void) { +function checkUserCanDeleteVideo (user: UserInstance, video: VideoInstance, res: express.Response) { // Retrieve the user who did the request - if (res.locals.video.isOwned() === false) { - return res.status(403) + if (video.isOwned() === false) { + res.status(403) .json({ error: 'Cannot remove video of another server, blacklist it' }) .end() + return false } // Check if the user can delete the video // The user can delete it if s/he is an admin // Or if s/he is the video's account - const account = res.locals.video.VideoChannel.Account + const account = video.VideoChannel.Account if (user.hasRight(UserRight.REMOVE_ANY_VIDEO) === false && account.userId !== user.id) { - return res.status(403) + res.status(403) .json({ error: 'Cannot remove video of another user' }) .end() + return false } - // If we reach this comment, we can delete the video - callback() + return true } diff --git a/server/middlewares/validators/webfinger.ts b/server/middlewares/validators/webfinger.ts index 7852c1c2b..34e62c66d 100644 --- a/server/middlewares/validators/webfinger.ts +++ b/server/middlewares/validators/webfinger.ts @@ -1,37 +1,31 @@ import * as express from 'express' import { query } from 'express-validator/check' import { isWebfingerResourceValid } from '../../helpers/custom-validators/webfinger' -import { database as db } from '../../initializers' -import { checkErrors } from './utils' import { logger } from '../../helpers/logger' +import { database as db } from '../../initializers' +import { areValidationErrors } from './utils' const webfingerValidator = [ query('resource').custom(isWebfingerResourceValid).withMessage('Should have a valid webfinger resource'), - (req: express.Request, res: express.Response, next: express.NextFunction) => { + async (req: express.Request, res: express.Response, next: express.NextFunction) => { logger.debug('Checking webfinger parameters', { parameters: req.query }) - checkErrors(req, res, () => { - // Remove 'acct:' from the beginning of the string - const nameWithHost = req.query.resource.substr(5) - const [ name ] = nameWithHost.split('@') - - db.Account.loadLocalByName(name) - .then(account => { - if (!account) { - return res.status(404) - .send({ error: 'Account not found' }) - .end() - } - - res.locals.account = account - return next() - }) - .catch(err => { - logger.error('Error in webfinger validator.', err) - return res.sendStatus(500) - }) - }) + if (areValidationErrors(req, res)) return + + // Remove 'acct:' from the beginning of the string + const nameWithHost = req.query.resource.substr(5) + const [ name ] = nameWithHost.split('@') + + const account = await db.Account.loadLocalByName(name) + if (!account) { + return res.status(404) + .send({ error: 'Account not found' }) + .end() + } + + res.locals.account = account + return next() } ] -- cgit v1.2.3