]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blobdiff - server/middlewares/validators/videos.ts
Don't leak unlisted videos
[github/Chocobozzz/PeerTube.git] / server / middlewares / validators / videos.ts
index ee2ac50c8931840d923447a37181966ab810af8b..a365ed217e8e6a7c2224b117bdd389c880d15548 100644 (file)
@@ -1,27 +1,19 @@
 import * as express from 'express'
+import 'express-validator'
 import { body, param, query } from 'express-validator/check'
 import { UserRight, VideoPrivacy } from '../../../shared'
-import { isIdOrUUIDValid, isIdValid } from '../../helpers/custom-validators/misc'
+import { isBooleanValid, isIdOrUUIDValid, isIdValid, isUUIDValid } from '../../helpers/custom-validators/misc'
 import {
-  isVideoAbuseReasonValid,
-  isVideoCategoryValid,
-  isVideoDescriptionValid,
-  isVideoExist,
-  isVideoFile,
-  isVideoLanguageValid,
-  isVideoLicenceValid,
-  isVideoNameValid,
-  isVideoNSFWValid,
-  isVideoPrivacyValid,
-  isVideoRatingTypeValid,
-  isVideoTagsValid
+  isVideoAbuseReasonValid, isVideoCategoryValid, isVideoDescriptionValid, isVideoExist, isVideoFile, isVideoLanguageValid,
+  isVideoLicenceValid, isVideoNameValid, isVideoPrivacyValid, isVideoRatingTypeValid, isVideoTagsValid
 } from '../../helpers/custom-validators/videos'
 import { getDurationFromVideoFile } from '../../helpers/ffmpeg-utils'
 import { logger } from '../../helpers/logger'
 import { CONSTRAINTS_FIELDS } from '../../initializers'
-import { database as db } from '../../initializers/database'
-import { UserInstance } from '../../models/account/user-interface'
-import { VideoInstance } from '../../models/video/video-interface'
+import { UserModel } from '../../models/account/user'
+import { VideoModel } from '../../models/video/video'
+import { VideoChannelModel } from '../../models/video/video-channel'
+import { VideoShareModel } from '../../models/video/video-share'
 import { authenticate } from '../oauth'
 import { areValidationErrors } from './utils'
 
@@ -31,14 +23,15 @@ const videosAddValidator = [
     + 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('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').custom(isVideoNSFWValid).withMessage('Should have a valid NSFW attribute'),
-  body('description').custom(isVideoDescriptionValid).withMessage('Should have a valid description'),
+  body('nsfw').custom(isBooleanValid).withMessage('Should have a valid NSFW attribute'),
+  body('description').optional().custom(isVideoDescriptionValid).withMessage('Should have a valid description'),
   body('channelId').custom(isIdValid).withMessage('Should have correct video channel id'),
   body('privacy').custom(isVideoPrivacyValid).withMessage('Should have correct video privacy'),
   body('tags').optional().custom(isVideoTagsValid).withMessage('Should have correct tags'),
+  body('commentsEnabled').custom(isBooleanValid).withMessage('Should have comments enabled boolean'),
 
   async (req: express.Request, res: express.Response, next: express.NextFunction) => {
     logger.debug('Checking videosAdd parameters', { parameters: req.body, files: req.files })
@@ -48,7 +41,7 @@ const videosAddValidator = [
     const videoFile: Express.Multer.File = req.files['videofile'][0]
     const user = res.locals.oauth.token.User
 
-    const videoChannel = await db.VideoChannel.loadByIdAndAccount(req.body.channelId, user.Account.id)
+    const videoChannel = await VideoChannelModel.loadByIdAndAccount(req.body.channelId, user.Account.id)
     if (!videoChannel) {
       res.status(400)
         .json({ error: 'Unknown video video channel for this account.' })
@@ -93,10 +86,11 @@ const videosUpdateValidator = [
   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('nsfw').optional().custom(isBooleanValid).withMessage('Should have a valid NSFW attribute'),
   body('privacy').optional().custom(isVideoPrivacyValid).withMessage('Should have correct video privacy'),
   body('description').optional().custom(isVideoDescriptionValid).withMessage('Should have a valid description'),
   body('tags').optional().custom(isVideoTagsValid).withMessage('Should have correct tags'),
+  body('commentsEnabled').optional().custom(isBooleanValid).withMessage('Should have comments enabled boolean'),
 
   async (req: express.Request, res: express.Response, next: express.NextFunction) => {
     logger.debug('Checking videosUpdate parameters', { parameters: req.body })
@@ -140,9 +134,18 @@ const videosGetValidator = [
 
     const video = res.locals.video
 
-    // Video is not private, anyone can access it
-    if (video.privacy !== VideoPrivacy.PRIVATE) return next()
+    // Video is public, anyone can access it
+    if (video.privacy === VideoPrivacy.PUBLIC) return next()
+
+    // Video is unlisted, check we used the uuid to fetch it
+    if (video.privacy === VideoPrivacy.UNLISTED) {
+      if (isUUIDValid(req.params.id)) return next()
+
+      // Don't leak this unlisted video
+      return res.status(404).end()
+    }
 
+    // Video is private, check the user
     authenticate(req, res, () => {
       if (video.VideoChannel.Account.userId !== res.locals.oauth.token.User.id) {
         return res.status(403)
@@ -221,7 +224,7 @@ const videosShareValidator = [
     if (areValidationErrors(req, res)) return
     if (!await isVideoExist(req.params.id, res)) return
 
-    const share = await db.VideoShare.load(req.params.accountId, res.locals.video.id, undefined)
+    const share = await VideoShareModel.load(req.params.accountId, res.locals.video.id, undefined)
     if (!share) {
       return res.status(404)
         .end()
@@ -249,7 +252,7 @@ export {
 
 // ---------------------------------------------------------------------------
 
-function checkUserCanDeleteVideo (user: UserInstance, video: VideoInstance, res: express.Response) {
+function checkUserCanDeleteVideo (user: UserModel, video: VideoModel, res: express.Response) {
   // Retrieve the user who did the request
   if (video.isOwned() === false) {
     res.status(403)
@@ -259,7 +262,7 @@ function checkUserCanDeleteVideo (user: UserInstance, video: VideoInstance, res:
   }
 
   // Check if the user can delete the video
-  // The user can delete it if s/he is an admin
+  // The user can delete it if he has the right
   // Or if s/he is the video's account
   const account = video.VideoChannel.Account
   if (user.hasRight(UserRight.REMOVE_ANY_VIDEO) === false && account.userId !== user.id) {