aboutsummaryrefslogtreecommitdiffhomepage
path: root/server
diff options
context:
space:
mode:
authorkontrollanten <6680299+kontrollanten@users.noreply.github.com>2021-12-13 15:29:13 +0100
committerGitHub <noreply@github.com>2021-12-13 15:29:13 +0100
commita37e9e74ff07b057370d1ed6c0b391a02be8a6d2 (patch)
tree30d59e12518149a309bbd10bee1485f8be523c75 /server
parent11e520b50d791a0dd48cbb2d0fc681b25eb7cd53 (diff)
downloadPeerTube-a37e9e74ff07b057370d1ed6c0b391a02be8a6d2.tar.gz
PeerTube-a37e9e74ff07b057370d1ed6c0b391a02be8a6d2.tar.zst
PeerTube-a37e9e74ff07b057370d1ed6c0b391a02be8a6d2.zip
Give moderators access to edit channels (#4608)
* give admins access to edit all channels closes #4598 * test(channels): +admin update another users channel * Fix tests * fix(server): delete another users channel Since the channel owner isn't necessary the auth user we need to check the right account whether it's the last video or not. * REMOVE_ANY_VIDEO_CHANNEL > MANAGE_ANY_VIDEO_CHANNEL Merge REMOVE_ANY_VIDEO_CHANNEL and MANY_VIDEO_CHANNELS to MANAGE_ANY_VIDEO_CHANNEL. * user-right: moderator can't manage admins channel * client: MyVideoChannelCreateComponent > VideoChannelCreateComponent * client: MyVideoChannelEdit > VideoChannelEdit * Revert "user-right: moderator can't manage admins channel" This reverts commit 2c627c154e2bfe6af2e0f45efb27faf4117572f3. * server: clean dupl validator functionality * fix ensureUserCanManageChannel usage It's not async anymore. * server: merge channel validator middleares ensureAuthUserOwnsChannelValidator & ensureUserCanManageChannel gets merged into one middleware. * client(VideoChannelEdit): redirect to prev route * fix(VideoChannels): handle anon users * client: new routes for create/update channel * Refactor channel validators Co-authored-by: Chocobozzz <me@florianbigard.com>
Diffstat (limited to 'server')
-rw-r--r--server/controllers/activitypub/client.ts23
-rw-r--r--server/controllers/activitypub/inbox.ts14
-rw-r--r--server/controllers/activitypub/outbox.ts13
-rw-r--r--server/controllers/api/video-channel.ts23
-rw-r--r--server/middlewares/validators/shared/video-channels.ts7
-rw-r--r--server/middlewares/validators/users.ts17
-rw-r--r--server/middlewares/validators/videos/video-channels.ts60
-rw-r--r--server/tests/api/videos/video-channels.ts37
8 files changed, 107 insertions, 87 deletions
diff --git a/server/controllers/activitypub/client.ts b/server/controllers/activitypub/client.ts
index c4e3cec6b..4e6bd5e25 100644
--- a/server/controllers/activitypub/client.ts
+++ b/server/controllers/activitypub/client.ts
@@ -20,7 +20,8 @@ import {
20 asyncMiddleware, 20 asyncMiddleware,
21 executeIfActivityPub, 21 executeIfActivityPub,
22 localAccountValidator, 22 localAccountValidator,
23 localVideoChannelValidator, 23 videoChannelsNameWithHostValidator,
24 ensureIsLocalChannel,
24 videosCustomGetValidator, 25 videosCustomGetValidator,
25 videosShareValidator 26 videosShareValidator
26} from '../../middlewares' 27} from '../../middlewares'
@@ -123,24 +124,28 @@ activityPubClientRouter.get('/videos/watch/:videoId/comments/:commentId/activity
123) 124)
124 125
125activityPubClientRouter.get( 126activityPubClientRouter.get(
126 [ '/video-channels/:name', '/video-channels/:name/videos', '/c/:name', '/c/:name/videos' ], 127 [ '/video-channels/:nameWithHost', '/video-channels/:nameWithHost/videos', '/c/:nameWithHost', '/c/:nameWithHost/videos' ],
127 executeIfActivityPub, 128 executeIfActivityPub,
128 asyncMiddleware(localVideoChannelValidator), 129 asyncMiddleware(videoChannelsNameWithHostValidator),
130 ensureIsLocalChannel,
129 videoChannelController 131 videoChannelController
130) 132)
131activityPubClientRouter.get('/video-channels/:name/followers', 133activityPubClientRouter.get('/video-channels/:nameWithHost/followers',
132 executeIfActivityPub, 134 executeIfActivityPub,
133 asyncMiddleware(localVideoChannelValidator), 135 asyncMiddleware(videoChannelsNameWithHostValidator),
136 ensureIsLocalChannel,
134 asyncMiddleware(videoChannelFollowersController) 137 asyncMiddleware(videoChannelFollowersController)
135) 138)
136activityPubClientRouter.get('/video-channels/:name/following', 139activityPubClientRouter.get('/video-channels/:nameWithHost/following',
137 executeIfActivityPub, 140 executeIfActivityPub,
138 asyncMiddleware(localVideoChannelValidator), 141 asyncMiddleware(videoChannelsNameWithHostValidator),
142 ensureIsLocalChannel,
139 asyncMiddleware(videoChannelFollowingController) 143 asyncMiddleware(videoChannelFollowingController)
140) 144)
141activityPubClientRouter.get('/video-channels/:name/playlists', 145activityPubClientRouter.get('/video-channels/:nameWithHost/playlists',
142 executeIfActivityPub, 146 executeIfActivityPub,
143 asyncMiddleware(localVideoChannelValidator), 147 asyncMiddleware(videoChannelsNameWithHostValidator),
148 ensureIsLocalChannel,
144 asyncMiddleware(videoChannelPlaylistsController) 149 asyncMiddleware(videoChannelPlaylistsController)
145) 150)
146 151
diff --git a/server/controllers/activitypub/inbox.ts b/server/controllers/activitypub/inbox.ts
index ece4edff0..5995b8f3a 100644
--- a/server/controllers/activitypub/inbox.ts
+++ b/server/controllers/activitypub/inbox.ts
@@ -4,7 +4,14 @@ import { Activity, ActivityPubCollection, ActivityPubOrderedCollection, RootActi
4import { HttpStatusCode } from '../../../shared/models/http/http-error-codes' 4import { HttpStatusCode } from '../../../shared/models/http/http-error-codes'
5import { isActivityValid } from '../../helpers/custom-validators/activitypub/activity' 5import { isActivityValid } from '../../helpers/custom-validators/activitypub/activity'
6import { logger } from '../../helpers/logger' 6import { logger } from '../../helpers/logger'
7import { asyncMiddleware, checkSignature, localAccountValidator, localVideoChannelValidator, signatureValidator } from '../../middlewares' 7import {
8 asyncMiddleware,
9 checkSignature,
10 ensureIsLocalChannel,
11 localAccountValidator,
12 signatureValidator,
13 videoChannelsNameWithHostValidator
14} from '../../middlewares'
8import { activityPubValidator } from '../../middlewares/validators/activitypub/activity' 15import { activityPubValidator } from '../../middlewares/validators/activitypub/activity'
9 16
10const inboxRouter = express.Router() 17const inboxRouter = express.Router()
@@ -23,10 +30,11 @@ inboxRouter.post('/accounts/:name/inbox',
23 asyncMiddleware(activityPubValidator), 30 asyncMiddleware(activityPubValidator),
24 inboxController 31 inboxController
25) 32)
26inboxRouter.post('/video-channels/:name/inbox', 33inboxRouter.post('/video-channels/:nameWithHost/inbox',
27 signatureValidator, 34 signatureValidator,
28 asyncMiddleware(checkSignature), 35 asyncMiddleware(checkSignature),
29 asyncMiddleware(localVideoChannelValidator), 36 asyncMiddleware(videoChannelsNameWithHostValidator),
37 ensureIsLocalChannel,
30 asyncMiddleware(activityPubValidator), 38 asyncMiddleware(activityPubValidator),
31 inboxController 39 inboxController
32) 40)
diff --git a/server/controllers/activitypub/outbox.ts b/server/controllers/activitypub/outbox.ts
index bdf9d138b..cdef8e969 100644
--- a/server/controllers/activitypub/outbox.ts
+++ b/server/controllers/activitypub/outbox.ts
@@ -1,15 +1,15 @@
1import express from 'express' 1import express from 'express'
2import { MActorLight } from '@server/types/models'
2import { Activity } from '../../../shared/models/activitypub/activity' 3import { Activity } from '../../../shared/models/activitypub/activity'
3import { VideoPrivacy } from '../../../shared/models/videos' 4import { VideoPrivacy } from '../../../shared/models/videos'
4import { activityPubCollectionPagination, activityPubContextify } from '../../helpers/activitypub' 5import { activityPubCollectionPagination, activityPubContextify } from '../../helpers/activitypub'
5import { logger } from '../../helpers/logger' 6import { logger } from '../../helpers/logger'
6import { buildAnnounceActivity, buildCreateActivity } from '../../lib/activitypub/send'
7import { buildAudience } from '../../lib/activitypub/audience' 7import { buildAudience } from '../../lib/activitypub/audience'
8import { asyncMiddleware, localAccountValidator, localVideoChannelValidator } from '../../middlewares' 8import { buildAnnounceActivity, buildCreateActivity } from '../../lib/activitypub/send'
9import { asyncMiddleware, ensureIsLocalChannel, localAccountValidator, videoChannelsNameWithHostValidator } from '../../middlewares'
10import { apPaginationValidator } from '../../middlewares/validators/activitypub'
9import { VideoModel } from '../../models/video/video' 11import { VideoModel } from '../../models/video/video'
10import { activityPubResponse } from './utils' 12import { activityPubResponse } from './utils'
11import { MActorLight } from '@server/types/models'
12import { apPaginationValidator } from '../../middlewares/validators/activitypub'
13 13
14const outboxRouter = express.Router() 14const outboxRouter = express.Router()
15 15
@@ -19,9 +19,10 @@ outboxRouter.get('/accounts/:name/outbox',
19 asyncMiddleware(outboxController) 19 asyncMiddleware(outboxController)
20) 20)
21 21
22outboxRouter.get('/video-channels/:name/outbox', 22outboxRouter.get('/video-channels/:nameWithHost/outbox',
23 apPaginationValidator, 23 apPaginationValidator,
24 localVideoChannelValidator, 24 asyncMiddleware(videoChannelsNameWithHostValidator),
25 ensureIsLocalChannel,
25 asyncMiddleware(outboxController) 26 asyncMiddleware(outboxController)
26) 27)
27 28
diff --git a/server/controllers/api/video-channel.ts b/server/controllers/api/video-channel.ts
index d1a1e6473..abb777e08 100644
--- a/server/controllers/api/video-channel.ts
+++ b/server/controllers/api/video-channel.ts
@@ -24,6 +24,7 @@ import {
24 asyncRetryTransactionMiddleware, 24 asyncRetryTransactionMiddleware,
25 authenticate, 25 authenticate,
26 commonVideosFiltersValidator, 26 commonVideosFiltersValidator,
27 ensureCanManageChannel,
27 optionalAuthenticate, 28 optionalAuthenticate,
28 paginationValidator, 29 paginationValidator,
29 setDefaultPagination, 30 setDefaultPagination,
@@ -36,7 +37,7 @@ import {
36 videoPlaylistsSortValidator 37 videoPlaylistsSortValidator
37} from '../../middlewares' 38} from '../../middlewares'
38import { 39import {
39 ensureAuthUserOwnsChannelValidator, 40 ensureIsLocalChannel,
40 videoChannelsFollowersSortValidator, 41 videoChannelsFollowersSortValidator,
41 videoChannelsListValidator, 42 videoChannelsListValidator,
42 videoChannelsNameWithHostValidator, 43 videoChannelsNameWithHostValidator,
@@ -74,7 +75,8 @@ videoChannelRouter.post('/:nameWithHost/avatar/pick',
74 authenticate, 75 authenticate,
75 reqAvatarFile, 76 reqAvatarFile,
76 asyncMiddleware(videoChannelsNameWithHostValidator), 77 asyncMiddleware(videoChannelsNameWithHostValidator),
77 ensureAuthUserOwnsChannelValidator, 78 ensureIsLocalChannel,
79 ensureCanManageChannel,
78 updateAvatarValidator, 80 updateAvatarValidator,
79 asyncMiddleware(updateVideoChannelAvatar) 81 asyncMiddleware(updateVideoChannelAvatar)
80) 82)
@@ -83,7 +85,8 @@ videoChannelRouter.post('/:nameWithHost/banner/pick',
83 authenticate, 85 authenticate,
84 reqBannerFile, 86 reqBannerFile,
85 asyncMiddleware(videoChannelsNameWithHostValidator), 87 asyncMiddleware(videoChannelsNameWithHostValidator),
86 ensureAuthUserOwnsChannelValidator, 88 ensureIsLocalChannel,
89 ensureCanManageChannel,
87 updateBannerValidator, 90 updateBannerValidator,
88 asyncMiddleware(updateVideoChannelBanner) 91 asyncMiddleware(updateVideoChannelBanner)
89) 92)
@@ -91,27 +94,33 @@ videoChannelRouter.post('/:nameWithHost/banner/pick',
91videoChannelRouter.delete('/:nameWithHost/avatar', 94videoChannelRouter.delete('/:nameWithHost/avatar',
92 authenticate, 95 authenticate,
93 asyncMiddleware(videoChannelsNameWithHostValidator), 96 asyncMiddleware(videoChannelsNameWithHostValidator),
94 ensureAuthUserOwnsChannelValidator, 97 ensureIsLocalChannel,
98 ensureCanManageChannel,
95 asyncMiddleware(deleteVideoChannelAvatar) 99 asyncMiddleware(deleteVideoChannelAvatar)
96) 100)
97 101
98videoChannelRouter.delete('/:nameWithHost/banner', 102videoChannelRouter.delete('/:nameWithHost/banner',
99 authenticate, 103 authenticate,
100 asyncMiddleware(videoChannelsNameWithHostValidator), 104 asyncMiddleware(videoChannelsNameWithHostValidator),
101 ensureAuthUserOwnsChannelValidator, 105 ensureIsLocalChannel,
106 ensureCanManageChannel,
102 asyncMiddleware(deleteVideoChannelBanner) 107 asyncMiddleware(deleteVideoChannelBanner)
103) 108)
104 109
105videoChannelRouter.put('/:nameWithHost', 110videoChannelRouter.put('/:nameWithHost',
106 authenticate, 111 authenticate,
107 asyncMiddleware(videoChannelsNameWithHostValidator), 112 asyncMiddleware(videoChannelsNameWithHostValidator),
108 ensureAuthUserOwnsChannelValidator, 113 ensureIsLocalChannel,
114 ensureCanManageChannel,
109 videoChannelsUpdateValidator, 115 videoChannelsUpdateValidator,
110 asyncRetryTransactionMiddleware(updateVideoChannel) 116 asyncRetryTransactionMiddleware(updateVideoChannel)
111) 117)
112 118
113videoChannelRouter.delete('/:nameWithHost', 119videoChannelRouter.delete('/:nameWithHost',
114 authenticate, 120 authenticate,
121 asyncMiddleware(videoChannelsNameWithHostValidator),
122 ensureIsLocalChannel,
123 ensureCanManageChannel,
115 asyncMiddleware(videoChannelsRemoveValidator), 124 asyncMiddleware(videoChannelsRemoveValidator),
116 asyncRetryTransactionMiddleware(removeVideoChannel) 125 asyncRetryTransactionMiddleware(removeVideoChannel)
117) 126)
@@ -145,7 +154,7 @@ videoChannelRouter.get('/:nameWithHost/videos',
145videoChannelRouter.get('/:nameWithHost/followers', 154videoChannelRouter.get('/:nameWithHost/followers',
146 authenticate, 155 authenticate,
147 asyncMiddleware(videoChannelsNameWithHostValidator), 156 asyncMiddleware(videoChannelsNameWithHostValidator),
148 ensureAuthUserOwnsChannelValidator, 157 ensureCanManageChannel,
149 paginationValidator, 158 paginationValidator,
150 videoChannelsFollowersSortValidator, 159 videoChannelsFollowersSortValidator,
151 setDefaultSort, 160 setDefaultSort,
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..7a6b2ce57 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 {
@@ -490,14 +490,17 @@ const ensureAuthUserOwnsAccountValidator = [
490 } 490 }
491] 491]
492 492
493const ensureAuthUserOwnsChannelValidator = [ 493const ensureCanManageChannel = [
494 (req: express.Request, res: express.Response, next: express.NextFunction) => { 494 (req: express.Request, res: express.Response, next: express.NextFunction) => {
495 const user = res.locals.oauth.token.User 495 const user = res.locals.oauth.token.user
496 const isUserOwner = res.locals.videoChannel.Account.userId === user.id
497
498 if (!isUserOwner && user.hasRight(UserRight.MANAGE_ANY_VIDEO_CHANNEL) === false) {
499 const message = `User ${user.username} does not have right to manage channel ${req.params.nameWithHost}.`
496 500
497 if (res.locals.videoChannel.Account.userId !== user.id) {
498 return res.fail({ 501 return res.fail({
499 status: HttpStatusCode.FORBIDDEN_403, 502 status: HttpStatusCode.FORBIDDEN_403,
500 message: 'Only owner of this video channel can access this ressource' 503 message
501 }) 504 })
502 } 505 }
503 506
@@ -542,8 +545,8 @@ export {
542 usersVerifyEmailValidator, 545 usersVerifyEmailValidator,
543 userAutocompleteValidator, 546 userAutocompleteValidator,
544 ensureAuthUserOwnsAccountValidator, 547 ensureAuthUserOwnsAccountValidator,
545 ensureAuthUserOwnsChannelValidator, 548 ensureCanManageUser,
546 ensureCanManageUser 549 ensureCanManageChannel
547} 550}
548 551
549// --------------------------------------------------------------------------- 552// ---------------------------------------------------------------------------
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/tests/api/videos/video-channels.ts b/server/tests/api/videos/video-channels.ts
index c25754eb6..6ab5faa07 100644
--- a/server/tests/api/videos/video-channels.ts
+++ b/server/tests/api/videos/video-channels.ts
@@ -33,6 +33,7 @@ describe('Test video channels', function () {
33 let totoChannel: number 33 let totoChannel: number
34 let videoUUID: string 34 let videoUUID: string
35 let accountName: string 35 let accountName: string
36 let secondUserChannelName: string
36 37
37 const avatarPaths: { [ port: number ]: string } = {} 38 const avatarPaths: { [ port: number ]: string } = {}
38 const bannerPaths: { [ port: number ]: string } = {} 39 const bannerPaths: { [ port: number ]: string } = {}
@@ -219,6 +220,35 @@ describe('Test video channels', function () {
219 } 220 }
220 }) 221 })
221 222
223 it('Should update another accounts video channel', async function () {
224 this.timeout(15000)
225
226 const result = await servers[0].users.generate('second_user')
227 secondUserChannelName = result.userChannelName
228
229 await servers[0].videos.quickUpload({ name: 'video', token: result.token })
230
231 const videoChannelAttributes = {
232 displayName: 'video channel updated',
233 description: 'video channel description updated',
234 support: 'support updated'
235 }
236
237 await servers[0].channels.update({ channelName: secondUserChannelName, attributes: videoChannelAttributes })
238
239 await waitJobs(servers)
240 })
241
242 it('Should have another accounts video channel updated', async function () {
243 for (const server of servers) {
244 const body = await server.channels.get({ channelName: `${secondUserChannelName}@${servers[0].host}` })
245
246 expect(body.displayName).to.equal('video channel updated')
247 expect(body.description).to.equal('video channel description updated')
248 expect(body.support).to.equal('support updated')
249 }
250 })
251
222 it('Should update the channel support field and update videos too', async function () { 252 it('Should update the channel support field and update videos too', async function () {
223 this.timeout(35000) 253 this.timeout(35000)
224 254
@@ -368,12 +398,13 @@ describe('Test video channels', function () {
368 }) 398 })
369 399
370 it('Should have video channel deleted', async function () { 400 it('Should have video channel deleted', async function () {
371 const body = await servers[0].channels.list({ start: 0, count: 10 }) 401 const body = await servers[0].channels.list({ start: 0, count: 10, sort: 'createdAt' })
372 402
373 expect(body.total).to.equal(1) 403 expect(body.total).to.equal(2)
374 expect(body.data).to.be.an('array') 404 expect(body.data).to.be.an('array')
375 expect(body.data).to.have.lengthOf(1) 405 expect(body.data).to.have.lengthOf(2)
376 expect(body.data[0].displayName).to.equal('Main root channel') 406 expect(body.data[0].displayName).to.equal('Main root channel')
407 expect(body.data[1].displayName).to.equal('video channel updated')
377 }) 408 })
378 409
379 it('Should create the main channel with an uuid if there is a conflict', async function () { 410 it('Should create the main channel with an uuid if there is a conflict', async function () {