Add ability for video owners to delete comments
authorChocobozzz <me@florianbigard.com>
Tue, 5 May 2020 15:22:11 +0000 (17:22 +0200)
committerChocobozzz <me@florianbigard.com>
Tue, 5 May 2020 15:22:11 +0000 (17:22 +0200)
client/src/app/videos/+video-watch/comment/video-comment.component.ts
server/middlewares/validators/videos/video-comments.ts
server/tests/api/check-params/video-comments.ts

index 1313b658534e4e5b03b1efcda274787f7918cb7d..868addd5894cad98f1ad06fd9a4aa0b87140c703 100644 (file)
@@ -98,6 +98,7 @@ export class VideoCommentComponent implements OnInit, OnChanges {
     return this.comment.account && this.isUserLoggedIn() &&
       (
         this.user.account.id === this.comment.account.id ||
+        this.user.account.id === this.video.account.id ||
         this.user.hasRight(UserRight.REMOVE_ANY_VIDEO_COMMENT)
       )
   }
index da2fafb10aaa32d6033244187b2fda2c5a72367a..8fa2d8561f615cf907c2ba9f13f4b80c1af41b07 100644 (file)
@@ -9,8 +9,8 @@ import { areValidationErrors } from '../utils'
 import { Hooks } from '../../../lib/plugins/hooks'
 import { AcceptResult, isLocalVideoCommentReplyAccepted, isLocalVideoThreadAccepted } from '../../../lib/moderation'
 import { doesVideoExist } from '../../../helpers/middlewares'
-import { MCommentOwner, MVideo, MVideoFullLight, MVideoId } from '../../../typings/models/video'
-import { MUser } from '@server/typings/models'
+import { MCommentOwner, MVideo, MVideoFullLight, MVideoId, MCommentOwnerVideoReply } from '../../../typings/models/video'
+import { MUser, MUserAccountUrl } from '@server/typings/models'
 
 const listVideoCommentThreadsValidator = [
   param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'),
@@ -188,7 +188,7 @@ function isVideoCommentsEnabled (video: MVideo, res: express.Response) {
   return true
 }
 
-function checkUserCanDeleteVideoComment (user: MUser, videoComment: MCommentOwner, res: express.Response) {
+function checkUserCanDeleteVideoComment (user: MUserAccountUrl, videoComment: MCommentOwnerVideoReply, res: express.Response) {
   if (videoComment.isDeleted()) {
     res.status(409)
       .json({ error: 'This comment is already deleted' })
@@ -196,11 +196,16 @@ function checkUserCanDeleteVideoComment (user: MUser, videoComment: MCommentOwne
     return false
   }
 
-  const account = videoComment.Account
-  if (user.hasRight(UserRight.REMOVE_ANY_VIDEO_COMMENT) === false && account.userId !== user.id) {
+  const userAccount = user.Account
+
+  if (
+    user.hasRight(UserRight.REMOVE_ANY_VIDEO_COMMENT) === false && // Not a moderator
+    videoComment.accountId !== userAccount.id && // Not the comment owner
+    videoComment.Video.VideoChannel.accountId !== userAccount.id // Not the video owner
+  ) {
     res.status(403)
       .json({ error: 'Cannot remove video comment of another user' })
-      .end()
+
     return false
   }
 
index 38f0695039d323c5935051f284ae339204311395..181282ce15df73cfb4e2b6bb92dc44ac11ee9b97 100644 (file)
@@ -29,6 +29,7 @@ describe('Test video comments API validator', function () {
   let server: ServerInfo
   let videoUUID: string
   let userAccessToken: string
+  let userAccessToken2: string
   let commentId: number
 
   // ---------------------------------------------------------------
@@ -53,13 +54,16 @@ describe('Test video comments API validator', function () {
     }
 
     {
-      const user = {
-        username: 'user1',
-        password: 'my super password'
-      }
+      const user = { username: 'user1', password: 'my super password' }
       await createUser({ url: server.url, accessToken: server.accessToken, username: user.username, password: user.password })
       userAccessToken = await userLogin(server, user)
     }
+
+    {
+      const user = { username: 'user2', password: 'my super password' }
+      await createUser({ url: server.url, accessToken: server.accessToken, username: user.username, password: user.password })
+      userAccessToken2 = await userLogin(server, user)
+    }
   })
 
   describe('When listing video comment threads', function () {
@@ -224,6 +228,40 @@ describe('Test video comments API validator', function () {
       await makeDeleteRequest({ url: server.url, path, token: server.accessToken, statusCodeExpected: 404 })
     })
 
+    it('Should succeed with the same user', async function () {
+      let commentToDelete: number
+
+      {
+        const res = await addVideoCommentThread(server.url, userAccessToken, videoUUID, 'hello')
+        commentToDelete = res.body.comment.id
+      }
+
+      const path = '/api/v1/videos/' + videoUUID + '/comments/' + commentToDelete
+
+      await makeDeleteRequest({ url: server.url, path, token: userAccessToken2, statusCodeExpected: 403 })
+      await makeDeleteRequest({ url: server.url, path, token: userAccessToken, statusCodeExpected: 204 })
+    })
+
+    it('Should succeed with the owner of the video', async function () {
+      let commentToDelete: number
+      let anotherVideoUUID: string
+
+      {
+        const res = await uploadVideo(server.url, userAccessToken, { name: 'video' })
+        anotherVideoUUID = res.body.video.uuid
+      }
+
+      {
+        const res = await addVideoCommentThread(server.url, server.accessToken, anotherVideoUUID, 'hello')
+        commentToDelete = res.body.comment.id
+      }
+
+      const path = '/api/v1/videos/' + anotherVideoUUID + '/comments/' + commentToDelete
+
+      await makeDeleteRequest({ url: server.url, path, token: userAccessToken2, statusCodeExpected: 403 })
+      await makeDeleteRequest({ url: server.url, path, token: userAccessToken, statusCodeExpected: 204 })
+    })
+
     it('Should succeed with the correct parameters', async function () {
       await makeDeleteRequest({ url: server.url, path: pathComment, token: server.accessToken, statusCodeExpected: 204 })
     })