From 11474c3cd904fa0fc07fc0a3a9a35496da17f300 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 31 Oct 2017 15:20:35 +0100 Subject: [PATCH] Add tests and fix bugs for video privacy --- server/controllers/api/users.ts | 2 +- server/middlewares/validators/videos.ts | 63 +-- server/tests/api/check-params/videos.ts | 537 ++++++++---------------- server/tests/api/index.ts | 1 + server/tests/api/single-pod.ts | 23 +- server/tests/api/users.ts | 15 +- server/tests/api/video-privacy.ts | 158 +++++++ server/tests/utils/videos.ts | 45 +- 8 files changed, 440 insertions(+), 404 deletions(-) create mode 100644 server/tests/api/video-privacy.ts diff --git a/server/controllers/api/users.ts b/server/controllers/api/users.ts index dcd407fdf..bacfc4552 100644 --- a/server/controllers/api/users.ts +++ b/server/controllers/api/users.ts @@ -171,7 +171,7 @@ async function getUserInformation (req: express.Request, res: express.Response, } function getUser (req: express.Request, res: express.Response, next: express.NextFunction) { - return res.json(res.locals.oauth.token.User.toFormattedJSON()) + return res.json(res.locals.user.toFormattedJSON()) } async function getUserVideoRating (req: express.Request, res: express.Response, next: express.NextFunction) { diff --git a/server/middlewares/validators/videos.ts b/server/middlewares/validators/videos.ts index e197d4606..15b4629c1 100644 --- a/server/middlewares/validators/videos.ts +++ b/server/middlewares/validators/videos.ts @@ -24,6 +24,7 @@ import { isVideoPrivacyValid } from '../../helpers' import { UserRight, VideoPrivacy } from '../../../shared' +import { authenticate } from '../oauth' const videosAddValidator = [ body('videofile').custom((value, { req }) => isVideoFile(req.files)).withMessage( @@ -112,7 +113,7 @@ const videosUpdateValidator = [ body('licence').optional().custom(isVideoLicenceValid).withMessage('Should have a valid licence'), body('language').optional().custom(isVideoLanguageValid).withMessage('Should have a valid language'), body('nsfw').optional().custom(isVideoNSFWValid).withMessage('Should have a valid NSFW attribute'), - body('privacy').custom(isVideoPrivacyValid).withMessage('Should have correct video privacy'), + body('privacy').optional().custom(isVideoPrivacyValid).withMessage('Should have correct video privacy'), body('description').optional().custom(isVideoDescriptionValid).withMessage('Should have a valid description'), body('tags').optional().custom(isVideoTagsValid).withMessage('Should have correct tags'), @@ -155,7 +156,22 @@ const videosGetValidator = [ logger.debug('Checking videosGet parameters', { parameters: req.params }) checkErrors(req, res, () => { - checkVideoExists(req.params.id, res, next) + checkVideoExists(req.params.id, res, () => { + const video = res.locals.video + + // Video is not private, anyone can access it + if (video.privacy !== VideoPrivacy.PRIVATE) return next() + + authenticate(req, res, () => { + if (video.VideoChannel.Author.userId !== res.locals.oauth.token.User.id) { + return res.status(403) + .json({ error: 'Cannot get this private video of another user' }) + .end() + } + + next() + }) + }) }) } ] @@ -232,28 +248,23 @@ export { function checkUserCanDeleteVideo (userId: number, res: express.Response, callback: () => void) { // Retrieve the user who did the request - db.User.loadById(userId) - .then(user => { - if (res.locals.video.isOwned() === false) { - return res.status(403) - .json({ error: 'Cannot remove video of another pod, blacklist it' }) - .end() - } - - // Check if the user can delete the video - // The user can delete it if s/he is an admin - // Or if s/he is the video's author - if (user.hasRight(UserRight.REMOVE_ANY_VIDEO) === false && res.locals.video.Author.userId !== res.locals.oauth.token.User.id) { - return res.status(403) - .json({ error: 'Cannot remove video of another user' }) - .end() - } - - // If we reach this comment, we can delete the video - callback() - }) - .catch(err => { - logger.error('Error in video request validator.', err) - return res.sendStatus(500) - }) + if (res.locals.video.isOwned() === false) { + return res.status(403) + .json({ error: 'Cannot remove video of another pod, blacklist it' }) + .end() + } + + // Check if the user can delete the video + // The user can delete it if s/he is an admin + // Or if s/he is the video's author + const author = res.locals.video.VideoChannel.Author + const user = res.locals.oauth.token.User + if (user.hasRight(UserRight.REMOVE_ANY_VIDEO) === false && author.userId !== user.id) { + return res.status(403) + .json({ error: 'Cannot remove video of another user' }) + .end() + } + + // If we reach this comment, we can delete the video + callback() } diff --git a/server/tests/api/check-params/videos.ts b/server/tests/api/check-params/videos.ts index c59f5da93..5860e9195 100644 --- a/server/tests/api/check-params/videos.ts +++ b/server/tests/api/check-params/videos.ts @@ -19,12 +19,46 @@ import { createUser, getUserAccessToken } from '../../utils' +import { VideoPrivacy } from '../../../../shared/models/videos/video-privacy.enum' describe('Test videos API validator', function () { const path = '/api/v1/videos/' let server: ServerInfo let channelId: number + function getCompleteVideoUploadAttributes () { + return { + name: 'my super name', + category: 5, + licence: 1, + language: 6, + nsfw: false, + description: 'my super description', + tags: [ 'tag1', 'tag2' ], + privacy: VideoPrivacy.PUBLIC, + channelId + } + } + + function getCompleteVideoUpdateAttributes () { + return { + name: 'my super name', + category: 5, + licence: 2, + language: 6, + nsfw: false, + description: 'my super description', + privacy: VideoPrivacy.PUBLIC, + tags: [ 'tag1', 'tag2' ] + } + } + + function getVideoUploadAttaches () { + return { + 'videofile': join(__dirname, '..', 'fixtures', 'video_short.webm') + } + } + // --------------------------------------------------------------- before(async function () { @@ -99,6 +133,37 @@ describe('Test videos API validator', function () { }) }) + describe('When listing my videos', function () { + const path = '/api/v1/users/me/videos' + + it('Should fail with a bad start pagination', async function () { + await request(server.url) + .get(path) + .set('Authorization', 'Bearer ' + server.accessToken) + .query({ start: 'hello' }) + .set('Accept', 'application/json') + .expect(400) + }) + + it('Should fail with a bad count pagination', async function () { + await request(server.url) + .get(path) + .set('Authorization', 'Bearer ' + server.accessToken) + .query({ count: 'hello' }) + .set('Accept', 'application/json') + .expect(400) + }) + + it('Should fail with an incorrect sort', async function () { + await request(server.url) + .get(path) + .set('Authorization', 'Bearer ' + server.accessToken) + .query({ sort: 'hello' }) + .set('Accept', 'application/json') + .expect(400) + }) + }) + describe('When adding a video', function () { it('Should fail with nothing', async function () { const fields = {} @@ -107,219 +172,108 @@ describe('Test videos API validator', function () { }) it('Should fail without name', async function () { - const fields = { - category: 5, - licence: 1, - language: 6, - nsfw: false, - description: 'my super description', - tags: [ 'tag1', 'tag2' ], - channelId - } - const attaches = { - 'videofile': join(__dirname, '..', 'fixtures', 'video_short.webm') - } + const fields = getCompleteVideoUploadAttributes() + delete fields.name + + const attaches = getVideoUploadAttaches() await makePostUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches }) }) it('Should fail with a long name', async function () { - const fields = { - name: 'My very very very very very very very very very very very very very very very very very ' + - 'very very very very very very very very very very very very very very very very long long' + - 'very very very very very very very very very very very very very very very very long name', - category: 5, - licence: 1, - language: 6, - nsfw: false, - description: 'my super description', - tags: [ 'tag1', 'tag2' ], - channelId - } - const attaches = { - 'videofile': join(__dirname, '..', 'fixtures', 'video_short.webm') - } + const fields = getCompleteVideoUploadAttributes() + fields.name = 'My very very very very very very very very very very very very very very very very very ' + + 'very very very very very very very very very very very very very very very very long long' + + 'very very very very very very very very very very very very very very very very long name' + + const attaches = getVideoUploadAttaches await makePostUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches }) }) it('Should fail without a category', async function () { - const fields = { - name: 'my super name', - licence: 1, - language: 6, - nsfw: false, - description: 'my super description', - tags: [ 'tag1', 'tag2' ], - channelId - } - const attaches = { - 'videofile': join(__dirname, '..', 'fixtures', 'video_short.webm') - } + const fields = getCompleteVideoUploadAttributes() + delete fields.category + + const attaches = getVideoUploadAttaches await makePostUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches }) }) it('Should fail with a bad category', async function () { - const fields = { - name: 'my super name', - category: 125, - licence: 1, - language: 6, - nsfw: false, - description: 'my super description', - tags: [ 'tag1', 'tag2' ], - channelId - } - const attaches = { - 'videofile': join(__dirname, '..', 'fixtures', 'video_short.webm') - } + const fields = getCompleteVideoUploadAttributes() + fields.category = 125 + + const attaches = getVideoUploadAttaches await makePostUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches }) }) it('Should fail without a licence', async function () { - const fields = { - name: 'my super name', - category: 5, - language: 6, - nsfw: false, - description: 'my super description', - tags: [ 'tag1', 'tag2' ], - channelId - } - const attaches = { - 'videofile': join(__dirname, '..', 'fixtures', 'video_short.webm') - } + const fields = getCompleteVideoUploadAttributes() + delete fields.licence + + const attaches = getVideoUploadAttaches() await makePostUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches }) }) it('Should fail with a bad licence', async function () { - const fields = { - name: 'my super name', - category: 5, - licence: 125, - language: 6, - nsfw: false, - description: 'my super description', - tags: [ 'tag1', 'tag2' ], - channelId - } - const attaches = { - 'videofile': join(__dirname, '..', 'fixtures', 'video_short.webm') - } + const fields = getCompleteVideoUploadAttributes() + fields.licence = 125 + + const attaches = getVideoUploadAttaches() await makePostUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches }) }) it('Should fail with a bad language', async function () { - const fields = { - name: 'my super name', - category: 5, - licence: 4, - language: 563, - nsfw: false, - description: 'my super description', - tags: [ 'tag1', 'tag2' ], - channelId - } - const attaches = { - 'videofile': join(__dirname, '..', 'fixtures', 'video_short.webm') - } + const fields = getCompleteVideoUploadAttributes() + fields.language = 563 + + const attaches = getVideoUploadAttaches() await makePostUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches }) }) it('Should fail without nsfw attribute', async function () { - const fields = { - name: 'my super name', - category: 5, - licence: 4, - language: 6, - description: 'my super description', - tags: [ 'tag1', 'tag2' ], - channelId - } - const attaches = { - 'videofile': join(__dirname, '..', 'fixtures', 'video_short.webm') - } + const fields = getCompleteVideoUploadAttributes() + delete fields.nsfw + + const attaches = getVideoUploadAttaches() await makePostUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches }) }) it('Should fail with a bad nsfw attribute', async function () { - const fields = { - name: 'my super name', - category: 5, - licence: 4, - language: 6, - nsfw: 2, - description: 'my super description', - tags: [ 'tag1', 'tag2' ], - channelId - } - const attaches = { - 'videofile': join(__dirname, '..', 'fixtures', 'video_short.webm') - } + const fields = getCompleteVideoUploadAttributes() + fields.nsfw = 2 as any + + const attaches = getVideoUploadAttaches() await makePostUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches }) }) it('Should fail without description', async function () { - const fields = { - name: 'my super name', - category: 5, - licence: 1, - language: 6, - nsfw: false, - tags: [ 'tag1', 'tag2' ], - channelId - } - const attaches = { - 'videofile': join(__dirname, '..', 'fixtures', 'video_short.webm') - } + const fields = getCompleteVideoUploadAttributes() + delete fields.description + + const attaches = getVideoUploadAttaches() await makePostUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches }) }) it('Should fail with a long description', async function () { - const fields = { - name: 'my super name', - category: 5, - licence: 1, - language: 6, - nsfw: false, - description: 'my super description which is very very very very very very very very very very very very very very long'.repeat(35), - tags: [ 'tag1', 'tag2' ], - channelId - } - const attaches = { - 'videofile': join(__dirname, '..', 'fixtures', 'video_short.webm') - } + const fields = getCompleteVideoUploadAttributes() + fields.description = 'my super description which is very very very very very very very very very very very very long'.repeat(35) + + const attaches = getVideoUploadAttaches() await makePostUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches }) }) it('Should fail without a channel', async function () { - const fields = { - name: 'my super name', - category: 5, - licence: 1, - language: 6, - nsfw: false, - description: 'my super description', - tags: [ 'tag1', 'tag2' ] - } - const attaches = { - 'videofile': join(__dirname, '..', 'fixtures', 'video_short.webm') - } + const fields = getCompleteVideoUploadAttributes() + delete fields.channelId + + const attaches = getVideoUploadAttaches() await makePostUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches }) }) it('Should fail with a bad channel', async function () { - const fields = { - name: 'my super name', - category: 5, - licence: 1, - language: 6, - nsfw: false, - description: 'my super description', - tags: [ 'tag1', 'tag2' ], - channelId: 545454 - } - const attaches = { - 'videofile': join(__dirname, '..', 'fixtures', 'video_short.webm') - } + const fields = getCompleteVideoUploadAttributes() + fields.channelId = 545454 + + const attaches = getVideoUploadAttaches() await makePostUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches }) }) @@ -332,101 +286,47 @@ describe('Test videos API validator', function () { const accessTokenUser = await getUserAccessToken(server, user) const res = await getMyUserInformation(server.url, accessTokenUser) - const channelId = res.body.videoChannels[0].id + const customChannelId = res.body.videoChannels[0].id - const fields = { - name: 'my super name', - category: 5, - licence: 1, - language: 6, - nsfw: false, - description: 'my super description', - tags: [ 'tag1', 'tag2' ], - channelId - } - const attaches = { - 'videofile': join(__dirname, '..', 'fixtures', 'video_short.webm') - } + const fields = getCompleteVideoUploadAttributes() + fields.channelId = customChannelId + + const attaches = getVideoUploadAttaches() await makePostUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches }) }) it('Should fail with too many tags', async function () { - const fields = { - name: 'my super name', - category: 5, - licence: 1, - language: 6, - nsfw: false, - description: 'my super description', - tags: [ 'tag1', 'tag2', 'tag3', 'tag4', 'tag5', 'tag6' ], - channelId - } - const attaches = { - 'videofile': join(__dirname, '..', 'fixtures', 'video_short.webm') - } + const fields = getCompleteVideoUploadAttributes() + fields.tags = [ 'tag1', 'tag2', 'tag3', 'tag4', 'tag5', 'tag6' ] + + const attaches = getVideoUploadAttaches() await makePostUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches }) }) it('Should fail with a tag length too low', async function () { - const fields = { - name: 'my super name', - category: 5, - licence: 1, - language: 6, - nsfw: false, - description: 'my super description', - tags: [ 'tag1', 't' ], - channelId - } - const attaches = { - 'videofile': join(__dirname, '..', 'fixtures', 'video_short.webm') - } + const fields = getCompleteVideoUploadAttributes() + fields.tags = [ 'tag1', 't' ] + + const attaches = getVideoUploadAttaches() await makePostUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches }) }) it('Should fail with a tag length too big', async function () { - const fields = { - name: 'my super name', - category: 5, - licence: 1, - language: 6, - nsfw: false, - description: 'my super description', - tags: [ 'my_super_tag_too_long_long_long_long_long_long', 'tag1' ], - channelId - } - const attaches = { - 'videofile': join(__dirname, '..', 'fixtures', 'video_short.webm') - } + const fields = getCompleteVideoUploadAttributes() + fields.tags = [ 'my_super_tag_too_long_long_long_long_long_long', 'tag1' ] + + const attaches = getVideoUploadAttaches() await makePostUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches }) }) it('Should fail without an input file', async function () { - const fields = { - name: 'my super name', - category: 5, - licence: 1, - language: 6, - nsfw: false, - description: 'my super description', - tags: [ 'tag1', 'tag2' ], - channelId - } + const fields = getCompleteVideoUploadAttributes() const attaches = {} await makePostUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches }) }) it('Should fail without an incorrect input file', async function () { - const fields = { - name: 'my super name', - category: 5, - licence: 1, - language: 6, - nsfw: false, - description: 'my super description', - tags: [ 'tag1', 'tag2' ], - channelId - } + const fields = getCompleteVideoUploadAttributes() const attaches = { 'videofile': join(__dirname, '..', 'fixtures', 'video_short_fake.webm') } @@ -434,16 +334,7 @@ describe('Test videos API validator', function () { }) it('Should fail with a too big duration', async function () { - const fields = { - name: 'my super name', - category: 5, - licence: 1, - language: 6, - nsfw: false, - description: 'my super description', - tags: [ 'tag1', 'tag2' ], - channelId - } + const fields = getCompleteVideoUploadAttributes() const attaches = { 'videofile': join(__dirname, '..', 'fixtures', 'video_too_long.webm') } @@ -453,19 +344,8 @@ describe('Test videos API validator', function () { it('Should succeed with the correct parameters', async function () { this.timeout(10000) - const fields = { - name: 'my super name', - category: 5, - licence: 1, - language: 6, - nsfw: false, - description: 'my super description', - tags: [ 'tag1', 'tag2' ], - channelId - } - const attaches = { - 'videofile': join(__dirname, '..', 'fixtures', 'video_short.webm') - } + const fields = getCompleteVideoUploadAttributes() + const attaches = getVideoUploadAttaches() await makePostUploadRequest({ url: server.url, @@ -512,26 +392,13 @@ describe('Test videos API validator', function () { }) it('Should fail without a valid uuid', async function () { - const fields = { - category: 5, - licence: 2, - language: 6, - nsfw: false, - description: 'my super description', - tags: [ 'tag1', 'tag2' ] - } + const fields = getCompleteVideoUpdateAttributes() await makePutBodyRequest({ url: server.url, path: path + 'blabla', token: server.accessToken, fields }) }) it('Should fail with an unknown id', async function () { - const fields = { - category: 5, - licence: 2, - language: 6, - nsfw: false, - description: 'my super description', - tags: [ 'tag1', 'tag2' ] - } + const fields = getCompleteVideoUpdateAttributes() + await makePutBodyRequest({ url: server.url, path: path + '4da6fde3-88f7-4d16-b119-108df5630b06', @@ -542,127 +409,77 @@ describe('Test videos API validator', function () { }) it('Should fail with a long name', async function () { - const fields = { - name: 'My very very very very very very very very very very very very very very very very very ' + - 'very very very very very very very very very very very very very very very very long long' + - 'very very very very very very very very very very very very very very very very long name', - category: 5, - licence: 2, - language: 6, - nsfw: false, - description: 'my super description', - tags: [ 'tag1', 'tag2' ] - } + const fields = getCompleteVideoUpdateAttributes() + fields.name = 'My very very very very very very very very very very very very very very very very long'.repeat(3) + await makePutBodyRequest({ url: server.url, path: path + videoId, token: server.accessToken, fields }) }) it('Should fail with a bad category', async function () { - const fields = { - name: 'my super name', - category: 128, - licence: 2, - language: 6, - nsfw: false, - description: 'my super description', - tags: [ 'tag1', 'tag2' ] - } + const fields = getCompleteVideoUpdateAttributes() + fields.category = 128 + await makePutBodyRequest({ url: server.url, path: path + videoId, token: server.accessToken, fields }) }) it('Should fail with a bad licence', async function () { - const fields = { - name: 'my super name', - category: 5, - licence: 128, - language: 6, - nsfw: false, - description: 'my super description', - tags: [ 'tag1', 'tag2' ] - } + const fields = getCompleteVideoUpdateAttributes() + fields.licence = 128 + await makePutBodyRequest({ url: server.url, path: path + videoId, token: server.accessToken, fields }) }) it('Should fail with a bad language', async function () { - const fields = { - name: 'my super name', - category: 5, - licence: 3, - language: 896, - nsfw: false, - description: 'my super description', - tags: [ 'tag1', 'tag2' ] - } + const fields = getCompleteVideoUpdateAttributes() + fields.language = 896 + await makePutBodyRequest({ url: server.url, path: path + videoId, token: server.accessToken, fields }) }) it('Should fail with a bad nsfw attribute', async function () { - const fields = { - name: 'my super name', - category: 5, - licence: 5, - language: 6, - nsfw: -4, - description: 'my super description', - tags: [ 'tag1', 'tag2' ] - } + const fields = getCompleteVideoUpdateAttributes() + fields.nsfw = (-4 as any) + await makePutBodyRequest({ url: server.url, path: path + videoId, token: server.accessToken, fields }) }) it('Should fail with a long description', async function () { - const fields = { - name: 'my super name', - category: 5, - licence: 2, - language: 6, - nsfw: false, - description: 'my super description which is very very very very very very very very very very very very very long'.repeat(35), - tags: [ 'tag1', 'tag2' ] - } + const fields = getCompleteVideoUpdateAttributes() + fields.description = 'my super description which is very very very very very very very very very very very very very long'.repeat(35) + await makePutBodyRequest({ url: server.url, path: path + videoId, token: server.accessToken, fields }) }) it('Should fail with too many tags', async function () { - const fields = { - name: 'my super name', - category: 5, - licence: 2, - language: 6, - nsfw: false, - description: 'my super description', - tags: [ 'tag1', 'tag2', 'tag3', 'tag4', 'tag5', 'tag6' ] - } + const fields = getCompleteVideoUpdateAttributes() + fields.tags = [ 'tag1', 'tag2', 'tag3', 'tag4', 'tag5', 'tag6' ] + await makePutBodyRequest({ url: server.url, path: path + videoId, token: server.accessToken, fields }) }) it('Should fail with a tag length too low', async function () { - const fields = { - name: 'my super name', - category: 5, - licence: 2, - language: 6, - nsfw: false, - description: 'my super description', - tags: [ 'tag1', 't' ] - } + const fields = getCompleteVideoUpdateAttributes() + fields.tags = [ 'tag1', 't' ] + await makePutBodyRequest({ url: server.url, path: path + videoId, token: server.accessToken, fields }) }) it('Should fail with a tag length too big', async function () { - const fields = { - name: 'my super name', - category: 5, - licence: 2, - language: 6, - nsfw: false, - description: 'my super description', - tags: [ 'my_super_tag_too_long_long_long_long', 'tag1' ] - } + const fields = getCompleteVideoUpdateAttributes() + fields.tags = [ 'my_super_tag_too_long_long_long_long', 'tag1' ] + await makePutBodyRequest({ url: server.url, path: path + videoId, token: server.accessToken, fields }) }) it('Should fail with a video of another user') it('Should fail with a video of another pod') + + it('Should succeed with the correct parameters', async function () { + const fields = getCompleteVideoUpdateAttributes() + + await makePutBodyRequest({ url: server.url, path: path + videoId, token: server.accessToken, fields, statusCodeExpected: 204 }) + }) }) describe('When getting a video', function () { diff --git a/server/tests/api/index.ts b/server/tests/api/index.ts index 2ff0ecf24..371f1bc43 100644 --- a/server/tests/api/index.ts +++ b/server/tests/api/index.ts @@ -8,6 +8,7 @@ import './video-abuse' import './video-blacklist' import './video-blacklist-management' import './video-description' +import './video-privacy' import './multiple-pods' import './services' import './request-schedulers' diff --git a/server/tests/api/single-pod.ts b/server/tests/api/single-pod.ts index d3a982436..3a05d0727 100644 --- a/server/tests/api/single-pod.ts +++ b/server/tests/api/single-pod.ts @@ -22,6 +22,7 @@ import { getVideoCategories, getVideoLicences, getVideoLanguages, + getVideoPrivacies, testVideoImage, webtorrentAdd, getVideo, @@ -76,6 +77,15 @@ describe('Test a single pod', function () { expect(languages[3]).to.equal('Mandarin') }) + it('Should list video privacies', async function () { + const res = await getVideoPrivacies(server.url) + + const privacies = res.body + expect(Object.keys(privacies)).to.have.length.at.least(3) + + expect(privacies[3]).to.equal('Private') + }) + it('Should not have videos', async function () { const res = await getVideosList(server.url) @@ -482,19 +492,6 @@ describe('Test a single pod', function () { // }) // }) - it('Should search the right magnetUri video', async function () { - const video = videosListBase[0] - const res = await getVideo(server.url, video.id) - const videoDetails = res.body - - const res2 = await searchVideoWithPagination(server.url, encodeURIComponent(videoDetails.files[0].magnetUri), 'magnetUri', 0, 15) - - const videos = res2.body.data - expect(res2.body.total).to.equal(1) - expect(videos.length).to.equal(1) - expect(videos[0].name).to.equal(video.name) - }) - it('Should list and sort by name in descending order', async function () { const res = await getVideosListSort(server.url, '-name') diff --git a/server/tests/api/users.ts b/server/tests/api/users.ts index bdef62c46..6f40ca3c9 100644 --- a/server/tests/api/users.ts +++ b/server/tests/api/users.ts @@ -31,6 +31,7 @@ import { getBlacklistedVideosList } from '../utils' import { UserRole } from '../../../shared' +import { getMyVideos } from '../utils/videos' describe('Test users', function () { let server: ServerInfo @@ -197,10 +198,22 @@ describe('Test users', function () { it('Should be able to upload a video with this user', async function () { this.timeout(5000) - const videoAttributes = {} + const videoAttributes = { + name: 'super user video' + } await uploadVideo(server.url, accessTokenUser, videoAttributes) }) + it('Should be able to list my videos', async function () { + const res = await getMyVideos(server.url, accessTokenUser, 0, 5) + expect(res.body.total).to.equal(1) + + const videos = res.body.data + expect(videos).to.have.lengthOf(1) + + expect(videos[0].name).to.equal('super user video') + }) + it('Should list all the users', async function () { const res = await getUsersList(server.url) const result = res.body diff --git a/server/tests/api/video-privacy.ts b/server/tests/api/video-privacy.ts new file mode 100644 index 000000000..beac1613e --- /dev/null +++ b/server/tests/api/video-privacy.ts @@ -0,0 +1,158 @@ +/* tslint:disable:no-unused-expression */ + +import 'mocha' +import * as chai from 'chai' +const expect = chai.expect + +import { + ServerInfo, + flushTests, + uploadVideo, + makeFriends, + getVideosList, + wait, + setAccessTokensToServers, + flushAndRunMultipleServers, + killallServers +} from '../utils' +import { VideoPrivacy } from '../../../shared/models/videos/video-privacy.enum' +import { getMyVideos, getVideo, getVideoWithToken, updateVideo } from '../utils/videos' +import { createUser } from '../utils/users' +import { getUserAccessToken } from '../utils/login' + +describe('Test video privacy', function () { + let servers: ServerInfo[] = [] + let privateVideoId + let privateVideoUUID + let unlistedVideoUUID + + before(async function () { + this.timeout(120000) + + // Run servers + servers = await flushAndRunMultipleServers(2) + + // Get the access tokens + await setAccessTokensToServers(servers) + + // Pod 1 makes friend with pod 2 + await makeFriends(servers[0].url, servers[0].accessToken) + }) + + it('Should upload a private video on pod 1', async function () { + this.timeout(15000) + + const attributes = { + privacy: VideoPrivacy.PRIVATE + } + await uploadVideo(servers[0].url, servers[0].accessToken, attributes) + + await wait(11000) + }) + + it('Should not have this private video on pod 2', async function () { + const res = await getVideosList(servers[1].url) + + expect(res.body.total).to.equal(0) + expect(res.body.data).to.have.lengthOf(0) + }) + + it('Should list my (private) videos', async function () { + const res = await getMyVideos(servers[0].url, servers[0].accessToken, 0, 1) + + expect(res.body.total).to.equal(1) + expect(res.body.data).to.have.lengthOf(1) + + privateVideoId = res.body.data[0].id + privateVideoUUID = res.body.data[0].uuid + }) + + it('Should not be able to watch this video with non authenticated user', async function () { + await getVideo(servers[0].url, privateVideoUUID, 401) + }) + + it('Should not be able to watch this private video with another user', async function () { + const user = { + username: 'hello', + password: 'super password' + } + await createUser(servers[0].url, servers[0].accessToken, user.username, user.password) + + const token = await getUserAccessToken(servers[0], user) + await getVideoWithToken(servers[0].url, token, privateVideoUUID, 403) + }) + + it('Should be able to watch this video with the correct user', async function () { + await getVideoWithToken(servers[0].url, servers[0].accessToken, privateVideoUUID) + }) + + it('Should upload a unlisted video on pod 2', async function () { + this.timeout(30000) + + const attributes = { + name: 'unlisted video', + privacy: VideoPrivacy.UNLISTED + } + await uploadVideo(servers[1].url, servers[1].accessToken, attributes) + + await wait(22000) + }) + + it('Should not have this unlisted video listed on pod 1 and 2', async function () { + for (const server of servers) { + const res = await getVideosList(server.url) + + expect(res.body.total).to.equal(0) + expect(res.body.data).to.have.lengthOf(0) + } + }) + + it('Should list my (unlisted) videos', async function () { + const res = await getMyVideos(servers[1].url, servers[1].accessToken, 0, 1) + + expect(res.body.total).to.equal(1) + expect(res.body.data).to.have.lengthOf(1) + + unlistedVideoUUID = res.body.data[0].uuid + }) + + it('Should be able to get this unlisted video', async function () { + for (const server of servers) { + const res = await getVideo(server.url, unlistedVideoUUID) + + expect(res.body.name).to.equal('unlisted video') + } + }) + + it('Should update the private video to public on pod 1', async function () { + this.timeout(15000) + + const attribute = { + name: 'super video public', + privacy: VideoPrivacy.PUBLIC + } + + await updateVideo(servers[0].url, servers[0].accessToken, privateVideoId, attribute) + + await wait(11000) + }) + + it('Should not have this new unlisted video listed on pod 1 and 2', async function () { + for (const server of servers) { + const res = await getVideosList(server.url) + + expect(res.body.total).to.equal(1) + expect(res.body.data).to.have.lengthOf(1) + expect(res.body.data[0].name).to.equal('super video public') + } + }) + + after(async function () { + killallServers(servers) + + // Keep the logs if the test failed + if (this['ok']) { + await flushTests() + } + }) +}) diff --git a/server/tests/utils/videos.ts b/server/tests/utils/videos.ts index 2a5d00255..d4d5faf0a 100644 --- a/server/tests/utils/videos.ts +++ b/server/tests/utils/videos.ts @@ -7,6 +7,7 @@ import { makeGetRequest } from './requests' import { readFilePromise } from './miscs' import { ServerInfo } from './servers' import { getMyUserInformation } from './users' +import { VideoPrivacy } from '../../../shared' type VideoAttributes = { name?: string @@ -17,6 +18,7 @@ type VideoAttributes = { description?: string tags?: string[] channelId?: number + privacy?: VideoPrivacy fixture?: string } @@ -38,6 +40,12 @@ function getVideoLanguages (url: string) { return makeGetRequest(url, path) } +function getVideoPrivacies (url: string) { + const path = '/api/v1/videos/privacies' + + return makeGetRequest(url, path) +} + function getAllVideosListBy (url: string) { const path = '/api/v1/videos' @@ -51,14 +59,23 @@ function getAllVideosListBy (url: string) { .expect('Content-Type', /json/) } -function getVideo (url: string, id: number | string) { +function getVideo (url: string, id: number | string, expectedStatus = 200) { const path = '/api/v1/videos/' + id return request(url) .get(path) .set('Accept', 'application/json') - .expect(200) - .expect('Content-Type', /json/) + .expect(expectedStatus) +} + +function getVideoWithToken (url: string, token: string, id: number | string, expectedStatus = 200) { + const path = '/api/v1/videos/' + id + + return request(url) + .get(path) + .set('Authorization', 'Bearer ' + token) + .set('Accept', 'application/json') + .expect(expectedStatus) } function getVideoDescription (url: string, descriptionPath: string) { @@ -80,6 +97,22 @@ function getVideosList (url: string) { .expect('Content-Type', /json/) } +function getMyVideos (url: string, accessToken: string, start: number, count: number, sort?: string) { + const path = '/api/v1/users/me/videos' + + const req = request(url) + .get(path) + .query({ start: start }) + .query({ count: count }) + + if (sort) req.query({ sort }) + + return req.set('Accept', 'application/json') + .set('Authorization', 'Bearer ' + accessToken) + .expect(200) + .expect('Content-Type', /json/) +} + function getVideosListPagination (url: string, start: number, count: number, sort?: string) { const path = '/api/v1/videos' @@ -191,6 +224,7 @@ async function uploadVideo (url: string, accessToken: string, videoAttributesArg nsfw: true, description: 'my super description', tags: [ 'tag' ], + privacy: VideoPrivacy.PUBLIC, fixture: 'video_short.webm' } attributes = Object.assign(attributes, videoAttributesArg) @@ -204,6 +238,7 @@ async function uploadVideo (url: string, accessToken: string, videoAttributesArg .field('licence', attributes.licence.toString()) .field('nsfw', JSON.stringify(attributes.nsfw)) .field('description', attributes.description) + .field('privacy', attributes.privacy.toString()) .field('channelId', attributes.channelId) if (attributes.language !== undefined) { @@ -236,6 +271,7 @@ function updateVideo (url: string, accessToken: string, id: number, attributes: if (attributes.nsfw) body['nsfw'] = attributes.nsfw if (attributes.description) body['description'] = attributes.description if (attributes.tags) body['tags'] = attributes.tags + if (attributes.privacy) body['privacy'] = attributes.privacy return request(url) .put(path) @@ -274,9 +310,12 @@ export { getVideoDescription, getVideoCategories, getVideoLicences, + getVideoPrivacies, getVideoLanguages, getAllVideosListBy, + getMyVideos, getVideo, + getVideoWithToken, getVideosList, getVideosListPagination, getVideosListSort, -- 2.41.0