diff options
-rw-r--r-- | client/src/app/shared/video/video-details.model.ts | 2 | ||||
-rw-r--r-- | client/src/app/videos/+video-edit/video-update.component.ts | 10 | ||||
-rw-r--r-- | server/middlewares/validators/videos.ts | 20 | ||||
-rw-r--r-- | server/tests/api/check-params/videos.ts | 28 | ||||
-rw-r--r-- | shared/models/users/user-right.enum.ts | 3 | ||||
-rw-r--r-- | shared/models/users/user-role.ts | 3 |
6 files changed, 39 insertions, 27 deletions
diff --git a/client/src/app/shared/video/video-details.model.ts b/client/src/app/shared/video/video-details.model.ts index 4e4f64c7b..a22ed68da 100644 --- a/client/src/app/shared/video/video-details.model.ts +++ b/client/src/app/shared/video/video-details.model.ts | |||
@@ -88,6 +88,6 @@ export class VideoDetails extends Video implements VideoDetailsServerModel { | |||
88 | } | 88 | } |
89 | 89 | ||
90 | isUpdatableBy (user: AuthUser) { | 90 | isUpdatableBy (user: AuthUser) { |
91 | return user && this.isLocal === true && user.username === this.accountName | 91 | return user && this.isLocal === true && (this.accountName === user.username || user.hasRight(UserRight.UPDATE_ANY_VIDEO)) |
92 | } | 92 | } |
93 | } | 93 | } |
diff --git a/client/src/app/videos/+video-edit/video-update.component.ts b/client/src/app/videos/+video-edit/video-update.component.ts index d97e00a3a..2fc09278c 100644 --- a/client/src/app/videos/+video-edit/video-update.component.ts +++ b/client/src/app/videos/+video-edit/video-update.component.ts | |||
@@ -53,9 +53,6 @@ export class VideoUpdateComponent extends FormReactive implements OnInit { | |||
53 | this.serverService.videoPrivaciesLoaded | 53 | this.serverService.videoPrivaciesLoaded |
54 | .subscribe(() => this.videoPrivacies = this.serverService.getVideoPrivacies()) | 54 | .subscribe(() => this.videoPrivacies = this.serverService.getVideoPrivacies()) |
55 | 55 | ||
56 | populateAsyncUserVideoChannels(this.authService, this.userVideoChannels) | ||
57 | .catch(err => console.error('Cannot populate async user video channels.', err)) | ||
58 | |||
59 | const uuid: string = this.route.snapshot.params['uuid'] | 56 | const uuid: string = this.route.snapshot.params['uuid'] |
60 | this.videoService.getVideo(uuid) | 57 | this.videoService.getVideo(uuid) |
61 | .switchMap(video => { | 58 | .switchMap(video => { |
@@ -67,6 +64,13 @@ export class VideoUpdateComponent extends FormReactive implements OnInit { | |||
67 | video => { | 64 | video => { |
68 | this.video = new VideoEdit(video) | 65 | this.video = new VideoEdit(video) |
69 | 66 | ||
67 | this.userVideoChannels = [ | ||
68 | { | ||
69 | id: video.channel.id, | ||
70 | label: video.channel.displayName | ||
71 | } | ||
72 | ] | ||
73 | |||
70 | // We cannot set private a video that was not private | 74 | // We cannot set private a video that was not private |
71 | if (video.privacy !== VideoPrivacy.PRIVATE) { | 75 | if (video.privacy !== VideoPrivacy.PRIVATE) { |
72 | const newVideoPrivacies = [] | 76 | const newVideoPrivacies = [] |
diff --git a/server/middlewares/validators/videos.ts b/server/middlewares/validators/videos.ts index e91739f81..1dc8429c8 100644 --- a/server/middlewares/validators/videos.ts +++ b/server/middlewares/validators/videos.ts | |||
@@ -130,18 +130,8 @@ const videosUpdateValidator = [ | |||
130 | 130 | ||
131 | const video = res.locals.video | 131 | const video = res.locals.video |
132 | 132 | ||
133 | // We need to make additional checks | 133 | // Check if the user who did the request is able to update the video |
134 | if (video.isOwned() === false) { | 134 | if (!checkUserCanManageVideo(res.locals.oauth.token.User, res.locals.video, UserRight.UPDATE_ANY_VIDEO, res)) return |
135 | return res.status(403) | ||
136 | .json({ error: 'Cannot update video of another server' }) | ||
137 | .end() | ||
138 | } | ||
139 | |||
140 | if (video.VideoChannel.Account.userId !== res.locals.oauth.token.User.id) { | ||
141 | return res.status(403) | ||
142 | .json({ error: 'Cannot update video of another user' }) | ||
143 | .end() | ||
144 | } | ||
145 | 135 | ||
146 | if (video.privacy !== VideoPrivacy.PRIVATE && req.body.privacy === VideoPrivacy.PRIVATE) { | 136 | if (video.privacy !== VideoPrivacy.PRIVATE && req.body.privacy === VideoPrivacy.PRIVATE) { |
147 | return res.status(409) | 137 | return res.status(409) |
@@ -198,7 +188,7 @@ const videosRemoveValidator = [ | |||
198 | if (!await isVideoExist(req.params.id, res)) return | 188 | if (!await isVideoExist(req.params.id, res)) return |
199 | 189 | ||
200 | // Check if the user who did the request is able to delete the video | 190 | // Check if the user who did the request is able to delete the video |
201 | if (!checkUserCanDeleteVideo(res.locals.oauth.token.User, res.locals.video, res)) return | 191 | if (!checkUserCanManageVideo(res.locals.oauth.token.User, res.locals.video, UserRight.REMOVE_ANY_VIDEO, res)) return |
202 | 192 | ||
203 | return next() | 193 | return next() |
204 | } | 194 | } |
@@ -282,7 +272,7 @@ export { | |||
282 | 272 | ||
283 | // --------------------------------------------------------------------------- | 273 | // --------------------------------------------------------------------------- |
284 | 274 | ||
285 | function checkUserCanDeleteVideo (user: UserModel, video: VideoModel, res: express.Response) { | 275 | function checkUserCanManageVideo (user: UserModel, video: VideoModel, right: UserRight, res: express.Response) { |
286 | // Retrieve the user who did the request | 276 | // Retrieve the user who did the request |
287 | if (video.isOwned() === false) { | 277 | if (video.isOwned() === false) { |
288 | res.status(403) | 278 | res.status(403) |
@@ -295,7 +285,7 @@ function checkUserCanDeleteVideo (user: UserModel, video: VideoModel, res: expre | |||
295 | // The user can delete it if he has the right | 285 | // The user can delete it if he has the right |
296 | // Or if s/he is the video's account | 286 | // Or if s/he is the video's account |
297 | const account = video.VideoChannel.Account | 287 | const account = video.VideoChannel.Account |
298 | if (user.hasRight(UserRight.REMOVE_ANY_VIDEO) === false && account.userId !== user.id) { | 288 | if (user.hasRight(right) === false && account.userId !== user.id) { |
299 | res.status(403) | 289 | res.status(403) |
300 | .json({ error: 'Cannot remove video of another user' }) | 290 | .json({ error: 'Cannot remove video of another user' }) |
301 | .end() | 291 | .end() |
diff --git a/server/tests/api/check-params/videos.ts b/server/tests/api/check-params/videos.ts index 1d5c8543d..1d19daa60 100644 --- a/server/tests/api/check-params/videos.ts +++ b/server/tests/api/check-params/videos.ts | |||
@@ -16,7 +16,9 @@ const expect = chai.expect | |||
16 | describe('Test videos API validator', function () { | 16 | describe('Test videos API validator', function () { |
17 | const path = '/api/v1/videos/' | 17 | const path = '/api/v1/videos/' |
18 | let server: ServerInfo | 18 | let server: ServerInfo |
19 | let userAccessToken = '' | ||
19 | let channelId: number | 20 | let channelId: number |
21 | let videoId | ||
20 | 22 | ||
21 | // --------------------------------------------------------------- | 23 | // --------------------------------------------------------------- |
22 | 24 | ||
@@ -29,6 +31,11 @@ describe('Test videos API validator', function () { | |||
29 | 31 | ||
30 | await setAccessTokensToServers([ server ]) | 32 | await setAccessTokensToServers([ server ]) |
31 | 33 | ||
34 | const username = 'user1' | ||
35 | const password = 'my super password' | ||
36 | await createUser(server.url, server.accessToken, username, password) | ||
37 | userAccessToken = await userLogin(server, { username, password }) | ||
38 | |||
32 | const res = await getMyUserInformation(server.url, server.accessToken) | 39 | const res = await getMyUserInformation(server.url, server.accessToken) |
33 | channelId = res.body.videoChannels[0].id | 40 | channelId = res.body.videoChannels[0].id |
34 | }) | 41 | }) |
@@ -359,11 +366,10 @@ describe('Test videos API validator', function () { | |||
359 | privacy: VideoPrivacy.PUBLIC, | 366 | privacy: VideoPrivacy.PUBLIC, |
360 | tags: [ 'tag1', 'tag2' ] | 367 | tags: [ 'tag1', 'tag2' ] |
361 | } | 368 | } |
362 | let videoId | ||
363 | 369 | ||
364 | before(async function () { | 370 | before(async function () { |
365 | const res = await getVideosList(server.url) | 371 | const res = await getVideosList(server.url) |
366 | videoId = res.body.data[0].id | 372 | videoId = res.body.data[0].uuid |
367 | }) | 373 | }) |
368 | 374 | ||
369 | it('Should fail with nothing', async function () { | 375 | it('Should fail with nothing', async function () { |
@@ -518,7 +524,11 @@ describe('Test videos API validator', function () { | |||
518 | }) | 524 | }) |
519 | }) | 525 | }) |
520 | 526 | ||
521 | it('Should fail with a video of another user') | 527 | it('Should fail with a video of another user without the appropriate right', async function () { |
528 | const fields = baseCorrectParams | ||
529 | |||
530 | await makePutBodyRequest({ url: server.url, path: path + videoId, token: userAccessToken, fields, statusCodeExpected: 403 }) | ||
531 | }) | ||
522 | 532 | ||
523 | it('Should fail with a video of another server') | 533 | it('Should fail with a video of another server') |
524 | 534 | ||
@@ -549,7 +559,9 @@ describe('Test videos API validator', function () { | |||
549 | await getVideo(server.url, '4da6fde3-88f7-4d16-b119-108df5630b06', 404) | 559 | await getVideo(server.url, '4da6fde3-88f7-4d16-b119-108df5630b06', 404) |
550 | }) | 560 | }) |
551 | 561 | ||
552 | it('Should succeed with the correct parameters') | 562 | it('Should succeed with the correct parameters', async function () { |
563 | await getVideo(server.url, videoId) | ||
564 | }) | ||
553 | }) | 565 | }) |
554 | 566 | ||
555 | describe('When rating a video', function () { | 567 | describe('When rating a video', function () { |
@@ -618,11 +630,15 @@ describe('Test videos API validator', function () { | |||
618 | await removeVideo(server.url, server.accessToken, '4da6fde3-88f7-4d16-b119-108df5630b06', 404) | 630 | await removeVideo(server.url, server.accessToken, '4da6fde3-88f7-4d16-b119-108df5630b06', 404) |
619 | }) | 631 | }) |
620 | 632 | ||
621 | it('Should fail with a video of another user') | 633 | it('Should fail with a video of another user without the appropriate right', async function () { |
634 | await removeVideo(server.url, userAccessToken, videoId, 403) | ||
635 | }) | ||
622 | 636 | ||
623 | it('Should fail with a video of another server') | 637 | it('Should fail with a video of another server') |
624 | 638 | ||
625 | it('Should succeed with the correct parameters') | 639 | it('Should succeed with the correct parameters', async function () { |
640 | await removeVideo(server.url, server.accessToken, videoId) | ||
641 | }) | ||
626 | }) | 642 | }) |
627 | 643 | ||
628 | after(async function () { | 644 | after(async function () { |
diff --git a/shared/models/users/user-right.enum.ts b/shared/models/users/user-right.enum.ts index 1fa149999..ff6ec61f4 100644 --- a/shared/models/users/user-right.enum.ts +++ b/shared/models/users/user-right.enum.ts | |||
@@ -8,5 +8,6 @@ export enum UserRight { | |||
8 | MANAGE_CONFIGURATION, | 8 | MANAGE_CONFIGURATION, |
9 | REMOVE_ANY_VIDEO, | 9 | REMOVE_ANY_VIDEO, |
10 | REMOVE_ANY_VIDEO_CHANNEL, | 10 | REMOVE_ANY_VIDEO_CHANNEL, |
11 | REMOVE_ANY_VIDEO_COMMENT | 11 | REMOVE_ANY_VIDEO_COMMENT, |
12 | UPDATE_ANY_VIDEO | ||
12 | } | 13 | } |
diff --git a/shared/models/users/user-role.ts b/shared/models/users/user-role.ts index 271c9a46f..552aad999 100644 --- a/shared/models/users/user-role.ts +++ b/shared/models/users/user-role.ts | |||
@@ -25,7 +25,8 @@ const userRoleRights: { [ id: number ]: UserRight[] } = { | |||
25 | UserRight.MANAGE_VIDEO_ABUSES, | 25 | UserRight.MANAGE_VIDEO_ABUSES, |
26 | UserRight.REMOVE_ANY_VIDEO, | 26 | UserRight.REMOVE_ANY_VIDEO, |
27 | UserRight.REMOVE_ANY_VIDEO_CHANNEL, | 27 | UserRight.REMOVE_ANY_VIDEO_CHANNEL, |
28 | UserRight.REMOVE_ANY_VIDEO_COMMENT | 28 | UserRight.REMOVE_ANY_VIDEO_COMMENT, |
29 | UserRight.UPDATE_ANY_VIDEO | ||
29 | ], | 30 | ], |
30 | 31 | ||
31 | [UserRole.USER]: [] | 32 | [UserRole.USER]: [] |