Add ability for an administrator to remove any video (#61)
[oweals/peertube.git] / server / middlewares / validators / videos.js
1 'use strict'
2
3 const checkErrors = require('./utils').checkErrors
4 const constants = require('../../initializers/constants')
5 const customVideosValidators = require('../../helpers/custom-validators').videos
6 const db = require('../../initializers/database')
7 const logger = require('../../helpers/logger')
8
9 const validatorsVideos = {
10   videosAdd,
11   videosUpdate,
12   videosGet,
13   videosRemove,
14   videosSearch,
15
16   videoAbuseReport,
17
18   videoRate,
19
20   videosBlacklist
21 }
22
23 function videosAdd (req, res, next) {
24   req.checkBody('videofile', 'Should have a valid file').isVideoFile(req.files)
25   req.checkBody('name', 'Should have a valid name').isVideoNameValid()
26   req.checkBody('category', 'Should have a valid category').isVideoCategoryValid()
27   req.checkBody('licence', 'Should have a valid licence').isVideoLicenceValid()
28   req.checkBody('language', 'Should have a valid language').optional().isVideoLanguageValid()
29   req.checkBody('nsfw', 'Should have a valid NSFW attribute').isVideoNSFWValid()
30   req.checkBody('description', 'Should have a valid description').isVideoDescriptionValid()
31   req.checkBody('tags', 'Should have correct tags').optional().isVideoTagsValid()
32
33   logger.debug('Checking videosAdd parameters', { parameters: req.body, files: req.files })
34
35   checkErrors(req, res, function () {
36     const videoFile = req.files.videofile[0]
37
38     db.Video.getDurationFromFile(videoFile.path, function (err, duration) {
39       if (err) {
40         return res.status(400).send('Cannot retrieve metadata of the file.')
41       }
42
43       if (!customVideosValidators.isVideoDurationValid(duration)) {
44         return res.status(400).send('Duration of the video file is too big (max: ' + constants.CONSTRAINTS_FIELDS.VIDEOS.DURATION.max + 's).')
45       }
46
47       videoFile.duration = duration
48       next()
49     })
50   })
51 }
52
53 function videosUpdate (req, res, next) {
54   req.checkParams('id', 'Should have a valid id').notEmpty().isUUID(4)
55   req.checkBody('name', 'Should have a valid name').optional().isVideoNameValid()
56   req.checkBody('category', 'Should have a valid category').optional().isVideoCategoryValid()
57   req.checkBody('licence', 'Should have a valid licence').optional().isVideoLicenceValid()
58   req.checkBody('language', 'Should have a valid language').optional().isVideoLanguageValid()
59   req.checkBody('nsfw', 'Should have a valid NSFW attribute').optional().isVideoNSFWValid()
60   req.checkBody('description', 'Should have a valid description').optional().isVideoDescriptionValid()
61   req.checkBody('tags', 'Should have correct tags').optional().isVideoTagsValid()
62
63   logger.debug('Checking videosUpdate parameters', { parameters: req.body })
64
65   checkErrors(req, res, function () {
66     checkVideoExists(req.params.id, res, function () {
67       // We need to make additional checks
68       if (res.locals.video.isOwned() === false) {
69         return res.status(403).send('Cannot update video of another pod')
70       }
71
72       if (res.locals.video.Author.userId !== res.locals.oauth.token.User.id) {
73         return res.status(403).send('Cannot update video of another user')
74       }
75
76       next()
77     })
78   })
79 }
80
81 function videosGet (req, res, next) {
82   req.checkParams('id', 'Should have a valid id').notEmpty().isUUID(4)
83
84   logger.debug('Checking videosGet parameters', { parameters: req.params })
85
86   checkErrors(req, res, function () {
87     checkVideoExists(req.params.id, res, next)
88   })
89 }
90
91 function videosRemove (req, res, next) {
92   req.checkParams('id', 'Should have a valid id').notEmpty().isUUID(4)
93
94   logger.debug('Checking videosRemove parameters', { parameters: req.params })
95
96   checkErrors(req, res, function () {
97     checkVideoExists(req.params.id, res, function () {
98       // We need to make additional checks
99
100       // Check if the user who did the request is able to delete the video
101       checkUserCanDeleteVideo(res.locals.oauth.token.User.id, res, function () {
102         next()
103       })
104     })
105   })
106 }
107
108 function videosSearch (req, res, next) {
109   const searchableColumns = constants.SEARCHABLE_COLUMNS.VIDEOS
110   req.checkParams('value', 'Should have a valid search').notEmpty()
111   req.checkQuery('field', 'Should have correct searchable column').optional().isIn(searchableColumns)
112
113   logger.debug('Checking videosSearch parameters', { parameters: req.params })
114
115   checkErrors(req, res, next)
116 }
117
118 function videoAbuseReport (req, res, next) {
119   req.checkParams('id', 'Should have a valid id').notEmpty().isUUID(4)
120   req.checkBody('reason', 'Should have a valid reason').isVideoAbuseReasonValid()
121
122   logger.debug('Checking videoAbuseReport parameters', { parameters: req.body })
123
124   checkErrors(req, res, function () {
125     checkVideoExists(req.params.id, res, next)
126   })
127 }
128
129 function videoRate (req, res, next) {
130   req.checkParams('id', 'Should have a valid id').notEmpty().isUUID(4)
131   req.checkBody('rating', 'Should have a valid rate type').isVideoRatingTypeValid()
132
133   logger.debug('Checking videoRate parameters', { parameters: req.body })
134
135   checkErrors(req, res, function () {
136     checkVideoExists(req.params.id, res, next)
137   })
138 }
139
140 // ---------------------------------------------------------------------------
141
142 module.exports = validatorsVideos
143
144 // ---------------------------------------------------------------------------
145
146 function checkVideoExists (id, res, callback) {
147   db.Video.loadAndPopulateAuthorAndPodAndTags(id, function (err, video) {
148     if (err) {
149       logger.error('Error in video request validator.', { error: err })
150       return res.sendStatus(500)
151     }
152
153     if (!video) return res.status(404).send('Video not found')
154
155     res.locals.video = video
156     callback()
157   })
158 }
159
160 function checkUserCanDeleteVideo (userId, res, callback) {
161   // Retrieve the user who did the request
162   db.User.loadById(userId, function (err, user) {
163     if (err) {
164       logger.error('Error in video request validator.', { error: err })
165       return res.sendStatus(500)
166     }
167
168     // Check if the user can delete the video
169     //  The user can delete it if s/he an admin
170     //  Or if s/he is the video's author
171     if (user.isAdmin() === false) {
172       if (res.locals.video.isOwned() === false) {
173         return res.status(403).send('Cannot remove video of another pod')
174       }
175
176       if (res.locals.video.Author.userId !== res.locals.oauth.token.User.id) {
177         return res.status(403).send('Cannot remove video of another user')
178       }
179     }
180
181     // If we reach this comment, we can delete the video
182     callback()
183   })
184 }
185
186 function checkVideoIsBlacklistable (req, res, callback) {
187   if (res.locals.video.isOwned() === true) {
188         return res.status(403).send('Cannot blacklist a local video')
189   }
190
191   callback()
192 }
193
194 function videosBlacklist (req, res, next) {
195   req.checkParams('id', 'Should have a valid id').notEmpty().isUUID(4)
196
197   logger.debug('Checking videosBlacklist parameters', { parameters: req.params })
198
199   checkErrors(req, res, function () {
200     checkVideoExists(req.params.id, res, function() {
201       checkVideoIsBlacklistable(req, res, next)
202     })
203   })
204 }