/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ import { expect } from 'chai' import { decode } from 'magnet-uri' import { expectStartWith } from '@server/tests/shared' import { getAllFiles, wait } from '@shared/core-utils' import { HttpStatusCode, LiveVideo, VideoDetails, VideoPrivacy } from '@shared/models' import { cleanupTests, createSingleServer, findExternalSavedVideo, makeRawRequest, parseTorrentVideo, PeerTubeServer, sendRTMPStream, setAccessTokensToServers, setDefaultVideoChannel, stopFfmpeg, waitJobs } from '@shared/server-commands' describe('Test video static file privacy', function () { let server: PeerTubeServer let userToken: string before(async function () { this.timeout(50000) server = await createSingleServer(1) await setAccessTokensToServers([ server ]) await setDefaultVideoChannel([ server ]) userToken = await server.users.generateUserAndToken('user1') }) describe('VOD static file path', function () { function runSuite () { async function checkPrivateWebTorrentFiles (uuid: string) { const video = await server.videos.getWithToken({ id: uuid }) for (const file of video.files) { expect(file.fileDownloadUrl).to.not.include('/private/') expectStartWith(file.fileUrl, server.url + '/static/webseed/private/') const torrent = await parseTorrentVideo(server, file) expect(torrent.urlList).to.have.lengthOf(0) const magnet = decode(file.magnetUri) expect(magnet.urlList).to.have.lengthOf(0) await makeRawRequest({ url: file.fileUrl, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 }) } const hls = video.streamingPlaylists[0] if (hls) { expectStartWith(hls.playlistUrl, server.url + '/static/streaming-playlists/hls/private/') expectStartWith(hls.segmentsSha256Url, server.url + '/static/streaming-playlists/hls/private/') await makeRawRequest({ url: hls.playlistUrl, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 }) await makeRawRequest({ url: hls.segmentsSha256Url, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 }) } } async function checkPublicWebTorrentFiles (uuid: string) { const video = await server.videos.get({ id: uuid }) for (const file of getAllFiles(video)) { expect(file.fileDownloadUrl).to.not.include('/private/') expect(file.fileUrl).to.not.include('/private/') const torrent = await parseTorrentVideo(server, file) expect(torrent.urlList[0]).to.not.include('private') const magnet = decode(file.magnetUri) expect(magnet.urlList[0]).to.not.include('private') await makeRawRequest({ url: file.fileUrl, expectedStatus: HttpStatusCode.OK_200 }) await makeRawRequest({ url: torrent.urlList[0], expectedStatus: HttpStatusCode.OK_200 }) await makeRawRequest({ url: magnet.urlList[0], expectedStatus: HttpStatusCode.OK_200 }) } const hls = video.streamingPlaylists[0] if (hls) { expect(hls.playlistUrl).to.not.include('private') expect(hls.segmentsSha256Url).to.not.include('private') await makeRawRequest({ url: hls.playlistUrl, expectedStatus: HttpStatusCode.OK_200 }) await makeRawRequest({ url: hls.segmentsSha256Url, expectedStatus: HttpStatusCode.OK_200 }) } } it('Should upload a private/internal video and have a private static path', async function () { this.timeout(120000) for (const privacy of [ VideoPrivacy.PRIVATE, VideoPrivacy.INTERNAL ]) { const { uuid } = await server.videos.quickUpload({ name: 'video', privacy }) await waitJobs([ server ]) await checkPrivateWebTorrentFiles(uuid) } }) it('Should upload a public video and update it as private/internal to have a private static path', async function () { this.timeout(120000) for (const privacy of [ VideoPrivacy.PRIVATE, VideoPrivacy.INTERNAL ]) { const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.PUBLIC }) await waitJobs([ server ]) await server.videos.update({ id: uuid, attributes: { privacy } }) await waitJobs([ server ]) await checkPrivateWebTorrentFiles(uuid) } }) it('Should upload a private video and update it to unlisted to have a public static path', async function () { this.timeout(120000) const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.PRIVATE }) await waitJobs([ server ]) await server.videos.update({ id: uuid, attributes: { privacy: VideoPrivacy.UNLISTED } }) await waitJobs([ server ]) await checkPublicWebTorrentFiles(uuid) }) it('Should upload an internal video and update it to public to have a public static path', async function () { this.timeout(120000) const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.INTERNAL }) await waitJobs([ server ]) await server.videos.update({ id: uuid, attributes: { privacy: VideoPrivacy.PUBLIC } }) await waitJobs([ server ]) await checkPublicWebTorrentFiles(uuid) }) it('Should upload an internal video and schedule a public publish', async function () { this.timeout(120000) const attributes = { name: 'video', privacy: VideoPrivacy.PRIVATE, scheduleUpdate: { updateAt: new Date(Date.now() + 1000).toISOString(), privacy: VideoPrivacy.PUBLIC as VideoPrivacy.PUBLIC } } const { uuid } = await server.videos.upload({ attributes }) await waitJobs([ server ]) await wait(1000) await server.debug.sendCommand({ body: { command: 'process-update-videos-scheduler' } }) await waitJobs([ server ]) await checkPublicWebTorrentFiles(uuid) }) } describe('Without transcoding', function () { runSuite() }) describe('With transcoding', function () { before(async function () { await server.config.enableMinimumTranscoding() }) runSuite() }) }) describe('VOD static file right check', function () { let unrelatedFileToken: string async function checkVideoFiles (options: { id: string expectedStatus: HttpStatusCode token: string videoFileToken: string }) { const { id, expectedStatus, token, videoFileToken } = options const video = await server.videos.getWithToken({ id }) for (const file of getAllFiles(video)) { await makeRawRequest({ url: file.fileUrl, token, expectedStatus }) await makeRawRequest({ url: file.fileDownloadUrl, token, expectedStatus }) await makeRawRequest({ url: file.fileUrl, query: { videoFileToken }, expectedStatus }) await makeRawRequest({ url: file.fileDownloadUrl, query: { videoFileToken }, expectedStatus }) } const hls = video.streamingPlaylists[0] await makeRawRequest({ url: hls.playlistUrl, token, expectedStatus }) await makeRawRequest({ url: hls.segmentsSha256Url, token, expectedStatus }) await makeRawRequest({ url: hls.playlistUrl, query: { videoFileToken }, expectedStatus }) await makeRawRequest({ url: hls.segmentsSha256Url, query: { videoFileToken }, expectedStatus }) } before(async function () { await server.config.enableMinimumTranscoding() const { uuid } = await server.videos.quickUpload({ name: 'another video' }) unrelatedFileToken = await server.videoToken.getVideoFileToken({ videoId: uuid }) }) it('Should not be able to access a private video files without OAuth token and file token', async function () { this.timeout(120000) const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.INTERNAL }) await waitJobs([ server ]) await checkVideoFiles({ id: uuid, expectedStatus: HttpStatusCode.FORBIDDEN_403, token: null, videoFileToken: null }) }) it('Should not be able to access an internal video files without appropriate OAuth token and file token', async function () { this.timeout(120000) const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.PRIVATE }) await waitJobs([ server ]) await checkVideoFiles({ id: uuid, expectedStatus: HttpStatusCode.FORBIDDEN_403, token: userToken, videoFileToken: unrelatedFileToken }) }) it('Should be able to access a private video files with appropriate OAuth token or file token', async function () { this.timeout(120000) const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.PRIVATE }) const videoFileToken = await server.videoToken.getVideoFileToken({ videoId: uuid }) await waitJobs([ server ]) await checkVideoFiles({ id: uuid, expectedStatus: HttpStatusCode.OK_200, token: server.accessToken, videoFileToken }) }) it('Should be able to access a private video of another user with an admin OAuth token or file token', async function () { this.timeout(120000) const { uuid } = await server.videos.quickUpload({ name: 'video', token: userToken, privacy: VideoPrivacy.PRIVATE }) const videoFileToken = await server.videoToken.getVideoFileToken({ videoId: uuid }) await waitJobs([ server ]) await checkVideoFiles({ id: uuid, expectedStatus: HttpStatusCode.OK_200, token: server.accessToken, videoFileToken }) }) }) describe('Live static file path and check', function () { let normalLiveId: string let normalLive: LiveVideo let permanentLiveId: string let permanentLive: LiveVideo let unrelatedFileToken: string async function checkLiveFiles (live: LiveVideo, liveId: string) { const ffmpegCommand = sendRTMPStream({ rtmpBaseUrl: live.rtmpUrl, streamKey: live.streamKey }) await server.live.waitUntilPublished({ videoId: liveId }) const video = await server.videos.getWithToken({ id: liveId }) const fileToken = await server.videoToken.getVideoFileToken({ videoId: video.uuid }) const hls = video.streamingPlaylists[0] for (const url of [ hls.playlistUrl, hls.segmentsSha256Url ]) { expectStartWith(url, server.url + '/static/streaming-playlists/hls/private/') await makeRawRequest({ url, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 }) await makeRawRequest({ url, query: { videoFileToken: fileToken }, expectedStatus: HttpStatusCode.OK_200 }) await makeRawRequest({ url, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) await makeRawRequest({ url, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) await makeRawRequest({ url, query: { videoFileToken: unrelatedFileToken }, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) } await stopFfmpeg(ffmpegCommand) } async function checkReplay (replay: VideoDetails) { const fileToken = await server.videoToken.getVideoFileToken({ videoId: replay.uuid }) const hls = replay.streamingPlaylists[0] expect(hls.files).to.not.have.lengthOf(0) for (const file of hls.files) { await makeRawRequest({ url: file.fileUrl, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 }) await makeRawRequest({ url: file.fileUrl, query: { videoFileToken: fileToken }, expectedStatus: HttpStatusCode.OK_200 }) await makeRawRequest({ url: file.fileUrl, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) await makeRawRequest({ url: file.fileUrl, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) await makeRawRequest({ url: file.fileUrl, query: { videoFileToken: unrelatedFileToken }, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) } for (const url of [ hls.playlistUrl, hls.segmentsSha256Url ]) { expectStartWith(url, server.url + '/static/streaming-playlists/hls/private/') await makeRawRequest({ url, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 }) await makeRawRequest({ url, query: { videoFileToken: fileToken }, expectedStatus: HttpStatusCode.OK_200 }) await makeRawRequest({ url, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) await makeRawRequest({ url, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) await makeRawRequest({ url, query: { videoFileToken: unrelatedFileToken }, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) } } before(async function () { await server.config.enableMinimumTranscoding() const { uuid } = await server.videos.quickUpload({ name: 'another video' }) unrelatedFileToken = await server.videoToken.getVideoFileToken({ videoId: uuid }) await server.config.enableLive({ allowReplay: true, transcoding: true, resolutions: 'min' }) { const { video, live } = await server.live.quickCreate({ saveReplay: true, permanentLive: false, privacy: VideoPrivacy.PRIVATE }) normalLiveId = video.uuid normalLive = live } { const { video, live } = await server.live.quickCreate({ saveReplay: true, permanentLive: true, privacy: VideoPrivacy.PRIVATE }) permanentLiveId = video.uuid permanentLive = live } }) it('Should create a private normal live and have a private static path', async function () { this.timeout(240000) await checkLiveFiles(normalLive, normalLiveId) }) it('Should create a private permanent live and have a private static path', async function () { this.timeout(240000) await checkLiveFiles(permanentLive, permanentLiveId) }) it('Should have created a replay of the normal live with a private static path', async function () { this.timeout(240000) await server.live.waitUntilReplacedByReplay({ videoId: normalLiveId }) const replay = await server.videos.getWithToken({ id: normalLiveId }) await checkReplay(replay) }) it('Should have created a replay of the permanent live with a private static path', async function () { this.timeout(240000) await server.live.waitUntilWaiting({ videoId: permanentLiveId }) await waitJobs([ server ]) const live = await server.videos.getWithToken({ id: permanentLiveId }) const replayFromList = await findExternalSavedVideo(server, live) const replay = await server.videos.getWithToken({ id: replayFromList.id }) await checkReplay(replay) }) }) after(async function () { await cleanupTests([ server ]) }) })