1 /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
3 import { expect } from 'chai'
4 import { decode } from 'magnet-uri'
5 import { checkVideoFileTokenReinjection, expectStartWith, parseTorrentVideo } from '@server/tests/shared'
6 import { getAllFiles, wait } from '@shared/core-utils'
7 import { HttpStatusCode, LiveVideo, VideoDetails, VideoPrivacy } from '@shared/models'
11 findExternalSavedVideo,
15 setAccessTokensToServers,
16 setDefaultVideoChannel,
19 } from '@shared/server-commands'
21 describe('Test video static file privacy', function () {
22 let server: PeerTubeServer
25 before(async function () {
28 server = await createSingleServer(1)
29 await setAccessTokensToServers([ server ])
30 await setDefaultVideoChannel([ server ])
32 userToken = await server.users.generateUserAndToken('user1')
35 describe('VOD static file path', function () {
37 function runSuite () {
39 async function checkPrivateFiles (uuid: string) {
40 const video = await server.videos.getWithToken({ id: uuid })
42 for (const file of video.files) {
43 expect(file.fileDownloadUrl).to.not.include('/private/')
44 expectStartWith(file.fileUrl, server.url + '/static/webseed/private/')
46 const torrent = await parseTorrentVideo(server, file)
47 expect(torrent.urlList).to.have.lengthOf(0)
49 const magnet = decode(file.magnetUri)
50 expect(magnet.urlList).to.have.lengthOf(0)
52 await makeRawRequest({ url: file.fileUrl, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
55 const hls = video.streamingPlaylists[0]
57 expectStartWith(hls.playlistUrl, server.url + '/static/streaming-playlists/hls/private/')
58 expectStartWith(hls.segmentsSha256Url, server.url + '/static/streaming-playlists/hls/private/')
60 await makeRawRequest({ url: hls.playlistUrl, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
61 await makeRawRequest({ url: hls.segmentsSha256Url, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
65 async function checkPublicFiles (uuid: string) {
66 const video = await server.videos.get({ id: uuid })
68 for (const file of getAllFiles(video)) {
69 expect(file.fileDownloadUrl).to.not.include('/private/')
70 expect(file.fileUrl).to.not.include('/private/')
72 const torrent = await parseTorrentVideo(server, file)
73 expect(torrent.urlList[0]).to.not.include('private')
75 const magnet = decode(file.magnetUri)
76 expect(magnet.urlList[0]).to.not.include('private')
78 await makeRawRequest({ url: file.fileUrl, expectedStatus: HttpStatusCode.OK_200 })
79 await makeRawRequest({ url: torrent.urlList[0], expectedStatus: HttpStatusCode.OK_200 })
80 await makeRawRequest({ url: magnet.urlList[0], expectedStatus: HttpStatusCode.OK_200 })
83 const hls = video.streamingPlaylists[0]
85 expect(hls.playlistUrl).to.not.include('private')
86 expect(hls.segmentsSha256Url).to.not.include('private')
88 await makeRawRequest({ url: hls.playlistUrl, expectedStatus: HttpStatusCode.OK_200 })
89 await makeRawRequest({ url: hls.segmentsSha256Url, expectedStatus: HttpStatusCode.OK_200 })
93 it('Should upload a private/internal video and have a private static path', async function () {
96 for (const privacy of [ VideoPrivacy.PRIVATE, VideoPrivacy.INTERNAL ]) {
97 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy })
98 await waitJobs([ server ])
100 await checkPrivateFiles(uuid)
104 it('Should upload a public video and update it as private/internal to have a private static path', async function () {
107 for (const privacy of [ VideoPrivacy.PRIVATE, VideoPrivacy.INTERNAL ]) {
108 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.PUBLIC })
109 await waitJobs([ server ])
111 await server.videos.update({ id: uuid, attributes: { privacy } })
112 await waitJobs([ server ])
114 await checkPrivateFiles(uuid)
118 it('Should upload a private video and update it to unlisted to have a public static path', async function () {
121 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.PRIVATE })
122 await waitJobs([ server ])
124 await server.videos.update({ id: uuid, attributes: { privacy: VideoPrivacy.UNLISTED } })
125 await waitJobs([ server ])
127 await checkPublicFiles(uuid)
130 it('Should upload an internal video and update it to public to have a public static path', async function () {
133 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.INTERNAL })
134 await waitJobs([ server ])
136 await server.videos.update({ id: uuid, attributes: { privacy: VideoPrivacy.PUBLIC } })
137 await waitJobs([ server ])
139 await checkPublicFiles(uuid)
142 it('Should upload an internal video and schedule a public publish', async function () {
147 privacy: VideoPrivacy.PRIVATE,
149 updateAt: new Date(Date.now() + 1000).toISOString(),
150 privacy: VideoPrivacy.PUBLIC as VideoPrivacy.PUBLIC
154 const { uuid } = await server.videos.upload({ attributes })
156 await waitJobs([ server ])
158 await server.debug.sendCommand({ body: { command: 'process-update-videos-scheduler' } })
160 await waitJobs([ server ])
162 await checkPublicFiles(uuid)
166 describe('Without transcoding', function () {
170 describe('With transcoding', function () {
172 before(async function () {
173 await server.config.enableMinimumTranscoding()
180 describe('VOD static file right check', function () {
181 let unrelatedFileToken: string
183 async function checkVideoFiles (options: {
185 expectedStatus: HttpStatusCode
187 videoFileToken: string
189 const { id, expectedStatus, token, videoFileToken } = options
191 const video = await server.videos.getWithToken({ id })
193 for (const file of getAllFiles(video)) {
194 await makeRawRequest({ url: file.fileUrl, token, expectedStatus })
195 await makeRawRequest({ url: file.fileDownloadUrl, token, expectedStatus })
197 await makeRawRequest({ url: file.fileUrl, query: { videoFileToken }, expectedStatus })
198 await makeRawRequest({ url: file.fileDownloadUrl, query: { videoFileToken }, expectedStatus })
201 const hls = video.streamingPlaylists[0]
202 await makeRawRequest({ url: hls.playlistUrl, token, expectedStatus })
203 await makeRawRequest({ url: hls.segmentsSha256Url, token, expectedStatus })
205 await makeRawRequest({ url: hls.playlistUrl, query: { videoFileToken }, expectedStatus })
206 await makeRawRequest({ url: hls.segmentsSha256Url, query: { videoFileToken }, expectedStatus })
209 before(async function () {
210 await server.config.enableMinimumTranscoding()
212 const { uuid } = await server.videos.quickUpload({ name: 'another video' })
213 unrelatedFileToken = await server.videoToken.getVideoFileToken({ videoId: uuid })
216 it('Should not be able to access a private video files without OAuth token and file token', async function () {
219 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.INTERNAL })
220 await waitJobs([ server ])
222 await checkVideoFiles({ id: uuid, expectedStatus: HttpStatusCode.FORBIDDEN_403, token: null, videoFileToken: null })
225 it('Should not be able to access an internal video files without appropriate OAuth token and file token', async function () {
228 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.PRIVATE })
229 await waitJobs([ server ])
231 await checkVideoFiles({
233 expectedStatus: HttpStatusCode.FORBIDDEN_403,
235 videoFileToken: unrelatedFileToken
239 it('Should be able to access a private video files with appropriate OAuth token or file token', async function () {
242 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.PRIVATE })
243 const videoFileToken = await server.videoToken.getVideoFileToken({ videoId: uuid })
245 await waitJobs([ server ])
247 await checkVideoFiles({ id: uuid, expectedStatus: HttpStatusCode.OK_200, token: server.accessToken, videoFileToken })
250 it('Should reinject video file token', async function () {
253 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.PRIVATE })
255 const videoFileToken = await server.videoToken.getVideoFileToken({ videoId: uuid })
256 await waitJobs([ server ])
259 const video = await server.videos.getWithToken({ id: uuid })
260 const hls = video.streamingPlaylists[0]
261 const query = { videoFileToken }
262 const { text } = await makeRawRequest({ url: hls.playlistUrl, query, expectedStatus: HttpStatusCode.OK_200 })
264 expect(text).to.not.include(videoFileToken)
268 await checkVideoFileTokenReinjection({
272 resolutions: [ 240, 720 ],
278 it('Should be able to access a private video of another user with an admin OAuth token or file token', async function () {
281 const { uuid } = await server.videos.quickUpload({ name: 'video', token: userToken, privacy: VideoPrivacy.PRIVATE })
282 const videoFileToken = await server.videoToken.getVideoFileToken({ videoId: uuid })
284 await waitJobs([ server ])
286 await checkVideoFiles({ id: uuid, expectedStatus: HttpStatusCode.OK_200, token: server.accessToken, videoFileToken })
290 describe('Live static file path and check', function () {
291 let normalLiveId: string
292 let normalLive: LiveVideo
294 let permanentLiveId: string
295 let permanentLive: LiveVideo
297 let unrelatedFileToken: string
299 async function checkLiveFiles (live: LiveVideo, liveId: string) {
300 const ffmpegCommand = sendRTMPStream({ rtmpBaseUrl: live.rtmpUrl, streamKey: live.streamKey })
301 await server.live.waitUntilPublished({ videoId: liveId })
303 const video = await server.videos.getWithToken({ id: liveId })
304 const fileToken = await server.videoToken.getVideoFileToken({ videoId: video.uuid })
306 const hls = video.streamingPlaylists[0]
308 for (const url of [ hls.playlistUrl, hls.segmentsSha256Url ]) {
309 expectStartWith(url, server.url + '/static/streaming-playlists/hls/private/')
311 await makeRawRequest({ url, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
312 await makeRawRequest({ url, query: { videoFileToken: fileToken }, expectedStatus: HttpStatusCode.OK_200 })
314 await makeRawRequest({ url, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
315 await makeRawRequest({ url, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
316 await makeRawRequest({ url, query: { videoFileToken: unrelatedFileToken }, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
319 await stopFfmpeg(ffmpegCommand)
322 async function checkReplay (replay: VideoDetails) {
323 const fileToken = await server.videoToken.getVideoFileToken({ videoId: replay.uuid })
325 const hls = replay.streamingPlaylists[0]
326 expect(hls.files).to.not.have.lengthOf(0)
328 for (const file of hls.files) {
329 await makeRawRequest({ url: file.fileUrl, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
330 await makeRawRequest({ url: file.fileUrl, query: { videoFileToken: fileToken }, expectedStatus: HttpStatusCode.OK_200 })
332 await makeRawRequest({ url: file.fileUrl, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
333 await makeRawRequest({ url: file.fileUrl, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
334 await makeRawRequest({
336 query: { videoFileToken: unrelatedFileToken },
337 expectedStatus: HttpStatusCode.FORBIDDEN_403
341 for (const url of [ hls.playlistUrl, hls.segmentsSha256Url ]) {
342 expectStartWith(url, server.url + '/static/streaming-playlists/hls/private/')
344 await makeRawRequest({ url, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
345 await makeRawRequest({ url, query: { videoFileToken: fileToken }, expectedStatus: HttpStatusCode.OK_200 })
347 await makeRawRequest({ url, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
348 await makeRawRequest({ url, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
349 await makeRawRequest({ url, query: { videoFileToken: unrelatedFileToken }, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
353 before(async function () {
354 await server.config.enableMinimumTranscoding()
356 const { uuid } = await server.videos.quickUpload({ name: 'another video' })
357 unrelatedFileToken = await server.videoToken.getVideoFileToken({ videoId: uuid })
359 await server.config.enableLive({
366 const { video, live } = await server.live.quickCreate({
368 permanentLive: false,
369 privacy: VideoPrivacy.PRIVATE
371 normalLiveId = video.uuid
376 const { video, live } = await server.live.quickCreate({
379 privacy: VideoPrivacy.PRIVATE
381 permanentLiveId = video.uuid
386 it('Should create a private normal live and have a private static path', async function () {
389 await checkLiveFiles(normalLive, normalLiveId)
392 it('Should create a private permanent live and have a private static path', async function () {
395 await checkLiveFiles(permanentLive, permanentLiveId)
398 it('Should reinject video file token on permanent live', async function () {
401 const ffmpegCommand = sendRTMPStream({ rtmpBaseUrl: permanentLive.rtmpUrl, streamKey: permanentLive.streamKey })
402 await server.live.waitUntilPublished({ videoId: permanentLiveId })
404 const video = await server.videos.getWithToken({ id: permanentLiveId })
405 const videoFileToken = await server.videoToken.getVideoFileToken({ videoId: video.uuid })
406 const hls = video.streamingPlaylists[0]
409 const query = { videoFileToken }
410 const { text } = await makeRawRequest({ url: hls.playlistUrl, query, expectedStatus: HttpStatusCode.OK_200 })
412 expect(text).to.not.include(videoFileToken)
416 await checkVideoFileTokenReinjection({
418 videoUUID: permanentLiveId,
420 resolutions: [ 720 ],
425 await stopFfmpeg(ffmpegCommand)
428 it('Should have created a replay of the normal live with a private static path', async function () {
431 await server.live.waitUntilReplacedByReplay({ videoId: normalLiveId })
433 const replay = await server.videos.getWithToken({ id: normalLiveId })
434 await checkReplay(replay)
437 it('Should have created a replay of the permanent live with a private static path', async function () {
440 await server.live.waitUntilWaiting({ videoId: permanentLiveId })
441 await waitJobs([ server ])
443 const live = await server.videos.getWithToken({ id: permanentLiveId })
444 const replayFromList = await findExternalSavedVideo(server, live)
445 const replay = await server.videos.getWithToken({ id: replayFromList.id })
447 await checkReplay(replay)
451 describe('With static file right check disabled', function () {
452 let videoUUID: string
454 before(async function () {
461 private_files_require_auth: false
465 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.INTERNAL })
468 await waitJobs([ server ])
471 it('Should not check auth for private static files', async function () {
472 const video = await server.videos.getWithToken({ id: videoUUID })
474 for (const file of getAllFiles(video)) {
475 await makeRawRequest({ url: file.fileUrl, expectedStatus: HttpStatusCode.OK_200 })
478 const hls = video.streamingPlaylists[0]
479 await makeRawRequest({ url: hls.playlistUrl, expectedStatus: HttpStatusCode.OK_200 })
480 await makeRawRequest({ url: hls.segmentsSha256Url, expectedStatus: HttpStatusCode.OK_200 })
484 after(async function () {
485 await cleanupTests([ server ])