diff options
Diffstat (limited to 'server/middlewares/validators/videos.ts')
-rw-r--r-- | server/middlewares/validators/videos.ts | 252 |
1 files changed, 121 insertions, 131 deletions
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' | |||
3 | import { UserRight, VideoPrivacy } from '../../../shared' | 3 | import { UserRight, VideoPrivacy } from '../../../shared' |
4 | import { isIdOrUUIDValid, isIdValid } from '../../helpers/custom-validators/misc' | 4 | import { isIdOrUUIDValid, isIdValid } from '../../helpers/custom-validators/misc' |
5 | import { | 5 | import { |
6 | checkVideoExists, | ||
7 | isVideoAbuseReasonValid, | 6 | isVideoAbuseReasonValid, |
8 | isVideoCategoryValid, | 7 | isVideoCategoryValid, |
9 | isVideoDescriptionValid, | 8 | isVideoDescriptionValid, |
10 | isVideoDurationValid, | 9 | isVideoDurationValid, |
10 | isVideoExist, | ||
11 | isVideoFile, | 11 | isVideoFile, |
12 | isVideoLanguageValid, | 12 | isVideoLanguageValid, |
13 | isVideoLicenceValid, | 13 | isVideoLicenceValid, |
@@ -20,12 +20,11 @@ import { | |||
20 | import { getDurationFromVideoFile } from '../../helpers/ffmpeg-utils' | 20 | import { getDurationFromVideoFile } from '../../helpers/ffmpeg-utils' |
21 | import { logger } from '../../helpers/logger' | 21 | import { logger } from '../../helpers/logger' |
22 | import { CONSTRAINTS_FIELDS, SEARCHABLE_COLUMNS } from '../../initializers' | 22 | import { CONSTRAINTS_FIELDS, SEARCHABLE_COLUMNS } from '../../initializers' |
23 | |||
24 | import { database as db } from '../../initializers/database' | 23 | import { database as db } from '../../initializers/database' |
25 | import { UserInstance } from '../../models/account/user-interface' | 24 | import { UserInstance } from '../../models/account/user-interface' |
25 | import { VideoInstance } from '../../models/video/video-interface' | ||
26 | import { authenticate } from '../oauth' | 26 | import { authenticate } from '../oauth' |
27 | import { areValidationErrors, checkErrors } from './utils' | 27 | import { areValidationErrors } from './utils' |
28 | import { isVideoExistsPromise } from '../../helpers/index' | ||
29 | 28 | ||
30 | const videosAddValidator = [ | 29 | const videosAddValidator = [ |
31 | body('videofile').custom((value, { req }) => isVideoFile(req.files)).withMessage( | 30 | body('videofile').custom((value, { req }) => isVideoFile(req.files)).withMessage( |
@@ -42,68 +41,58 @@ const videosAddValidator = [ | |||
42 | body('privacy').custom(isVideoPrivacyValid).withMessage('Should have correct video privacy'), | 41 | body('privacy').custom(isVideoPrivacyValid).withMessage('Should have correct video privacy'), |
43 | body('tags').optional().custom(isVideoTagsValid).withMessage('Should have correct tags'), | 42 | body('tags').optional().custom(isVideoTagsValid).withMessage('Should have correct tags'), |
44 | 43 | ||
45 | (req: express.Request, res: express.Response, next: express.NextFunction) => { | 44 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { |
46 | logger.debug('Checking videosAdd parameters', { parameters: req.body, files: req.files }) | 45 | logger.debug('Checking videosAdd parameters', { parameters: req.body, files: req.files }) |
47 | 46 | ||
48 | checkErrors(req, res, () => { | 47 | if (areValidationErrors(req, res)) return |
49 | const videoFile: Express.Multer.File = req.files['videofile'][0] | 48 | |
50 | const user = res.locals.oauth.token.User | 49 | const videoFile: Express.Multer.File = req.files['videofile'][0] |
50 | const user = res.locals.oauth.token.User | ||
51 | 51 | ||
52 | return db.VideoChannel.loadByIdAndAccount(req.body.channelId, user.Account.id) | 52 | const videoChannel = await db.VideoChannel.loadByIdAndAccount(req.body.channelId, user.Account.id) |
53 | .then(videoChannel => { | 53 | if (!videoChannel) { |
54 | if (!videoChannel) { | 54 | res.status(400) |
55 | res.status(400) | 55 | .json({ error: 'Unknown video video channel for this account.' }) |
56 | .json({ error: 'Unknown video video channel for this account.' }) | 56 | .end() |
57 | .end() | ||
58 | 57 | ||
59 | return undefined | 58 | return |
60 | } | 59 | } |
61 | 60 | ||
62 | res.locals.videoChannel = videoChannel | 61 | res.locals.videoChannel = videoChannel |
63 | 62 | ||
64 | return user.isAbleToUploadVideo(videoFile) | 63 | const isAble = await user.isAbleToUploadVideo(videoFile) |
65 | }) | 64 | if (isAble === false) { |
66 | .then(isAble => { | 65 | res.status(403) |
67 | if (isAble === false) { | 66 | .json({ error: 'The user video quota is exceeded with this video.' }) |
68 | res.status(403) | 67 | .end() |
69 | .json({ error: 'The user video quota is exceeded with this video.' }) | 68 | |
70 | .end() | 69 | return |
71 | 70 | } | |
72 | return undefined | 71 | |
73 | } | 72 | let duration: number |
74 | 73 | ||
75 | return getDurationFromVideoFile(videoFile.path) | 74 | try { |
76 | .catch(err => { | 75 | duration = await getDurationFromVideoFile(videoFile.path) |
77 | logger.error('Invalid input file in videosAddValidator.', err) | 76 | } catch (err) { |
78 | res.status(400) | 77 | logger.error('Invalid input file in videosAddValidator.', err) |
79 | .json({ error: 'Invalid input file.' }) | 78 | res.status(400) |
80 | .end() | 79 | .json({ error: 'Invalid input file.' }) |
81 | 80 | .end() | |
82 | return undefined | 81 | |
83 | }) | 82 | return |
84 | }) | 83 | } |
85 | .then(duration => { | 84 | |
86 | // Previous test failed, abort | 85 | if (!isVideoDurationValid('' + duration)) { |
87 | if (duration === undefined) return undefined | 86 | return res.status(400) |
88 | 87 | .json({ | |
89 | if (!isVideoDurationValid('' + duration)) { | 88 | error: 'Duration of the video file is too big (max: ' + CONSTRAINTS_FIELDS.VIDEOS.DURATION.max + 's).' |
90 | return res.status(400) | 89 | }) |
91 | .json({ | 90 | .end() |
92 | error: 'Duration of the video file is too big (max: ' + CONSTRAINTS_FIELDS.VIDEOS.DURATION.max + 's).' | 91 | } |
93 | }) | 92 | |
94 | .end() | 93 | videoFile['duration'] = duration |
95 | } | 94 | |
96 | 95 | return next() | |
97 | videoFile['duration'] = duration | ||
98 | next() | ||
99 | }) | ||
100 | .catch(err => { | ||
101 | logger.error('Error in video add validator', err) | ||
102 | res.sendStatus(500) | ||
103 | |||
104 | return undefined | ||
105 | }) | ||
106 | }) | ||
107 | } | 96 | } |
108 | ] | 97 | ] |
109 | 98 | ||
@@ -118,61 +107,59 @@ const videosUpdateValidator = [ | |||
118 | body('description').optional().custom(isVideoDescriptionValid).withMessage('Should have a valid description'), | 107 | body('description').optional().custom(isVideoDescriptionValid).withMessage('Should have a valid description'), |
119 | body('tags').optional().custom(isVideoTagsValid).withMessage('Should have correct tags'), | 108 | body('tags').optional().custom(isVideoTagsValid).withMessage('Should have correct tags'), |
120 | 109 | ||
121 | (req: express.Request, res: express.Response, next: express.NextFunction) => { | 110 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { |
122 | logger.debug('Checking videosUpdate parameters', { parameters: req.body }) | 111 | logger.debug('Checking videosUpdate parameters', { parameters: req.body }) |
123 | 112 | ||
124 | checkErrors(req, res, () => { | 113 | if (areValidationErrors(req, res)) return |
125 | checkVideoExists(req.params.id, res, () => { | 114 | if (!await isVideoExist(req.params.id, res)) return |
126 | const video = res.locals.video | 115 | |
127 | 116 | const video = res.locals.video | |
128 | // We need to make additional checks | 117 | |
129 | if (video.isOwned() === false) { | 118 | // We need to make additional checks |
130 | return res.status(403) | 119 | if (video.isOwned() === false) { |
131 | .json({ error: 'Cannot update video of another server' }) | 120 | return res.status(403) |
132 | .end() | 121 | .json({ error: 'Cannot update video of another server' }) |
133 | } | 122 | .end() |
134 | 123 | } | |
135 | if (video.VideoChannel.Account.userId !== res.locals.oauth.token.User.id) { | 124 | |
136 | return res.status(403) | 125 | if (video.VideoChannel.Account.userId !== res.locals.oauth.token.User.id) { |
137 | .json({ error: 'Cannot update video of another user' }) | 126 | return res.status(403) |
138 | .end() | 127 | .json({ error: 'Cannot update video of another user' }) |
139 | } | 128 | .end() |
140 | 129 | } | |
141 | if (video.privacy !== VideoPrivacy.PRIVATE && req.body.privacy === VideoPrivacy.PRIVATE) { | 130 | |
142 | return res.status(409) | 131 | if (video.privacy !== VideoPrivacy.PRIVATE && req.body.privacy === VideoPrivacy.PRIVATE) { |
143 | .json({ error: 'Cannot set "private" a video that was not private anymore.' }) | 132 | return res.status(409) |
144 | .end() | 133 | .json({ error: 'Cannot set "private" a video that was not private anymore.' }) |
145 | } | 134 | .end() |
146 | 135 | } | |
147 | next() | 136 | |
148 | }) | 137 | return next() |
149 | }) | ||
150 | } | 138 | } |
151 | ] | 139 | ] |
152 | 140 | ||
153 | const videosGetValidator = [ | 141 | const videosGetValidator = [ |
154 | param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), | 142 | param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), |
155 | 143 | ||
156 | (req: express.Request, res: express.Response, next: express.NextFunction) => { | 144 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { |
157 | logger.debug('Checking videosGet parameters', { parameters: req.params }) | 145 | logger.debug('Checking videosGet parameters', { parameters: req.params }) |
158 | 146 | ||
159 | checkErrors(req, res, () => { | 147 | if (areValidationErrors(req, res)) return |
160 | checkVideoExists(req.params.id, res, () => { | 148 | if (!await isVideoExist(req.params.id, res)) return |
161 | const video = res.locals.video | ||
162 | 149 | ||
163 | // Video is not private, anyone can access it | 150 | const video = res.locals.video |
164 | if (video.privacy !== VideoPrivacy.PRIVATE) return next() | ||
165 | 151 | ||
166 | authenticate(req, res, () => { | 152 | // Video is not private, anyone can access it |
167 | if (video.VideoChannel.Account.userId !== res.locals.oauth.token.User.id) { | 153 | if (video.privacy !== VideoPrivacy.PRIVATE) return next() |
168 | return res.status(403) | ||
169 | .json({ error: 'Cannot get this private video of another user' }) | ||
170 | .end() | ||
171 | } | ||
172 | 154 | ||
173 | next() | 155 | authenticate(req, res, () => { |
174 | }) | 156 | if (video.VideoChannel.Account.userId !== res.locals.oauth.token.User.id) { |
175 | }) | 157 | return res.status(403) |
158 | .json({ error: 'Cannot get this private video of another user' }) | ||
159 | .end() | ||
160 | } | ||
161 | |||
162 | return next() | ||
176 | }) | 163 | }) |
177 | } | 164 | } |
178 | ] | 165 | ] |
@@ -180,17 +167,16 @@ const videosGetValidator = [ | |||
180 | const videosRemoveValidator = [ | 167 | const videosRemoveValidator = [ |
181 | param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), | 168 | param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), |
182 | 169 | ||
183 | (req: express.Request, res: express.Response, next: express.NextFunction) => { | 170 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { |
184 | logger.debug('Checking videosRemove parameters', { parameters: req.params }) | 171 | logger.debug('Checking videosRemove parameters', { parameters: req.params }) |
185 | 172 | ||
186 | checkErrors(req, res, () => { | 173 | if (areValidationErrors(req, res)) return |
187 | checkVideoExists(req.params.id, res, () => { | 174 | if (!await isVideoExist(req.params.id, res)) return |
188 | // Check if the user who did the request is able to delete the video | 175 | |
189 | checkUserCanDeleteVideo(res.locals.oauth.token.User, res, () => { | 176 | // Check if the user who did the request is able to delete the video |
190 | next() | 177 | if (!checkUserCanDeleteVideo(res.locals.oauth.token.User, res.locals.video, res)) return |
191 | }) | 178 | |
192 | }) | 179 | return next() |
193 | }) | ||
194 | } | 180 | } |
195 | ] | 181 | ] |
196 | 182 | ||
@@ -201,7 +187,9 @@ const videosSearchValidator = [ | |||
201 | (req: express.Request, res: express.Response, next: express.NextFunction) => { | 187 | (req: express.Request, res: express.Response, next: express.NextFunction) => { |
202 | logger.debug('Checking videosSearch parameters', { parameters: req.params }) | 188 | logger.debug('Checking videosSearch parameters', { parameters: req.params }) |
203 | 189 | ||
204 | checkErrors(req, res, next) | 190 | if (areValidationErrors(req, res)) return |
191 | |||
192 | return next() | ||
205 | } | 193 | } |
206 | ] | 194 | ] |
207 | 195 | ||
@@ -209,12 +197,13 @@ const videoAbuseReportValidator = [ | |||
209 | param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), | 197 | param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), |
210 | body('reason').custom(isVideoAbuseReasonValid).withMessage('Should have a valid reason'), | 198 | body('reason').custom(isVideoAbuseReasonValid).withMessage('Should have a valid reason'), |
211 | 199 | ||
212 | (req: express.Request, res: express.Response, next: express.NextFunction) => { | 200 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { |
213 | logger.debug('Checking videoAbuseReport parameters', { parameters: req.body }) | 201 | logger.debug('Checking videoAbuseReport parameters', { parameters: req.body }) |
214 | 202 | ||
215 | checkErrors(req, res, () => { | 203 | if (areValidationErrors(req, res)) return |
216 | checkVideoExists(req.params.id, res, next) | 204 | if (!await isVideoExist(req.params.id, res)) return |
217 | }) | 205 | |
206 | return next() | ||
218 | } | 207 | } |
219 | ] | 208 | ] |
220 | 209 | ||
@@ -222,12 +211,13 @@ const videoRateValidator = [ | |||
222 | param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), | 211 | param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), |
223 | body('rating').custom(isVideoRatingTypeValid).withMessage('Should have a valid rate type'), | 212 | body('rating').custom(isVideoRatingTypeValid).withMessage('Should have a valid rate type'), |
224 | 213 | ||
225 | (req: express.Request, res: express.Response, next: express.NextFunction) => { | 214 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { |
226 | logger.debug('Checking videoRate parameters', { parameters: req.body }) | 215 | logger.debug('Checking videoRate parameters', { parameters: req.body }) |
227 | 216 | ||
228 | checkErrors(req, res, () => { | 217 | if (areValidationErrors(req, res)) return |
229 | checkVideoExists(req.params.id, res, next) | 218 | if (!await isVideoExist(req.params.id, res)) return |
230 | }) | 219 | |
220 | return next() | ||
231 | } | 221 | } |
232 | ] | 222 | ] |
233 | 223 | ||
@@ -239,7 +229,7 @@ const videosShareValidator = [ | |||
239 | logger.debug('Checking videoShare parameters', { parameters: req.params }) | 229 | logger.debug('Checking videoShare parameters', { parameters: req.params }) |
240 | 230 | ||
241 | if (areValidationErrors(req, res)) return | 231 | if (areValidationErrors(req, res)) return |
242 | if (!await isVideoExistsPromise(req.params.id, res)) return | 232 | if (!await isVideoExist(req.params.id, res)) return |
243 | 233 | ||
244 | const share = await db.VideoShare.load(req.params.accountId, res.locals.video.id) | 234 | const share = await db.VideoShare.load(req.params.accountId, res.locals.video.id) |
245 | if (!share) { | 235 | if (!share) { |
@@ -248,7 +238,6 @@ const videosShareValidator = [ | |||
248 | } | 238 | } |
249 | 239 | ||
250 | res.locals.videoShare = share | 240 | res.locals.videoShare = share |
251 | |||
252 | return next() | 241 | return next() |
253 | } | 242 | } |
254 | ] | 243 | ] |
@@ -270,24 +259,25 @@ export { | |||
270 | 259 | ||
271 | // --------------------------------------------------------------------------- | 260 | // --------------------------------------------------------------------------- |
272 | 261 | ||
273 | function checkUserCanDeleteVideo (user: UserInstance, res: express.Response, callback: () => void) { | 262 | function checkUserCanDeleteVideo (user: UserInstance, video: VideoInstance, res: express.Response) { |
274 | // Retrieve the user who did the request | 263 | // Retrieve the user who did the request |
275 | if (res.locals.video.isOwned() === false) { | 264 | if (video.isOwned() === false) { |
276 | return res.status(403) | 265 | res.status(403) |
277 | .json({ error: 'Cannot remove video of another server, blacklist it' }) | 266 | .json({ error: 'Cannot remove video of another server, blacklist it' }) |
278 | .end() | 267 | .end() |
268 | return false | ||
279 | } | 269 | } |
280 | 270 | ||
281 | // Check if the user can delete the video | 271 | // Check if the user can delete the video |
282 | // The user can delete it if s/he is an admin | 272 | // The user can delete it if s/he is an admin |
283 | // Or if s/he is the video's account | 273 | // Or if s/he is the video's account |
284 | const account = res.locals.video.VideoChannel.Account | 274 | const account = video.VideoChannel.Account |
285 | if (user.hasRight(UserRight.REMOVE_ANY_VIDEO) === false && account.userId !== user.id) { | 275 | if (user.hasRight(UserRight.REMOVE_ANY_VIDEO) === false && account.userId !== user.id) { |
286 | return res.status(403) | 276 | res.status(403) |
287 | .json({ error: 'Cannot remove video of another user' }) | 277 | .json({ error: 'Cannot remove video of another user' }) |
288 | .end() | 278 | .end() |
279 | return false | ||
289 | } | 280 | } |
290 | 281 | ||
291 | // If we reach this comment, we can delete the video | 282 | return true |
292 | callback() | ||
293 | } | 283 | } |