From 5abb9fbbd12e7097e348d6a38622d364b1fa47ed Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 10 Jan 2019 15:39:51 +0100 Subject: [PATCH] Add ability to unfederate a local video (on blacklist) --- .../video-blacklist-list.component.html | 6 +- .../video-blacklist-list.component.ts | 6 + .../users/user-list/user-list.component.html | 3 + .../video-blacklist.service.ts | 7 +- .../modal/video-blacklist.component.html | 7 + .../modal/video-blacklist.component.ts | 10 +- server/controllers/api/videos/blacklist.ts | 17 +- server/controllers/api/videos/index.ts | 6 +- server/initializers/constants.ts | 2 +- .../migrations/0320-blacklist-unfederate.ts | 27 ++ server/lib/activitypub/share.ts | 16 +- .../validators/videos/video-blacklist.ts | 15 +- server/models/video/video-blacklist.ts | 21 +- .../tests/api/check-params/video-blacklist.ts | 118 ++++--- server/tests/api/videos/index.ts | 1 - .../api/videos/video-blacklist-management.ts | 192 ----------- server/tests/api/videos/video-blacklist.ts | 299 ++++++++++++++++-- .../blacklist/video-blacklist-create.model.ts | 1 + .../videos/blacklist/video-blacklist.model.ts | 1 + shared/utils/server/servers.ts | 4 + shared/utils/videos/video-blacklist.ts | 11 +- 21 files changed, 454 insertions(+), 316 deletions(-) create mode 100644 server/initializers/migrations/0320-blacklist-unfederate.ts delete mode 100644 server/tests/api/videos/video-blacklist-management.ts diff --git a/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.html b/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.html index 7cef787d2..6398af218 100644 --- a/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.html +++ b/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.html @@ -7,6 +7,7 @@ Video name Sensitive + Unfederated Date @@ -26,7 +27,8 @@ - {{ videoBlacklist.video.nsfw }} + {{ booleanToText(videoBlacklist.video.nsfw) }} + {{ booleanToText(videoBlacklist.unfederated) }} {{ videoBlacklist.createdAt }} @@ -37,7 +39,7 @@ - + Blacklist reason: {{ videoBlacklist.reason }} diff --git a/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.ts b/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.ts index a02e84f67..6c6f17f0c 100644 --- a/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.ts +++ b/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.ts @@ -46,6 +46,12 @@ export class VideoBlacklistListComponent extends RestTable implements OnInit { return Video.buildClientUrl(videoBlacklist.video.uuid) } + booleanToText (value: boolean) { + if (value === true) return this.i18n('yes') + + return this.i18n('no') + } + async removeVideoFromBlacklist (entry: VideoBlacklist) { const confirmMessage = this.i18n( 'Do you really want to remove this video from the blacklist? It will be available again in the videos list.' diff --git a/client/src/app/+admin/users/user-list/user-list.component.html b/client/src/app/+admin/users/user-list/user-list.component.html index 556ab3c5d..8c03a924b 100644 --- a/client/src/app/+admin/users/user-list/user-list.component.html +++ b/client/src/app/+admin/users/user-list/user-list.component.html @@ -65,7 +65,9 @@ (banned) + {{ user.email }} + ? {{ user.email }} @@ -76,6 +78,7 @@ + {{ user.videoQuotaUsed }} / {{ user.videoQuota }} {{ user.roleLabel }} {{ user.createdAt }} diff --git a/client/src/app/shared/video-blacklist/video-blacklist.service.ts b/client/src/app/shared/video-blacklist/video-blacklist.service.ts index 7d39fd4f2..94e46d7c2 100644 --- a/client/src/app/shared/video-blacklist/video-blacklist.service.ts +++ b/client/src/app/shared/video-blacklist/video-blacklist.service.ts @@ -36,8 +36,11 @@ export class VideoBlacklistService { ) } - blacklistVideo (videoId: number, reason?: string) { - const body = reason ? { reason } : {} + blacklistVideo (videoId: number, reason: string, unfederate: boolean) { + const body = { + unfederate, + reason + } return this.authHttp.post(VideoBlacklistService.BASE_VIDEOS_URL + videoId + '/blacklist', body) .pipe( diff --git a/client/src/app/videos/+video-watch/modal/video-blacklist.component.html b/client/src/app/videos/+video-watch/modal/video-blacklist.component.html index c436501b4..83600fa81 100644 --- a/client/src/app/videos/+video-watch/modal/video-blacklist.component.html +++ b/client/src/app/videos/+video-watch/modal/video-blacklist.component.html @@ -15,6 +15,13 @@ +
+ +
+
Cancel diff --git a/client/src/app/videos/+video-watch/modal/video-blacklist.component.ts b/client/src/app/videos/+video-watch/modal/video-blacklist.component.ts index 357ce39ce..50a7cadd1 100644 --- a/client/src/app/videos/+video-watch/modal/video-blacklist.component.ts +++ b/client/src/app/videos/+video-watch/modal/video-blacklist.component.ts @@ -34,9 +34,12 @@ export class VideoBlacklistComponent extends FormReactive implements OnInit { } ngOnInit () { + const defaultValues = { unfederate: 'true' } + this.buildForm({ - reason: this.videoBlacklistValidatorsService.VIDEO_BLACKLIST_REASON - }) + reason: this.videoBlacklistValidatorsService.VIDEO_BLACKLIST_REASON, + unfederate: null + }, defaultValues) } show () { @@ -50,8 +53,9 @@ export class VideoBlacklistComponent extends FormReactive implements OnInit { blacklist () { const reason = this.form.value[ 'reason' ] || undefined + const unfederate = this.video.isLocal ? this.form.value[ 'unfederate' ] : undefined - this.videoBlacklistService.blacklistVideo(this.video.id, reason) + this.videoBlacklistService.blacklistVideo(this.video.id, reason, unfederate) .subscribe( () => { this.notifier.success(this.i18n('Video blacklisted.')) diff --git a/server/controllers/api/videos/blacklist.ts b/server/controllers/api/videos/blacklist.ts index 9ef08812b..43b0516e7 100644 --- a/server/controllers/api/videos/blacklist.ts +++ b/server/controllers/api/videos/blacklist.ts @@ -18,6 +18,8 @@ import { VideoBlacklistModel } from '../../../models/video/video-blacklist' import { sequelizeTypescript } from '../../../initializers' import { Notifier } from '../../../lib/notifier' import { VideoModel } from '../../../models/video/video' +import { sendCreateVideo, sendDeleteVideo, sendUpdateVideo } from '../../../lib/activitypub/send' +import { federateVideoIfNeeded } from '../../../lib/activitypub' const blacklistRouter = express.Router() @@ -66,12 +68,17 @@ async function addVideoToBlacklist (req: express.Request, res: express.Response) const toCreate = { videoId: videoInstance.id, + unfederated: body.unfederate === true, reason: body.reason } const blacklist = await VideoBlacklistModel.create(toCreate) blacklist.Video = videoInstance + if (body.unfederate === true) { + await sendDeleteVideo(videoInstance, undefined) + } + Notifier.Instance.notifyOnVideoBlacklist(blacklist) logger.info('Video %s blacklisted.', res.locals.video.uuid) @@ -101,8 +108,14 @@ async function removeVideoFromBlacklistController (req: express.Request, res: ex const videoBlacklist = res.locals.videoBlacklist as VideoBlacklistModel const video: VideoModel = res.locals.video - await sequelizeTypescript.transaction(t => { - return videoBlacklist.destroy({ transaction: t }) + await sequelizeTypescript.transaction(async t => { + const unfederated = videoBlacklist.unfederated + await videoBlacklist.destroy({ transaction: t }) + + // Re federate the video + if (unfederated === true) { + await federateVideoIfNeeded(video, true, t) + } }) Notifier.Instance.notifyOnVideoUnblacklist(video) diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts index 33521a8c1..28ac26598 100644 --- a/server/controllers/api/videos/index.ts +++ b/server/controllers/api/videos/index.ts @@ -364,7 +364,11 @@ async function updateVideo (req: express.Request, res: express.Response) { } const isNewVideo = wasPrivateVideo && videoInstanceUpdated.privacy !== VideoPrivacy.PRIVATE - await federateVideoIfNeeded(videoInstanceUpdated, isNewVideo, t) + + // Don't send update if the video was unfederated + if (!videoInstanceUpdated.VideoBlacklist || videoInstanceUpdated.VideoBlacklist.unfederated === false) { + await federateVideoIfNeeded(videoInstanceUpdated, isNewVideo, t) + } auditLogger.update( getAuditIdFromRes(res), diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts index 4a88aef87..b18884eeb 100644 --- a/server/initializers/constants.ts +++ b/server/initializers/constants.ts @@ -16,7 +16,7 @@ let config: IConfig = require('config') // --------------------------------------------------------------------------- -const LAST_MIGRATION_VERSION = 315 +const LAST_MIGRATION_VERSION = 320 // --------------------------------------------------------------------------- diff --git a/server/initializers/migrations/0320-blacklist-unfederate.ts b/server/initializers/migrations/0320-blacklist-unfederate.ts new file mode 100644 index 000000000..6fb7bbb90 --- /dev/null +++ b/server/initializers/migrations/0320-blacklist-unfederate.ts @@ -0,0 +1,27 @@ +import * as Sequelize from 'sequelize' + +async function up (utils: { + transaction: Sequelize.Transaction, + queryInterface: Sequelize.QueryInterface, + sequelize: Sequelize.Sequelize +}): Promise { + + { + const data = { + type: Sequelize.BOOLEAN, + allowNull: false, + defaultValue: false + } + + await utils.queryInterface.addColumn('videoBlacklist', 'unfederated', data) + } +} + +function down (options) { + throw new Error('Not implemented.') +} + +export { + up, + down +} diff --git a/server/lib/activitypub/share.ts b/server/lib/activitypub/share.ts index 5dcba778c..170e49238 100644 --- a/server/lib/activitypub/share.ts +++ b/server/lib/activitypub/share.ts @@ -78,7 +78,7 @@ async function shareByServer (video: VideoModel, t: Transaction) { const serverActor = await getServerActor() const serverShareUrl = getVideoAnnounceActivityPubUrl(serverActor, video) - return VideoShareModel.findOrCreate({ + const [ serverShare ] = await VideoShareModel.findOrCreate({ defaults: { actorId: serverActor.id, videoId: video.id, @@ -88,16 +88,14 @@ async function shareByServer (video: VideoModel, t: Transaction) { url: serverShareUrl }, transaction: t - }).then(([ serverShare, created ]) => { - if (created) return sendVideoAnnounce(serverActor, serverShare, video, t) - - return undefined }) + + return sendVideoAnnounce(serverActor, serverShare, video, t) } async function shareByVideoChannel (video: VideoModel, t: Transaction) { const videoChannelShareUrl = getVideoAnnounceActivityPubUrl(video.VideoChannel.Actor, video) - return VideoShareModel.findOrCreate({ + const [ videoChannelShare ] = await VideoShareModel.findOrCreate({ defaults: { actorId: video.VideoChannel.actorId, videoId: video.id, @@ -107,11 +105,9 @@ async function shareByVideoChannel (video: VideoModel, t: Transaction) { url: videoChannelShareUrl }, transaction: t - }).then(([ videoChannelShare, created ]) => { - if (created) return sendVideoAnnounce(video.VideoChannel.Actor, videoChannelShare, video, t) - - return undefined }) + + return sendVideoAnnounce(video.VideoChannel.Actor, videoChannelShare, video, t) } async function undoShareByVideoChannel (video: VideoModel, oldVideoChannel: VideoChannelModel, t: Transaction) { diff --git a/server/middlewares/validators/videos/video-blacklist.ts b/server/middlewares/validators/videos/video-blacklist.ts index 13da7acff..2688f63ae 100644 --- a/server/middlewares/validators/videos/video-blacklist.ts +++ b/server/middlewares/validators/videos/video-blacklist.ts @@ -1,10 +1,11 @@ import * as express from 'express' import { body, param } from 'express-validator/check' -import { isIdOrUUIDValid } from '../../../helpers/custom-validators/misc' +import { isBooleanValid, isIdOrUUIDValid } from '../../../helpers/custom-validators/misc' import { isVideoExist } from '../../../helpers/custom-validators/videos' import { logger } from '../../../helpers/logger' import { areValidationErrors } from '../utils' import { isVideoBlacklistExist, isVideoBlacklistReasonValid } from '../../../helpers/custom-validators/video-blacklist' +import { VideoModel } from '../../../models/video/video' const videosBlacklistRemoveValidator = [ param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'), @@ -22,6 +23,10 @@ const videosBlacklistRemoveValidator = [ const videosBlacklistAddValidator = [ param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'), + body('unfederate') + .optional() + .toBoolean() + .custom(isBooleanValid).withMessage('Should have a valid unfederate boolean'), body('reason') .optional() .custom(isVideoBlacklistReasonValid).withMessage('Should have a valid reason'), @@ -32,6 +37,14 @@ const videosBlacklistAddValidator = [ if (areValidationErrors(req, res)) return if (!await isVideoExist(req.params.videoId, res)) return + const video: VideoModel = res.locals.video + if (req.body.unfederate === true && video.remote === true) { + return res + .status(409) + .send({ error: 'You cannot unfederate a remote video.' }) + .end() + } + return next() } ] diff --git a/server/models/video/video-blacklist.ts b/server/models/video/video-blacklist.ts index 23e992685..3b567e488 100644 --- a/server/models/video/video-blacklist.ts +++ b/server/models/video/video-blacklist.ts @@ -1,21 +1,7 @@ -import { - AfterCreate, - AfterDestroy, - AllowNull, - BelongsTo, - Column, - CreatedAt, - DataType, - ForeignKey, - Is, - Model, - Table, - UpdatedAt -} from 'sequelize-typescript' +import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' import { getSortOnModel, SortType, throwIfNotValid } from '../utils' import { VideoModel } from './video' import { isVideoBlacklistReasonValid } from '../../helpers/custom-validators/video-blacklist' -import { Emailer } from '../../lib/emailer' import { VideoBlacklist } from '../../../shared/models/videos' import { CONSTRAINTS_FIELDS } from '../../initializers' @@ -35,6 +21,10 @@ export class VideoBlacklistModel extends Model { @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_BLACKLIST.REASON.max)) reason: string + @AllowNull(false) + @Column + unfederated: boolean + @CreatedAt createdAt: Date @@ -93,6 +83,7 @@ export class VideoBlacklistModel extends Model { createdAt: this.createdAt, updatedAt: this.updatedAt, reason: this.reason, + unfederated: this.unfederated, video: { id: video.id, diff --git a/server/tests/api/check-params/video-blacklist.ts b/server/tests/api/check-params/video-blacklist.ts index 8e1206db3..6b82643f4 100644 --- a/server/tests/api/check-params/video-blacklist.ts +++ b/server/tests/api/check-params/video-blacklist.ts @@ -4,17 +4,20 @@ import 'mocha' import { createUser, + doubleFollow, + flushAndRunMultipleServers, flushTests, - getBlacklistedVideosList, getVideo, getVideoWithToken, + getBlacklistedVideosList, + getVideo, + getVideoWithToken, killallServers, makePostBodyRequest, makePutBodyRequest, removeVideoFromBlacklist, - runServer, ServerInfo, setAccessTokensToServers, uploadVideo, - userLogin + userLogin, waitJobs } from '../../../../shared/utils' import { checkBadCountPagination, @@ -25,8 +28,9 @@ import { VideoDetails } from '../../../../shared/models/videos' import { expect } from 'chai' describe('Test video blacklist API validators', function () { - let server: ServerInfo + let servers: ServerInfo[] let notBlacklistedVideoId: number + let remoteVideoUUID: string let userAccessToken1 = '' let userAccessToken2 = '' @@ -36,75 +40,89 @@ describe('Test video blacklist API validators', function () { this.timeout(120000) await flushTests() + servers = await flushAndRunMultipleServers(2) - server = await runServer(1) - - await setAccessTokensToServers([ server ]) + await setAccessTokensToServers(servers) + await doubleFollow(servers[0], servers[1]) { const username = 'user1' const password = 'my super password' - await createUser(server.url, server.accessToken, username, password) - userAccessToken1 = await userLogin(server, { username, password }) + await createUser(servers[0].url, servers[0].accessToken, username, password) + userAccessToken1 = await userLogin(servers[0], { username, password }) } { const username = 'user2' const password = 'my super password' - await createUser(server.url, server.accessToken, username, password) - userAccessToken2 = await userLogin(server, { username, password }) + await createUser(servers[0].url, servers[0].accessToken, username, password) + userAccessToken2 = await userLogin(servers[0], { username, password }) } { - const res = await uploadVideo(server.url, userAccessToken1, {}) - server.video = res.body.video + const res = await uploadVideo(servers[0].url, userAccessToken1, {}) + servers[0].video = res.body.video } { - const res = await uploadVideo(server.url, server.accessToken, {}) + const res = await uploadVideo(servers[0].url, servers[0].accessToken, {}) notBlacklistedVideoId = res.body.video.uuid } + + { + const res = await uploadVideo(servers[1].url, servers[1].accessToken, {}) + remoteVideoUUID = res.body.video.uuid + } + + await waitJobs(servers) }) describe('When adding a video in blacklist', function () { const basePath = '/api/v1/videos/' it('Should fail with nothing', async function () { - const path = basePath + server.video + '/blacklist' + const path = basePath + servers[0].video + '/blacklist' const fields = {} - await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) + await makePostBodyRequest({ url: servers[0].url, path, token: servers[0].accessToken, fields }) }) it('Should fail with a wrong video', async function () { const wrongPath = '/api/v1/videos/blabla/blacklist' const fields = {} - await makePostBodyRequest({ url: server.url, path: wrongPath, token: server.accessToken, fields }) + await makePostBodyRequest({ url: servers[0].url, path: wrongPath, token: servers[0].accessToken, fields }) }) it('Should fail with a non authenticated user', async function () { - const path = basePath + server.video + '/blacklist' + const path = basePath + servers[0].video + '/blacklist' const fields = {} - await makePostBodyRequest({ url: server.url, path, token: 'hello', fields, statusCodeExpected: 401 }) + await makePostBodyRequest({ url: servers[0].url, path, token: 'hello', fields, statusCodeExpected: 401 }) }) it('Should fail with a non admin user', async function () { - const path = basePath + server.video + '/blacklist' + const path = basePath + servers[0].video + '/blacklist' const fields = {} - await makePostBodyRequest({ url: server.url, path, token: userAccessToken2, fields, statusCodeExpected: 403 }) + await makePostBodyRequest({ url: servers[0].url, path, token: userAccessToken2, fields, statusCodeExpected: 403 }) }) it('Should fail with an invalid reason', async function () { - const path = basePath + server.video.uuid + '/blacklist' + const path = basePath + servers[0].video.uuid + '/blacklist' const fields = { reason: 'a'.repeat(305) } - await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) + await makePostBodyRequest({ url: servers[0].url, path, token: servers[0].accessToken, fields }) + }) + + it('Should fail to unfederate a remote video', async function () { + const path = basePath + remoteVideoUUID + '/blacklist' + const fields = { unfederate: true } + + await makePostBodyRequest({ url: servers[0].url, path, token: servers[0].accessToken, fields, statusCodeExpected: 409 }) }) it('Should succeed with the correct params', async function () { - const path = basePath + server.video.uuid + '/blacklist' + const path = basePath + servers[0].video.uuid + '/blacklist' const fields = { } - await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields, statusCodeExpected: 204 }) + await makePostBodyRequest({ url: servers[0].url, path, token: servers[0].accessToken, fields, statusCodeExpected: 204 }) }) }) @@ -114,61 +132,61 @@ describe('Test video blacklist API validators', function () { it('Should fail with a wrong video', async function () { const wrongPath = '/api/v1/videos/blabla/blacklist' const fields = {} - await makePutBodyRequest({ url: server.url, path: wrongPath, token: server.accessToken, fields }) + await makePutBodyRequest({ url: servers[0].url, path: wrongPath, token: servers[0].accessToken, fields }) }) it('Should fail with a video not blacklisted', async function () { const path = '/api/v1/videos/' + notBlacklistedVideoId + '/blacklist' const fields = {} - await makePutBodyRequest({ url: server.url, path, token: server.accessToken, fields, statusCodeExpected: 404 }) + await makePutBodyRequest({ url: servers[0].url, path, token: servers[0].accessToken, fields, statusCodeExpected: 404 }) }) it('Should fail with a non authenticated user', async function () { - const path = basePath + server.video + '/blacklist' + const path = basePath + servers[0].video + '/blacklist' const fields = {} - await makePutBodyRequest({ url: server.url, path, token: 'hello', fields, statusCodeExpected: 401 }) + await makePutBodyRequest({ url: servers[0].url, path, token: 'hello', fields, statusCodeExpected: 401 }) }) it('Should fail with a non admin user', async function () { - const path = basePath + server.video + '/blacklist' + const path = basePath + servers[0].video + '/blacklist' const fields = {} - await makePutBodyRequest({ url: server.url, path, token: userAccessToken2, fields, statusCodeExpected: 403 }) + await makePutBodyRequest({ url: servers[0].url, path, token: userAccessToken2, fields, statusCodeExpected: 403 }) }) it('Should fail with an invalid reason', async function () { - const path = basePath + server.video.uuid + '/blacklist' + const path = basePath + servers[0].video.uuid + '/blacklist' const fields = { reason: 'a'.repeat(305) } - await makePutBodyRequest({ url: server.url, path, token: server.accessToken, fields }) + await makePutBodyRequest({ url: servers[0].url, path, token: servers[0].accessToken, fields }) }) it('Should succeed with the correct params', async function () { - const path = basePath + server.video.uuid + '/blacklist' + const path = basePath + servers[0].video.uuid + '/blacklist' const fields = { reason: 'hello' } - await makePutBodyRequest({ url: server.url, path, token: server.accessToken, fields, statusCodeExpected: 204 }) + await makePutBodyRequest({ url: servers[0].url, path, token: servers[0].accessToken, fields, statusCodeExpected: 204 }) }) }) describe('When getting blacklisted video', function () { it('Should fail with a non authenticated user', async function () { - await getVideo(server.url, server.video.uuid, 401) + await getVideo(servers[0].url, servers[0].video.uuid, 401) }) it('Should fail with another user', async function () { - await getVideoWithToken(server.url, userAccessToken2, server.video.uuid, 403) + await getVideoWithToken(servers[0].url, userAccessToken2, servers[0].video.uuid, 403) }) it('Should succeed with the owner authenticated user', async function () { - const res = await getVideoWithToken(server.url, userAccessToken1, server.video.uuid, 200) + const res = await getVideoWithToken(servers[0].url, userAccessToken1, servers[0].video.uuid, 200) const video: VideoDetails = res.body expect(video.blacklisted).to.be.true }) it('Should succeed with an admin', async function () { - const res = await getVideoWithToken(server.url, server.accessToken, server.video.uuid, 200) + const res = await getVideoWithToken(servers[0].url, servers[0].accessToken, servers[0].video.uuid, 200) const video: VideoDetails = res.body expect(video.blacklisted).to.be.true @@ -177,24 +195,24 @@ describe('Test video blacklist API validators', function () { describe('When removing a video in blacklist', function () { it('Should fail with a non authenticated user', async function () { - await removeVideoFromBlacklist(server.url, 'fake token', server.video.uuid, 401) + await removeVideoFromBlacklist(servers[0].url, 'fake token', servers[0].video.uuid, 401) }) it('Should fail with a non admin user', async function () { - await removeVideoFromBlacklist(server.url, userAccessToken2, server.video.uuid, 403) + await removeVideoFromBlacklist(servers[0].url, userAccessToken2, servers[0].video.uuid, 403) }) it('Should fail with an incorrect id', async function () { - await removeVideoFromBlacklist(server.url, server.accessToken, 'hello', 400) + await removeVideoFromBlacklist(servers[0].url, servers[0].accessToken, 'hello', 400) }) it('Should fail with a not blacklisted video', async function () { // The video was not added to the blacklist so it should fail - await removeVideoFromBlacklist(server.url, server.accessToken, notBlacklistedVideoId, 404) + await removeVideoFromBlacklist(servers[0].url, servers[0].accessToken, notBlacklistedVideoId, 404) }) it('Should succeed with the correct params', async function () { - await removeVideoFromBlacklist(server.url, server.accessToken, server.video.uuid, 204) + await removeVideoFromBlacklist(servers[0].url, servers[0].accessToken, servers[0].video.uuid, 204) }) }) @@ -202,28 +220,28 @@ describe('Test video blacklist API validators', function () { const basePath = '/api/v1/videos/blacklist/' it('Should fail with a non authenticated user', async function () { - await getBlacklistedVideosList(server.url, 'fake token', 401) + await getBlacklistedVideosList(servers[0].url, 'fake token', 401) }) it('Should fail with a non admin user', async function () { - await getBlacklistedVideosList(server.url, userAccessToken2, 403) + await getBlacklistedVideosList(servers[0].url, userAccessToken2, 403) }) it('Should fail with a bad start pagination', async function () { - await checkBadStartPagination(server.url, basePath, server.accessToken) + await checkBadStartPagination(servers[0].url, basePath, servers[0].accessToken) }) it('Should fail with a bad count pagination', async function () { - await checkBadCountPagination(server.url, basePath, server.accessToken) + await checkBadCountPagination(servers[0].url, basePath, servers[0].accessToken) }) it('Should fail with an incorrect sort', async function () { - await checkBadSortPagination(server.url, basePath, server.accessToken) + await checkBadSortPagination(servers[0].url, basePath, servers[0].accessToken) }) }) after(async function () { - killallServers([ server ]) + killallServers(servers) // Keep the logs if the test failed if (this['ok']) { diff --git a/server/tests/api/videos/index.ts b/server/tests/api/videos/index.ts index 9bdb78491..97f467aae 100644 --- a/server/tests/api/videos/index.ts +++ b/server/tests/api/videos/index.ts @@ -3,7 +3,6 @@ import './services' import './single-server' import './video-abuse' import './video-blacklist' -import './video-blacklist-management' import './video-captions' import './video-change-ownership' import './video-channels' diff --git a/server/tests/api/videos/video-blacklist-management.ts b/server/tests/api/videos/video-blacklist-management.ts deleted file mode 100644 index 61411e30d..000000000 --- a/server/tests/api/videos/video-blacklist-management.ts +++ /dev/null @@ -1,192 +0,0 @@ -/* tslint:disable:no-unused-expression */ - -import * as chai from 'chai' -import { orderBy } from 'lodash' -import 'mocha' -import { - addVideoToBlacklist, - flushAndRunMultipleServers, - getBlacklistedVideosList, - getMyVideos, - getSortedBlacklistedVideosList, - getVideosList, - killallServers, - removeVideoFromBlacklist, - ServerInfo, - setAccessTokensToServers, - updateVideoBlacklist, - uploadVideo -} from '../../../../shared/utils/index' -import { doubleFollow } from '../../../../shared/utils/server/follows' -import { waitJobs } from '../../../../shared/utils/server/jobs' -import { VideoAbuse } from '../../../../shared/models/videos' - -const expect = chai.expect - -describe('Test video blacklist management', function () { - let servers: ServerInfo[] = [] - let videoId: number - - async function blacklistVideosOnServer (server: ServerInfo) { - const res = await getVideosList(server.url) - - const videos = res.body.data - for (let video of videos) { - await addVideoToBlacklist(server.url, server.accessToken, video.id, 'super reason') - } - } - - before(async function () { - this.timeout(50000) - - // Run servers - servers = await flushAndRunMultipleServers(2) - - // Get the access tokens - await setAccessTokensToServers(servers) - - // Server 1 and server 2 follow each other - await doubleFollow(servers[0], servers[1]) - - // Upload 2 videos on server 2 - await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'My 1st video', description: 'A video on server 2' }) - await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'My 2nd video', description: 'A video on server 2' }) - - // Wait videos propagation, server 2 has transcoding enabled - await waitJobs(servers) - - // Blacklist the two videos on server 1 - await blacklistVideosOnServer(servers[0]) - }) - - describe('When listing blacklisted videos', function () { - it('Should display all the blacklisted videos', async function () { - const res = await getBlacklistedVideosList(servers[0].url, servers[0].accessToken) - - expect(res.body.total).to.equal(2) - - const blacklistedVideos = res.body.data - expect(blacklistedVideos).to.be.an('array') - expect(blacklistedVideos.length).to.equal(2) - - for (const blacklistedVideo of blacklistedVideos) { - expect(blacklistedVideo.reason).to.equal('super reason') - videoId = blacklistedVideo.video.id - } - }) - - it('Should get the correct sort when sorting by descending id', async function () { - const res = await getSortedBlacklistedVideosList(servers[0].url, servers[0].accessToken, '-id') - expect(res.body.total).to.equal(2) - - const blacklistedVideos = res.body.data - expect(blacklistedVideos).to.be.an('array') - expect(blacklistedVideos.length).to.equal(2) - - const result = orderBy(res.body.data, [ 'id' ], [ 'desc' ]) - - expect(blacklistedVideos).to.deep.equal(result) - }) - - it('Should get the correct sort when sorting by descending video name', async function () { - const res = await getSortedBlacklistedVideosList(servers[0].url, servers[0].accessToken, '-name') - expect(res.body.total).to.equal(2) - - const blacklistedVideos = res.body.data - expect(blacklistedVideos).to.be.an('array') - expect(blacklistedVideos.length).to.equal(2) - - const result = orderBy(res.body.data, [ 'name' ], [ 'desc' ]) - - expect(blacklistedVideos).to.deep.equal(result) - }) - - it('Should get the correct sort when sorting by ascending creation date', async function () { - const res = await getSortedBlacklistedVideosList(servers[0].url, servers[0].accessToken, 'createdAt') - expect(res.body.total).to.equal(2) - - const blacklistedVideos = res.body.data - expect(blacklistedVideos).to.be.an('array') - expect(blacklistedVideos.length).to.equal(2) - - const result = orderBy(res.body.data, [ 'createdAt' ]) - - expect(blacklistedVideos).to.deep.equal(result) - }) - }) - - describe('When updating blacklisted videos', function () { - it('Should change the reason', async function () { - await updateVideoBlacklist(servers[0].url, servers[0].accessToken, videoId, 'my super reason updated') - - const res = await getSortedBlacklistedVideosList(servers[0].url, servers[0].accessToken, '-name') - const video = res.body.data.find(b => b.video.id === videoId) - - expect(video.reason).to.equal('my super reason updated') - }) - }) - - describe('When listing my videos', function () { - it('Should display blacklisted videos', async function () { - await blacklistVideosOnServer(servers[1]) - - const res = await getMyVideos(servers[1].url, servers[1].accessToken, 0, 5) - - expect(res.body.total).to.equal(2) - expect(res.body.data).to.have.lengthOf(2) - - for (const video of res.body.data) { - expect(video.blacklisted).to.be.true - expect(video.blacklistedReason).to.equal('super reason') - } - }) - }) - - describe('When removing a blacklisted video', function () { - let videoToRemove: VideoAbuse - let blacklist = [] - - it('Should not have any video in videos list on server 1', async function () { - const res = await getVideosList(servers[0].url) - expect(res.body.total).to.equal(0) - expect(res.body.data).to.be.an('array') - expect(res.body.data.length).to.equal(0) - }) - - it('Should remove a video from the blacklist on server 1', async function () { - // Get one video in the blacklist - const res = await getSortedBlacklistedVideosList(servers[0].url, servers[0].accessToken, '-name') - videoToRemove = res.body.data[0] - blacklist = res.body.data.slice(1) - - // Remove it - await removeVideoFromBlacklist(servers[0].url, servers[0].accessToken, videoToRemove.video.id) - }) - - it('Should have the ex-blacklisted video in videos list on server 1', async function () { - const res = await getVideosList(servers[0].url) - expect(res.body.total).to.equal(1) - - const videos = res.body.data - expect(videos).to.be.an('array') - expect(videos.length).to.equal(1) - - expect(videos[0].name).to.equal(videoToRemove.video.name) - expect(videos[0].id).to.equal(videoToRemove.video.id) - }) - - it('Should not have the ex-blacklisted video in videos blacklist list on server 1', async function () { - const res = await getSortedBlacklistedVideosList(servers[0].url, servers[0].accessToken, '-name') - expect(res.body.total).to.equal(1) - - const videos = res.body.data - expect(videos).to.be.an('array') - expect(videos.length).to.equal(1) - expect(videos).to.deep.equal(blacklist) - }) - }) - - after(async function () { - killallServers(servers) - }) -}) diff --git a/server/tests/api/videos/video-blacklist.ts b/server/tests/api/videos/video-blacklist.ts index 1cce82d2a..d39ad63b4 100644 --- a/server/tests/api/videos/video-blacklist.ts +++ b/server/tests/api/videos/video-blacklist.ts @@ -1,24 +1,43 @@ /* tslint:disable:no-unused-expression */ import * as chai from 'chai' +import { orderBy } from 'lodash' import 'mocha' import { addVideoToBlacklist, flushAndRunMultipleServers, + getBlacklistedVideosList, + getMyVideos, + getSortedBlacklistedVideosList, getVideosList, killallServers, + removeVideoFromBlacklist, searchVideo, ServerInfo, setAccessTokensToServers, - uploadVideo + updateVideo, + updateVideoBlacklist, + uploadVideo, + viewVideo } from '../../../../shared/utils/index' import { doubleFollow } from '../../../../shared/utils/server/follows' import { waitJobs } from '../../../../shared/utils/server/jobs' +import { VideoBlacklist } from '../../../../shared/models/videos' const expect = chai.expect -describe('Test video blacklists', function () { +describe('Test video blacklist management', function () { let servers: ServerInfo[] = [] + let videoId: number + + async function blacklistVideosOnServer (server: ServerInfo) { + const res = await getVideosList(server.url) + + const videos = res.body.data + for (let video of videos) { + await addVideoToBlacklist(server.url, server.accessToken, video.id, 'super reason') + } + } before(async function () { this.timeout(50000) @@ -32,58 +51,270 @@ describe('Test video blacklists', function () { // Server 1 and server 2 follow each other await doubleFollow(servers[0], servers[1]) - // Upload a video on server 2 - const videoAttributes = { - name: 'my super name for server 2', - description: 'my super description for server 2' - } - await uploadVideo(servers[1].url, servers[1].accessToken, videoAttributes) + // Upload 2 videos on server 2 + await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'My 1st video', description: 'A video on server 2' }) + await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'My 2nd video', description: 'A video on server 2' }) // Wait videos propagation, server 2 has transcoding enabled await waitJobs(servers) - const res = await getVideosList(servers[0].url) - const videos = res.body.data + // Blacklist the two videos on server 1 + await blacklistVideosOnServer(servers[0]) + }) + + describe('When listing/searching videos', function () { - expect(videos.length).to.equal(1) + it('Should not have the video blacklisted in videos list/search on server 1', async function () { + { + const res = await getVideosList(servers[ 0 ].url) - servers[0].remoteVideo = videos.find(video => video.name === 'my super name for server 2') + expect(res.body.total).to.equal(0) + expect(res.body.data).to.be.an('array') + expect(res.body.data.length).to.equal(0) + } + + { + const res = await searchVideo(servers[ 0 ].url, 'name') + + expect(res.body.total).to.equal(0) + expect(res.body.data).to.be.an('array') + expect(res.body.data.length).to.equal(0) + } + }) + + it('Should have the blacklisted video in videos list/search on server 2', async function () { + { + const res = await getVideosList(servers[ 1 ].url) + + expect(res.body.total).to.equal(2) + expect(res.body.data).to.be.an('array') + expect(res.body.data.length).to.equal(2) + } + + { + const res = await searchVideo(servers[ 1 ].url, 'video') + + expect(res.body.total).to.equal(2) + expect(res.body.data).to.be.an('array') + expect(res.body.data.length).to.equal(2) + } + }) }) - it('Should blacklist a remote video on server 1', async function () { - await addVideoToBlacklist(servers[0].url, servers[0].accessToken, servers[0].remoteVideo.id) + describe('When listing blacklisted videos', function () { + it('Should display all the blacklisted videos', async function () { + const res = await getBlacklistedVideosList(servers[0].url, servers[0].accessToken) + + expect(res.body.total).to.equal(2) + + const blacklistedVideos = res.body.data + expect(blacklistedVideos).to.be.an('array') + expect(blacklistedVideos.length).to.equal(2) + + for (const blacklistedVideo of blacklistedVideos) { + expect(blacklistedVideo.reason).to.equal('super reason') + videoId = blacklistedVideo.video.id + } + }) + + it('Should get the correct sort when sorting by descending id', async function () { + const res = await getSortedBlacklistedVideosList(servers[0].url, servers[0].accessToken, '-id') + expect(res.body.total).to.equal(2) + + const blacklistedVideos = res.body.data + expect(blacklistedVideos).to.be.an('array') + expect(blacklistedVideos.length).to.equal(2) + + const result = orderBy(res.body.data, [ 'id' ], [ 'desc' ]) + + expect(blacklistedVideos).to.deep.equal(result) + }) + + it('Should get the correct sort when sorting by descending video name', async function () { + const res = await getSortedBlacklistedVideosList(servers[0].url, servers[0].accessToken, '-name') + expect(res.body.total).to.equal(2) + + const blacklistedVideos = res.body.data + expect(blacklistedVideos).to.be.an('array') + expect(blacklistedVideos.length).to.equal(2) + + const result = orderBy(res.body.data, [ 'name' ], [ 'desc' ]) + + expect(blacklistedVideos).to.deep.equal(result) + }) + + it('Should get the correct sort when sorting by ascending creation date', async function () { + const res = await getSortedBlacklistedVideosList(servers[0].url, servers[0].accessToken, 'createdAt') + expect(res.body.total).to.equal(2) + + const blacklistedVideos = res.body.data + expect(blacklistedVideos).to.be.an('array') + expect(blacklistedVideos.length).to.equal(2) + + const result = orderBy(res.body.data, [ 'createdAt' ]) + + expect(blacklistedVideos).to.deep.equal(result) + }) }) - it('Should not have the video blacklisted in videos list on server 1', async function () { - const res = await getVideosList(servers[0].url) + describe('When updating blacklisted videos', function () { + it('Should change the reason', async function () { + await updateVideoBlacklist(servers[0].url, servers[0].accessToken, videoId, 'my super reason updated') + + const res = await getSortedBlacklistedVideosList(servers[0].url, servers[0].accessToken, '-name') + const video = res.body.data.find(b => b.video.id === videoId) - expect(res.body.total).to.equal(0) - expect(res.body.data).to.be.an('array') - expect(res.body.data.length).to.equal(0) + expect(video.reason).to.equal('my super reason updated') + }) }) - it('Should not have the video blacklisted in videos search on server 1', async function () { - const res = await searchVideo(servers[0].url, 'name') + describe('When listing my videos', function () { + it('Should display blacklisted videos', async function () { + await blacklistVideosOnServer(servers[1]) + + const res = await getMyVideos(servers[1].url, servers[1].accessToken, 0, 5) - expect(res.body.total).to.equal(0) - expect(res.body.data).to.be.an('array') - expect(res.body.data.length).to.equal(0) + expect(res.body.total).to.equal(2) + expect(res.body.data).to.have.lengthOf(2) + + for (const video of res.body.data) { + expect(video.blacklisted).to.be.true + expect(video.blacklistedReason).to.equal('super reason') + } + }) }) - it('Should have the blacklisted video in videos list on server 2', async function () { - const res = await getVideosList(servers[1].url) + describe('When removing a blacklisted video', function () { + let videoToRemove: VideoBlacklist + let blacklist = [] + + it('Should not have any video in videos list on server 1', async function () { + const res = await getVideosList(servers[0].url) + expect(res.body.total).to.equal(0) + expect(res.body.data).to.be.an('array') + expect(res.body.data.length).to.equal(0) + }) + + it('Should remove a video from the blacklist on server 1', async function () { + // Get one video in the blacklist + const res = await getSortedBlacklistedVideosList(servers[0].url, servers[0].accessToken, '-name') + videoToRemove = res.body.data[0] + blacklist = res.body.data.slice(1) + + // Remove it + await removeVideoFromBlacklist(servers[0].url, servers[0].accessToken, videoToRemove.video.id) + }) + + it('Should have the ex-blacklisted video in videos list on server 1', async function () { + const res = await getVideosList(servers[0].url) + expect(res.body.total).to.equal(1) + + const videos = res.body.data + expect(videos).to.be.an('array') + expect(videos.length).to.equal(1) + + expect(videos[0].name).to.equal(videoToRemove.video.name) + expect(videos[0].id).to.equal(videoToRemove.video.id) + }) + + it('Should not have the ex-blacklisted video in videos blacklist list on server 1', async function () { + const res = await getSortedBlacklistedVideosList(servers[0].url, servers[0].accessToken, '-name') + expect(res.body.total).to.equal(1) - expect(res.body.total).to.equal(1) - expect(res.body.data).to.be.an('array') - expect(res.body.data.length).to.equal(1) + const videos = res.body.data + expect(videos).to.be.an('array') + expect(videos.length).to.equal(1) + expect(videos).to.deep.equal(blacklist) + }) }) - it('Should have the video blacklisted in videos search on server 2', async function () { - const res = await searchVideo(servers[1].url, 'name') + describe('When blacklisting local videos', function () { + let video3UUID: string + let video4UUID: string + + before(async function () { + this.timeout(10000) + + { + const res = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'Video 3' }) + video3UUID = res.body.video.uuid + } + { + const res = await uploadVideo(servers[ 0 ].url, servers[ 0 ].accessToken, { name: 'Video 4' }) + video4UUID = res.body.video.uuid + } + + await waitJobs(servers) + }) + + it('Should blacklist video 3 and keep it federated', async function () { + this.timeout(10000) + + await addVideoToBlacklist(servers[ 0 ].url, servers[ 0 ].accessToken, video3UUID, 'super reason', false) + + await waitJobs(servers) + + { + const res = await getVideosList(servers[ 0 ].url) + expect(res.body.data.find(v => v.uuid === video3UUID)).to.be.undefined + } + + { + const res = await getVideosList(servers[ 1 ].url) + expect(res.body.data.find(v => v.uuid === video3UUID)).to.not.be.undefined + } + }) + + it('Should unfederate the video', async function () { + this.timeout(10000) + + await addVideoToBlacklist(servers[ 0 ].url, servers[ 0 ].accessToken, video4UUID, 'super reason', true) + + await waitJobs(servers) + + for (const server of servers) { + const res = await getVideosList(server.url) + expect(res.body.data.find(v => v.uuid === video4UUID)).to.be.undefined + } + }) + + it('Should have the video unfederated even after an Update AP message', async function () { + this.timeout(10000) + + await updateVideo(servers[ 0 ].url, servers[ 0 ].accessToken, video4UUID, { description: 'super description' }) + + await waitJobs(servers) + + for (const server of servers) { + const res = await getVideosList(server.url) + expect(res.body.data.find(v => v.uuid === video4UUID)).to.be.undefined + } + }) + + it('Should have the correct video blacklist unfederate attribute', async function () { + const res = await getSortedBlacklistedVideosList(servers[0].url, servers[0].accessToken, 'createdAt') + + const blacklistedVideos: VideoBlacklist[] = res.body.data + const video3Blacklisted = blacklistedVideos.find(b => b.video.uuid === video3UUID) + const video4Blacklisted = blacklistedVideos.find(b => b.video.uuid === video4UUID) + + expect(video3Blacklisted.unfederated).to.be.false + expect(video4Blacklisted.unfederated).to.be.true + }) + + it('Should remove the video from blacklist and refederate the video', async function () { + this.timeout(10000) + + await removeVideoFromBlacklist(servers[ 0 ].url, servers[ 0 ].accessToken, video4UUID) + + await waitJobs(servers) + + for (const server of servers) { + const res = await getVideosList(server.url) + expect(res.body.data.find(v => v.uuid === video4UUID)).to.not.be.undefined + } + }) - expect(res.body.total).to.equal(1) - expect(res.body.data).to.be.an('array') - expect(res.body.data.length).to.equal(1) }) after(async function () { diff --git a/shared/models/videos/blacklist/video-blacklist-create.model.ts b/shared/models/videos/blacklist/video-blacklist-create.model.ts index 89c69cb56..6e7d36421 100644 --- a/shared/models/videos/blacklist/video-blacklist-create.model.ts +++ b/shared/models/videos/blacklist/video-blacklist-create.model.ts @@ -1,3 +1,4 @@ export interface VideoBlacklistCreate { reason?: string + unfederate?: boolean } diff --git a/shared/models/videos/blacklist/video-blacklist.model.ts b/shared/models/videos/blacklist/video-blacklist.model.ts index ef4e5e3a2..4bd976190 100644 --- a/shared/models/videos/blacklist/video-blacklist.model.ts +++ b/shared/models/videos/blacklist/video-blacklist.model.ts @@ -2,6 +2,7 @@ export interface VideoBlacklist { id: number createdAt: Date updatedAt: Date + unfederated: boolean reason?: string video: { diff --git a/shared/utils/server/servers.ts b/shared/utils/server/servers.ts index 568385a41..1e9c83c72 100644 --- a/shared/utils/server/servers.ts +++ b/shared/utils/server/servers.ts @@ -145,8 +145,12 @@ function runServer (serverNumber: number, configOverride?: Object, args = []) { if (dontContinue === true) return server.app.stdout.removeListener('data', onStdout) + + process.on('exit', () => process.kill(server.app.pid)) + res(server) }) + }) } diff --git a/shared/utils/videos/video-blacklist.ts b/shared/utils/videos/video-blacklist.ts index 2c176fde0..f2ae0ed26 100644 --- a/shared/utils/videos/video-blacklist.ts +++ b/shared/utils/videos/video-blacklist.ts @@ -1,11 +1,18 @@ import * as request from 'supertest' -function addVideoToBlacklist (url: string, token: string, videoId: number | string, reason?: string, specialStatus = 204) { +function addVideoToBlacklist ( + url: string, + token: string, + videoId: number | string, + reason?: string, + unfederate?: boolean, + specialStatus = 204 +) { const path = '/api/v1/videos/' + videoId + '/blacklist' return request(url) .post(path) - .send({ reason }) + .send({ reason, unfederate }) .set('Accept', 'application/json') .set('Authorization', 'Bearer ' + token) .expect(specialStatus) -- 2.41.0