X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=server%2Fmiddlewares%2Fvalidators%2Fvideos.ts;h=8a9b383b88c567abf1b0db3f1eb5d7673a2a24f4;hb=72c7248b6fdcdb2175e726ff51b42e7555f2bd84;hp=0a88e064e54a84b3fbebeadefdb489073d661375;hpb=0a6658fdcbd779ada8f3758048c326e997902d5a;p=github%2FChocobozzz%2FPeerTube.git diff --git a/server/middlewares/validators/videos.ts b/server/middlewares/validators/videos.ts index 0a88e064e..8a9b383b8 100644 --- a/server/middlewares/validators/videos.ts +++ b/server/middlewares/validators/videos.ts @@ -1,145 +1,206 @@ -import 'express-validator' +import { body, param, query } from 'express-validator/check' 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 { CONSTRAINTS_FIELDS, SEARCHABLE_COLUMNS } from '../../initializers' -import { logger, isVideoDurationValid } from '../../helpers' -import { VideoInstance } from '../../models' - -function videosAddValidator (req: express.Request, res: express.Response, next: express.NextFunction) { - // FIXME: Don't write an error message, it seems there is a bug with express-validator - // 'Should have a valid file' - req.checkBody('videofile').isVideoFile(req.files) - req.checkBody('name', 'Should have a valid name').isVideoNameValid() - req.checkBody('category', 'Should have a valid category').isVideoCategoryValid() - req.checkBody('licence', 'Should have a valid licence').isVideoLicenceValid() - req.checkBody('language', 'Should have a valid language').optional().isVideoLanguageValid() - req.checkBody('nsfw', 'Should have a valid NSFW attribute').isVideoNSFWValid() - req.checkBody('description', 'Should have a valid description').isVideoDescriptionValid() - req.checkBody('tags', 'Should have correct tags').optional().isVideoTagsValid() - - logger.debug('Checking videosAdd parameters', { parameters: req.body, files: req.files }) - - checkErrors(req, res, function () { - const videoFile = req.files.videofile[0] - - db.Video.getDurationFromFile(videoFile.path) - .then(duration => { - if (!isVideoDurationValid('' + duration)) { - return res.status(400).send('Duration of the video file is too big (max: ' + CONSTRAINTS_FIELDS.VIDEOS.DURATION.max + 's).') +import { + logger, + isVideoDurationValid, + isVideoFile, + isVideoNameValid, + isVideoCategoryValid, + isVideoLicenceValid, + isVideoDescriptionValid, + isVideoLanguageValid, + isVideoTagsValid, + isVideoNSFWValid, + isIdOrUUIDValid, + isVideoAbuseReasonValid, + isVideoRatingTypeValid, + getDurationFromVideoFile, + checkVideoExists, + isIdValid +} from '../../helpers' + +const videosAddValidator = [ + body('videofile').custom((value, { req }) => isVideoFile(req.files)).withMessage( + 'This file is not supported. Please, make sure it is of the following type : ' + + CONSTRAINTS_FIELDS.VIDEOS.EXTNAME.join(', ') + ), + body('name').custom(isVideoNameValid).withMessage('Should have a valid name'), + body('category').custom(isVideoCategoryValid).withMessage('Should have a valid category'), + body('licence').custom(isVideoLicenceValid).withMessage('Should have a valid licence'), + body('language').optional().custom(isVideoLanguageValid).withMessage('Should have a valid language'), + body('nsfw').custom(isVideoNSFWValid).withMessage('Should have a valid NSFW attribute'), + body('description').custom(isVideoDescriptionValid).withMessage('Should have a valid description'), + body('channelId').custom(isIdValid).withMessage('Should have correct video channel id'), + body('tags').optional().custom(isVideoTagsValid).withMessage('Should have correct tags'), + + (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 + + return db.VideoChannel.loadByIdAndAuthor(req.body.channelId, user.Author.id) + .then(videoChannel => { + if (!videoChannel) { + res.status(400) + .json({ error: 'Unknown video video channel for this author.' }) + .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 + + 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 + }) + }) + } +] + +const videosUpdateValidator = [ + param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), + body('name').optional().custom(isVideoNameValid).withMessage('Should have a valid name'), + body('category').optional().custom(isVideoCategoryValid).withMessage('Should have a valid category'), + body('licence').optional().custom(isVideoLicenceValid).withMessage('Should have a valid licence'), + body('language').optional().custom(isVideoLanguageValid).withMessage('Should have a valid language'), + body('nsfw').optional().custom(isVideoNSFWValid).withMessage('Should have a valid NSFW attribute'), + 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) => { + logger.debug('Checking videosUpdate parameters', { parameters: req.body }) + + checkErrors(req, res, () => { + checkVideoExists(req.params.id, res, () => { + // We need to make additional checks + if (res.locals.video.isOwned() === false) { + return res.status(403) + .json({ error: 'Cannot update video of another pod' }) + .end() + } + + if (res.locals.video.VideoChannel.Author.userId !== res.locals.oauth.token.User.id) { + return res.status(403) + .json({ error: 'Cannot update video of another user' }) + .end() } - videoFile['duration'] = duration next() }) - .catch(err => { - logger.error('Error in getting duration from file.', err) - res.status(400).send('Cannot retrieve metadata of the file.') - }) - }) -} - -function videosUpdateValidator (req: express.Request, res: express.Response, next: express.NextFunction) { - req.checkParams('id', 'Should have a valid id').notEmpty().isVideoIdOrUUIDValid() - req.checkBody('name', 'Should have a valid name').optional().isVideoNameValid() - req.checkBody('category', 'Should have a valid category').optional().isVideoCategoryValid() - req.checkBody('licence', 'Should have a valid licence').optional().isVideoLicenceValid() - req.checkBody('language', 'Should have a valid language').optional().isVideoLanguageValid() - req.checkBody('nsfw', 'Should have a valid NSFW attribute').optional().isVideoNSFWValid() - req.checkBody('description', 'Should have a valid description').optional().isVideoDescriptionValid() - req.checkBody('tags', 'Should have correct tags').optional().isVideoTagsValid() - - logger.debug('Checking videosUpdate parameters', { parameters: req.body }) - - checkErrors(req, res, function () { - checkVideoExists(req.params.id, res, function () { - // We need to make additional checks - if (res.locals.video.isOwned() === false) { - return res.status(403).send('Cannot update video of another pod') - } - - if (res.locals.video.Author.userId !== res.locals.oauth.token.User.id) { - return res.status(403).send('Cannot update video of another user') - } - - next() }) - }) -} - -function videosGetValidator (req: express.Request, res: express.Response, next: express.NextFunction) { - req.checkParams('id', 'Should have a valid id').notEmpty().isVideoIdOrUUIDValid() + } +] - logger.debug('Checking videosGet parameters', { parameters: req.params }) +const videosGetValidator = [ + param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), - checkErrors(req, res, function () { - checkVideoExists(req.params.id, res, next) - }) -} + (req: express.Request, res: express.Response, next: express.NextFunction) => { + logger.debug('Checking videosGet parameters', { parameters: req.params }) -function videosRemoveValidator (req: express.Request, res: express.Response, next: express.NextFunction) { - req.checkParams('id', 'Should have a valid id').notEmpty().isVideoIdOrUUIDValid() + checkErrors(req, res, () => { + checkVideoExists(req.params.id, res, next) + }) + } +] - logger.debug('Checking videosRemove parameters', { parameters: req.params }) +const videosRemoveValidator = [ + param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), - checkErrors(req, res, function () { - checkVideoExists(req.params.id, res, function () { - // We need to make additional checks + (req: express.Request, res: express.Response, next: express.NextFunction) => { + logger.debug('Checking videosRemove parameters', { parameters: req.params }) - // Check if the user who did the request is able to delete the video - checkUserCanDeleteVideo(res.locals.oauth.token.User.id, res, function () { - next() + 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.id, res, () => { + next() + }) }) }) - }) -} - -function videosSearchValidator (req: express.Request, res: express.Response, next: express.NextFunction) { - const searchableColumns = SEARCHABLE_COLUMNS.VIDEOS - req.checkParams('value', 'Should have a valid search').notEmpty() - req.checkQuery('field', 'Should have correct searchable column').optional().isIn(searchableColumns) - - logger.debug('Checking videosSearch parameters', { parameters: req.params }) - - checkErrors(req, res, next) -} + } +] -function videoAbuseReportValidator (req: express.Request, res: express.Response, next: express.NextFunction) { - req.checkParams('id', 'Should have a valid id').notEmpty().isVideoIdOrUUIDValid() - req.checkBody('reason', 'Should have a valid reason').isVideoAbuseReasonValid() +const videosSearchValidator = [ + param('value').not().isEmpty().withMessage('Should have a valid search'), + query('field').optional().isIn(SEARCHABLE_COLUMNS.VIDEOS).withMessage('Should have correct searchable column'), - logger.debug('Checking videoAbuseReport parameters', { parameters: req.body }) + (req: express.Request, res: express.Response, next: express.NextFunction) => { + logger.debug('Checking videosSearch parameters', { parameters: req.params }) - checkErrors(req, res, function () { - checkVideoExists(req.params.id, res, next) - }) -} + checkErrors(req, res, next) + } +] -function videoRateValidator (req: express.Request, res: express.Response, next: express.NextFunction) { - req.checkParams('id', 'Should have a valid id').notEmpty().isVideoIdOrUUIDValid() - req.checkBody('rating', 'Should have a valid rate type').isVideoRatingTypeValid() +const videoAbuseReportValidator = [ + param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), + body('reason').custom(isVideoAbuseReasonValid).withMessage('Should have a valid reason'), - logger.debug('Checking videoRate parameters', { parameters: req.body }) + (req: express.Request, res: express.Response, next: express.NextFunction) => { + logger.debug('Checking videoAbuseReport parameters', { parameters: req.body }) - checkErrors(req, res, function () { - checkVideoExists(req.params.id, res, next) - }) -} + checkErrors(req, res, () => { + checkVideoExists(req.params.id, res, next) + }) + } +] -function videosBlacklistValidator (req: express.Request, res: express.Response, next: express.NextFunction) { - req.checkParams('id', 'Should have a valid id').notEmpty().isVideoIdOrUUIDValid() +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'), - logger.debug('Checking videosBlacklist parameters', { parameters: req.params }) + (req: express.Request, res: express.Response, next: express.NextFunction) => { + logger.debug('Checking videoRate parameters', { parameters: req.body }) - checkErrors(req, res, function () { - checkVideoExists(req.params.id, res, function () { - checkVideoIsBlacklistable(req, res, next) + checkErrors(req, res, () => { + checkVideoExists(req.params.id, res, next) }) - }) -} + } +] // --------------------------------------------------------------------------- @@ -152,48 +213,28 @@ export { videoAbuseReportValidator, - videoRateValidator, - - videosBlacklistValidator + videoRateValidator } // --------------------------------------------------------------------------- -function checkVideoExists (id: string, res: express.Response, callback: () => void) { - let promise: Promise - if (validator.isInt(id)) { - promise = db.Video.loadAndPopulateAuthorAndPodAndTags(+id) - } else { // UUID - promise = db.Video.loadByUUIDAndPopulateAuthorAndPodAndTags(id) - } - - promise.then(video => { - if (!video) return res.status(404).send('Video not found') - - res.locals.video = video - callback() - }) - .catch(err => { - logger.error('Error in video request validator.', err) - return res.sendStatus(500) - }) -} - function checkUserCanDeleteVideo (userId: number, res: express.Response, callback: () => void) { // Retrieve the user who did the request db.User.loadById(userId) .then(user => { + if (res.locals.video.isOwned() === false) { + return res.status(403) + .json({ error: 'Cannot remove video of another pod, blacklist it' }) + .end() + } + // 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 author - if (user.isAdmin() === false) { - if (res.locals.video.isOwned() === false) { - return res.status(403).send('Cannot remove video of another pod') - } - - if (res.locals.video.Author.userId !== res.locals.oauth.token.User.id) { - return res.status(403).send('Cannot remove video of another user') - } + if (user.isAdmin() === false && res.locals.video.Author.userId !== res.locals.oauth.token.User.id) { + return res.status(403) + .json({ error: 'Cannot remove video of another user' }) + .end() } // If we reach this comment, we can delete the video @@ -204,11 +245,3 @@ function checkUserCanDeleteVideo (userId: number, res: express.Response, callbac return res.sendStatus(500) }) } - -function checkVideoIsBlacklistable (req: express.Request, res: express.Response, callback: () => void) { - if (res.locals.video.isOwned() === true) { - return res.status(403).send('Cannot blacklist a local video') - } - - callback() -}