aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/middlewares
diff options
context:
space:
mode:
Diffstat (limited to 'server/middlewares')
-rw-r--r--server/middlewares/activitypub.ts3
-rw-r--r--server/middlewares/async.ts2
-rw-r--r--server/middlewares/cache/cache.ts4
-rw-r--r--server/middlewares/cache/shared/api-cache.ts50
-rw-r--r--server/middlewares/error.ts10
-rw-r--r--server/middlewares/user-right.ts3
-rw-r--r--server/middlewares/validators/blocklist.ts29
-rw-r--r--server/middlewares/validators/plugins.ts6
-rw-r--r--server/middlewares/validators/shared/video-channels.ts7
-rw-r--r--server/middlewares/validators/users.ts21
-rw-r--r--server/middlewares/validators/videos/video-captions.ts2
-rw-r--r--server/middlewares/validators/videos/video-channels.ts60
-rw-r--r--server/middlewares/validators/videos/video-comments.ts3
-rw-r--r--server/middlewares/validators/videos/video-files.ts2
-rw-r--r--server/middlewares/validators/videos/video-playlists.ts14
-rw-r--r--server/middlewares/validators/videos/videos.ts6
16 files changed, 113 insertions, 109 deletions
diff --git a/server/middlewares/activitypub.ts b/server/middlewares/activitypub.ts
index 6ef90b275..86d3c1d6c 100644
--- a/server/middlewares/activitypub.ts
+++ b/server/middlewares/activitypub.ts
@@ -1,8 +1,7 @@
1import { NextFunction, Request, Response } from 'express' 1import { NextFunction, Request, Response } from 'express'
2import { getAPId } from '@server/helpers/activitypub' 2import { getAPId } from '@server/helpers/activitypub'
3import { isActorDeleteActivityValid } from '@server/helpers/custom-validators/activitypub/actor' 3import { isActorDeleteActivityValid } from '@server/helpers/custom-validators/activitypub/actor'
4import { ActivityDelete, ActivityPubSignature } from '../../shared' 4import { ActivityDelete, ActivityPubSignature, HttpStatusCode } from '@shared/models'
5import { HttpStatusCode } from '../../shared/models/http/http-error-codes'
6import { logger } from '../helpers/logger' 5import { logger } from '../helpers/logger'
7import { isHTTPSignatureVerified, isJsonLDSignatureVerified, parseHTTPSignature } from '../helpers/peertube-crypto' 6import { isHTTPSignatureVerified, isJsonLDSignatureVerified, parseHTTPSignature } from '../helpers/peertube-crypto'
8import { ACCEPT_HEADERS, ACTIVITY_PUB, HTTP_SIGNATURE } from '../initializers/constants' 7import { ACCEPT_HEADERS, ACTIVITY_PUB, HTTP_SIGNATURE } from '../initializers/constants'
diff --git a/server/middlewares/async.ts b/server/middlewares/async.ts
index 3d6e38809..9d0193536 100644
--- a/server/middlewares/async.ts
+++ b/server/middlewares/async.ts
@@ -1,7 +1,7 @@
1import { eachSeries } from 'async' 1import { eachSeries } from 'async'
2import { NextFunction, Request, RequestHandler, Response } from 'express' 2import { NextFunction, Request, RequestHandler, Response } from 'express'
3import { ValidationChain } from 'express-validator' 3import { ValidationChain } from 'express-validator'
4import { ExpressPromiseHandler } from '@server/types/express' 4import { ExpressPromiseHandler } from '@server/types/express-handler'
5import { retryTransactionWrapper } from '../helpers/database-utils' 5import { retryTransactionWrapper } from '../helpers/database-utils'
6 6
7// Syntactic sugar to avoid try/catch in express controllers 7// Syntactic sugar to avoid try/catch in express controllers
diff --git a/server/middlewares/cache/cache.ts b/server/middlewares/cache/cache.ts
index 48162a0ae..e14160ba8 100644
--- a/server/middlewares/cache/cache.ts
+++ b/server/middlewares/cache/cache.ts
@@ -1,10 +1,6 @@
1import { HttpStatusCode } from '../../../shared/models/http/http-error-codes' 1import { HttpStatusCode } from '../../../shared/models/http/http-error-codes'
2import { Redis } from '../../lib/redis'
3import { ApiCache, APICacheOptions } from './shared' 2import { ApiCache, APICacheOptions } from './shared'
4 3
5// Ensure Redis is initialized
6Redis.Instance.init()
7
8const defaultOptions: APICacheOptions = { 4const defaultOptions: APICacheOptions = {
9 excludeStatus: [ 5 excludeStatus: [
10 HttpStatusCode.FORBIDDEN_403, 6 HttpStatusCode.FORBIDDEN_403,
diff --git a/server/middlewares/cache/shared/api-cache.ts b/server/middlewares/cache/shared/api-cache.ts
index f8846dcfc..86c5095b5 100644
--- a/server/middlewares/cache/shared/api-cache.ts
+++ b/server/middlewares/cache/shared/api-cache.ts
@@ -7,6 +7,7 @@ import { isTestInstance, parseDurationToMs } from '@server/helpers/core-utils'
7import { logger } from '@server/helpers/logger' 7import { logger } from '@server/helpers/logger'
8import { Redis } from '@server/lib/redis' 8import { Redis } from '@server/lib/redis'
9import { HttpStatusCode } from '@shared/models' 9import { HttpStatusCode } from '@shared/models'
10import { asyncMiddleware } from '@server/middlewares'
10 11
11export interface APICacheOptions { 12export interface APICacheOptions {
12 headerBlacklist?: string[] 13 headerBlacklist?: string[]
@@ -40,24 +41,25 @@ export class ApiCache {
40 buildMiddleware (strDuration: string) { 41 buildMiddleware (strDuration: string) {
41 const duration = parseDurationToMs(strDuration) 42 const duration = parseDurationToMs(strDuration)
42 43
43 return (req: express.Request, res: express.Response, next: express.NextFunction) => { 44 return asyncMiddleware(
44 const key = Redis.Instance.getPrefix() + 'api-cache-' + req.originalUrl 45 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
45 const redis = Redis.Instance.getClient() 46 const key = Redis.Instance.getPrefix() + 'api-cache-' + req.originalUrl
47 const redis = Redis.Instance.getClient()
46 48
47 if (!redis.connected) return this.makeResponseCacheable(res, next, key, duration) 49 if (!Redis.Instance.isConnected()) return this.makeResponseCacheable(res, next, key, duration)
48 50
49 try { 51 try {
50 redis.hgetall(key, (err, obj) => { 52 const obj = await redis.hGetAll(key)
51 if (!err && obj && obj.response) { 53 if (obj?.response) {
52 return this.sendCachedResponse(req, res, JSON.parse(obj.response), duration) 54 return this.sendCachedResponse(req, res, JSON.parse(obj.response), duration)
53 } 55 }
54 56
55 return this.makeResponseCacheable(res, next, key, duration) 57 return this.makeResponseCacheable(res, next, key, duration)
56 }) 58 } catch (err) {
57 } catch (err) { 59 return this.makeResponseCacheable(res, next, key, duration)
58 return this.makeResponseCacheable(res, next, key, duration) 60 }
59 } 61 }
60 } 62 )
61 } 63 }
62 64
63 private shouldCacheResponse (response: express.Response) { 65 private shouldCacheResponse (response: express.Response) {
@@ -93,21 +95,22 @@ export class ApiCache {
93 } as CacheObject 95 } as CacheObject
94 } 96 }
95 97
96 private cacheResponse (key: string, value: object, duration: number) { 98 private async cacheResponse (key: string, value: object, duration: number) {
97 const redis = Redis.Instance.getClient() 99 const redis = Redis.Instance.getClient()
98 100
99 if (redis.connected) { 101 if (Redis.Instance.isConnected()) {
100 try { 102 await Promise.all([
101 redis.hset(key, 'response', JSON.stringify(value)) 103 redis.hSet(key, 'response', JSON.stringify(value)),
102 redis.hset(key, 'duration', duration + '') 104 redis.hSet(key, 'duration', duration + ''),
103 redis.expire(key, duration / 1000) 105 redis.expire(key, duration / 1000)
104 } catch (err) { 106 ])
105 logger.error('Cannot set cache in redis.', { err })
106 }
107 } 107 }
108 108
109 // add automatic cache clearing from duration, includes max limit on setTimeout 109 // add automatic cache clearing from duration, includes max limit on setTimeout
110 this.timers[key] = setTimeout(() => this.clear(key), Math.min(duration, 2147483647)) 110 this.timers[key] = setTimeout(() => {
111 this.clear(key)
112 .catch(err => logger.error('Cannot clear Redis key %s.', key, { err }))
113 }, Math.min(duration, 2147483647))
111 } 114 }
112 115
113 private accumulateContent (res: express.Response, content: any) { 116 private accumulateContent (res: express.Response, content: any) {
@@ -184,6 +187,7 @@ export class ApiCache {
184 encoding 187 encoding
185 ) 188 )
186 self.cacheResponse(key, cacheObject, duration) 189 self.cacheResponse(key, cacheObject, duration)
190 .catch(err => logger.error('Cannot cache response', { err }))
187 } 191 }
188 } 192 }
189 193
@@ -235,7 +239,7 @@ export class ApiCache {
235 return response.end(data, cacheObject.encoding) 239 return response.end(data, cacheObject.encoding)
236 } 240 }
237 241
238 private clear (target: string) { 242 private async clear (target: string) {
239 const redis = Redis.Instance.getClient() 243 const redis = Redis.Instance.getClient()
240 244
241 if (target) { 245 if (target) {
@@ -243,7 +247,7 @@ export class ApiCache {
243 delete this.timers[target] 247 delete this.timers[target]
244 248
245 try { 249 try {
246 redis.del(target) 250 await redis.del(target)
247 } catch (err) { 251 } catch (err) {
248 logger.error('Cannot delete %s in redis cache.', target, { err }) 252 logger.error('Cannot delete %s in redis cache.', target, { err })
249 } 253 }
@@ -255,7 +259,7 @@ export class ApiCache {
255 delete this.timers[key] 259 delete this.timers[key]
256 260
257 try { 261 try {
258 redis.del(key) 262 await redis.del(key)
259 } catch (err) { 263 } catch (err) {
260 logger.error('Cannot delete %s in redis cache.', key, { err }) 264 logger.error('Cannot delete %s in redis cache.', key, { err })
261 } 265 }
diff --git a/server/middlewares/error.ts b/server/middlewares/error.ts
index 6c52ce7bd..34c87a26d 100644
--- a/server/middlewares/error.ts
+++ b/server/middlewares/error.ts
@@ -1,5 +1,6 @@
1import express from 'express' 1import express from 'express'
2import { ProblemDocument, ProblemDocumentExtension } from 'http-problem-details' 2import { ProblemDocument, ProblemDocumentExtension } from 'http-problem-details'
3import { logger } from '@server/helpers/logger'
3import { HttpStatusCode } from '@shared/models' 4import { HttpStatusCode } from '@shared/models'
4 5
5function apiFailMiddleware (req: express.Request, res: express.Response, next: express.NextFunction) { 6function apiFailMiddleware (req: express.Request, res: express.Response, next: express.NextFunction) {
@@ -18,7 +19,8 @@ function apiFailMiddleware (req: express.Request, res: express.Response, next: e
18 19
19 res.status(status) 20 res.status(status)
20 res.setHeader('Content-Type', 'application/problem+json') 21 res.setHeader('Content-Type', 'application/problem+json')
21 res.json(new ProblemDocument({ 22
23 const json = new ProblemDocument({
22 status, 24 status,
23 title, 25 title,
24 instance, 26 instance,
@@ -28,7 +30,11 @@ function apiFailMiddleware (req: express.Request, res: express.Response, next: e
28 type: type 30 type: type
29 ? `https://docs.joinpeertube.org/api-rest-reference.html#section/Errors/${type}` 31 ? `https://docs.joinpeertube.org/api-rest-reference.html#section/Errors/${type}`
30 : undefined 32 : undefined
31 }, extension)) 33 }, extension)
34
35 logger.debug('Bad HTTP request.', { json })
36
37 res.json(json)
32 } 38 }
33 39
34 if (next) next() 40 if (next) next()
diff --git a/server/middlewares/user-right.ts b/server/middlewares/user-right.ts
index ea95b16c2..7d53e8341 100644
--- a/server/middlewares/user-right.ts
+++ b/server/middlewares/user-right.ts
@@ -1,6 +1,5 @@
1import express from 'express' 1import express from 'express'
2import { UserRight } from '../../shared' 2import { HttpStatusCode, UserRight } from '@shared/models'
3import { HttpStatusCode } from '../../shared/models/http/http-error-codes'
4import { logger } from '../helpers/logger' 3import { logger } from '../helpers/logger'
5 4
6function ensureUserHasRight (userRight: UserRight) { 5function ensureUserHasRight (userRight: UserRight) {
diff --git a/server/middlewares/validators/blocklist.ts b/server/middlewares/validators/blocklist.ts
index b7749e204..12980ced4 100644
--- a/server/middlewares/validators/blocklist.ts
+++ b/server/middlewares/validators/blocklist.ts
@@ -1,8 +1,10 @@
1import express from 'express' 1import express from 'express'
2import { body, param } from 'express-validator' 2import { body, param, query } from 'express-validator'
3import { areValidActorHandles } from '@server/helpers/custom-validators/activitypub/actor'
4import { toArray } from '@server/helpers/custom-validators/misc'
3import { getServerActor } from '@server/models/application/application' 5import { getServerActor } from '@server/models/application/application'
4import { HttpStatusCode } from '../../../shared/models/http/http-error-codes' 6import { HttpStatusCode } from '../../../shared/models/http/http-error-codes'
5import { isHostValid } from '../../helpers/custom-validators/servers' 7import { isEachUniqueHostValid, isHostValid } from '../../helpers/custom-validators/servers'
6import { logger } from '../../helpers/logger' 8import { logger } from '../../helpers/logger'
7import { WEBSERVER } from '../../initializers/constants' 9import { WEBSERVER } from '../../initializers/constants'
8import { AccountBlocklistModel } from '../../models/account/account-blocklist' 10import { AccountBlocklistModel } from '../../models/account/account-blocklist'
@@ -123,6 +125,26 @@ const unblockServerByServerValidator = [
123 } 125 }
124] 126]
125 127
128const blocklistStatusValidator = [
129 query('hosts')
130 .optional()
131 .customSanitizer(toArray)
132 .custom(isEachUniqueHostValid).withMessage('Should have a valid hosts array'),
133
134 query('accounts')
135 .optional()
136 .customSanitizer(toArray)
137 .custom(areValidActorHandles).withMessage('Should have a valid accounts array'),
138
139 (req: express.Request, res: express.Response, next: express.NextFunction) => {
140 logger.debug('Checking blocklistStatusValidator parameters', { query: req.query })
141
142 if (areValidationErrors(req, res)) return
143
144 return next()
145 }
146]
147
126// --------------------------------------------------------------------------- 148// ---------------------------------------------------------------------------
127 149
128export { 150export {
@@ -131,7 +153,8 @@ export {
131 unblockAccountByAccountValidator, 153 unblockAccountByAccountValidator,
132 unblockServerByAccountValidator, 154 unblockServerByAccountValidator,
133 unblockAccountByServerValidator, 155 unblockAccountByServerValidator,
134 unblockServerByServerValidator 156 unblockServerByServerValidator,
157 blocklistStatusValidator
135} 158}
136 159
137// --------------------------------------------------------------------------- 160// ---------------------------------------------------------------------------
diff --git a/server/middlewares/validators/plugins.ts b/server/middlewares/validators/plugins.ts
index 21171af23..c1e9ebefb 100644
--- a/server/middlewares/validators/plugins.ts
+++ b/server/middlewares/validators/plugins.ts
@@ -116,6 +116,9 @@ const installOrUpdatePluginValidator = [
116 body('npmName') 116 body('npmName')
117 .optional() 117 .optional()
118 .custom(isNpmPluginNameValid).withMessage('Should have a valid npm name'), 118 .custom(isNpmPluginNameValid).withMessage('Should have a valid npm name'),
119 body('pluginVersion')
120 .optional()
121 .custom(isPluginVersionValid).withMessage('Should have a valid plugin version'),
119 body('path') 122 body('path')
120 .optional() 123 .optional()
121 .custom(isSafePath).withMessage('Should have a valid safe path'), 124 .custom(isSafePath).withMessage('Should have a valid safe path'),
@@ -129,6 +132,9 @@ const installOrUpdatePluginValidator = [
129 if (!body.path && !body.npmName) { 132 if (!body.path && !body.npmName) {
130 return res.fail({ message: 'Should have either a npmName or a path' }) 133 return res.fail({ message: 'Should have either a npmName or a path' })
131 } 134 }
135 if (body.pluginVersion && !body.npmName) {
136 return res.fail({ message: 'Should have a npmName when specifying a pluginVersion' })
137 }
132 138
133 return next() 139 return next()
134 } 140 }
diff --git a/server/middlewares/validators/shared/video-channels.ts b/server/middlewares/validators/shared/video-channels.ts
index 7c0c89267..bed9f5dbe 100644
--- a/server/middlewares/validators/shared/video-channels.ts
+++ b/server/middlewares/validators/shared/video-channels.ts
@@ -3,12 +3,6 @@ import { VideoChannelModel } from '@server/models/video/video-channel'
3import { MChannelBannerAccountDefault } from '@server/types/models' 3import { MChannelBannerAccountDefault } from '@server/types/models'
4import { HttpStatusCode } from '@shared/models' 4import { HttpStatusCode } from '@shared/models'
5 5
6async function doesLocalVideoChannelNameExist (name: string, res: express.Response) {
7 const videoChannel = await VideoChannelModel.loadLocalByNameAndPopulateAccount(name)
8
9 return processVideoChannelExist(videoChannel, res)
10}
11
12async function doesVideoChannelIdExist (id: number, res: express.Response) { 6async function doesVideoChannelIdExist (id: number, res: express.Response) {
13 const videoChannel = await VideoChannelModel.loadAndPopulateAccount(+id) 7 const videoChannel = await VideoChannelModel.loadAndPopulateAccount(+id)
14 8
@@ -24,7 +18,6 @@ async function doesVideoChannelNameWithHostExist (nameWithDomain: string, res: e
24// --------------------------------------------------------------------------- 18// ---------------------------------------------------------------------------
25 19
26export { 20export {
27 doesLocalVideoChannelNameExist,
28 doesVideoChannelIdExist, 21 doesVideoChannelIdExist,
29 doesVideoChannelNameWithHostExist 22 doesVideoChannelNameWithHostExist
30} 23}
diff --git a/server/middlewares/validators/users.ts b/server/middlewares/validators/users.ts
index 33b31d54b..bc6007c6d 100644
--- a/server/middlewares/validators/users.ts
+++ b/server/middlewares/validators/users.ts
@@ -3,7 +3,7 @@ import { body, param, query } from 'express-validator'
3import { omit } from 'lodash' 3import { omit } from 'lodash'
4import { Hooks } from '@server/lib/plugins/hooks' 4import { Hooks } from '@server/lib/plugins/hooks'
5import { MUserDefault } from '@server/types/models' 5import { MUserDefault } from '@server/types/models'
6import { HttpStatusCode, UserRegister, UserRole } from '@shared/models' 6import { HttpStatusCode, UserRegister, UserRight, UserRole } from '@shared/models'
7import { isBooleanValid, isIdValid, toBooleanOrNull, toIntOrNull } from '../../helpers/custom-validators/misc' 7import { isBooleanValid, isIdValid, toBooleanOrNull, toIntOrNull } from '../../helpers/custom-validators/misc'
8import { isThemeNameValid } from '../../helpers/custom-validators/plugins' 8import { isThemeNameValid } from '../../helpers/custom-validators/plugins'
9import { 9import {
@@ -15,6 +15,7 @@ import {
15 isUserDisplayNameValid, 15 isUserDisplayNameValid,
16 isUserNoModal, 16 isUserNoModal,
17 isUserNSFWPolicyValid, 17 isUserNSFWPolicyValid,
18 isUserP2PEnabledValid,
18 isUserPasswordValid, 19 isUserPasswordValid,
19 isUserPasswordValidOrEmpty, 20 isUserPasswordValidOrEmpty,
20 isUserRoleValid, 21 isUserRoleValid,
@@ -239,6 +240,9 @@ const usersUpdateMeValidator = [
239 body('autoPlayVideo') 240 body('autoPlayVideo')
240 .optional() 241 .optional()
241 .custom(isUserAutoPlayVideoValid).withMessage('Should have a valid automatically plays video attribute'), 242 .custom(isUserAutoPlayVideoValid).withMessage('Should have a valid automatically plays video attribute'),
243 body('p2pEnabled')
244 .optional()
245 .custom(isUserP2PEnabledValid).withMessage('Should have a valid p2p enabled boolean'),
242 body('videoLanguages') 246 body('videoLanguages')
243 .optional() 247 .optional()
244 .custom(isUserVideoLanguages).withMessage('Should have a valid video languages attribute'), 248 .custom(isUserVideoLanguages).withMessage('Should have a valid video languages attribute'),
@@ -490,14 +494,17 @@ const ensureAuthUserOwnsAccountValidator = [
490 } 494 }
491] 495]
492 496
493const ensureAuthUserOwnsChannelValidator = [ 497const ensureCanManageChannel = [
494 (req: express.Request, res: express.Response, next: express.NextFunction) => { 498 (req: express.Request, res: express.Response, next: express.NextFunction) => {
495 const user = res.locals.oauth.token.User 499 const user = res.locals.oauth.token.user
500 const isUserOwner = res.locals.videoChannel.Account.userId === user.id
501
502 if (!isUserOwner && user.hasRight(UserRight.MANAGE_ANY_VIDEO_CHANNEL) === false) {
503 const message = `User ${user.username} does not have right to manage channel ${req.params.nameWithHost}.`
496 504
497 if (res.locals.videoChannel.Account.userId !== user.id) {
498 return res.fail({ 505 return res.fail({
499 status: HttpStatusCode.FORBIDDEN_403, 506 status: HttpStatusCode.FORBIDDEN_403,
500 message: 'Only owner of this video channel can access this ressource' 507 message
501 }) 508 })
502 } 509 }
503 510
@@ -542,8 +549,8 @@ export {
542 usersVerifyEmailValidator, 549 usersVerifyEmailValidator,
543 userAutocompleteValidator, 550 userAutocompleteValidator,
544 ensureAuthUserOwnsAccountValidator, 551 ensureAuthUserOwnsAccountValidator,
545 ensureAuthUserOwnsChannelValidator, 552 ensureCanManageUser,
546 ensureCanManageUser 553 ensureCanManageChannel
547} 554}
548 555
549// --------------------------------------------------------------------------- 556// ---------------------------------------------------------------------------
diff --git a/server/middlewares/validators/videos/video-captions.ts b/server/middlewares/validators/videos/video-captions.ts
index 4fc4c8ec5..a399871e1 100644
--- a/server/middlewares/validators/videos/video-captions.ts
+++ b/server/middlewares/validators/videos/video-captions.ts
@@ -1,6 +1,6 @@
1import express from 'express' 1import express from 'express'
2import { body, param } from 'express-validator' 2import { body, param } from 'express-validator'
3import { HttpStatusCode, UserRight } from '../../../../shared' 3import { HttpStatusCode, UserRight } from '@shared/models'
4import { isVideoCaptionFile, isVideoCaptionLanguageValid } from '../../../helpers/custom-validators/video-captions' 4import { isVideoCaptionFile, isVideoCaptionLanguageValid } from '../../../helpers/custom-validators/video-captions'
5import { cleanUpReqFiles } from '../../../helpers/express-utils' 5import { cleanUpReqFiles } from '../../../helpers/express-utils'
6import { logger } from '../../../helpers/logger' 6import { logger } from '../../../helpers/logger'
diff --git a/server/middlewares/validators/videos/video-channels.ts b/server/middlewares/validators/videos/video-channels.ts
index edce48c7f..3bfdebbb1 100644
--- a/server/middlewares/validators/videos/video-channels.ts
+++ b/server/middlewares/validators/videos/video-channels.ts
@@ -1,7 +1,7 @@
1import express from 'express' 1import express from 'express'
2import { body, param, query } from 'express-validator' 2import { body, param, query } from 'express-validator'
3import { MChannelAccountDefault, MUser } from '@server/types/models' 3import { CONFIG } from '@server/initializers/config'
4import { UserRight } from '../../../../shared' 4import { MChannelAccountDefault } from '@server/types/models'
5import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes' 5import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
6import { isBooleanValid, toBooleanOrNull } from '../../../helpers/custom-validators/misc' 6import { isBooleanValid, toBooleanOrNull } from '../../../helpers/custom-validators/misc'
7import { 7import {
@@ -13,8 +13,7 @@ import {
13import { logger } from '../../../helpers/logger' 13import { logger } from '../../../helpers/logger'
14import { ActorModel } from '../../../models/actor/actor' 14import { ActorModel } from '../../../models/actor/actor'
15import { VideoChannelModel } from '../../../models/video/video-channel' 15import { VideoChannelModel } from '../../../models/video/video-channel'
16import { areValidationErrors, doesLocalVideoChannelNameExist, doesVideoChannelNameWithHostExist } from '../shared' 16import { areValidationErrors, doesVideoChannelNameWithHostExist } from '../shared'
17import { CONFIG } from '@server/initializers/config'
18 17
19const videoChannelsAddValidator = [ 18const videoChannelsAddValidator = [
20 body('name').custom(isVideoChannelUsernameValid).withMessage('Should have a valid channel name'), 19 body('name').custom(isVideoChannelUsernameValid).withMessage('Should have a valid channel name'),
@@ -71,16 +70,10 @@ const videoChannelsUpdateValidator = [
71] 70]
72 71
73const videoChannelsRemoveValidator = [ 72const videoChannelsRemoveValidator = [
74 param('nameWithHost').exists().withMessage('Should have an video channel name with host'),
75
76 async (req: express.Request, res: express.Response, next: express.NextFunction) => { 73 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
77 logger.debug('Checking videoChannelsRemove parameters', { parameters: req.params }) 74 logger.debug('Checking videoChannelsRemove parameters', { parameters: req.params })
78 75
79 if (areValidationErrors(req, res)) return 76 if (!await checkVideoChannelIsNotTheLastOne(res.locals.videoChannel, res)) return
80 if (!await doesVideoChannelNameWithHostExist(req.params.nameWithHost, res)) return
81
82 if (!checkUserCanDeleteVideoChannel(res.locals.oauth.token.User, res.locals.videoChannel, res)) return
83 if (!await checkVideoChannelIsNotTheLastOne(res)) return
84 77
85 return next() 78 return next()
86 } 79 }
@@ -100,14 +93,14 @@ const videoChannelsNameWithHostValidator = [
100 } 93 }
101] 94]
102 95
103const localVideoChannelValidator = [ 96const ensureIsLocalChannel = [
104 param('name').custom(isVideoChannelDisplayNameValid).withMessage('Should have a valid video channel name'), 97 (req: express.Request, res: express.Response, next: express.NextFunction) => {
105 98 if (res.locals.videoChannel.Actor.isOwned() === false) {
106 async (req: express.Request, res: express.Response, next: express.NextFunction) => { 99 return res.fail({
107 logger.debug('Checking localVideoChannelValidator parameters', { parameters: req.params }) 100 status: HttpStatusCode.FORBIDDEN_403,
108 101 message: 'This channel is not owned.'
109 if (areValidationErrors(req, res)) return 102 })
110 if (!await doesLocalVideoChannelNameExist(req.params.name, res)) return 103 }
111 104
112 return next() 105 return next()
113 } 106 }
@@ -144,38 +137,15 @@ export {
144 videoChannelsUpdateValidator, 137 videoChannelsUpdateValidator,
145 videoChannelsRemoveValidator, 138 videoChannelsRemoveValidator,
146 videoChannelsNameWithHostValidator, 139 videoChannelsNameWithHostValidator,
140 ensureIsLocalChannel,
147 videoChannelsListValidator, 141 videoChannelsListValidator,
148 localVideoChannelValidator,
149 videoChannelStatsValidator 142 videoChannelStatsValidator
150} 143}
151 144
152// --------------------------------------------------------------------------- 145// ---------------------------------------------------------------------------
153 146
154function checkUserCanDeleteVideoChannel (user: MUser, videoChannel: MChannelAccountDefault, res: express.Response) { 147async function checkVideoChannelIsNotTheLastOne (videoChannel: MChannelAccountDefault, res: express.Response) {
155 if (videoChannel.Actor.isOwned() === false) { 148 const count = await VideoChannelModel.countByAccount(videoChannel.Account.id)
156 res.fail({
157 status: HttpStatusCode.FORBIDDEN_403,
158 message: 'Cannot remove video channel of another server.'
159 })
160 return false
161 }
162
163 // Check if the user can delete the video channel
164 // The user can delete it if s/he is an admin
165 // Or if s/he is the video channel's account
166 if (user.hasRight(UserRight.REMOVE_ANY_VIDEO_CHANNEL) === false && videoChannel.Account.userId !== user.id) {
167 res.fail({
168 status: HttpStatusCode.FORBIDDEN_403,
169 message: 'Cannot remove video channel of another user'
170 })
171 return false
172 }
173
174 return true
175}
176
177async function checkVideoChannelIsNotTheLastOne (res: express.Response) {
178 const count = await VideoChannelModel.countByAccount(res.locals.oauth.token.User.Account.id)
179 149
180 if (count <= 1) { 150 if (count <= 1) {
181 res.fail({ 151 res.fail({
diff --git a/server/middlewares/validators/videos/video-comments.ts b/server/middlewares/validators/videos/video-comments.ts
index 3ea8bdcbb..665fb04c8 100644
--- a/server/middlewares/validators/videos/video-comments.ts
+++ b/server/middlewares/validators/videos/video-comments.ts
@@ -1,8 +1,7 @@
1import express from 'express' 1import express from 'express'
2import { body, param, query } from 'express-validator' 2import { body, param, query } from 'express-validator'
3import { MUserAccountUrl } from '@server/types/models' 3import { MUserAccountUrl } from '@server/types/models'
4import { UserRight } from '../../../../shared' 4import { HttpStatusCode, UserRight } from '@shared/models'
5import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
6import { exists, isBooleanValid, isIdValid, toBooleanOrNull } from '../../../helpers/custom-validators/misc' 5import { exists, isBooleanValid, isIdValid, toBooleanOrNull } from '../../../helpers/custom-validators/misc'
7import { isValidVideoCommentText } from '../../../helpers/custom-validators/video-comments' 6import { isValidVideoCommentText } from '../../../helpers/custom-validators/video-comments'
8import { logger } from '../../../helpers/logger' 7import { logger } from '../../../helpers/logger'
diff --git a/server/middlewares/validators/videos/video-files.ts b/server/middlewares/validators/videos/video-files.ts
index c1fa77502..35b0ac757 100644
--- a/server/middlewares/validators/videos/video-files.ts
+++ b/server/middlewares/validators/videos/video-files.ts
@@ -1,6 +1,6 @@
1import express from 'express' 1import express from 'express'
2import { MVideo } from '@server/types/models' 2import { MVideo } from '@server/types/models'
3import { HttpStatusCode } from '../../../../shared' 3import { HttpStatusCode } from '@shared/models'
4import { logger } from '../../../helpers/logger' 4import { logger } from '../../../helpers/logger'
5import { areValidationErrors, doesVideoExist, isValidVideoIdParam } from '../shared' 5import { areValidationErrors, doesVideoExist, isValidVideoIdParam } from '../shared'
6 6
diff --git a/server/middlewares/validators/videos/video-playlists.ts b/server/middlewares/validators/videos/video-playlists.ts
index 8f5c75fd5..f5fee845e 100644
--- a/server/middlewares/validators/videos/video-playlists.ts
+++ b/server/middlewares/validators/videos/video-playlists.ts
@@ -1,11 +1,15 @@
1import express from 'express' 1import express from 'express'
2import { body, param, query, ValidationChain } from 'express-validator' 2import { body, param, query, ValidationChain } from 'express-validator'
3import { ExpressPromiseHandler } from '@server/types/express' 3import { ExpressPromiseHandler } from '@server/types/express-handler'
4import { MUserAccountId } from '@server/types/models' 4import { MUserAccountId } from '@server/types/models'
5import { UserRight, VideoPlaylistCreate, VideoPlaylistUpdate } from '../../../../shared' 5import {
6import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes' 6 HttpStatusCode,
7import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model' 7 UserRight,
8import { VideoPlaylistType } from '../../../../shared/models/videos/playlist/video-playlist-type.model' 8 VideoPlaylistCreate,
9 VideoPlaylistPrivacy,
10 VideoPlaylistType,
11 VideoPlaylistUpdate
12} from '@shared/models'
9import { 13import {
10 isArrayOf, 14 isArrayOf,
11 isIdOrUUIDValid, 15 isIdOrUUIDValid,
diff --git a/server/middlewares/validators/videos/videos.ts b/server/middlewares/validators/videos/videos.ts
index 782f495e8..3a1a905f3 100644
--- a/server/middlewares/validators/videos/videos.ts
+++ b/server/middlewares/validators/videos/videos.ts
@@ -5,12 +5,10 @@ import { getResumableUploadPath } from '@server/helpers/upload'
5import { Redis } from '@server/lib/redis' 5import { Redis } from '@server/lib/redis'
6import { isAbleToUploadVideo } from '@server/lib/user' 6import { isAbleToUploadVideo } from '@server/lib/user'
7import { getServerActor } from '@server/models/application/application' 7import { getServerActor } from '@server/models/application/application'
8import { ExpressPromiseHandler } from '@server/types/express' 8import { ExpressPromiseHandler } from '@server/types/express-handler'
9import { MUserAccountId, MVideoFullLight } from '@server/types/models' 9import { MUserAccountId, MVideoFullLight } from '@server/types/models'
10import { getAllPrivacies } from '@shared/core-utils' 10import { getAllPrivacies } from '@shared/core-utils'
11import { VideoInclude } from '@shared/models' 11import { HttpStatusCode, ServerErrorCode, UserRight, VideoInclude, VideoPrivacy } from '@shared/models'
12import { ServerErrorCode, UserRight, VideoPrivacy } from '../../../../shared'
13import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
14import { 12import {
15 exists, 13 exists,
16 isBooleanValid, 14 isBooleanValid,