aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/middlewares
diff options
context:
space:
mode:
Diffstat (limited to 'server/middlewares')
-rw-r--r--server/middlewares/activitypub.ts33
-rw-r--r--server/middlewares/cache.ts72
-rw-r--r--server/middlewares/csp.ts14
-rw-r--r--server/middlewares/oauth.ts4
-rw-r--r--server/middlewares/pagination.ts2
-rw-r--r--server/middlewares/user-right.ts3
-rw-r--r--server/middlewares/validators/account.ts10
-rw-r--r--server/middlewares/validators/activitypub/activity.ts3
-rw-r--r--server/middlewares/validators/avatar.ts2
-rw-r--r--server/middlewares/validators/blocklist.ts31
-rw-r--r--server/middlewares/validators/config.ts21
-rw-r--r--server/middlewares/validators/feeds.ts16
-rw-r--r--server/middlewares/validators/follows.ts61
-rw-r--r--server/middlewares/validators/logs.ts31
-rw-r--r--server/middlewares/validators/oembed.ts8
-rw-r--r--server/middlewares/validators/redundancy.ts45
-rw-r--r--server/middlewares/validators/search.ts3
-rw-r--r--server/middlewares/validators/server.ts4
-rw-r--r--server/middlewares/validators/sort.ts10
-rw-r--r--server/middlewares/validators/user-notifications.ts14
-rw-r--r--server/middlewares/validators/user-subscriptions.ts7
-rw-r--r--server/middlewares/validators/users.ts38
-rw-r--r--server/middlewares/validators/videos/video-abuses.ts14
-rw-r--r--server/middlewares/validators/videos/video-blacklist.ts40
-rw-r--r--server/middlewares/validators/videos/video-captions.ts14
-rw-r--r--server/middlewares/validators/videos/video-channels.ts27
-rw-r--r--server/middlewares/validators/videos/video-comments.ts26
-rw-r--r--server/middlewares/validators/videos/video-imports.ts12
-rw-r--r--server/middlewares/validators/videos/video-playlists.ts408
-rw-r--r--server/middlewares/validators/videos/video-rates.ts24
-rw-r--r--server/middlewares/validators/videos/video-shares.ts7
-rw-r--r--server/middlewares/validators/videos/video-watch.ts7
-rw-r--r--server/middlewares/validators/videos/videos.ts54
33 files changed, 798 insertions, 267 deletions
diff --git a/server/middlewares/activitypub.ts b/server/middlewares/activitypub.ts
index 01e5dd24e..b1e5b5236 100644
--- a/server/middlewares/activitypub.ts
+++ b/server/middlewares/activitypub.ts
@@ -1,11 +1,9 @@
1import { eachSeries } from 'async' 1import { NextFunction, Request, Response } from 'express'
2import { NextFunction, Request, RequestHandler, Response } from 'express'
3import { ActivityPubSignature } from '../../shared' 2import { ActivityPubSignature } from '../../shared'
4import { logger } from '../helpers/logger' 3import { logger } from '../helpers/logger'
5import { isHTTPSignatureVerified, isJsonLDSignatureVerified, parseHTTPSignature } from '../helpers/peertube-crypto' 4import { isHTTPSignatureVerified, isJsonLDSignatureVerified, parseHTTPSignature } from '../helpers/peertube-crypto'
6import { ACCEPT_HEADERS, ACTIVITY_PUB, HTTP_SIGNATURE } from '../initializers' 5import { ACCEPT_HEADERS, ACTIVITY_PUB, HTTP_SIGNATURE } from '../initializers/constants'
7import { getOrCreateActorAndServerAndModel } from '../lib/activitypub' 6import { getOrCreateActorAndServerAndModel } from '../lib/activitypub'
8import { ActorModel } from '../models/activitypub/actor'
9import { loadActorUrlOrGetFromWebfinger } from '../helpers/webfinger' 7import { loadActorUrlOrGetFromWebfinger } from '../helpers/webfinger'
10 8
11async function checkSignature (req: Request, res: Response, next: NextFunction) { 9async function checkSignature (req: Request, res: Response, next: NextFunction) {
@@ -13,7 +11,7 @@ async function checkSignature (req: Request, res: Response, next: NextFunction)
13 const httpSignatureChecked = await checkHttpSignature(req, res) 11 const httpSignatureChecked = await checkHttpSignature(req, res)
14 if (httpSignatureChecked !== true) return 12 if (httpSignatureChecked !== true) return
15 13
16 const actor: ActorModel = res.locals.signature.actor 14 const actor = res.locals.signature.actor
17 15
18 // Forwarded activity 16 // Forwarded activity
19 const bodyActor = req.body.actor 17 const bodyActor = req.body.actor
@@ -30,23 +28,16 @@ async function checkSignature (req: Request, res: Response, next: NextFunction)
30 } 28 }
31} 29}
32 30
33function executeIfActivityPub (fun: RequestHandler | RequestHandler[]) { 31function executeIfActivityPub (req: Request, res: Response, next: NextFunction) {
34 return (req: Request, res: Response, next: NextFunction) => { 32 const accepted = req.accepts(ACCEPT_HEADERS)
35 const accepted = req.accepts(ACCEPT_HEADERS) 33 if (accepted === false || ACTIVITY_PUB.POTENTIAL_ACCEPT_HEADERS.indexOf(accepted) === -1) {
36 if (accepted === false || ACTIVITY_PUB.POTENTIAL_ACCEPT_HEADERS.indexOf(accepted) === -1) { 34 // Bypass this route
37 return next() 35 return next('route')
38 } 36 }
39
40 logger.debug('ActivityPub request for %s.', req.url)
41 37
42 if (Array.isArray(fun) === true) { 38 logger.debug('ActivityPub request for %s.', req.url)
43 return eachSeries(fun as RequestHandler[], (f, cb) => {
44 f(req, res, cb)
45 }, next)
46 }
47 39
48 return (fun as RequestHandler)(req, res, next) 40 return next()
49 }
50} 41}
51 42
52// --------------------------------------------------------------------------- 43// ---------------------------------------------------------------------------
@@ -83,6 +74,8 @@ async function checkHttpSignature (req: Request, res: Response) {
83 74
84 const verified = isHTTPSignatureVerified(parsed, actor) 75 const verified = isHTTPSignatureVerified(parsed, actor)
85 if (verified !== true) { 76 if (verified !== true) {
77 logger.warn('Signature from %s is invalid', actorUrl, { parsed })
78
86 res.sendStatus(403) 79 res.sendStatus(403)
87 return false 80 return false
88 } 81 }
diff --git a/server/middlewares/cache.ts b/server/middlewares/cache.ts
index 8ffe75700..ef8611875 100644
--- a/server/middlewares/cache.ts
+++ b/server/middlewares/cache.ts
@@ -1,72 +1,16 @@
1import * as express from 'express'
2import * as AsyncLock from 'async-lock'
3import { parseDuration } from '../helpers/core-utils'
4import { Redis } from '../lib/redis' 1import { Redis } from '../lib/redis'
5import { logger } from '../helpers/logger' 2import * as apicache from 'apicache'
6 3
7const lock = new AsyncLock({ timeout: 5000 }) 4// Ensure Redis is initialized
5Redis.Instance.init()
8 6
9function cacheRoute (lifetimeArg: string | number) { 7const options = {
10 return async function (req: express.Request, res: express.Response, next: express.NextFunction) { 8 redisClient: Redis.Instance.getClient(),
11 const redisKey = Redis.Instance.generateCachedRouteKey(req) 9 appendKey: () => Redis.Instance.getPrefix()
12
13 try {
14 await lock.acquire(redisKey, async (done) => {
15 const cached = await Redis.Instance.getCachedRoute(req)
16
17 // Not cached
18 if (!cached) {
19 logger.debug('No cached results for route %s.', req.originalUrl)
20
21 const sendSave = res.send.bind(res)
22 const redirectSave = res.redirect.bind(res)
23
24 res.send = (body) => {
25 if (res.statusCode >= 200 && res.statusCode < 400) {
26 const contentType = res.get('content-type')
27 const lifetime = parseDuration(lifetimeArg)
28
29 Redis.Instance.setCachedRoute(req, body, lifetime, contentType, res.statusCode)
30 .then(() => done())
31 .catch(err => {
32 logger.error('Cannot cache route.', { err })
33 return done(err)
34 })
35 } else {
36 done()
37 }
38
39 return sendSave(body)
40 }
41
42 res.redirect = url => {
43 done()
44
45 return redirectSave(url)
46 }
47
48 return next()
49 }
50
51 if (cached.contentType) res.set('content-type', cached.contentType)
52
53 if (cached.statusCode) {
54 const statusCode = parseInt(cached.statusCode, 10)
55 if (!isNaN(statusCode)) res.status(statusCode)
56 }
57
58 logger.debug('Use cached result for %s.', req.originalUrl)
59 res.send(cached.body).end()
60
61 return done()
62 })
63 } catch (err) {
64 logger.error('Cannot serve cached route.', { err })
65 return next()
66 }
67 }
68} 10}
69 11
12const cacheRoute = apicache.options(options).middleware
13
70// --------------------------------------------------------------------------- 14// ---------------------------------------------------------------------------
71 15
72export { 16export {
diff --git a/server/middlewares/csp.ts b/server/middlewares/csp.ts
index 8b919af0d..d484b3021 100644
--- a/server/middlewares/csp.ts
+++ b/server/middlewares/csp.ts
@@ -1,5 +1,5 @@
1import * as helmet from 'helmet' 1import * as helmet from 'helmet'
2import { CONFIG } from '../initializers/constants' 2import { CONFIG } from '../initializers/config'
3 3
4const baseDirectives = Object.assign({}, 4const baseDirectives = Object.assign({},
5 { 5 {
@@ -16,24 +16,22 @@ const baseDirectives = Object.assign({},
16 baseUri: ["'self'"], 16 baseUri: ["'self'"],
17 manifestSrc: ["'self'"], 17 manifestSrc: ["'self'"],
18 frameSrc: ["'self'"], // instead of deprecated child-src / self because of test-embed 18 frameSrc: ["'self'"], // instead of deprecated child-src / self because of test-embed
19 workerSrc: ["'self'"] // instead of deprecated child-src 19 workerSrc: ["'self'", 'blob:'] // instead of deprecated child-src
20 }, 20 },
21 CONFIG.SERVICES['CSP-LOGGER'] ? { reportUri: CONFIG.SERVICES['CSP-LOGGER'] } : {}, 21 CONFIG.CSP.REPORT_URI ? { reportUri: CONFIG.CSP.REPORT_URI } : {},
22 CONFIG.WEBSERVER.SCHEME === 'https' ? { upgradeInsecureRequests: true } : {} 22 CONFIG.WEBSERVER.SCHEME === 'https' ? { upgradeInsecureRequests: true } : {}
23) 23)
24 24
25const baseCSP = helmet.contentSecurityPolicy({ 25const baseCSP = helmet.contentSecurityPolicy({
26 directives: baseDirectives, 26 directives: baseDirectives,
27 browserSniff: false, 27 browserSniff: false,
28 reportOnly: true 28 reportOnly: CONFIG.CSP.REPORT_ONLY
29}) 29})
30 30
31const embedCSP = helmet.contentSecurityPolicy({ 31const embedCSP = helmet.contentSecurityPolicy({
32 directives: Object.assign(baseDirectives, { 32 directives: Object.assign({}, baseDirectives, { frameAncestors: ['*'] }),
33 frameAncestors: ['*']
34 }),
35 browserSniff: false, // assumes a modern browser, but allows CDN in front 33 browserSniff: false, // assumes a modern browser, but allows CDN in front
36 reportOnly: true 34 reportOnly: CONFIG.CSP.REPORT_ONLY
37}) 35})
38 36
39// --------------------------------------------------------------------------- 37// ---------------------------------------------------------------------------
diff --git a/server/middlewares/oauth.ts b/server/middlewares/oauth.ts
index 1d193d467..2b4e300e4 100644
--- a/server/middlewares/oauth.ts
+++ b/server/middlewares/oauth.ts
@@ -1,7 +1,7 @@
1import * as express from 'express' 1import * as express from 'express'
2import * as OAuthServer from 'express-oauth-server' 2import * as OAuthServer from 'express-oauth-server'
3import 'express-validator' 3import 'express-validator'
4import { OAUTH_LIFETIME } from '../initializers' 4import { OAUTH_LIFETIME } from '../initializers/constants'
5import { logger } from '../helpers/logger' 5import { logger } from '../helpers/logger'
6import { Socket } from 'socket.io' 6import { Socket } from 'socket.io'
7import { getAccessToken } from '../lib/oauth-model' 7import { getAccessToken } from '../lib/oauth-model'
@@ -35,6 +35,8 @@ function authenticateSocket (socket: Socket, next: (err?: any) => void) {
35 35
36 logger.debug('Checking socket access token %s.', accessToken) 36 logger.debug('Checking socket access token %s.', accessToken)
37 37
38 if (!accessToken) return next(new Error('No access token provided'))
39
38 getAccessToken(accessToken) 40 getAccessToken(accessToken)
39 .then(tokenDB => { 41 .then(tokenDB => {
40 const now = new Date() 42 const now = new Date()
diff --git a/server/middlewares/pagination.ts b/server/middlewares/pagination.ts
index 9b497b19e..83304940f 100644
--- a/server/middlewares/pagination.ts
+++ b/server/middlewares/pagination.ts
@@ -1,7 +1,7 @@
1import 'express-validator' 1import 'express-validator'
2import * as express from 'express' 2import * as express from 'express'
3 3
4import { PAGINATION } from '../initializers' 4import { PAGINATION } from '../initializers/constants'
5 5
6function setDefaultPagination (req: express.Request, res: express.Response, next: express.NextFunction) { 6function setDefaultPagination (req: express.Request, res: express.Response, next: express.NextFunction) {
7 if (!req.query.start) req.query.start = 0 7 if (!req.query.start) req.query.start = 0
diff --git a/server/middlewares/user-right.ts b/server/middlewares/user-right.ts
index 7cea7aa1e..498e3d677 100644
--- a/server/middlewares/user-right.ts
+++ b/server/middlewares/user-right.ts
@@ -2,11 +2,10 @@ import * as express from 'express'
2import 'express-validator' 2import 'express-validator'
3import { UserRight } from '../../shared' 3import { UserRight } from '../../shared'
4import { logger } from '../helpers/logger' 4import { logger } from '../helpers/logger'
5import { UserModel } from '../models/account/user'
6 5
7function ensureUserHasRight (userRight: UserRight) { 6function ensureUserHasRight (userRight: UserRight) {
8 return function (req: express.Request, res: express.Response, next: express.NextFunction) { 7 return function (req: express.Request, res: express.Response, next: express.NextFunction) {
9 const user = res.locals.oauth.token.user as UserModel 8 const user = res.locals.oauth.token.user
10 if (user.hasRight(userRight) === false) { 9 if (user.hasRight(userRight) === false) {
11 const message = `User ${user.username} does not have right ${UserRight[userRight]} to access to ${req.path}.` 10 const message = `User ${user.username} does not have right ${UserRight[userRight]} to access to ${req.path}.`
12 logger.info(message) 11 logger.info(message)
diff --git a/server/middlewares/validators/account.ts b/server/middlewares/validators/account.ts
index b3a51e631..96e120a38 100644
--- a/server/middlewares/validators/account.ts
+++ b/server/middlewares/validators/account.ts
@@ -1,6 +1,6 @@
1import * as express from 'express' 1import * as express from 'express'
2import { param } from 'express-validator/check' 2import { param } from 'express-validator/check'
3import { isAccountNameValid, isAccountNameWithHostExist, isLocalAccountNameExist } from '../../helpers/custom-validators/accounts' 3import { isAccountNameValid, doesAccountNameWithHostExist, doesLocalAccountNameExist } from '../../helpers/custom-validators/accounts'
4import { logger } from '../../helpers/logger' 4import { logger } from '../../helpers/logger'
5import { areValidationErrors } from './utils' 5import { areValidationErrors } from './utils'
6 6
@@ -11,20 +11,20 @@ const localAccountValidator = [
11 logger.debug('Checking localAccountValidator parameters', { parameters: req.params }) 11 logger.debug('Checking localAccountValidator parameters', { parameters: req.params })
12 12
13 if (areValidationErrors(req, res)) return 13 if (areValidationErrors(req, res)) return
14 if (!await isLocalAccountNameExist(req.params.name, res)) return 14 if (!await doesLocalAccountNameExist(req.params.name, res)) return
15 15
16 return next() 16 return next()
17 } 17 }
18] 18]
19 19
20const accountsNameWithHostGetValidator = [ 20const accountNameWithHostGetValidator = [
21 param('accountName').exists().withMessage('Should have an account name with host'), 21 param('accountName').exists().withMessage('Should have an account name with host'),
22 22
23 async (req: express.Request, res: express.Response, next: express.NextFunction) => { 23 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
24 logger.debug('Checking accountsNameWithHostGetValidator parameters', { parameters: req.params }) 24 logger.debug('Checking accountsNameWithHostGetValidator parameters', { parameters: req.params })
25 25
26 if (areValidationErrors(req, res)) return 26 if (areValidationErrors(req, res)) return
27 if (!await isAccountNameWithHostExist(req.params.accountName, res)) return 27 if (!await doesAccountNameWithHostExist(req.params.accountName, res)) return
28 28
29 return next() 29 return next()
30 } 30 }
@@ -34,5 +34,5 @@ const accountsNameWithHostGetValidator = [
34 34
35export { 35export {
36 localAccountValidator, 36 localAccountValidator,
37 accountsNameWithHostGetValidator 37 accountNameWithHostGetValidator
38} 38}
diff --git a/server/middlewares/validators/activitypub/activity.ts b/server/middlewares/validators/activitypub/activity.ts
index 3f9057c0c..7582f65e7 100644
--- a/server/middlewares/validators/activitypub/activity.ts
+++ b/server/middlewares/validators/activitypub/activity.ts
@@ -2,7 +2,6 @@ import * as express from 'express'
2import { isRootActivityValid } from '../../../helpers/custom-validators/activitypub/activity' 2import { isRootActivityValid } from '../../../helpers/custom-validators/activitypub/activity'
3import { logger } from '../../../helpers/logger' 3import { logger } from '../../../helpers/logger'
4import { getServerActor } from '../../../helpers/utils' 4import { getServerActor } from '../../../helpers/utils'
5import { ActorModel } from '../../../models/activitypub/actor'
6 5
7async function activityPubValidator (req: express.Request, res: express.Response, next: express.NextFunction) { 6async function activityPubValidator (req: express.Request, res: express.Response, next: express.NextFunction) {
8 logger.debug('Checking activity pub parameters') 7 logger.debug('Checking activity pub parameters')
@@ -13,7 +12,7 @@ async function activityPubValidator (req: express.Request, res: express.Response
13 } 12 }
14 13
15 const serverActor = await getServerActor() 14 const serverActor = await getServerActor()
16 const remoteActor = res.locals.signature.actor as ActorModel 15 const remoteActor = res.locals.signature.actor
17 if (serverActor.id === remoteActor.id) { 16 if (serverActor.id === remoteActor.id) {
18 logger.error('Receiving request in INBOX by ourselves!', req.body) 17 logger.error('Receiving request in INBOX by ourselves!', req.body)
19 return res.status(409).end() 18 return res.status(409).end()
diff --git a/server/middlewares/validators/avatar.ts b/server/middlewares/validators/avatar.ts
index ddc14f531..bab3ed118 100644
--- a/server/middlewares/validators/avatar.ts
+++ b/server/middlewares/validators/avatar.ts
@@ -2,7 +2,7 @@ import * as express from 'express'
2import { body } from 'express-validator/check' 2import { body } from 'express-validator/check'
3import { isAvatarFile } from '../../helpers/custom-validators/users' 3import { isAvatarFile } from '../../helpers/custom-validators/users'
4import { areValidationErrors } from './utils' 4import { areValidationErrors } from './utils'
5import { CONSTRAINTS_FIELDS } from '../../initializers' 5import { CONSTRAINTS_FIELDS } from '../../initializers/constants'
6import { logger } from '../../helpers/logger' 6import { logger } from '../../helpers/logger'
7import { cleanUpReqFiles } from '../../helpers/express-utils' 7import { cleanUpReqFiles } from '../../helpers/express-utils'
8 8
diff --git a/server/middlewares/validators/blocklist.ts b/server/middlewares/validators/blocklist.ts
index 109276c63..7c494de78 100644
--- a/server/middlewares/validators/blocklist.ts
+++ b/server/middlewares/validators/blocklist.ts
@@ -2,14 +2,13 @@ import { body, param } from 'express-validator/check'
2import * as express from 'express' 2import * as express from 'express'
3import { logger } from '../../helpers/logger' 3import { logger } from '../../helpers/logger'
4import { areValidationErrors } from './utils' 4import { areValidationErrors } from './utils'
5import { isAccountNameWithHostExist } from '../../helpers/custom-validators/accounts' 5import { doesAccountNameWithHostExist } from '../../helpers/custom-validators/accounts'
6import { UserModel } from '../../models/account/user'
7import { AccountBlocklistModel } from '../../models/account/account-blocklist' 6import { AccountBlocklistModel } from '../../models/account/account-blocklist'
8import { isHostValid } from '../../helpers/custom-validators/servers' 7import { isHostValid } from '../../helpers/custom-validators/servers'
9import { ServerBlocklistModel } from '../../models/server/server-blocklist' 8import { ServerBlocklistModel } from '../../models/server/server-blocklist'
10import { ServerModel } from '../../models/server/server' 9import { ServerModel } from '../../models/server/server'
11import { CONFIG } from '../../initializers'
12import { getServerActor } from '../../helpers/utils' 10import { getServerActor } from '../../helpers/utils'
11import { WEBSERVER } from '../../initializers/constants'
13 12
14const blockAccountValidator = [ 13const blockAccountValidator = [
15 body('accountName').exists().withMessage('Should have an account name with host'), 14 body('accountName').exists().withMessage('Should have an account name with host'),
@@ -18,9 +17,9 @@ const blockAccountValidator = [
18 logger.debug('Checking blockAccountByAccountValidator parameters', { parameters: req.body }) 17 logger.debug('Checking blockAccountByAccountValidator parameters', { parameters: req.body })
19 18
20 if (areValidationErrors(req, res)) return 19 if (areValidationErrors(req, res)) return
21 if (!await isAccountNameWithHostExist(req.body.accountName, res)) return 20 if (!await doesAccountNameWithHostExist(req.body.accountName, res)) return
22 21
23 const user = res.locals.oauth.token.User as UserModel 22 const user = res.locals.oauth.token.User
24 const accountToBlock = res.locals.account 23 const accountToBlock = res.locals.account
25 24
26 if (user.Account.id === accountToBlock.id) { 25 if (user.Account.id === accountToBlock.id) {
@@ -42,11 +41,11 @@ const unblockAccountByAccountValidator = [
42 logger.debug('Checking unblockAccountByAccountValidator parameters', { parameters: req.params }) 41 logger.debug('Checking unblockAccountByAccountValidator parameters', { parameters: req.params })
43 42
44 if (areValidationErrors(req, res)) return 43 if (areValidationErrors(req, res)) return
45 if (!await isAccountNameWithHostExist(req.params.accountName, res)) return 44 if (!await doesAccountNameWithHostExist(req.params.accountName, res)) return
46 45
47 const user = res.locals.oauth.token.User as UserModel 46 const user = res.locals.oauth.token.User
48 const targetAccount = res.locals.account 47 const targetAccount = res.locals.account
49 if (!await isUnblockAccountExists(user.Account.id, targetAccount.id, res)) return 48 if (!await doesUnblockAccountExist(user.Account.id, targetAccount.id, res)) return
50 49
51 return next() 50 return next()
52 } 51 }
@@ -59,11 +58,11 @@ const unblockAccountByServerValidator = [
59 logger.debug('Checking unblockAccountByServerValidator parameters', { parameters: req.params }) 58 logger.debug('Checking unblockAccountByServerValidator parameters', { parameters: req.params })
60 59
61 if (areValidationErrors(req, res)) return 60 if (areValidationErrors(req, res)) return
62 if (!await isAccountNameWithHostExist(req.params.accountName, res)) return 61 if (!await doesAccountNameWithHostExist(req.params.accountName, res)) return
63 62
64 const serverActor = await getServerActor() 63 const serverActor = await getServerActor()
65 const targetAccount = res.locals.account 64 const targetAccount = res.locals.account
66 if (!await isUnblockAccountExists(serverActor.Account.id, targetAccount.id, res)) return 65 if (!await doesUnblockAccountExist(serverActor.Account.id, targetAccount.id, res)) return
67 66
68 return next() 67 return next()
69 } 68 }
@@ -79,7 +78,7 @@ const blockServerValidator = [
79 78
80 const host: string = req.body.host 79 const host: string = req.body.host
81 80
82 if (host === CONFIG.WEBSERVER.HOST) { 81 if (host === WEBSERVER.HOST) {
83 return res.status(409) 82 return res.status(409)
84 .send({ error: 'You cannot block your own server.' }) 83 .send({ error: 'You cannot block your own server.' })
85 .end() 84 .end()
@@ -106,8 +105,8 @@ const unblockServerByAccountValidator = [
106 105
107 if (areValidationErrors(req, res)) return 106 if (areValidationErrors(req, res)) return
108 107
109 const user = res.locals.oauth.token.User as UserModel 108 const user = res.locals.oauth.token.User
110 if (!await isUnblockServerExists(user.Account.id, req.params.host, res)) return 109 if (!await doesUnblockServerExist(user.Account.id, req.params.host, res)) return
111 110
112 return next() 111 return next()
113 } 112 }
@@ -122,7 +121,7 @@ const unblockServerByServerValidator = [
122 if (areValidationErrors(req, res)) return 121 if (areValidationErrors(req, res)) return
123 122
124 const serverActor = await getServerActor() 123 const serverActor = await getServerActor()
125 if (!await isUnblockServerExists(serverActor.Account.id, req.params.host, res)) return 124 if (!await doesUnblockServerExist(serverActor.Account.id, req.params.host, res)) return
126 125
127 return next() 126 return next()
128 } 127 }
@@ -141,7 +140,7 @@ export {
141 140
142// --------------------------------------------------------------------------- 141// ---------------------------------------------------------------------------
143 142
144async function isUnblockAccountExists (accountId: number, targetAccountId: number, res: express.Response) { 143async function doesUnblockAccountExist (accountId: number, targetAccountId: number, res: express.Response) {
145 const accountBlock = await AccountBlocklistModel.loadByAccountAndTarget(accountId, targetAccountId) 144 const accountBlock = await AccountBlocklistModel.loadByAccountAndTarget(accountId, targetAccountId)
146 if (!accountBlock) { 145 if (!accountBlock) {
147 res.status(404) 146 res.status(404)
@@ -156,7 +155,7 @@ async function isUnblockAccountExists (accountId: number, targetAccountId: numbe
156 return true 155 return true
157} 156}
158 157
159async function isUnblockServerExists (accountId: number, host: string, res: express.Response) { 158async function doesUnblockServerExist (accountId: number, host: string, res: express.Response) {
160 const serverBlock = await ServerBlocklistModel.loadByAccountAndHost(accountId, host) 159 const serverBlock = await ServerBlocklistModel.loadByAccountAndHost(accountId, host)
161 if (!serverBlock) { 160 if (!serverBlock) {
162 res.status(404) 161 res.status(404)
diff --git a/server/middlewares/validators/config.ts b/server/middlewares/validators/config.ts
index 90108fa82..d015fa6fe 100644
--- a/server/middlewares/validators/config.ts
+++ b/server/middlewares/validators/config.ts
@@ -2,6 +2,8 @@ import * as express from 'express'
2import { body } from 'express-validator/check' 2import { body } from 'express-validator/check'
3import { isUserNSFWPolicyValid, isUserVideoQuotaValid, isUserVideoQuotaDailyValid } from '../../helpers/custom-validators/users' 3import { isUserNSFWPolicyValid, isUserVideoQuotaValid, isUserVideoQuotaDailyValid } from '../../helpers/custom-validators/users'
4import { logger } from '../../helpers/logger' 4import { logger } from '../../helpers/logger'
5import { CustomConfig } from '../../../shared/models/server/custom-config.model'
6import { Emailer } from '../../lib/emailer'
5import { areValidationErrors } from './utils' 7import { areValidationErrors } from './utils'
6 8
7const customConfigUpdateValidator = [ 9const customConfigUpdateValidator = [
@@ -42,15 +44,34 @@ const customConfigUpdateValidator = [
42 body('import.videos.http.enabled').isBoolean().withMessage('Should have a valid import video http enabled boolean'), 44 body('import.videos.http.enabled').isBoolean().withMessage('Should have a valid import video http enabled boolean'),
43 body('import.videos.torrent.enabled').isBoolean().withMessage('Should have a valid import video torrent enabled boolean'), 45 body('import.videos.torrent.enabled').isBoolean().withMessage('Should have a valid import video torrent enabled boolean'),
44 46
47 body('followers.instance.enabled').isBoolean().withMessage('Should have a valid followers of instance boolean'),
48 body('followers.instance.manualApproval').isBoolean().withMessage('Should have a valid manual approval boolean'),
49
45 async (req: express.Request, res: express.Response, next: express.NextFunction) => { 50 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
46 logger.debug('Checking customConfigUpdateValidator parameters', { parameters: req.body }) 51 logger.debug('Checking customConfigUpdateValidator parameters', { parameters: req.body })
47 52
48 if (areValidationErrors(req, res)) return 53 if (areValidationErrors(req, res)) return
54 if (!checkInvalidConfigIfEmailDisabled(req.body as CustomConfig, res)) return
49 55
50 return next() 56 return next()
51 } 57 }
52] 58]
53 59
60// ---------------------------------------------------------------------------
61
54export { 62export {
55 customConfigUpdateValidator 63 customConfigUpdateValidator
56} 64}
65
66function checkInvalidConfigIfEmailDisabled (customConfig: CustomConfig, res: express.Response) {
67 if (Emailer.isEnabled()) return true
68
69 if (customConfig.signup.requiresEmailVerification === true) {
70 res.status(400)
71 .send({ error: 'Emailer is disabled but you require signup email verification.' })
72 .end()
73 return false
74 }
75
76 return true
77}
diff --git a/server/middlewares/validators/feeds.ts b/server/middlewares/validators/feeds.ts
index 969ce2526..e4f5c98fe 100644
--- a/server/middlewares/validators/feeds.ts
+++ b/server/middlewares/validators/feeds.ts
@@ -1,12 +1,12 @@
1import * as express from 'express' 1import * as express from 'express'
2import { param, query } from 'express-validator/check' 2import { param, query } from 'express-validator/check'
3import { isAccountIdExist, isAccountNameValid, isAccountNameWithHostExist } from '../../helpers/custom-validators/accounts' 3import { doesAccountIdExist, isAccountNameValid, doesAccountNameWithHostExist } from '../../helpers/custom-validators/accounts'
4import { isIdOrUUIDValid } from '../../helpers/custom-validators/misc' 4import { isIdOrUUIDValid } from '../../helpers/custom-validators/misc'
5import { logger } from '../../helpers/logger' 5import { logger } from '../../helpers/logger'
6import { areValidationErrors } from './utils' 6import { areValidationErrors } from './utils'
7import { isValidRSSFeed } from '../../helpers/custom-validators/feeds' 7import { isValidRSSFeed } from '../../helpers/custom-validators/feeds'
8import { isVideoChannelIdExist, isVideoChannelNameWithHostExist } from '../../helpers/custom-validators/video-channels' 8import { doesVideoChannelIdExist, doesVideoChannelNameWithHostExist } from '../../helpers/custom-validators/video-channels'
9import { isVideoExist } from '../../helpers/custom-validators/videos' 9import { doesVideoExist } from '../../helpers/custom-validators/videos'
10import { isActorPreferredUsernameValid } from '../../helpers/custom-validators/activitypub/actor' 10import { isActorPreferredUsernameValid } from '../../helpers/custom-validators/activitypub/actor'
11 11
12const videoFeedsValidator = [ 12const videoFeedsValidator = [
@@ -22,10 +22,10 @@ const videoFeedsValidator = [
22 22
23 if (areValidationErrors(req, res)) return 23 if (areValidationErrors(req, res)) return
24 24
25 if (req.query.accountId && !await isAccountIdExist(req.query.accountId, res)) return 25 if (req.query.accountId && !await doesAccountIdExist(req.query.accountId, res)) return
26 if (req.query.videoChannelId && !await isVideoChannelIdExist(req.query.videoChannelId, res)) return 26 if (req.query.videoChannelId && !await doesVideoChannelIdExist(req.query.videoChannelId, res)) return
27 if (req.query.accountName && !await isAccountNameWithHostExist(req.query.accountName, res)) return 27 if (req.query.accountName && !await doesAccountNameWithHostExist(req.query.accountName, res)) return
28 if (req.query.videoChannelName && !await isVideoChannelNameWithHostExist(req.query.videoChannelName, res)) return 28 if (req.query.videoChannelName && !await doesVideoChannelNameWithHostExist(req.query.videoChannelName, res)) return
29 29
30 return next() 30 return next()
31 } 31 }
@@ -41,7 +41,7 @@ const videoCommentsFeedsValidator = [
41 41
42 if (areValidationErrors(req, res)) return 42 if (areValidationErrors(req, res)) return
43 43
44 if (req.query.videoId && !await isVideoExist(req.query.videoId, res)) return 44 if (req.query.videoId && !await doesVideoExist(req.query.videoId, res)) return
45 45
46 return next() 46 return next()
47 } 47 }
diff --git a/server/middlewares/validators/follows.ts b/server/middlewares/validators/follows.ts
index 73fa28be9..2e5a02307 100644
--- a/server/middlewares/validators/follows.ts
+++ b/server/middlewares/validators/follows.ts
@@ -4,16 +4,19 @@ import { isTestInstance } from '../../helpers/core-utils'
4import { isEachUniqueHostValid, isHostValid } from '../../helpers/custom-validators/servers' 4import { isEachUniqueHostValid, isHostValid } from '../../helpers/custom-validators/servers'
5import { logger } from '../../helpers/logger' 5import { logger } from '../../helpers/logger'
6import { getServerActor } from '../../helpers/utils' 6import { getServerActor } from '../../helpers/utils'
7import { CONFIG, SERVER_ACTOR_NAME } from '../../initializers' 7import { SERVER_ACTOR_NAME, WEBSERVER } from '../../initializers/constants'
8import { ActorFollowModel } from '../../models/activitypub/actor-follow' 8import { ActorFollowModel } from '../../models/activitypub/actor-follow'
9import { areValidationErrors } from './utils' 9import { areValidationErrors } from './utils'
10import { ActorModel } from '../../models/activitypub/actor'
11import { loadActorUrlOrGetFromWebfinger } from '../../helpers/webfinger'
12import { isValidActorHandle } from '../../helpers/custom-validators/activitypub/actor'
10 13
11const followValidator = [ 14const followValidator = [
12 body('hosts').custom(isEachUniqueHostValid).withMessage('Should have an array of unique hosts'), 15 body('hosts').custom(isEachUniqueHostValid).withMessage('Should have an array of unique hosts'),
13 16
14 (req: express.Request, res: express.Response, next: express.NextFunction) => { 17 (req: express.Request, res: express.Response, next: express.NextFunction) => {
15 // Force https if the administrator wants to make friends 18 // Force https if the administrator wants to make friends
16 if (isTestInstance() === false && CONFIG.WEBSERVER.SCHEME === 'http') { 19 if (isTestInstance() === false && WEBSERVER.SCHEME === 'http') {
17 return res.status(500) 20 return res.status(500)
18 .json({ 21 .json({
19 error: 'Cannot follow on a non HTTPS web server.' 22 error: 'Cannot follow on a non HTTPS web server.'
@@ -33,7 +36,7 @@ const removeFollowingValidator = [
33 param('host').custom(isHostValid).withMessage('Should have a valid host'), 36 param('host').custom(isHostValid).withMessage('Should have a valid host'),
34 37
35 async (req: express.Request, res: express.Response, next: express.NextFunction) => { 38 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
36 logger.debug('Checking unfollow parameters', { parameters: req.params }) 39 logger.debug('Checking unfollowing parameters', { parameters: req.params })
37 40
38 if (areValidationErrors(req, res)) return 41 if (areValidationErrors(req, res)) return
39 42
@@ -44,7 +47,7 @@ const removeFollowingValidator = [
44 return res 47 return res
45 .status(404) 48 .status(404)
46 .json({ 49 .json({
47 error: `Follower ${req.params.host} not found.` 50 error: `Following ${req.params.host} not found.`
48 }) 51 })
49 .end() 52 .end()
50 } 53 }
@@ -54,9 +57,57 @@ const removeFollowingValidator = [
54 } 57 }
55] 58]
56 59
60const getFollowerValidator = [
61 param('nameWithHost').custom(isValidActorHandle).withMessage('Should have a valid nameWithHost'),
62
63 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
64 logger.debug('Checking get follower parameters', { parameters: req.params })
65
66 if (areValidationErrors(req, res)) return
67
68 let follow: ActorFollowModel
69 try {
70 const actorUrl = await loadActorUrlOrGetFromWebfinger(req.params.nameWithHost)
71 const actor = await ActorModel.loadByUrl(actorUrl)
72
73 const serverActor = await getServerActor()
74 follow = await ActorFollowModel.loadByActorAndTarget(actor.id, serverActor.id)
75 } catch (err) {
76 logger.warn('Cannot get actor from handle.', { handle: req.params.nameWithHost, err })
77 }
78
79 if (!follow) {
80 return res
81 .status(404)
82 .json({
83 error: `Follower ${req.params.nameWithHost} not found.`
84 })
85 .end()
86 }
87
88 res.locals.follow = follow
89 return next()
90 }
91]
92
93const acceptOrRejectFollowerValidator = [
94 (req: express.Request, res: express.Response, next: express.NextFunction) => {
95 logger.debug('Checking accept/reject follower parameters', { parameters: req.params })
96
97 const follow = res.locals.follow
98 if (follow.state !== 'pending') {
99 return res.status(400).json({ error: 'Follow is not in pending state.' }).end()
100 }
101
102 return next()
103 }
104]
105
57// --------------------------------------------------------------------------- 106// ---------------------------------------------------------------------------
58 107
59export { 108export {
60 followValidator, 109 followValidator,
61 removeFollowingValidator 110 removeFollowingValidator,
111 getFollowerValidator,
112 acceptOrRejectFollowerValidator
62} 113}
diff --git a/server/middlewares/validators/logs.ts b/server/middlewares/validators/logs.ts
new file mode 100644
index 000000000..7380c6edd
--- /dev/null
+++ b/server/middlewares/validators/logs.ts
@@ -0,0 +1,31 @@
1import * as express from 'express'
2import { logger } from '../../helpers/logger'
3import { areValidationErrors } from './utils'
4import { isDateValid } from '../../helpers/custom-validators/misc'
5import { query } from 'express-validator/check'
6import { isValidLogLevel } from '../../helpers/custom-validators/logs'
7
8const getLogsValidator = [
9 query('startDate')
10 .custom(isDateValid).withMessage('Should have a valid start date'),
11 query('level')
12 .optional()
13 .custom(isValidLogLevel).withMessage('Should have a valid level'),
14 query('endDate')
15 .optional()
16 .custom(isDateValid).withMessage('Should have a valid end date'),
17
18 (req: express.Request, res: express.Response, next: express.NextFunction) => {
19 logger.debug('Checking getLogsValidator parameters.', { parameters: req.query })
20
21 if (areValidationErrors(req, res)) return
22
23 return next()
24 }
25]
26
27// ---------------------------------------------------------------------------
28
29export {
30 getLogsValidator
31}
diff --git a/server/middlewares/validators/oembed.ts b/server/middlewares/validators/oembed.ts
index cd9b27b16..0bb908d0b 100644
--- a/server/middlewares/validators/oembed.ts
+++ b/server/middlewares/validators/oembed.ts
@@ -3,12 +3,12 @@ import { query } from 'express-validator/check'
3import { join } from 'path' 3import { join } from 'path'
4import { isTestInstance } from '../../helpers/core-utils' 4import { isTestInstance } from '../../helpers/core-utils'
5import { isIdOrUUIDValid } from '../../helpers/custom-validators/misc' 5import { isIdOrUUIDValid } from '../../helpers/custom-validators/misc'
6import { isVideoExist } from '../../helpers/custom-validators/videos' 6import { doesVideoExist } from '../../helpers/custom-validators/videos'
7import { logger } from '../../helpers/logger' 7import { logger } from '../../helpers/logger'
8import { CONFIG } from '../../initializers'
9import { areValidationErrors } from './utils' 8import { areValidationErrors } from './utils'
9import { WEBSERVER } from '../../initializers/constants'
10 10
11const urlShouldStartWith = CONFIG.WEBSERVER.SCHEME + '://' + join(CONFIG.WEBSERVER.HOST, 'videos', 'watch') + '/' 11const urlShouldStartWith = WEBSERVER.SCHEME + '://' + join(WEBSERVER.HOST, 'videos', 'watch') + '/'
12const videoWatchRegex = new RegExp('([^/]+)$') 12const videoWatchRegex = new RegExp('([^/]+)$')
13const isURLOptions = { 13const isURLOptions = {
14 require_host: true, 14 require_host: true,
@@ -52,7 +52,7 @@ const oembedValidator = [
52 .end() 52 .end()
53 } 53 }
54 54
55 if (!await isVideoExist(videoId, res)) return 55 if (!await doesVideoExist(videoId, res)) return
56 56
57 return next() 57 return next()
58 } 58 }
diff --git a/server/middlewares/validators/redundancy.ts b/server/middlewares/validators/redundancy.ts
index c72ab78b2..76cf89c40 100644
--- a/server/middlewares/validators/redundancy.ts
+++ b/server/middlewares/validators/redundancy.ts
@@ -1,19 +1,15 @@
1import * as express from 'express' 1import * as express from 'express'
2import 'express-validator' 2import 'express-validator'
3import { param, body } from 'express-validator/check' 3import { body, param } from 'express-validator/check'
4import { exists, isBooleanValid, isIdOrUUIDValid, toIntOrNull } from '../../helpers/custom-validators/misc' 4import { exists, isBooleanValid, isIdOrUUIDValid, toIntOrNull } from '../../helpers/custom-validators/misc'
5import { isVideoExist } from '../../helpers/custom-validators/videos' 5import { doesVideoExist } from '../../helpers/custom-validators/videos'
6import { logger } from '../../helpers/logger' 6import { logger } from '../../helpers/logger'
7import { areValidationErrors } from './utils' 7import { areValidationErrors } from './utils'
8import { VideoModel } from '../../models/video/video'
9import { VideoRedundancyModel } from '../../models/redundancy/video-redundancy' 8import { VideoRedundancyModel } from '../../models/redundancy/video-redundancy'
10import { isHostValid } from '../../helpers/custom-validators/servers' 9import { isHostValid } from '../../helpers/custom-validators/servers'
11import { getServerActor } from '../../helpers/utils'
12import { ActorFollowModel } from '../../models/activitypub/actor-follow'
13import { SERVER_ACTOR_NAME } from '../../initializers'
14import { ServerModel } from '../../models/server/server' 10import { ServerModel } from '../../models/server/server'
15 11
16const videoRedundancyGetValidator = [ 12const videoFileRedundancyGetValidator = [
17 param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid video id'), 13 param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid video id'),
18 param('resolution') 14 param('resolution')
19 .customSanitizer(toIntOrNull) 15 .customSanitizer(toIntOrNull)
@@ -24,12 +20,12 @@ const videoRedundancyGetValidator = [
24 .custom(exists).withMessage('Should have a valid fps'), 20 .custom(exists).withMessage('Should have a valid fps'),
25 21
26 async (req: express.Request, res: express.Response, next: express.NextFunction) => { 22 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
27 logger.debug('Checking videoRedundancyGetValidator parameters', { parameters: req.params }) 23 logger.debug('Checking videoFileRedundancyGetValidator parameters', { parameters: req.params })
28 24
29 if (areValidationErrors(req, res)) return 25 if (areValidationErrors(req, res)) return
30 if (!await isVideoExist(req.params.videoId, res)) return 26 if (!await doesVideoExist(req.params.videoId, res)) return
31 27
32 const video: VideoModel = res.locals.video 28 const video = res.locals.video
33 const videoFile = video.VideoFiles.find(f => { 29 const videoFile = video.VideoFiles.find(f => {
34 return f.resolution === req.params.resolution && (!req.params.fps || f.fps === req.params.fps) 30 return f.resolution === req.params.resolution && (!req.params.fps || f.fps === req.params.fps)
35 }) 31 })
@@ -38,7 +34,31 @@ const videoRedundancyGetValidator = [
38 res.locals.videoFile = videoFile 34 res.locals.videoFile = videoFile
39 35
40 const videoRedundancy = await VideoRedundancyModel.loadLocalByFileId(videoFile.id) 36 const videoRedundancy = await VideoRedundancyModel.loadLocalByFileId(videoFile.id)
41 if (!videoRedundancy)return res.status(404).json({ error: 'Video redundancy not found.' }) 37 if (!videoRedundancy) return res.status(404).json({ error: 'Video redundancy not found.' })
38 res.locals.videoRedundancy = videoRedundancy
39
40 return next()
41 }
42]
43
44const videoPlaylistRedundancyGetValidator = [
45 param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid video id'),
46 param('streamingPlaylistType').custom(exists).withMessage('Should have a valid streaming playlist type'),
47
48 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
49 logger.debug('Checking videoPlaylistRedundancyGetValidator parameters', { parameters: req.params })
50
51 if (areValidationErrors(req, res)) return
52 if (!await doesVideoExist(req.params.videoId, res)) return
53
54 const video = res.locals.video
55 const videoStreamingPlaylist = video.VideoStreamingPlaylists.find(p => p === req.params.streamingPlaylistType)
56
57 if (!videoStreamingPlaylist) return res.status(404).json({ error: 'Video playlist not found.' })
58 res.locals.videoStreamingPlaylist = videoStreamingPlaylist
59
60 const videoRedundancy = await VideoRedundancyModel.loadLocalByStreamingPlaylistId(videoStreamingPlaylist.id)
61 if (!videoRedundancy) return res.status(404).json({ error: 'Video redundancy not found.' })
42 res.locals.videoRedundancy = videoRedundancy 62 res.locals.videoRedundancy = videoRedundancy
43 63
44 return next() 64 return next()
@@ -75,6 +95,7 @@ const updateServerRedundancyValidator = [
75// --------------------------------------------------------------------------- 95// ---------------------------------------------------------------------------
76 96
77export { 97export {
78 videoRedundancyGetValidator, 98 videoFileRedundancyGetValidator,
99 videoPlaylistRedundancyGetValidator,
79 updateServerRedundancyValidator 100 updateServerRedundancyValidator
80} 101}
diff --git a/server/middlewares/validators/search.ts b/server/middlewares/validators/search.ts
index 6a95d6095..7816d229c 100644
--- a/server/middlewares/validators/search.ts
+++ b/server/middlewares/validators/search.ts
@@ -10,6 +10,9 @@ const videosSearchValidator = [
10 query('startDate').optional().custom(isDateValid).withMessage('Should have a valid start date'), 10 query('startDate').optional().custom(isDateValid).withMessage('Should have a valid start date'),
11 query('endDate').optional().custom(isDateValid).withMessage('Should have a valid end date'), 11 query('endDate').optional().custom(isDateValid).withMessage('Should have a valid end date'),
12 12
13 query('originallyPublishedStartDate').optional().custom(isDateValid).withMessage('Should have a valid published start date'),
14 query('originallyPublishedEndDate').optional().custom(isDateValid).withMessage('Should have a valid published end date'),
15
13 query('durationMin').optional().isInt().withMessage('Should have a valid min duration'), 16 query('durationMin').optional().isInt().withMessage('Should have a valid min duration'),
14 query('durationMax').optional().isInt().withMessage('Should have a valid max duration'), 17 query('durationMax').optional().isInt().withMessage('Should have a valid max duration'),
15 18
diff --git a/server/middlewares/validators/server.ts b/server/middlewares/validators/server.ts
index d85afc2ff..6eff8e9ee 100644
--- a/server/middlewares/validators/server.ts
+++ b/server/middlewares/validators/server.ts
@@ -7,7 +7,7 @@ import { body } from 'express-validator/check'
7import { isUserDisplayNameValid } from '../../helpers/custom-validators/users' 7import { isUserDisplayNameValid } from '../../helpers/custom-validators/users'
8import { Emailer } from '../../lib/emailer' 8import { Emailer } from '../../lib/emailer'
9import { Redis } from '../../lib/redis' 9import { Redis } from '../../lib/redis'
10import { CONFIG } from '../../initializers/constants' 10import { CONFIG } from '../../initializers/config'
11 11
12const serverGetValidator = [ 12const serverGetValidator = [
13 body('host').custom(isHostValid).withMessage('Should have a valid host'), 13 body('host').custom(isHostValid).withMessage('Should have a valid host'),
@@ -57,7 +57,7 @@ const contactAdministratorValidator = [
57 .end() 57 .end()
58 } 58 }
59 59
60 if (await Redis.Instance.isContactFormIpExists(req.ip)) { 60 if (await Redis.Instance.doesContactFormIpExist(req.ip)) {
61 logger.info('Refusing a contact form by %s: already sent one recently.', req.ip) 61 logger.info('Refusing a contact form by %s: already sent one recently.', req.ip)
62 62
63 return res 63 return res
diff --git a/server/middlewares/validators/sort.ts b/server/middlewares/validators/sort.ts
index 5ceda845f..b497798d1 100644
--- a/server/middlewares/validators/sort.ts
+++ b/server/middlewares/validators/sort.ts
@@ -1,4 +1,4 @@
1import { SORTABLE_COLUMNS } from '../../initializers' 1import { SORTABLE_COLUMNS } from '../../initializers/constants'
2import { checkSort, createSortableColumns } from './utils' 2import { checkSort, createSortableColumns } from './utils'
3 3
4// Initialize constants here for better performances 4// Initialize constants here for better performances
@@ -11,6 +11,7 @@ const SORTABLE_VIDEOS_SEARCH_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.VI
11const SORTABLE_VIDEO_CHANNELS_SEARCH_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.VIDEO_CHANNELS_SEARCH) 11const SORTABLE_VIDEO_CHANNELS_SEARCH_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.VIDEO_CHANNELS_SEARCH)
12const SORTABLE_VIDEO_IMPORTS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.VIDEO_IMPORTS) 12const SORTABLE_VIDEO_IMPORTS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.VIDEO_IMPORTS)
13const SORTABLE_VIDEO_COMMENT_THREADS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.VIDEO_COMMENT_THREADS) 13const SORTABLE_VIDEO_COMMENT_THREADS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.VIDEO_COMMENT_THREADS)
14const SORTABLE_VIDEO_RATES_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.VIDEO_RATES)
14const SORTABLE_BLACKLISTS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.BLACKLISTS) 15const SORTABLE_BLACKLISTS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.BLACKLISTS)
15const SORTABLE_VIDEO_CHANNELS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.VIDEO_CHANNELS) 16const SORTABLE_VIDEO_CHANNELS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.VIDEO_CHANNELS)
16const SORTABLE_FOLLOWERS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.FOLLOWERS) 17const SORTABLE_FOLLOWERS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.FOLLOWERS)
@@ -19,6 +20,7 @@ const SORTABLE_USER_SUBSCRIPTIONS_COLUMNS = createSortableColumns(SORTABLE_COLUM
19const SORTABLE_ACCOUNTS_BLOCKLIST_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.ACCOUNTS_BLOCKLIST) 20const SORTABLE_ACCOUNTS_BLOCKLIST_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.ACCOUNTS_BLOCKLIST)
20const SORTABLE_SERVERS_BLOCKLIST_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.SERVERS_BLOCKLIST) 21const SORTABLE_SERVERS_BLOCKLIST_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.SERVERS_BLOCKLIST)
21const SORTABLE_USER_NOTIFICATIONS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.USER_NOTIFICATIONS) 22const SORTABLE_USER_NOTIFICATIONS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.USER_NOTIFICATIONS)
23const SORTABLE_VIDEO_PLAYLISTS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.VIDEO_PLAYLISTS)
22 24
23const usersSortValidator = checkSort(SORTABLE_USERS_COLUMNS) 25const usersSortValidator = checkSort(SORTABLE_USERS_COLUMNS)
24const accountsSortValidator = checkSort(SORTABLE_ACCOUNTS_COLUMNS) 26const accountsSortValidator = checkSort(SORTABLE_ACCOUNTS_COLUMNS)
@@ -29,6 +31,7 @@ const videoImportsSortValidator = checkSort(SORTABLE_VIDEO_IMPORTS_COLUMNS)
29const videosSearchSortValidator = checkSort(SORTABLE_VIDEOS_SEARCH_COLUMNS) 31const videosSearchSortValidator = checkSort(SORTABLE_VIDEOS_SEARCH_COLUMNS)
30const videoChannelsSearchSortValidator = checkSort(SORTABLE_VIDEO_CHANNELS_SEARCH_COLUMNS) 32const videoChannelsSearchSortValidator = checkSort(SORTABLE_VIDEO_CHANNELS_SEARCH_COLUMNS)
31const videoCommentThreadsSortValidator = checkSort(SORTABLE_VIDEO_COMMENT_THREADS_COLUMNS) 33const videoCommentThreadsSortValidator = checkSort(SORTABLE_VIDEO_COMMENT_THREADS_COLUMNS)
34const videoRatesSortValidator = checkSort(SORTABLE_VIDEO_RATES_COLUMNS)
32const blacklistSortValidator = checkSort(SORTABLE_BLACKLISTS_COLUMNS) 35const blacklistSortValidator = checkSort(SORTABLE_BLACKLISTS_COLUMNS)
33const videoChannelsSortValidator = checkSort(SORTABLE_VIDEO_CHANNELS_COLUMNS) 36const videoChannelsSortValidator = checkSort(SORTABLE_VIDEO_CHANNELS_COLUMNS)
34const followersSortValidator = checkSort(SORTABLE_FOLLOWERS_COLUMNS) 37const followersSortValidator = checkSort(SORTABLE_FOLLOWERS_COLUMNS)
@@ -37,6 +40,7 @@ const userSubscriptionsSortValidator = checkSort(SORTABLE_USER_SUBSCRIPTIONS_COL
37const accountsBlocklistSortValidator = checkSort(SORTABLE_ACCOUNTS_BLOCKLIST_COLUMNS) 40const accountsBlocklistSortValidator = checkSort(SORTABLE_ACCOUNTS_BLOCKLIST_COLUMNS)
38const serversBlocklistSortValidator = checkSort(SORTABLE_SERVERS_BLOCKLIST_COLUMNS) 41const serversBlocklistSortValidator = checkSort(SORTABLE_SERVERS_BLOCKLIST_COLUMNS)
39const userNotificationsSortValidator = checkSort(SORTABLE_USER_NOTIFICATIONS_COLUMNS) 42const userNotificationsSortValidator = checkSort(SORTABLE_USER_NOTIFICATIONS_COLUMNS)
43const videoPlaylistsSortValidator = checkSort(SORTABLE_VIDEO_PLAYLISTS_COLUMNS)
40 44
41// --------------------------------------------------------------------------- 45// ---------------------------------------------------------------------------
42 46
@@ -53,9 +57,11 @@ export {
53 followingSortValidator, 57 followingSortValidator,
54 jobsSortValidator, 58 jobsSortValidator,
55 videoCommentThreadsSortValidator, 59 videoCommentThreadsSortValidator,
60 videoRatesSortValidator,
56 userSubscriptionsSortValidator, 61 userSubscriptionsSortValidator,
57 videoChannelsSearchSortValidator, 62 videoChannelsSearchSortValidator,
58 accountsBlocklistSortValidator, 63 accountsBlocklistSortValidator,
59 serversBlocklistSortValidator, 64 serversBlocklistSortValidator,
60 userNotificationsSortValidator 65 userNotificationsSortValidator,
66 videoPlaylistsSortValidator
61} 67}
diff --git a/server/middlewares/validators/user-notifications.ts b/server/middlewares/validators/user-notifications.ts
index 46486e081..3ded8d8cf 100644
--- a/server/middlewares/validators/user-notifications.ts
+++ b/server/middlewares/validators/user-notifications.ts
@@ -28,8 +28,22 @@ const updateNotificationSettingsValidator = [
28 .custom(isUserNotificationSettingValid).withMessage('Should have a valid new comment on my video notification setting'), 28 .custom(isUserNotificationSettingValid).withMessage('Should have a valid new comment on my video notification setting'),
29 body('videoAbuseAsModerator') 29 body('videoAbuseAsModerator')
30 .custom(isUserNotificationSettingValid).withMessage('Should have a valid new video abuse as moderator notification setting'), 30 .custom(isUserNotificationSettingValid).withMessage('Should have a valid new video abuse as moderator notification setting'),
31 body('videoAutoBlacklistAsModerator')
32 .custom(isUserNotificationSettingValid).withMessage('Should have a valid video auto blacklist notification setting'),
31 body('blacklistOnMyVideo') 33 body('blacklistOnMyVideo')
32 .custom(isUserNotificationSettingValid).withMessage('Should have a valid new blacklist on my video notification setting'), 34 .custom(isUserNotificationSettingValid).withMessage('Should have a valid new blacklist on my video notification setting'),
35 body('myVideoImportFinished')
36 .custom(isUserNotificationSettingValid).withMessage('Should have a valid video import finished video notification setting'),
37 body('myVideoPublished')
38 .custom(isUserNotificationSettingValid).withMessage('Should have a valid video published notification setting'),
39 body('commentMention')
40 .custom(isUserNotificationSettingValid).withMessage('Should have a valid comment mention notification setting'),
41 body('newFollow')
42 .custom(isUserNotificationSettingValid).withMessage('Should have a valid new follow notification setting'),
43 body('newUserRegistration')
44 .custom(isUserNotificationSettingValid).withMessage('Should have a valid new user registration notification setting'),
45 body('newInstanceFollower')
46 .custom(isUserNotificationSettingValid).withMessage('Should have a valid new instance follower notification setting'),
33 47
34 (req: express.Request, res: express.Response, next: express.NextFunction) => { 48 (req: express.Request, res: express.Response, next: express.NextFunction) => {
35 logger.debug('Checking updateNotificationSettingsValidator parameters', { parameters: req.body }) 49 logger.debug('Checking updateNotificationSettingsValidator parameters', { parameters: req.body })
diff --git a/server/middlewares/validators/user-subscriptions.ts b/server/middlewares/validators/user-subscriptions.ts
index c5f8d9d4c..2356745d7 100644
--- a/server/middlewares/validators/user-subscriptions.ts
+++ b/server/middlewares/validators/user-subscriptions.ts
@@ -5,9 +5,8 @@ import { logger } from '../../helpers/logger'
5import { areValidationErrors } from './utils' 5import { areValidationErrors } from './utils'
6import { ActorFollowModel } from '../../models/activitypub/actor-follow' 6import { ActorFollowModel } from '../../models/activitypub/actor-follow'
7import { areValidActorHandles, isValidActorHandle } from '../../helpers/custom-validators/activitypub/actor' 7import { areValidActorHandles, isValidActorHandle } from '../../helpers/custom-validators/activitypub/actor'
8import { UserModel } from '../../models/account/user'
9import { CONFIG } from '../../initializers'
10import { toArray } from '../../helpers/custom-validators/misc' 8import { toArray } from '../../helpers/custom-validators/misc'
9import { WEBSERVER } from '../../initializers/constants'
11 10
12const userSubscriptionAddValidator = [ 11const userSubscriptionAddValidator = [
13 body('uri').custom(isValidActorHandle).withMessage('Should have a valid URI to follow (username@domain)'), 12 body('uri').custom(isValidActorHandle).withMessage('Should have a valid URI to follow (username@domain)'),
@@ -44,9 +43,9 @@ const userSubscriptionGetValidator = [
44 if (areValidationErrors(req, res)) return 43 if (areValidationErrors(req, res)) return
45 44
46 let [ name, host ] = req.params.uri.split('@') 45 let [ name, host ] = req.params.uri.split('@')
47 if (host === CONFIG.WEBSERVER.HOST) host = null 46 if (host === WEBSERVER.HOST) host = null
48 47
49 const user: UserModel = res.locals.oauth.token.User 48 const user = res.locals.oauth.token.User
50 const subscription = await ActorFollowModel.loadByActorAndTargetNameAndHostForAPI(user.Account.Actor.id, name, host) 49 const subscription = await ActorFollowModel.loadByActorAndTargetNameAndHostForAPI(user.Account.Actor.id, name, host)
51 50
52 if (!subscription || !subscription.ActorFollowing.VideoChannel) { 51 if (!subscription || !subscription.ActorFollowing.VideoChannel) {
diff --git a/server/middlewares/validators/users.ts b/server/middlewares/validators/users.ts
index 1bb0bfb1b..6d8cd7894 100644
--- a/server/middlewares/validators/users.ts
+++ b/server/middlewares/validators/users.ts
@@ -5,6 +5,7 @@ import { body, param } from 'express-validator/check'
5import { omit } from 'lodash' 5import { omit } from 'lodash'
6import { isIdOrUUIDValid } from '../../helpers/custom-validators/misc' 6import { isIdOrUUIDValid } from '../../helpers/custom-validators/misc'
7import { 7import {
8 isUserAdminFlagsValid,
8 isUserAutoPlayVideoValid, 9 isUserAutoPlayVideoValid,
9 isUserBlockedReasonValid, 10 isUserBlockedReasonValid,
10 isUserDescriptionValid, 11 isUserDescriptionValid,
@@ -14,9 +15,10 @@ import {
14 isUserRoleValid, 15 isUserRoleValid,
15 isUserUsernameValid, 16 isUserUsernameValid,
16 isUserVideoQuotaDailyValid, 17 isUserVideoQuotaDailyValid,
17 isUserVideoQuotaValid, isUserVideosHistoryEnabledValid 18 isUserVideoQuotaValid,
19 isUserVideosHistoryEnabledValid
18} from '../../helpers/custom-validators/users' 20} from '../../helpers/custom-validators/users'
19import { isVideoExist } from '../../helpers/custom-validators/videos' 21import { doesVideoExist } from '../../helpers/custom-validators/videos'
20import { logger } from '../../helpers/logger' 22import { logger } from '../../helpers/logger'
21import { isSignupAllowed, isSignupAllowedForCurrentIP } from '../../helpers/signup' 23import { isSignupAllowed, isSignupAllowedForCurrentIP } from '../../helpers/signup'
22import { Redis } from '../../lib/redis' 24import { Redis } from '../../lib/redis'
@@ -31,6 +33,7 @@ const usersAddValidator = [
31 body('videoQuota').custom(isUserVideoQuotaValid).withMessage('Should have a valid user quota'), 33 body('videoQuota').custom(isUserVideoQuotaValid).withMessage('Should have a valid user quota'),
32 body('videoQuotaDaily').custom(isUserVideoQuotaDailyValid).withMessage('Should have a valid daily user quota'), 34 body('videoQuotaDaily').custom(isUserVideoQuotaDailyValid).withMessage('Should have a valid daily user quota'),
33 body('role').custom(isUserRoleValid).withMessage('Should have a valid role'), 35 body('role').custom(isUserRoleValid).withMessage('Should have a valid role'),
36 body('adminFlags').optional().custom(isUserAdminFlagsValid).withMessage('Should have a valid admin flags'),
34 37
35 async (req: express.Request, res: express.Response, next: express.NextFunction) => { 38 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
36 logger.debug('Checking usersAdd parameters', { parameters: omit(req.body, 'password') }) 39 logger.debug('Checking usersAdd parameters', { parameters: omit(req.body, 'password') })
@@ -100,7 +103,7 @@ const usersBlockingValidator = [
100 103
101const deleteMeValidator = [ 104const deleteMeValidator = [
102 async (req: express.Request, res: express.Response, next: express.NextFunction) => { 105 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
103 const user: UserModel = res.locals.oauth.token.User 106 const user = res.locals.oauth.token.User
104 if (user.username === 'root') { 107 if (user.username === 'root') {
105 return res.status(400) 108 return res.status(400)
106 .send({ error: 'You cannot delete your root account.' }) 109 .send({ error: 'You cannot delete your root account.' })
@@ -113,11 +116,13 @@ const deleteMeValidator = [
113 116
114const usersUpdateValidator = [ 117const usersUpdateValidator = [
115 param('id').isInt().not().isEmpty().withMessage('Should have a valid id'), 118 param('id').isInt().not().isEmpty().withMessage('Should have a valid id'),
119 body('password').optional().custom(isUserPasswordValid).withMessage('Should have a valid password'),
116 body('email').optional().isEmail().withMessage('Should have a valid email attribute'), 120 body('email').optional().isEmail().withMessage('Should have a valid email attribute'),
117 body('emailVerified').optional().isBoolean().withMessage('Should have a valid email verified attribute'), 121 body('emailVerified').optional().isBoolean().withMessage('Should have a valid email verified attribute'),
118 body('videoQuota').optional().custom(isUserVideoQuotaValid).withMessage('Should have a valid user quota'), 122 body('videoQuota').optional().custom(isUserVideoQuotaValid).withMessage('Should have a valid user quota'),
119 body('videoQuotaDaily').optional().custom(isUserVideoQuotaDailyValid).withMessage('Should have a valid daily user quota'), 123 body('videoQuotaDaily').optional().custom(isUserVideoQuotaDailyValid).withMessage('Should have a valid daily user quota'),
120 body('role').optional().custom(isUserRoleValid).withMessage('Should have a valid role'), 124 body('role').optional().custom(isUserRoleValid).withMessage('Should have a valid role'),
125 body('adminFlags').optional().custom(isUserAdminFlagsValid).withMessage('Should have a valid admin flags'),
121 126
122 async (req: express.Request, res: express.Response, next: express.NextFunction) => { 127 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
123 logger.debug('Checking usersUpdate parameters', { parameters: req.body }) 128 logger.debug('Checking usersUpdate parameters', { parameters: req.body })
@@ -158,8 +163,7 @@ const usersUpdateMeValidator = [
158 .end() 163 .end()
159 } 164 }
160 165
161 const user: UserModel = res.locals.oauth.token.User 166 const user = res.locals.oauth.token.User
162
163 if (await user.isPasswordMatch(req.body.currentPassword) !== true) { 167 if (await user.isPasswordMatch(req.body.currentPassword) !== true) {
164 return res.status(401) 168 return res.status(401)
165 .send({ error: 'currentPassword is invalid.' }) 169 .send({ error: 'currentPassword is invalid.' })
@@ -193,7 +197,7 @@ const usersVideoRatingValidator = [
193 logger.debug('Checking usersVideoRating parameters', { parameters: req.params }) 197 logger.debug('Checking usersVideoRating parameters', { parameters: req.params })
194 198
195 if (areValidationErrors(req, res)) return 199 if (areValidationErrors(req, res)) return
196 if (!await isVideoExist(req.params.videoId, res, 'id')) return 200 if (!await doesVideoExist(req.params.videoId, res, 'id')) return
197 201
198 return next() 202 return next()
199 } 203 }
@@ -233,6 +237,7 @@ const usersAskResetPasswordValidator = [
233 logger.debug('Checking usersAskResetPassword parameters', { parameters: req.body }) 237 logger.debug('Checking usersAskResetPassword parameters', { parameters: req.body })
234 238
235 if (areValidationErrors(req, res)) return 239 if (areValidationErrors(req, res)) return
240
236 const exists = await checkUserEmailExist(req.body.email, res, false) 241 const exists = await checkUserEmailExist(req.body.email, res, false)
237 if (!exists) { 242 if (!exists) {
238 logger.debug('User with email %s does not exist (asking reset password).', req.body.email) 243 logger.debug('User with email %s does not exist (asking reset password).', req.body.email)
@@ -255,7 +260,7 @@ const usersResetPasswordValidator = [
255 if (areValidationErrors(req, res)) return 260 if (areValidationErrors(req, res)) return
256 if (!await checkUserIdExist(req.params.id, res)) return 261 if (!await checkUserIdExist(req.params.id, res)) return
257 262
258 const user = res.locals.user as UserModel 263 const user = res.locals.user
259 const redisVerificationString = await Redis.Instance.getResetPasswordLink(user.id) 264 const redisVerificationString = await Redis.Instance.getResetPasswordLink(user.id)
260 265
261 if (redisVerificationString !== req.body.verificationString) { 266 if (redisVerificationString !== req.body.verificationString) {
@@ -297,7 +302,7 @@ const usersVerifyEmailValidator = [
297 if (areValidationErrors(req, res)) return 302 if (areValidationErrors(req, res)) return
298 if (!await checkUserIdExist(req.params.id, res)) return 303 if (!await checkUserIdExist(req.params.id, res)) return
299 304
300 const user = res.locals.user as UserModel 305 const user = res.locals.user
301 const redisVerificationString = await Redis.Instance.getVerifyEmailLink(user.id) 306 const redisVerificationString = await Redis.Instance.getVerifyEmailLink(user.id)
302 307
303 if (redisVerificationString !== req.body.verificationString) { 308 if (redisVerificationString !== req.body.verificationString) {
@@ -315,6 +320,20 @@ const userAutocompleteValidator = [
315 param('search').isString().not().isEmpty().withMessage('Should have a search parameter') 320 param('search').isString().not().isEmpty().withMessage('Should have a search parameter')
316] 321]
317 322
323const ensureAuthUserOwnsAccountValidator = [
324 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
325 const user = res.locals.oauth.token.User
326
327 if (res.locals.account.id !== user.Account.id) {
328 return res.status(403)
329 .send({ error: 'Only owner can access ratings list.' })
330 .end()
331 }
332
333 return next()
334 }
335]
336
318// --------------------------------------------------------------------------- 337// ---------------------------------------------------------------------------
319 338
320export { 339export {
@@ -333,7 +352,8 @@ export {
333 usersResetPasswordValidator, 352 usersResetPasswordValidator,
334 usersAskSendVerifyEmailValidator, 353 usersAskSendVerifyEmailValidator,
335 usersVerifyEmailValidator, 354 usersVerifyEmailValidator,
336 userAutocompleteValidator 355 userAutocompleteValidator,
356 ensureAuthUserOwnsAccountValidator
337} 357}
338 358
339// --------------------------------------------------------------------------- 359// ---------------------------------------------------------------------------
diff --git a/server/middlewares/validators/videos/video-abuses.ts b/server/middlewares/validators/videos/video-abuses.ts
index be26ca16a..d1910a992 100644
--- a/server/middlewares/validators/videos/video-abuses.ts
+++ b/server/middlewares/validators/videos/video-abuses.ts
@@ -2,11 +2,11 @@ import * as express from 'express'
2import 'express-validator' 2import 'express-validator'
3import { body, param } from 'express-validator/check' 3import { body, param } from 'express-validator/check'
4import { isIdOrUUIDValid, isIdValid } from '../../../helpers/custom-validators/misc' 4import { isIdOrUUIDValid, isIdValid } from '../../../helpers/custom-validators/misc'
5import { isVideoExist } from '../../../helpers/custom-validators/videos' 5import { doesVideoExist } from '../../../helpers/custom-validators/videos'
6import { logger } from '../../../helpers/logger' 6import { logger } from '../../../helpers/logger'
7import { areValidationErrors } from '../utils' 7import { areValidationErrors } from '../utils'
8import { 8import {
9 isVideoAbuseExist, 9 doesVideoAbuseExist,
10 isVideoAbuseModerationCommentValid, 10 isVideoAbuseModerationCommentValid,
11 isVideoAbuseReasonValid, 11 isVideoAbuseReasonValid,
12 isVideoAbuseStateValid 12 isVideoAbuseStateValid
@@ -20,7 +20,7 @@ const videoAbuseReportValidator = [
20 logger.debug('Checking videoAbuseReport parameters', { parameters: req.body }) 20 logger.debug('Checking videoAbuseReport parameters', { parameters: req.body })
21 21
22 if (areValidationErrors(req, res)) return 22 if (areValidationErrors(req, res)) return
23 if (!await isVideoExist(req.params.videoId, res)) return 23 if (!await doesVideoExist(req.params.videoId, res)) return
24 24
25 return next() 25 return next()
26 } 26 }
@@ -34,8 +34,8 @@ const videoAbuseGetValidator = [
34 logger.debug('Checking videoAbuseGetValidator parameters', { parameters: req.body }) 34 logger.debug('Checking videoAbuseGetValidator parameters', { parameters: req.body })
35 35
36 if (areValidationErrors(req, res)) return 36 if (areValidationErrors(req, res)) return
37 if (!await isVideoExist(req.params.videoId, res)) return 37 if (!await doesVideoExist(req.params.videoId, res)) return
38 if (!await isVideoAbuseExist(req.params.id, res.locals.video.id, res)) return 38 if (!await doesVideoAbuseExist(req.params.id, res.locals.video.id, res)) return
39 39
40 return next() 40 return next()
41 } 41 }
@@ -55,8 +55,8 @@ const videoAbuseUpdateValidator = [
55 logger.debug('Checking videoAbuseUpdateValidator parameters', { parameters: req.body }) 55 logger.debug('Checking videoAbuseUpdateValidator parameters', { parameters: req.body })
56 56
57 if (areValidationErrors(req, res)) return 57 if (areValidationErrors(req, res)) return
58 if (!await isVideoExist(req.params.videoId, res)) return 58 if (!await doesVideoExist(req.params.videoId, res)) return
59 if (!await isVideoAbuseExist(req.params.id, res.locals.video.id, res)) return 59 if (!await doesVideoAbuseExist(req.params.id, res.locals.video.id, res)) return
60 60
61 return next() 61 return next()
62 } 62 }
diff --git a/server/middlewares/validators/videos/video-blacklist.ts b/server/middlewares/validators/videos/video-blacklist.ts
index 2688f63ae..1d7ddb2e3 100644
--- a/server/middlewares/validators/videos/video-blacklist.ts
+++ b/server/middlewares/validators/videos/video-blacklist.ts
@@ -1,11 +1,14 @@
1import * as express from 'express' 1import * as express from 'express'
2import { body, param } from 'express-validator/check' 2import { body, param, query } from 'express-validator/check'
3import { isBooleanValid, isIdOrUUIDValid } from '../../../helpers/custom-validators/misc' 3import { isBooleanValid, isIdOrUUIDValid } from '../../../helpers/custom-validators/misc'
4import { isVideoExist } from '../../../helpers/custom-validators/videos' 4import { doesVideoExist } from '../../../helpers/custom-validators/videos'
5import { logger } from '../../../helpers/logger' 5import { logger } from '../../../helpers/logger'
6import { areValidationErrors } from '../utils' 6import { areValidationErrors } from '../utils'
7import { isVideoBlacklistExist, isVideoBlacklistReasonValid } from '../../../helpers/custom-validators/video-blacklist' 7import {
8import { VideoModel } from '../../../models/video/video' 8 doesVideoBlacklistExist,
9 isVideoBlacklistReasonValid,
10 isVideoBlacklistTypeValid
11} from '../../../helpers/custom-validators/video-blacklist'
9 12
10const videosBlacklistRemoveValidator = [ 13const videosBlacklistRemoveValidator = [
11 param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'), 14 param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'),
@@ -14,8 +17,8 @@ const videosBlacklistRemoveValidator = [
14 logger.debug('Checking blacklistRemove parameters.', { parameters: req.params }) 17 logger.debug('Checking blacklistRemove parameters.', { parameters: req.params })
15 18
16 if (areValidationErrors(req, res)) return 19 if (areValidationErrors(req, res)) return
17 if (!await isVideoExist(req.params.videoId, res)) return 20 if (!await doesVideoExist(req.params.videoId, res)) return
18 if (!await isVideoBlacklistExist(res.locals.video.id, res)) return 21 if (!await doesVideoBlacklistExist(res.locals.video.id, res)) return
19 22
20 return next() 23 return next()
21 } 24 }
@@ -35,9 +38,9 @@ const videosBlacklistAddValidator = [
35 logger.debug('Checking videosBlacklistAdd parameters', { parameters: req.params }) 38 logger.debug('Checking videosBlacklistAdd parameters', { parameters: req.params })
36 39
37 if (areValidationErrors(req, res)) return 40 if (areValidationErrors(req, res)) return
38 if (!await isVideoExist(req.params.videoId, res)) return 41 if (!await doesVideoExist(req.params.videoId, res)) return
39 42
40 const video: VideoModel = res.locals.video 43 const video = res.locals.video
41 if (req.body.unfederate === true && video.remote === true) { 44 if (req.body.unfederate === true && video.remote === true) {
42 return res 45 return res
43 .status(409) 46 .status(409)
@@ -59,8 +62,22 @@ const videosBlacklistUpdateValidator = [
59 logger.debug('Checking videosBlacklistUpdate parameters', { parameters: req.params }) 62 logger.debug('Checking videosBlacklistUpdate parameters', { parameters: req.params })
60 63
61 if (areValidationErrors(req, res)) return 64 if (areValidationErrors(req, res)) return
62 if (!await isVideoExist(req.params.videoId, res)) return 65 if (!await doesVideoExist(req.params.videoId, res)) return
63 if (!await isVideoBlacklistExist(res.locals.video.id, res)) return 66 if (!await doesVideoBlacklistExist(res.locals.video.id, res)) return
67
68 return next()
69 }
70]
71
72const videosBlacklistFiltersValidator = [
73 query('type')
74 .optional()
75 .custom(isVideoBlacklistTypeValid).withMessage('Should have a valid video blacklist type attribute'),
76
77 (req: express.Request, res: express.Response, next: express.NextFunction) => {
78 logger.debug('Checking videos blacklist filters query', { parameters: req.query })
79
80 if (areValidationErrors(req, res)) return
64 81
65 return next() 82 return next()
66 } 83 }
@@ -71,5 +88,6 @@ const videosBlacklistUpdateValidator = [
71export { 88export {
72 videosBlacklistAddValidator, 89 videosBlacklistAddValidator,
73 videosBlacklistRemoveValidator, 90 videosBlacklistRemoveValidator,
74 videosBlacklistUpdateValidator 91 videosBlacklistUpdateValidator,
92 videosBlacklistFiltersValidator
75} 93}
diff --git a/server/middlewares/validators/videos/video-captions.ts b/server/middlewares/validators/videos/video-captions.ts
index 63d84fbec..d857ac3ec 100644
--- a/server/middlewares/validators/videos/video-captions.ts
+++ b/server/middlewares/validators/videos/video-captions.ts
@@ -1,12 +1,12 @@
1import * as express from 'express' 1import * as express from 'express'
2import { areValidationErrors } from '../utils' 2import { areValidationErrors } from '../utils'
3import { checkUserCanManageVideo, isVideoExist } from '../../../helpers/custom-validators/videos' 3import { checkUserCanManageVideo, doesVideoExist } from '../../../helpers/custom-validators/videos'
4import { isIdOrUUIDValid } from '../../../helpers/custom-validators/misc' 4import { isIdOrUUIDValid } from '../../../helpers/custom-validators/misc'
5import { body, param } from 'express-validator/check' 5import { body, param } from 'express-validator/check'
6import { CONSTRAINTS_FIELDS } from '../../../initializers' 6import { CONSTRAINTS_FIELDS } from '../../../initializers/constants'
7import { UserRight } from '../../../../shared' 7import { UserRight } from '../../../../shared'
8import { logger } from '../../../helpers/logger' 8import { logger } from '../../../helpers/logger'
9import { isVideoCaptionExist, isVideoCaptionFile, isVideoCaptionLanguageValid } from '../../../helpers/custom-validators/video-captions' 9import { doesVideoCaptionExist, isVideoCaptionFile, isVideoCaptionLanguageValid } from '../../../helpers/custom-validators/video-captions'
10import { cleanUpReqFiles } from '../../../helpers/express-utils' 10import { cleanUpReqFiles } from '../../../helpers/express-utils'
11 11
12const addVideoCaptionValidator = [ 12const addVideoCaptionValidator = [
@@ -22,7 +22,7 @@ const addVideoCaptionValidator = [
22 logger.debug('Checking addVideoCaption parameters', { parameters: req.body }) 22 logger.debug('Checking addVideoCaption parameters', { parameters: req.body })
23 23
24 if (areValidationErrors(req, res)) return cleanUpReqFiles(req) 24 if (areValidationErrors(req, res)) return cleanUpReqFiles(req)
25 if (!await isVideoExist(req.params.videoId, res)) return cleanUpReqFiles(req) 25 if (!await doesVideoExist(req.params.videoId, res)) return cleanUpReqFiles(req)
26 26
27 // Check if the user who did the request is able to update the video 27 // Check if the user who did the request is able to update the video
28 const user = res.locals.oauth.token.User 28 const user = res.locals.oauth.token.User
@@ -40,8 +40,8 @@ const deleteVideoCaptionValidator = [
40 logger.debug('Checking deleteVideoCaption parameters', { parameters: req.params }) 40 logger.debug('Checking deleteVideoCaption parameters', { parameters: req.params })
41 41
42 if (areValidationErrors(req, res)) return 42 if (areValidationErrors(req, res)) return
43 if (!await isVideoExist(req.params.videoId, res)) return 43 if (!await doesVideoExist(req.params.videoId, res)) return
44 if (!await isVideoCaptionExist(res.locals.video, req.params.captionLanguage, res)) return 44 if (!await doesVideoCaptionExist(res.locals.video, req.params.captionLanguage, res)) return
45 45
46 // Check if the user who did the request is able to update the video 46 // Check if the user who did the request is able to update the video
47 const user = res.locals.oauth.token.User 47 const user = res.locals.oauth.token.User
@@ -58,7 +58,7 @@ const listVideoCaptionsValidator = [
58 logger.debug('Checking listVideoCaptions parameters', { parameters: req.params }) 58 logger.debug('Checking listVideoCaptions parameters', { parameters: req.params })
59 59
60 if (areValidationErrors(req, res)) return 60 if (areValidationErrors(req, res)) return
61 if (!await isVideoExist(req.params.videoId, res, 'id')) return 61 if (!await doesVideoExist(req.params.videoId, res, 'id')) return
62 62
63 return next() 63 return next()
64 } 64 }
diff --git a/server/middlewares/validators/videos/video-channels.ts b/server/middlewares/validators/videos/video-channels.ts
index f039794e0..4b26f0bc4 100644
--- a/server/middlewares/validators/videos/video-channels.ts
+++ b/server/middlewares/validators/videos/video-channels.ts
@@ -1,12 +1,11 @@
1import * as express from 'express' 1import * as express from 'express'
2import { body, param } from 'express-validator/check' 2import { body, param } from 'express-validator/check'
3import { UserRight } from '../../../../shared' 3import { UserRight } from '../../../../shared'
4import { isAccountNameWithHostExist } from '../../../helpers/custom-validators/accounts'
5import { 4import {
6 isLocalVideoChannelNameExist, 5 doesLocalVideoChannelNameExist,
6 doesVideoChannelNameWithHostExist,
7 isVideoChannelDescriptionValid, 7 isVideoChannelDescriptionValid,
8 isVideoChannelNameValid, 8 isVideoChannelNameValid,
9 isVideoChannelNameWithHostExist,
10 isVideoChannelSupportValid 9 isVideoChannelSupportValid
11} from '../../../helpers/custom-validators/video-channels' 10} from '../../../helpers/custom-validators/video-channels'
12import { logger } from '../../../helpers/logger' 11import { logger } from '../../../helpers/logger'
@@ -16,19 +15,6 @@ import { areValidationErrors } from '../utils'
16import { isActorPreferredUsernameValid } from '../../../helpers/custom-validators/activitypub/actor' 15import { isActorPreferredUsernameValid } from '../../../helpers/custom-validators/activitypub/actor'
17import { ActorModel } from '../../../models/activitypub/actor' 16import { ActorModel } from '../../../models/activitypub/actor'
18 17
19const listVideoAccountChannelsValidator = [
20 param('accountName').exists().withMessage('Should have a valid account name'),
21
22 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
23 logger.debug('Checking listVideoAccountChannelsValidator parameters', { parameters: req.body })
24
25 if (areValidationErrors(req, res)) return
26 if (!await isAccountNameWithHostExist(req.params.accountName, res)) return
27
28 return next()
29 }
30]
31
32const videoChannelsAddValidator = [ 18const videoChannelsAddValidator = [
33 body('name').custom(isActorPreferredUsernameValid).withMessage('Should have a valid channel name'), 19 body('name').custom(isActorPreferredUsernameValid).withMessage('Should have a valid channel name'),
34 body('displayName').custom(isVideoChannelNameValid).withMessage('Should have a valid display name'), 20 body('displayName').custom(isVideoChannelNameValid).withMessage('Should have a valid display name'),
@@ -62,7 +48,7 @@ const videoChannelsUpdateValidator = [
62 logger.debug('Checking videoChannelsUpdate parameters', { parameters: req.body }) 48 logger.debug('Checking videoChannelsUpdate parameters', { parameters: req.body })
63 49
64 if (areValidationErrors(req, res)) return 50 if (areValidationErrors(req, res)) return
65 if (!await isVideoChannelNameWithHostExist(req.params.nameWithHost, res)) return 51 if (!await doesVideoChannelNameWithHostExist(req.params.nameWithHost, res)) return
66 52
67 // We need to make additional checks 53 // We need to make additional checks
68 if (res.locals.videoChannel.Actor.isOwned() === false) { 54 if (res.locals.videoChannel.Actor.isOwned() === false) {
@@ -88,7 +74,7 @@ const videoChannelsRemoveValidator = [
88 logger.debug('Checking videoChannelsRemove parameters', { parameters: req.params }) 74 logger.debug('Checking videoChannelsRemove parameters', { parameters: req.params })
89 75
90 if (areValidationErrors(req, res)) return 76 if (areValidationErrors(req, res)) return
91 if (!await isVideoChannelNameWithHostExist(req.params.nameWithHost, res)) return 77 if (!await doesVideoChannelNameWithHostExist(req.params.nameWithHost, res)) return
92 78
93 if (!checkUserCanDeleteVideoChannel(res.locals.oauth.token.User, res.locals.videoChannel, res)) return 79 if (!checkUserCanDeleteVideoChannel(res.locals.oauth.token.User, res.locals.videoChannel, res)) return
94 if (!await checkVideoChannelIsNotTheLastOne(res)) return 80 if (!await checkVideoChannelIsNotTheLastOne(res)) return
@@ -105,7 +91,7 @@ const videoChannelsNameWithHostValidator = [
105 91
106 if (areValidationErrors(req, res)) return 92 if (areValidationErrors(req, res)) return
107 93
108 if (!await isVideoChannelNameWithHostExist(req.params.nameWithHost, res)) return 94 if (!await doesVideoChannelNameWithHostExist(req.params.nameWithHost, res)) return
109 95
110 return next() 96 return next()
111 } 97 }
@@ -118,7 +104,7 @@ const localVideoChannelValidator = [
118 logger.debug('Checking localVideoChannelValidator parameters', { parameters: req.params }) 104 logger.debug('Checking localVideoChannelValidator parameters', { parameters: req.params })
119 105
120 if (areValidationErrors(req, res)) return 106 if (areValidationErrors(req, res)) return
121 if (!await isLocalVideoChannelNameExist(req.params.name, res)) return 107 if (!await doesLocalVideoChannelNameExist(req.params.name, res)) return
122 108
123 return next() 109 return next()
124 } 110 }
@@ -127,7 +113,6 @@ const localVideoChannelValidator = [
127// --------------------------------------------------------------------------- 113// ---------------------------------------------------------------------------
128 114
129export { 115export {
130 listVideoAccountChannelsValidator,
131 videoChannelsAddValidator, 116 videoChannelsAddValidator,
132 videoChannelsUpdateValidator, 117 videoChannelsUpdateValidator,
133 videoChannelsRemoveValidator, 118 videoChannelsRemoveValidator,
diff --git a/server/middlewares/validators/videos/video-comments.ts b/server/middlewares/validators/videos/video-comments.ts
index 348d33082..ffde208b7 100644
--- a/server/middlewares/validators/videos/video-comments.ts
+++ b/server/middlewares/validators/videos/video-comments.ts
@@ -3,7 +3,7 @@ import { body, param } from 'express-validator/check'
3import { UserRight } from '../../../../shared' 3import { UserRight } from '../../../../shared'
4import { isIdOrUUIDValid, isIdValid } from '../../../helpers/custom-validators/misc' 4import { isIdOrUUIDValid, isIdValid } from '../../../helpers/custom-validators/misc'
5import { isValidVideoCommentText } from '../../../helpers/custom-validators/video-comments' 5import { isValidVideoCommentText } from '../../../helpers/custom-validators/video-comments'
6import { isVideoExist } from '../../../helpers/custom-validators/videos' 6import { doesVideoExist } from '../../../helpers/custom-validators/videos'
7import { logger } from '../../../helpers/logger' 7import { logger } from '../../../helpers/logger'
8import { UserModel } from '../../../models/account/user' 8import { UserModel } from '../../../models/account/user'
9import { VideoModel } from '../../../models/video/video' 9import { VideoModel } from '../../../models/video/video'
@@ -17,7 +17,7 @@ const listVideoCommentThreadsValidator = [
17 logger.debug('Checking listVideoCommentThreads parameters.', { parameters: req.params }) 17 logger.debug('Checking listVideoCommentThreads parameters.', { parameters: req.params })
18 18
19 if (areValidationErrors(req, res)) return 19 if (areValidationErrors(req, res)) return
20 if (!await isVideoExist(req.params.videoId, res, 'only-video')) return 20 if (!await doesVideoExist(req.params.videoId, res, 'only-video')) return
21 21
22 return next() 22 return next()
23 } 23 }
@@ -31,8 +31,8 @@ const listVideoThreadCommentsValidator = [
31 logger.debug('Checking listVideoThreadComments parameters.', { parameters: req.params }) 31 logger.debug('Checking listVideoThreadComments parameters.', { parameters: req.params })
32 32
33 if (areValidationErrors(req, res)) return 33 if (areValidationErrors(req, res)) return
34 if (!await isVideoExist(req.params.videoId, res, 'only-video')) return 34 if (!await doesVideoExist(req.params.videoId, res, 'only-video')) return
35 if (!await isVideoCommentThreadExist(req.params.threadId, res.locals.video, res)) return 35 if (!await doesVideoCommentThreadExist(req.params.threadId, res.locals.video, res)) return
36 36
37 return next() 37 return next()
38 } 38 }
@@ -46,7 +46,7 @@ const addVideoCommentThreadValidator = [
46 logger.debug('Checking addVideoCommentThread parameters.', { parameters: req.params, body: req.body }) 46 logger.debug('Checking addVideoCommentThread parameters.', { parameters: req.params, body: req.body })
47 47
48 if (areValidationErrors(req, res)) return 48 if (areValidationErrors(req, res)) return
49 if (!await isVideoExist(req.params.videoId, res)) return 49 if (!await doesVideoExist(req.params.videoId, res)) return
50 if (!isVideoCommentsEnabled(res.locals.video, res)) return 50 if (!isVideoCommentsEnabled(res.locals.video, res)) return
51 51
52 return next() 52 return next()
@@ -62,9 +62,9 @@ const addVideoCommentReplyValidator = [
62 logger.debug('Checking addVideoCommentReply parameters.', { parameters: req.params, body: req.body }) 62 logger.debug('Checking addVideoCommentReply parameters.', { parameters: req.params, body: req.body })
63 63
64 if (areValidationErrors(req, res)) return 64 if (areValidationErrors(req, res)) return
65 if (!await isVideoExist(req.params.videoId, res)) return 65 if (!await doesVideoExist(req.params.videoId, res)) return
66 if (!isVideoCommentsEnabled(res.locals.video, res)) return 66 if (!isVideoCommentsEnabled(res.locals.video, res)) return
67 if (!await isVideoCommentExist(req.params.commentId, res.locals.video, res)) return 67 if (!await doesVideoCommentExist(req.params.commentId, res.locals.video, res)) return
68 68
69 return next() 69 return next()
70 } 70 }
@@ -78,8 +78,8 @@ const videoCommentGetValidator = [
78 logger.debug('Checking videoCommentGetValidator parameters.', { parameters: req.params }) 78 logger.debug('Checking videoCommentGetValidator parameters.', { parameters: req.params })
79 79
80 if (areValidationErrors(req, res)) return 80 if (areValidationErrors(req, res)) return
81 if (!await isVideoExist(req.params.videoId, res, 'id')) return 81 if (!await doesVideoExist(req.params.videoId, res, 'id')) return
82 if (!await isVideoCommentExist(req.params.commentId, res.locals.video, res)) return 82 if (!await doesVideoCommentExist(req.params.commentId, res.locals.video, res)) return
83 83
84 return next() 84 return next()
85 } 85 }
@@ -93,8 +93,8 @@ const removeVideoCommentValidator = [
93 logger.debug('Checking removeVideoCommentValidator parameters.', { parameters: req.params }) 93 logger.debug('Checking removeVideoCommentValidator parameters.', { parameters: req.params })
94 94
95 if (areValidationErrors(req, res)) return 95 if (areValidationErrors(req, res)) return
96 if (!await isVideoExist(req.params.videoId, res)) return 96 if (!await doesVideoExist(req.params.videoId, res)) return
97 if (!await isVideoCommentExist(req.params.commentId, res.locals.video, res)) return 97 if (!await doesVideoCommentExist(req.params.commentId, res.locals.video, res)) return
98 98
99 // Check if the user who did the request is able to delete the video 99 // Check if the user who did the request is able to delete the video
100 if (!checkUserCanDeleteVideoComment(res.locals.oauth.token.User, res.locals.videoComment, res)) return 100 if (!checkUserCanDeleteVideoComment(res.locals.oauth.token.User, res.locals.videoComment, res)) return
@@ -116,7 +116,7 @@ export {
116 116
117// --------------------------------------------------------------------------- 117// ---------------------------------------------------------------------------
118 118
119async function isVideoCommentThreadExist (id: number, video: VideoModel, res: express.Response) { 119async function doesVideoCommentThreadExist (id: number, video: VideoModel, res: express.Response) {
120 const videoComment = await VideoCommentModel.loadById(id) 120 const videoComment = await VideoCommentModel.loadById(id)
121 121
122 if (!videoComment) { 122 if (!videoComment) {
@@ -147,7 +147,7 @@ async function isVideoCommentThreadExist (id: number, video: VideoModel, res: ex
147 return true 147 return true
148} 148}
149 149
150async function isVideoCommentExist (id: number, video: VideoModel, res: express.Response) { 150async function doesVideoCommentExist (id: number, video: VideoModel, res: express.Response) {
151 const videoComment = await VideoCommentModel.loadByIdAndPopulateVideoAndAccountAndReply(id) 151 const videoComment = await VideoCommentModel.loadByIdAndPopulateVideoAndAccountAndReply(id)
152 152
153 if (!videoComment) { 153 if (!videoComment) {
diff --git a/server/middlewares/validators/videos/video-imports.ts b/server/middlewares/validators/videos/video-imports.ts
index 48d20f904..452084a7c 100644
--- a/server/middlewares/validators/videos/video-imports.ts
+++ b/server/middlewares/validators/videos/video-imports.ts
@@ -3,14 +3,14 @@ import { body } from 'express-validator/check'
3import { isIdValid } from '../../../helpers/custom-validators/misc' 3import { isIdValid } from '../../../helpers/custom-validators/misc'
4import { logger } from '../../../helpers/logger' 4import { logger } from '../../../helpers/logger'
5import { areValidationErrors } from '../utils' 5import { areValidationErrors } from '../utils'
6import { getCommonVideoAttributes } from './videos' 6import { getCommonVideoEditAttributes } from './videos'
7import { isVideoImportTargetUrlValid, isVideoImportTorrentFile } from '../../../helpers/custom-validators/video-imports' 7import { isVideoImportTargetUrlValid, isVideoImportTorrentFile } from '../../../helpers/custom-validators/video-imports'
8import { cleanUpReqFiles } from '../../../helpers/express-utils' 8import { cleanUpReqFiles } from '../../../helpers/express-utils'
9import { isVideoChannelOfAccountExist, isVideoMagnetUriValid, isVideoNameValid } from '../../../helpers/custom-validators/videos' 9import { doesVideoChannelOfAccountExist, isVideoMagnetUriValid, isVideoNameValid } from '../../../helpers/custom-validators/videos'
10import { CONFIG } from '../../../initializers/constants' 10import { CONFIG } from '../../../initializers/config'
11import { CONSTRAINTS_FIELDS } from '../../../initializers' 11import { CONSTRAINTS_FIELDS } from '../../../initializers/constants'
12 12
13const videoImportAddValidator = getCommonVideoAttributes().concat([ 13const videoImportAddValidator = getCommonVideoEditAttributes().concat([
14 body('channelId') 14 body('channelId')
15 .toInt() 15 .toInt()
16 .custom(isIdValid).withMessage('Should have correct video channel id'), 16 .custom(isIdValid).withMessage('Should have correct video channel id'),
@@ -51,7 +51,7 @@ const videoImportAddValidator = getCommonVideoAttributes().concat([
51 .end() 51 .end()
52 } 52 }
53 53
54 if (!await isVideoChannelOfAccountExist(req.body.channelId, user, res)) return cleanUpReqFiles(req) 54 if (!await doesVideoChannelOfAccountExist(req.body.channelId, user, res)) return cleanUpReqFiles(req)
55 55
56 // Check we have at least 1 required param 56 // Check we have at least 1 required param
57 if (!req.body.targetUrl && !req.body.magnetUri && !torrentFile) { 57 if (!req.body.targetUrl && !req.body.magnetUri && !torrentFile) {
diff --git a/server/middlewares/validators/videos/video-playlists.ts b/server/middlewares/validators/videos/video-playlists.ts
new file mode 100644
index 000000000..2c3f7e542
--- /dev/null
+++ b/server/middlewares/validators/videos/video-playlists.ts
@@ -0,0 +1,408 @@
1import * as express from 'express'
2import { body, param, query, ValidationChain } from 'express-validator/check'
3import { UserRight, VideoPlaylistCreate, VideoPlaylistUpdate } from '../../../../shared'
4import { logger } from '../../../helpers/logger'
5import { UserModel } from '../../../models/account/user'
6import { areValidationErrors } from '../utils'
7import { doesVideoExist, isVideoImage } from '../../../helpers/custom-validators/videos'
8import { CONSTRAINTS_FIELDS } from '../../../initializers/constants'
9import { isArrayOf, isIdOrUUIDValid, isIdValid, isUUIDValid, toIntArray, toValueOrNull } from '../../../helpers/custom-validators/misc'
10import {
11 doesVideoPlaylistExist,
12 isVideoPlaylistDescriptionValid,
13 isVideoPlaylistNameValid,
14 isVideoPlaylistPrivacyValid,
15 isVideoPlaylistTimestampValid,
16 isVideoPlaylistTypeValid
17} from '../../../helpers/custom-validators/video-playlists'
18import { VideoPlaylistModel } from '../../../models/video/video-playlist'
19import { cleanUpReqFiles } from '../../../helpers/express-utils'
20import { doesVideoChannelIdExist } from '../../../helpers/custom-validators/video-channels'
21import { VideoPlaylistElementModel } from '../../../models/video/video-playlist-element'
22import { authenticatePromiseIfNeeded } from '../../oauth'
23import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model'
24import { VideoPlaylistType } from '../../../../shared/models/videos/playlist/video-playlist-type.model'
25
26const videoPlaylistsAddValidator = getCommonPlaylistEditAttributes().concat([
27 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
28 logger.debug('Checking videoPlaylistsAddValidator parameters', { parameters: req.body })
29
30 if (areValidationErrors(req, res)) return cleanUpReqFiles(req)
31
32 const body: VideoPlaylistCreate = req.body
33 if (body.videoChannelId && !await doesVideoChannelIdExist(body.videoChannelId, res)) return cleanUpReqFiles(req)
34
35 if (body.privacy === VideoPlaylistPrivacy.PUBLIC && !body.videoChannelId) {
36 cleanUpReqFiles(req)
37 return res.status(400)
38 .json({ error: 'Cannot set "public" a playlist that is not assigned to a channel.' })
39 }
40
41 return next()
42 }
43])
44
45const videoPlaylistsUpdateValidator = getCommonPlaylistEditAttributes().concat([
46 param('playlistId')
47 .custom(isIdOrUUIDValid).withMessage('Should have a valid playlist id/uuid'),
48
49 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
50 logger.debug('Checking videoPlaylistsUpdateValidator parameters', { parameters: req.body })
51
52 if (areValidationErrors(req, res)) return cleanUpReqFiles(req)
53
54 if (!await doesVideoPlaylistExist(req.params.playlistId, res, 'all')) return cleanUpReqFiles(req)
55
56 const videoPlaylist = res.locals.videoPlaylist
57
58 if (!checkUserCanManageVideoPlaylist(res.locals.oauth.token.User, res.locals.videoPlaylist, UserRight.REMOVE_ANY_VIDEO_PLAYLIST, res)) {
59 return cleanUpReqFiles(req)
60 }
61
62 const body: VideoPlaylistUpdate = req.body
63
64 if (videoPlaylist.privacy !== VideoPlaylistPrivacy.PRIVATE && body.privacy === VideoPlaylistPrivacy.PRIVATE) {
65 cleanUpReqFiles(req)
66 return res.status(400)
67 .json({ error: 'Cannot set "private" a video playlist that was not private.' })
68 }
69
70 const newPrivacy = body.privacy || videoPlaylist.privacy
71 if (newPrivacy === VideoPlaylistPrivacy.PUBLIC &&
72 (
73 (!videoPlaylist.videoChannelId && !body.videoChannelId) ||
74 body.videoChannelId === null
75 )
76 ) {
77 cleanUpReqFiles(req)
78 return res.status(400)
79 .json({ error: 'Cannot set "public" a playlist that is not assigned to a channel.' })
80 }
81
82 if (videoPlaylist.type === VideoPlaylistType.WATCH_LATER) {
83 cleanUpReqFiles(req)
84 return res.status(400)
85 .json({ error: 'Cannot update a watch later playlist.' })
86 }
87
88 if (body.videoChannelId && !await doesVideoChannelIdExist(body.videoChannelId, res)) return cleanUpReqFiles(req)
89
90 return next()
91 }
92])
93
94const videoPlaylistsDeleteValidator = [
95 param('playlistId')
96 .custom(isIdOrUUIDValid).withMessage('Should have a valid playlist id/uuid'),
97
98 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
99 logger.debug('Checking videoPlaylistsDeleteValidator parameters', { parameters: req.params })
100
101 if (areValidationErrors(req, res)) return
102
103 if (!await doesVideoPlaylistExist(req.params.playlistId, res)) return
104
105 const videoPlaylist = res.locals.videoPlaylist
106 if (videoPlaylist.type === VideoPlaylistType.WATCH_LATER) {
107 return res.status(400)
108 .json({ error: 'Cannot delete a watch later playlist.' })
109 }
110
111 if (!checkUserCanManageVideoPlaylist(res.locals.oauth.token.User, res.locals.videoPlaylist, UserRight.REMOVE_ANY_VIDEO_PLAYLIST, res)) {
112 return
113 }
114
115 return next()
116 }
117]
118
119const videoPlaylistsGetValidator = [
120 param('playlistId')
121 .custom(isIdOrUUIDValid).withMessage('Should have a valid playlist id/uuid'),
122
123 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
124 logger.debug('Checking videoPlaylistsGetValidator parameters', { parameters: req.params })
125
126 if (areValidationErrors(req, res)) return
127
128 if (!await doesVideoPlaylistExist(req.params.playlistId, res)) return
129
130 const videoPlaylist = res.locals.videoPlaylist
131
132 // Video is unlisted, check we used the uuid to fetch it
133 if (videoPlaylist.privacy === VideoPlaylistPrivacy.UNLISTED) {
134 if (isUUIDValid(req.params.playlistId)) return next()
135
136 return res.status(404).end()
137 }
138
139 if (videoPlaylist.privacy === VideoPlaylistPrivacy.PRIVATE) {
140 await authenticatePromiseIfNeeded(req, res)
141
142 const user = res.locals.oauth ? res.locals.oauth.token.User : null
143 if (
144 !user ||
145 (videoPlaylist.OwnerAccount.userId !== user.id && !user.hasRight(UserRight.UPDATE_ANY_VIDEO_PLAYLIST))
146 ) {
147 return res.status(403)
148 .json({ error: 'Cannot get this private video playlist.' })
149 }
150
151 return next()
152 }
153
154 return next()
155 }
156]
157
158const videoPlaylistsAddVideoValidator = [
159 param('playlistId')
160 .custom(isIdOrUUIDValid).withMessage('Should have a valid playlist id/uuid'),
161 body('videoId')
162 .custom(isIdOrUUIDValid).withMessage('Should have a valid video id/uuid'),
163 body('startTimestamp')
164 .optional()
165 .custom(isVideoPlaylistTimestampValid).withMessage('Should have a valid start timestamp'),
166 body('stopTimestamp')
167 .optional()
168 .custom(isVideoPlaylistTimestampValid).withMessage('Should have a valid stop timestamp'),
169
170 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
171 logger.debug('Checking videoPlaylistsAddVideoValidator parameters', { parameters: req.params })
172
173 if (areValidationErrors(req, res)) return
174
175 if (!await doesVideoPlaylistExist(req.params.playlistId, res, 'all')) return
176 if (!await doesVideoExist(req.body.videoId, res, 'only-video')) return
177
178 const videoPlaylist = res.locals.videoPlaylist
179 const video = res.locals.video
180
181 const videoPlaylistElement = await VideoPlaylistElementModel.loadByPlaylistAndVideo(videoPlaylist.id, video.id)
182 if (videoPlaylistElement) {
183 res.status(409)
184 .json({ error: 'This video in this playlist already exists' })
185 .end()
186
187 return
188 }
189
190 if (!checkUserCanManageVideoPlaylist(res.locals.oauth.token.User, res.locals.videoPlaylist, UserRight.UPDATE_ANY_VIDEO_PLAYLIST, res)) {
191 return
192 }
193
194 return next()
195 }
196]
197
198const videoPlaylistsUpdateOrRemoveVideoValidator = [
199 param('playlistId')
200 .custom(isIdOrUUIDValid).withMessage('Should have a valid playlist id/uuid'),
201 param('videoId')
202 .custom(isIdOrUUIDValid).withMessage('Should have an video id/uuid'),
203 body('startTimestamp')
204 .optional()
205 .custom(isVideoPlaylistTimestampValid).withMessage('Should have a valid start timestamp'),
206 body('stopTimestamp')
207 .optional()
208 .custom(isVideoPlaylistTimestampValid).withMessage('Should have a valid stop timestamp'),
209
210 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
211 logger.debug('Checking videoPlaylistsRemoveVideoValidator parameters', { parameters: req.params })
212
213 if (areValidationErrors(req, res)) return
214
215 if (!await doesVideoPlaylistExist(req.params.playlistId, res, 'all')) return
216 if (!await doesVideoExist(req.params.videoId, res, 'id')) return
217
218 const videoPlaylist = res.locals.videoPlaylist
219 const video = res.locals.video
220
221 const videoPlaylistElement = await VideoPlaylistElementModel.loadByPlaylistAndVideo(videoPlaylist.id, video.id)
222 if (!videoPlaylistElement) {
223 res.status(404)
224 .json({ error: 'Video playlist element not found' })
225 .end()
226
227 return
228 }
229 res.locals.videoPlaylistElement = videoPlaylistElement
230
231 if (!checkUserCanManageVideoPlaylist(res.locals.oauth.token.User, videoPlaylist, UserRight.UPDATE_ANY_VIDEO_PLAYLIST, res)) return
232
233 return next()
234 }
235]
236
237const videoPlaylistElementAPGetValidator = [
238 param('playlistId')
239 .custom(isIdOrUUIDValid).withMessage('Should have a valid playlist id/uuid'),
240 param('videoId')
241 .custom(isIdOrUUIDValid).withMessage('Should have an video id/uuid'),
242
243 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
244 logger.debug('Checking videoPlaylistElementAPGetValidator parameters', { parameters: req.params })
245
246 if (areValidationErrors(req, res)) return
247
248 const videoPlaylistElement = await VideoPlaylistElementModel.loadByPlaylistAndVideoForAP(req.params.playlistId, req.params.videoId)
249 if (!videoPlaylistElement) {
250 res.status(404)
251 .json({ error: 'Video playlist element not found' })
252 .end()
253
254 return
255 }
256
257 if (videoPlaylistElement.VideoPlaylist.privacy === VideoPlaylistPrivacy.PRIVATE) {
258 return res.status(403).end()
259 }
260
261 res.locals.videoPlaylistElement = videoPlaylistElement
262
263 return next()
264 }
265]
266
267const videoPlaylistsReorderVideosValidator = [
268 param('playlistId')
269 .custom(isIdOrUUIDValid).withMessage('Should have a valid playlist id/uuid'),
270 body('startPosition')
271 .isInt({ min: 1 }).withMessage('Should have a valid start position'),
272 body('insertAfterPosition')
273 .isInt({ min: 0 }).withMessage('Should have a valid insert after position'),
274 body('reorderLength')
275 .optional()
276 .isInt({ min: 1 }).withMessage('Should have a valid range length'),
277
278 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
279 logger.debug('Checking videoPlaylistsReorderVideosValidator parameters', { parameters: req.params })
280
281 if (areValidationErrors(req, res)) return
282
283 if (!await doesVideoPlaylistExist(req.params.playlistId, res, 'all')) return
284
285 const videoPlaylist = res.locals.videoPlaylist
286 if (!checkUserCanManageVideoPlaylist(res.locals.oauth.token.User, videoPlaylist, UserRight.UPDATE_ANY_VIDEO_PLAYLIST, res)) return
287
288 const nextPosition = await VideoPlaylistElementModel.getNextPositionOf(videoPlaylist.id)
289 const startPosition: number = req.body.startPosition
290 const insertAfterPosition: number = req.body.insertAfterPosition
291 const reorderLength: number = req.body.reorderLength
292
293 if (startPosition >= nextPosition || insertAfterPosition >= nextPosition) {
294 res.status(400)
295 .json({ error: `Start position or insert after position exceed the playlist limits (max: ${nextPosition - 1})` })
296 .end()
297
298 return
299 }
300
301 if (reorderLength && reorderLength + startPosition > nextPosition) {
302 res.status(400)
303 .json({ error: `Reorder length with this start position exceeds the playlist limits (max: ${nextPosition - startPosition})` })
304 .end()
305
306 return
307 }
308
309 return next()
310 }
311]
312
313const commonVideoPlaylistFiltersValidator = [
314 query('playlistType')
315 .optional()
316 .custom(isVideoPlaylistTypeValid).withMessage('Should have a valid playlist type'),
317
318 (req: express.Request, res: express.Response, next: express.NextFunction) => {
319 logger.debug('Checking commonVideoPlaylistFiltersValidator parameters', { parameters: req.params })
320
321 if (areValidationErrors(req, res)) return
322
323 return next()
324 }
325]
326
327const doVideosInPlaylistExistValidator = [
328 query('videoIds')
329 .customSanitizer(toIntArray)
330 .custom(v => isArrayOf(v, isIdValid)).withMessage('Should have a valid video ids array'),
331
332 (req: express.Request, res: express.Response, next: express.NextFunction) => {
333 logger.debug('Checking areVideosInPlaylistExistValidator parameters', { parameters: req.query })
334
335 if (areValidationErrors(req, res)) return
336
337 return next()
338 }
339]
340
341// ---------------------------------------------------------------------------
342
343export {
344 videoPlaylistsAddValidator,
345 videoPlaylistsUpdateValidator,
346 videoPlaylistsDeleteValidator,
347 videoPlaylistsGetValidator,
348
349 videoPlaylistsAddVideoValidator,
350 videoPlaylistsUpdateOrRemoveVideoValidator,
351 videoPlaylistsReorderVideosValidator,
352
353 videoPlaylistElementAPGetValidator,
354
355 commonVideoPlaylistFiltersValidator,
356
357 doVideosInPlaylistExistValidator
358}
359
360// ---------------------------------------------------------------------------
361
362function getCommonPlaylistEditAttributes () {
363 return [
364 body('thumbnailfile')
365 .custom((value, { req }) => isVideoImage(req.files, 'thumbnailfile')).withMessage(
366 'This thumbnail file is not supported or too large. Please, make sure it is of the following type: '
367 + CONSTRAINTS_FIELDS.VIDEO_PLAYLISTS.IMAGE.EXTNAME.join(', ')
368 ),
369
370 body('displayName')
371 .custom(isVideoPlaylistNameValid).withMessage('Should have a valid display name'),
372 body('description')
373 .optional()
374 .customSanitizer(toValueOrNull)
375 .custom(isVideoPlaylistDescriptionValid).withMessage('Should have a valid description'),
376 body('privacy')
377 .optional()
378 .toInt()
379 .custom(isVideoPlaylistPrivacyValid).withMessage('Should have correct playlist privacy'),
380 body('videoChannelId')
381 .optional()
382 .customSanitizer(toValueOrNull)
383 .toInt()
384 ] as (ValidationChain | express.Handler)[]
385}
386
387function checkUserCanManageVideoPlaylist (user: UserModel, videoPlaylist: VideoPlaylistModel, right: UserRight, res: express.Response) {
388 if (videoPlaylist.isOwned() === false) {
389 res.status(403)
390 .json({ error: 'Cannot manage video playlist of another server.' })
391 .end()
392
393 return false
394 }
395
396 // Check if the user can manage the video playlist
397 // The user can delete it if s/he is an admin
398 // Or if s/he is the video playlist's owner
399 if (user.hasRight(right) === false && videoPlaylist.ownerAccountId !== user.Account.id) {
400 res.status(403)
401 .json({ error: 'Cannot manage video playlist of another user' })
402 .end()
403
404 return false
405 }
406
407 return true
408}
diff --git a/server/middlewares/validators/videos/video-rates.ts b/server/middlewares/validators/videos/video-rates.ts
index 793354520..204b4a78d 100644
--- a/server/middlewares/validators/videos/video-rates.ts
+++ b/server/middlewares/validators/videos/video-rates.ts
@@ -1,8 +1,9 @@
1import * as express from 'express' 1import * as express from 'express'
2import 'express-validator' 2import 'express-validator'
3import { body, param } from 'express-validator/check' 3import { body, param, query } from 'express-validator/check'
4import { isIdOrUUIDValid, isIdValid } from '../../../helpers/custom-validators/misc' 4import { isIdOrUUIDValid } from '../../../helpers/custom-validators/misc'
5import { isVideoExist, isVideoRatingTypeValid } from '../../../helpers/custom-validators/videos' 5import { isRatingValid } from '../../../helpers/custom-validators/video-rates'
6import { doesVideoExist, isVideoRatingTypeValid } from '../../../helpers/custom-validators/videos'
6import { logger } from '../../../helpers/logger' 7import { logger } from '../../../helpers/logger'
7import { areValidationErrors } from '../utils' 8import { areValidationErrors } from '../utils'
8import { AccountVideoRateModel } from '../../../models/account/account-video-rate' 9import { AccountVideoRateModel } from '../../../models/account/account-video-rate'
@@ -17,7 +18,7 @@ const videoUpdateRateValidator = [
17 logger.debug('Checking videoRate parameters', { parameters: req.body }) 18 logger.debug('Checking videoRate parameters', { parameters: req.body })
18 19
19 if (areValidationErrors(req, res)) return 20 if (areValidationErrors(req, res)) return
20 if (!await isVideoExist(req.params.id, res)) return 21 if (!await doesVideoExist(req.params.id, res)) return
21 22
22 return next() 23 return next()
23 } 24 }
@@ -47,9 +48,22 @@ const getAccountVideoRateValidator = function (rateType: VideoRateType) {
47 ] 48 ]
48} 49}
49 50
51const videoRatingValidator = [
52 query('rating').optional().custom(isRatingValid).withMessage('Value must be one of "like" or "dislike"'),
53
54 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
55 logger.debug('Checking rating parameter', { parameters: req.params })
56
57 if (areValidationErrors(req, res)) return
58
59 return next()
60 }
61]
62
50// --------------------------------------------------------------------------- 63// ---------------------------------------------------------------------------
51 64
52export { 65export {
53 videoUpdateRateValidator, 66 videoUpdateRateValidator,
54 getAccountVideoRateValidator 67 getAccountVideoRateValidator,
68 videoRatingValidator
55} 69}
diff --git a/server/middlewares/validators/videos/video-shares.ts b/server/middlewares/validators/videos/video-shares.ts
index 646d7acb1..d5cbdb03e 100644
--- a/server/middlewares/validators/videos/video-shares.ts
+++ b/server/middlewares/validators/videos/video-shares.ts
@@ -2,11 +2,10 @@ import * as express from 'express'
2import 'express-validator' 2import 'express-validator'
3import { param } from 'express-validator/check' 3import { param } from 'express-validator/check'
4import { isIdOrUUIDValid, isIdValid } from '../../../helpers/custom-validators/misc' 4import { isIdOrUUIDValid, isIdValid } from '../../../helpers/custom-validators/misc'
5import { isVideoExist } from '../../../helpers/custom-validators/videos' 5import { doesVideoExist } from '../../../helpers/custom-validators/videos'
6import { logger } from '../../../helpers/logger' 6import { logger } from '../../../helpers/logger'
7import { VideoShareModel } from '../../../models/video/video-share' 7import { VideoShareModel } from '../../../models/video/video-share'
8import { areValidationErrors } from '../utils' 8import { areValidationErrors } from '../utils'
9import { VideoModel } from '../../../models/video/video'
10 9
11const videosShareValidator = [ 10const videosShareValidator = [
12 param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), 11 param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'),
@@ -16,9 +15,9 @@ const videosShareValidator = [
16 logger.debug('Checking videoShare parameters', { parameters: req.params }) 15 logger.debug('Checking videoShare parameters', { parameters: req.params })
17 16
18 if (areValidationErrors(req, res)) return 17 if (areValidationErrors(req, res)) return
19 if (!await isVideoExist(req.params.id, res)) return 18 if (!await doesVideoExist(req.params.id, res)) return
20 19
21 const video: VideoModel = res.locals.video 20 const video = res.locals.video
22 21
23 const share = await VideoShareModel.load(req.params.actorId, video.id) 22 const share = await VideoShareModel.load(req.params.actorId, video.id)
24 if (!share) { 23 if (!share) {
diff --git a/server/middlewares/validators/videos/video-watch.ts b/server/middlewares/validators/videos/video-watch.ts
index c38ad8a10..a3a800d14 100644
--- a/server/middlewares/validators/videos/video-watch.ts
+++ b/server/middlewares/validators/videos/video-watch.ts
@@ -1,10 +1,9 @@
1import { body, param } from 'express-validator/check' 1import { body, param } from 'express-validator/check'
2import * as express from 'express' 2import * as express from 'express'
3import { isIdOrUUIDValid } from '../../../helpers/custom-validators/misc' 3import { isIdOrUUIDValid } from '../../../helpers/custom-validators/misc'
4import { isVideoExist } from '../../../helpers/custom-validators/videos' 4import { doesVideoExist } from '../../../helpers/custom-validators/videos'
5import { areValidationErrors } from '../utils' 5import { areValidationErrors } from '../utils'
6import { logger } from '../../../helpers/logger' 6import { logger } from '../../../helpers/logger'
7import { UserModel } from '../../../models/account/user'
8 7
9const videoWatchingValidator = [ 8const videoWatchingValidator = [
10 param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), 9 param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'),
@@ -16,9 +15,9 @@ const videoWatchingValidator = [
16 logger.debug('Checking videoWatching parameters', { parameters: req.body }) 15 logger.debug('Checking videoWatching parameters', { parameters: req.body })
17 16
18 if (areValidationErrors(req, res)) return 17 if (areValidationErrors(req, res)) return
19 if (!await isVideoExist(req.params.videoId, res, 'id')) return 18 if (!await doesVideoExist(req.params.videoId, res, 'id')) return
20 19
21 const user = res.locals.oauth.token.User as UserModel 20 const user = res.locals.oauth.token.User
22 if (user.videosHistoryEnabled === false) { 21 if (user.videosHistoryEnabled === false) {
23 logger.warn('Cannot set videos to watch by user %d: videos history is disabled.', user.id) 22 logger.warn('Cannot set videos to watch by user %d: videos history is disabled.', user.id)
24 return res.status(409).end() 23 return res.status(409).end()
diff --git a/server/middlewares/validators/videos/videos.ts b/server/middlewares/validators/videos/videos.ts
index 051a19e16..2b01f108d 100644
--- a/server/middlewares/validators/videos/videos.ts
+++ b/server/middlewares/validators/videos/videos.ts
@@ -14,38 +14,38 @@ import {
14} from '../../../helpers/custom-validators/misc' 14} from '../../../helpers/custom-validators/misc'
15import { 15import {
16 checkUserCanManageVideo, 16 checkUserCanManageVideo,
17 doesVideoChannelOfAccountExist,
18 doesVideoExist,
17 isScheduleVideoUpdatePrivacyValid, 19 isScheduleVideoUpdatePrivacyValid,
18 isVideoCategoryValid, 20 isVideoCategoryValid,
19 isVideoChannelOfAccountExist,
20 isVideoDescriptionValid, 21 isVideoDescriptionValid,
21 isVideoExist,
22 isVideoFile, 22 isVideoFile,
23 isVideoFilterValid, 23 isVideoFilterValid,
24 isVideoImage, 24 isVideoImage,
25 isVideoLanguageValid, 25 isVideoLanguageValid,
26 isVideoLicenceValid, 26 isVideoLicenceValid,
27 isVideoNameValid, 27 isVideoNameValid,
28 isVideoOriginallyPublishedAtValid,
28 isVideoPrivacyValid, 29 isVideoPrivacyValid,
29 isVideoSupportValid, 30 isVideoSupportValid,
30 isVideoTagsValid 31 isVideoTagsValid
31} from '../../../helpers/custom-validators/videos' 32} from '../../../helpers/custom-validators/videos'
32import { getDurationFromVideoFile } from '../../../helpers/ffmpeg-utils' 33import { getDurationFromVideoFile } from '../../../helpers/ffmpeg-utils'
33import { logger } from '../../../helpers/logger' 34import { logger } from '../../../helpers/logger'
34import { CONFIG, CONSTRAINTS_FIELDS } from '../../../initializers' 35import { CONSTRAINTS_FIELDS } from '../../../initializers/constants'
35import { authenticatePromiseIfNeeded } from '../../oauth' 36import { authenticatePromiseIfNeeded } from '../../oauth'
36import { areValidationErrors } from '../utils' 37import { areValidationErrors } from '../utils'
37import { cleanUpReqFiles } from '../../../helpers/express-utils' 38import { cleanUpReqFiles } from '../../../helpers/express-utils'
38import { VideoModel } from '../../../models/video/video' 39import { VideoModel } from '../../../models/video/video'
39import { UserModel } from '../../../models/account/user'
40import { checkUserCanTerminateOwnershipChange, doesChangeVideoOwnershipExist } from '../../../helpers/custom-validators/video-ownership' 40import { checkUserCanTerminateOwnershipChange, doesChangeVideoOwnershipExist } from '../../../helpers/custom-validators/video-ownership'
41import { VideoChangeOwnershipAccept } from '../../../../shared/models/videos/video-change-ownership-accept.model' 41import { VideoChangeOwnershipAccept } from '../../../../shared/models/videos/video-change-ownership-accept.model'
42import { VideoChangeOwnershipModel } from '../../../models/video/video-change-ownership'
43import { AccountModel } from '../../../models/account/account' 42import { AccountModel } from '../../../models/account/account'
44import { VideoFetchType } from '../../../helpers/video' 43import { VideoFetchType } from '../../../helpers/video'
45import { isNSFWQueryValid, isNumberArray, isStringArray } from '../../../helpers/custom-validators/search' 44import { isNSFWQueryValid, isNumberArray, isStringArray } from '../../../helpers/custom-validators/search'
46import { getServerActor } from '../../../helpers/utils' 45import { getServerActor } from '../../../helpers/utils'
46import { CONFIG } from '../../../initializers/config'
47 47
48const videosAddValidator = getCommonVideoAttributes().concat([ 48const videosAddValidator = getCommonVideoEditAttributes().concat([
49 body('videofile') 49 body('videofile')
50 .custom((value, { req }) => isVideoFile(req.files)).withMessage( 50 .custom((value, { req }) => isVideoFile(req.files)).withMessage(
51 'This file is not supported or too large. Please, make sure it is of the following type: ' 51 'This file is not supported or too large. Please, make sure it is of the following type: '
@@ -65,9 +65,10 @@ const videosAddValidator = getCommonVideoAttributes().concat([
65 const videoFile: Express.Multer.File = req.files['videofile'][0] 65 const videoFile: Express.Multer.File = req.files['videofile'][0]
66 const user = res.locals.oauth.token.User 66 const user = res.locals.oauth.token.User
67 67
68 if (!await isVideoChannelOfAccountExist(req.body.channelId, user, res)) return cleanUpReqFiles(req) 68 if (!await doesVideoChannelOfAccountExist(req.body.channelId, user, res)) return cleanUpReqFiles(req)
69 69
70 const isAble = await user.isAbleToUploadVideo(videoFile) 70 const isAble = await user.isAbleToUploadVideo(videoFile)
71
71 if (isAble === false) { 72 if (isAble === false) {
72 res.status(403) 73 res.status(403)
73 .json({ error: 'The user video quota is exceeded with this video.' }) 74 .json({ error: 'The user video quota is exceeded with this video.' })
@@ -93,7 +94,7 @@ const videosAddValidator = getCommonVideoAttributes().concat([
93 } 94 }
94]) 95])
95 96
96const videosUpdateValidator = getCommonVideoAttributes().concat([ 97const videosUpdateValidator = getCommonVideoEditAttributes().concat([
97 param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), 98 param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'),
98 body('name') 99 body('name')
99 .optional() 100 .optional()
@@ -108,7 +109,7 @@ const videosUpdateValidator = getCommonVideoAttributes().concat([
108 109
109 if (areValidationErrors(req, res)) return cleanUpReqFiles(req) 110 if (areValidationErrors(req, res)) return cleanUpReqFiles(req)
110 if (areErrorsInScheduleUpdate(req, res)) return cleanUpReqFiles(req) 111 if (areErrorsInScheduleUpdate(req, res)) return cleanUpReqFiles(req)
111 if (!await isVideoExist(req.params.id, res)) return cleanUpReqFiles(req) 112 if (!await doesVideoExist(req.params.id, res)) return cleanUpReqFiles(req)
112 113
113 const video = res.locals.video 114 const video = res.locals.video
114 115
@@ -122,14 +123,14 @@ const videosUpdateValidator = getCommonVideoAttributes().concat([
122 .json({ error: 'Cannot set "private" a video that was not private.' }) 123 .json({ error: 'Cannot set "private" a video that was not private.' })
123 } 124 }
124 125
125 if (req.body.channelId && !await isVideoChannelOfAccountExist(req.body.channelId, user, res)) return cleanUpReqFiles(req) 126 if (req.body.channelId && !await doesVideoChannelOfAccountExist(req.body.channelId, user, res)) return cleanUpReqFiles(req)
126 127
127 return next() 128 return next()
128 } 129 }
129]) 130])
130 131
131async function checkVideoFollowConstraints (req: express.Request, res: express.Response, next: express.NextFunction) { 132async function checkVideoFollowConstraints (req: express.Request, res: express.Response, next: express.NextFunction) {
132 const video: VideoModel = res.locals.video 133 const video = res.locals.video
133 134
134 // Anybody can watch local videos 135 // Anybody can watch local videos
135 if (video.isOwned() === true) return next() 136 if (video.isOwned() === true) return next()
@@ -161,15 +162,15 @@ const videosCustomGetValidator = (fetchType: VideoFetchType) => {
161 logger.debug('Checking videosGet parameters', { parameters: req.params }) 162 logger.debug('Checking videosGet parameters', { parameters: req.params })
162 163
163 if (areValidationErrors(req, res)) return 164 if (areValidationErrors(req, res)) return
164 if (!await isVideoExist(req.params.id, res, fetchType)) return 165 if (!await doesVideoExist(req.params.id, res, fetchType)) return
165 166
166 const video: VideoModel = res.locals.video 167 const video = res.locals.video
167 168
168 // Video private or blacklisted 169 // Video private or blacklisted
169 if (video.privacy === VideoPrivacy.PRIVATE || video.VideoBlacklist) { 170 if (video.privacy === VideoPrivacy.PRIVATE || video.VideoBlacklist) {
170 await authenticatePromiseIfNeeded(req, res) 171 await authenticatePromiseIfNeeded(req, res)
171 172
172 const user: UserModel = res.locals.oauth ? res.locals.oauth.token.User : null 173 const user = res.locals.oauth ? res.locals.oauth.token.User : null
173 174
174 // Only the owner or a user that have blacklist rights can see the video 175 // Only the owner or a user that have blacklist rights can see the video
175 if ( 176 if (
@@ -206,7 +207,7 @@ const videosRemoveValidator = [
206 logger.debug('Checking videosRemove parameters', { parameters: req.params }) 207 logger.debug('Checking videosRemove parameters', { parameters: req.params })
207 208
208 if (areValidationErrors(req, res)) return 209 if (areValidationErrors(req, res)) return
209 if (!await isVideoExist(req.params.id, res)) return 210 if (!await doesVideoExist(req.params.id, res)) return
210 211
211 // Check if the user who did the request is able to delete the video 212 // Check if the user who did the request is able to delete the video
212 if (!checkUserCanManageVideo(res.locals.oauth.token.User, res.locals.video, UserRight.REMOVE_ANY_VIDEO, res)) return 213 if (!checkUserCanManageVideo(res.locals.oauth.token.User, res.locals.video, UserRight.REMOVE_ANY_VIDEO, res)) return
@@ -222,7 +223,7 @@ const videosChangeOwnershipValidator = [
222 logger.debug('Checking changeOwnership parameters', { parameters: req.params }) 223 logger.debug('Checking changeOwnership parameters', { parameters: req.params })
223 224
224 if (areValidationErrors(req, res)) return 225 if (areValidationErrors(req, res)) return
225 if (!await isVideoExist(req.params.videoId, res)) return 226 if (!await doesVideoExist(req.params.videoId, res)) return
226 227
227 // Check if the user who did the request is able to change the ownership of the video 228 // Check if the user who did the request is able to change the ownership of the video
228 if (!checkUserCanManageVideo(res.locals.oauth.token.User, res.locals.video, UserRight.CHANGE_VIDEO_OWNERSHIP, res)) return 229 if (!checkUserCanManageVideo(res.locals.oauth.token.User, res.locals.video, UserRight.CHANGE_VIDEO_OWNERSHIP, res)) return
@@ -255,7 +256,7 @@ const videosTerminateChangeOwnershipValidator = [
255 return next() 256 return next()
256 }, 257 },
257 async (req: express.Request, res: express.Response, next: express.NextFunction) => { 258 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
258 const videoChangeOwnership = res.locals.videoChangeOwnership as VideoChangeOwnershipModel 259 const videoChangeOwnership = res.locals.videoChangeOwnership
259 260
260 if (videoChangeOwnership.status === VideoChangeOwnershipStatus.WAITING) { 261 if (videoChangeOwnership.status === VideoChangeOwnershipStatus.WAITING) {
261 return next() 262 return next()
@@ -271,10 +272,10 @@ const videosTerminateChangeOwnershipValidator = [
271const videosAcceptChangeOwnershipValidator = [ 272const videosAcceptChangeOwnershipValidator = [
272 async (req: express.Request, res: express.Response, next: express.NextFunction) => { 273 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
273 const body = req.body as VideoChangeOwnershipAccept 274 const body = req.body as VideoChangeOwnershipAccept
274 if (!await isVideoChannelOfAccountExist(body.channelId, res.locals.oauth.token.User, res)) return 275 if (!await doesVideoChannelOfAccountExist(body.channelId, res.locals.oauth.token.User, res)) return
275 276
276 const user = res.locals.oauth.token.User 277 const user = res.locals.oauth.token.User
277 const videoChangeOwnership = res.locals.videoChangeOwnership as VideoChangeOwnershipModel 278 const videoChangeOwnership = res.locals.videoChangeOwnership
278 const isAble = await user.isAbleToUploadVideo(videoChangeOwnership.Video.getOriginalFile()) 279 const isAble = await user.isAbleToUploadVideo(videoChangeOwnership.Video.getOriginalFile())
279 if (isAble === false) { 280 if (isAble === false) {
280 res.status(403) 281 res.status(403)
@@ -287,7 +288,7 @@ const videosAcceptChangeOwnershipValidator = [
287 } 288 }
288] 289]
289 290
290function getCommonVideoAttributes () { 291function getCommonVideoEditAttributes () {
291 return [ 292 return [
292 body('thumbnailfile') 293 body('thumbnailfile')
293 .custom((value, { req }) => isVideoImage(req.files, 'thumbnailfile')).withMessage( 294 .custom((value, { req }) => isVideoImage(req.files, 'thumbnailfile')).withMessage(
@@ -340,7 +341,14 @@ function getCommonVideoAttributes () {
340 .optional() 341 .optional()
341 .toBoolean() 342 .toBoolean()
342 .custom(isBooleanValid).withMessage('Should have comments enabled boolean'), 343 .custom(isBooleanValid).withMessage('Should have comments enabled boolean'),
343 344 body('downloadEnabled')
345 .optional()
346 .toBoolean()
347 .custom(isBooleanValid).withMessage('Should have downloading enabled boolean'),
348 body('originallyPublishedAt')
349 .optional()
350 .customSanitizer(toValueOrNull)
351 .custom(isVideoOriginallyPublishedAtValid).withMessage('Should have a valid original publication date'),
344 body('scheduleUpdate') 352 body('scheduleUpdate')
345 .optional() 353 .optional()
346 .customSanitizer(toValueOrNull), 354 .customSanitizer(toValueOrNull),
@@ -387,7 +395,7 @@ const commonVideosFiltersValidator = [
387 395
388 if (areValidationErrors(req, res)) return 396 if (areValidationErrors(req, res)) return
389 397
390 const user: UserModel = res.locals.oauth ? res.locals.oauth.token.User : undefined 398 const user = res.locals.oauth ? res.locals.oauth.token.User : undefined
391 if (req.query.filter === 'all-local' && (!user || user.hasRight(UserRight.SEE_ALL_VIDEOS) === false)) { 399 if (req.query.filter === 'all-local' && (!user || user.hasRight(UserRight.SEE_ALL_VIDEOS) === false)) {
392 res.status(401) 400 res.status(401)
393 .json({ error: 'You are not allowed to see all local videos.' }) 401 .json({ error: 'You are not allowed to see all local videos.' })
@@ -413,7 +421,7 @@ export {
413 videosTerminateChangeOwnershipValidator, 421 videosTerminateChangeOwnershipValidator,
414 videosAcceptChangeOwnershipValidator, 422 videosAcceptChangeOwnershipValidator,
415 423
416 getCommonVideoAttributes, 424 getCommonVideoEditAttributes,
417 425
418 commonVideosFiltersValidator 426 commonVideosFiltersValidator
419} 427}