X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;ds=sidebyside;f=server%2Fmiddlewares%2Fvalidators%2Fvideos%2Fvideos.ts;h=1e727533bfef894fcff887883d236ceeda4374c8;hb=cea093bca5b9d311b5c1d0539d53e965c901015b;hp=374a59c50d850c13a83147b1d5b4f81f5045760a;hpb=171efc48e67498406feb6d7873b3482b41505515;p=github%2FChocobozzz%2FPeerTube.git diff --git a/server/middlewares/validators/videos/videos.ts b/server/middlewares/validators/videos/videos.ts index 374a59c50..1e727533b 100644 --- a/server/middlewares/validators/videos/videos.ts +++ b/server/middlewares/validators/videos/videos.ts @@ -1,12 +1,14 @@ -import * as express from 'express' +import express from 'express' import { body, header, param, query, ValidationChain } from 'express-validator' +import { isTestInstance } from '@server/helpers/core-utils' import { getResumableUploadPath } from '@server/helpers/upload' +import { Redis } from '@server/lib/redis' import { isAbleToUploadVideo } from '@server/lib/user' import { getServerActor } from '@server/models/application/application' import { ExpressPromiseHandler } from '@server/types/express' import { MUserAccountId, MVideoFullLight } from '@server/types/models' -import { ServerErrorCode, UserRight, VideoPrivacy } from '../../../../shared' -import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes' +import { getAllPrivacies } from '@shared/core-utils' +import { HttpStatusCode, ServerErrorCode, UserRight, VideoInclude, VideoPrivacy } from '@shared/models' import { exists, isBooleanValid, @@ -28,6 +30,7 @@ import { isVideoFileSizeValid, isVideoFilterValid, isVideoImage, + isVideoIncludeValid, isVideoLanguageValid, isVideoLicenceValid, isVideoNameValid, @@ -99,18 +102,56 @@ const videosAddLegacyValidator = getCommonVideoEditAttributes().concat([ } ]) +const videosResumableUploadIdValidator = [ + (req: express.Request, res: express.Response, next: express.NextFunction) => { + const user = res.locals.oauth.token.User + const uploadId = req.query.upload_id + + if (uploadId.startsWith(user.id + '-') !== true) { + return res.fail({ + status: HttpStatusCode.FORBIDDEN_403, + message: 'You cannot send chunks in another user upload' + }) + } + + return next() + } +] + /** * Gets called after the last PUT request */ const videosAddResumableValidator = [ async (req: express.Request, res: express.Response, next: express.NextFunction) => { const user = res.locals.oauth.token.User - const body: express.CustomUploadXFile = req.body - const file = { ...body, duration: undefined, path: getResumableUploadPath(body.id), filename: body.metadata.filename } - + const file = { ...body, duration: undefined, path: getResumableUploadPath(body.name), filename: body.metadata.filename } const cleanup = () => deleteFileAndCatch(file.path) + const uploadId = req.query.upload_id + const sessionExists = await Redis.Instance.doesUploadSessionExist(uploadId) + + if (sessionExists) { + const sessionResponse = await Redis.Instance.getUploadSession(uploadId) + + if (!sessionResponse) { + res.setHeader('Retry-After', 300) // ask to retry after 5 min, knowing the upload_id is kept for up to 15 min after completion + + return res.fail({ + status: HttpStatusCode.SERVICE_UNAVAILABLE_503, + message: 'The upload is already being processed' + }) + } + + if (isTestInstance()) { + res.setHeader('x-resumable-upload-cached', 'true') + } + + return res.json(sessionResponse) + } + + await Redis.Instance.setUploadSession(uploadId) + if (!await doesVideoChannelOfAccountExist(file.metadata.channelId, user, res)) return cleanup() try { @@ -168,7 +209,7 @@ const videosAddResumableInitValidator = getCommonVideoEditAttributes().concat([ const videoFileMetadata = { mimetype: req.headers['x-upload-content-type'] as string, size: +req.headers['x-upload-content-length'], - originalname: req.body.name + originalname: req.body.filename } const user = res.locals.oauth.token.User @@ -188,7 +229,7 @@ const videosAddResumableInitValidator = getCommonVideoEditAttributes().concat([ // multer required unsetting the Content-Type, now we can set it for node-uploadx req.headers['content-type'] = 'application/json; charset=utf-8' // place previewfile in metadata so that uploadx saves it in .META - if (req.files['previewfile']) req.body.previewfile = req.files['previewfile'] + if (req.files?.['previewfile']) req.body.previewfile = req.files['previewfile'] return next() } @@ -445,6 +486,10 @@ const commonVideosFiltersValidator = [ .optional() .customSanitizer(toArray) .custom(isStringArray).withMessage('Should have a valid one of language array'), + query('privacyOneOf') + .optional() + .customSanitizer(toArray) + .custom(isNumberArray).withMessage('Should have a valid one of privacy array'), query('tagsOneOf') .optional() .customSanitizer(toArray) @@ -463,6 +508,21 @@ const commonVideosFiltersValidator = [ query('filter') .optional() .custom(isVideoFilterValid).withMessage('Should have a valid filter attribute'), + query('include') + .optional() + .custom(isVideoIncludeValid).withMessage('Should have a valid include attribute'), + query('isLocal') + .optional() + .customSanitizer(toBooleanOrNull) + .custom(isBooleanValid).withMessage('Should have a valid local boolean'), + query('hasHLSFiles') + .optional() + .customSanitizer(toBooleanOrNull) + .custom(isBooleanValid).withMessage('Should have a valid has hls boolean'), + query('hasWebtorrentFiles') + .optional() + .customSanitizer(toBooleanOrNull) + .custom(isBooleanValid).withMessage('Should have a valid has webtorrent boolean'), query('skipCount') .optional() .customSanitizer(toBooleanOrNull) @@ -476,16 +536,31 @@ const commonVideosFiltersValidator = [ if (areValidationErrors(req, res)) return - const user = res.locals.oauth ? res.locals.oauth.token.User : undefined - if ( - (req.query.filter === 'all-local' || req.query.filter === 'all') && - (!user || user.hasRight(UserRight.SEE_ALL_VIDEOS) === false) - ) { - res.fail({ - status: HttpStatusCode.UNAUTHORIZED_401, - message: 'You are not allowed to see all local videos.' - }) - return + // FIXME: deprecated in 4.0, to remove + { + if (req.query.filter === 'all-local') { + req.query.include = VideoInclude.NOT_PUBLISHED_STATE + req.query.isLocal = true + req.query.privacyOneOf = getAllPrivacies() + } else if (req.query.filter === 'all') { + req.query.include = VideoInclude.NOT_PUBLISHED_STATE + req.query.privacyOneOf = getAllPrivacies() + } else if (req.query.filter === 'local') { + req.query.isLocal = true + } + + req.query.filter = undefined + } + + const user = res.locals.oauth?.token.User + + if ((!user || user.hasRight(UserRight.SEE_ALL_VIDEOS) !== true)) { + if (req.query.include || req.query.privacyOneOf) { + return res.fail({ + status: HttpStatusCode.UNAUTHORIZED_401, + message: 'You are not allowed to see all videos.' + }) + } } return next() @@ -498,6 +573,7 @@ export { videosAddLegacyValidator, videosAddResumableValidator, videosAddResumableInitValidator, + videosResumableUploadIdValidator, videosUpdateValidator, videosGetValidator,