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 } from '@server/tests/shared'
6 import { getAllFiles, wait } from '@shared/core-utils'
7 import { HttpStatusCode, LiveVideo, VideoDetails, VideoPrivacy } from '@shared/models'
11 findExternalSavedVideo,
16 setAccessTokensToServers,
17 setDefaultVideoChannel,
20 } from '@shared/server-commands'
22 describe('Test video static file privacy', function () {
23 let server: PeerTubeServer
26 before(async function () {
29 server = await createSingleServer(1)
30 await setAccessTokensToServers([ server ])
31 await setDefaultVideoChannel([ server ])
33 userToken = await server.users.generateUserAndToken('user1')
36 describe('VOD static file path', function () {
38 function runSuite () {
40 async function checkPrivateFiles (uuid: string) {
41 const video = await server.videos.getWithToken({ id: uuid })
43 for (const file of video.files) {
44 expect(file.fileDownloadUrl).to.not.include('/private/')
45 expectStartWith(file.fileUrl, server.url + '/static/webseed/private/')
47 const torrent = await parseTorrentVideo(server, file)
48 expect(torrent.urlList).to.have.lengthOf(0)
50 const magnet = decode(file.magnetUri)
51 expect(magnet.urlList).to.have.lengthOf(0)
53 await makeRawRequest({ url: file.fileUrl, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
56 const hls = video.streamingPlaylists[0]
58 expectStartWith(hls.playlistUrl, server.url + '/static/streaming-playlists/hls/private/')
59 expectStartWith(hls.segmentsSha256Url, server.url + '/static/streaming-playlists/hls/private/')
61 await makeRawRequest({ url: hls.playlistUrl, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
62 await makeRawRequest({ url: hls.segmentsSha256Url, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
66 async function checkPublicFiles (uuid: string) {
67 const video = await server.videos.get({ id: uuid })
69 for (const file of getAllFiles(video)) {
70 expect(file.fileDownloadUrl).to.not.include('/private/')
71 expect(file.fileUrl).to.not.include('/private/')
73 const torrent = await parseTorrentVideo(server, file)
74 expect(torrent.urlList[0]).to.not.include('private')
76 const magnet = decode(file.magnetUri)
77 expect(magnet.urlList[0]).to.not.include('private')
79 await makeRawRequest({ url: file.fileUrl, expectedStatus: HttpStatusCode.OK_200 })
80 await makeRawRequest({ url: torrent.urlList[0], expectedStatus: HttpStatusCode.OK_200 })
81 await makeRawRequest({ url: magnet.urlList[0], expectedStatus: HttpStatusCode.OK_200 })
84 const hls = video.streamingPlaylists[0]
86 expect(hls.playlistUrl).to.not.include('private')
87 expect(hls.segmentsSha256Url).to.not.include('private')
89 await makeRawRequest({ url: hls.playlistUrl, expectedStatus: HttpStatusCode.OK_200 })
90 await makeRawRequest({ url: hls.segmentsSha256Url, expectedStatus: HttpStatusCode.OK_200 })
94 it('Should upload a private/internal video and have a private static path', async function () {
97 for (const privacy of [ VideoPrivacy.PRIVATE, VideoPrivacy.INTERNAL ]) {
98 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy })
99 await waitJobs([ server ])
101 await checkPrivateFiles(uuid)
105 it('Should upload a public video and update it as private/internal to have a private static path', async function () {
108 for (const privacy of [ VideoPrivacy.PRIVATE, VideoPrivacy.INTERNAL ]) {
109 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.PUBLIC })
110 await waitJobs([ server ])
112 await server.videos.update({ id: uuid, attributes: { privacy } })
113 await waitJobs([ server ])
115 await checkPrivateFiles(uuid)
119 it('Should upload a private video and update it to unlisted to have a public static path', async function () {
122 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.PRIVATE })
123 await waitJobs([ server ])
125 await server.videos.update({ id: uuid, attributes: { privacy: VideoPrivacy.UNLISTED } })
126 await waitJobs([ server ])
128 await checkPublicFiles(uuid)
131 it('Should upload an internal video and update it to public to have a public static path', async function () {
134 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.INTERNAL })
135 await waitJobs([ server ])
137 await server.videos.update({ id: uuid, attributes: { privacy: VideoPrivacy.PUBLIC } })
138 await waitJobs([ server ])
140 await checkPublicFiles(uuid)
143 it('Should upload an internal video and schedule a public publish', async function () {
148 privacy: VideoPrivacy.PRIVATE,
150 updateAt: new Date(Date.now() + 1000).toISOString(),
151 privacy: VideoPrivacy.PUBLIC as VideoPrivacy.PUBLIC
155 const { uuid } = await server.videos.upload({ attributes })
157 await waitJobs([ server ])
159 await server.debug.sendCommand({ body: { command: 'process-update-videos-scheduler' } })
161 await waitJobs([ server ])
163 await checkPublicFiles(uuid)
167 describe('Without transcoding', function () {
171 describe('With transcoding', function () {
173 before(async function () {
174 await server.config.enableMinimumTranscoding()
181 describe('VOD static file right check', function () {
182 let unrelatedFileToken: string
184 async function checkVideoFiles (options: {
186 expectedStatus: HttpStatusCode
188 videoFileToken: string
190 const { id, expectedStatus, token, videoFileToken } = options
192 const video = await server.videos.getWithToken({ id })
194 for (const file of getAllFiles(video)) {
195 await makeRawRequest({ url: file.fileUrl, token, expectedStatus })
196 await makeRawRequest({ url: file.fileDownloadUrl, token, expectedStatus })
198 await makeRawRequest({ url: file.fileUrl, query: { videoFileToken }, expectedStatus })
199 await makeRawRequest({ url: file.fileDownloadUrl, query: { videoFileToken }, expectedStatus })
202 const hls = video.streamingPlaylists[0]
203 await makeRawRequest({ url: hls.playlistUrl, token, expectedStatus })
204 await makeRawRequest({ url: hls.segmentsSha256Url, token, expectedStatus })
206 await makeRawRequest({ url: hls.playlistUrl, query: { videoFileToken }, expectedStatus })
207 await makeRawRequest({ url: hls.segmentsSha256Url, query: { videoFileToken }, expectedStatus })
210 before(async function () {
211 await server.config.enableMinimumTranscoding()
213 const { uuid } = await server.videos.quickUpload({ name: 'another video' })
214 unrelatedFileToken = await server.videoToken.getVideoFileToken({ videoId: uuid })
217 it('Should not be able to access a private video files without OAuth token and file token', async function () {
220 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.INTERNAL })
221 await waitJobs([ server ])
223 await checkVideoFiles({ id: uuid, expectedStatus: HttpStatusCode.FORBIDDEN_403, token: null, videoFileToken: null })
226 it('Should not be able to access an internal video files without appropriate OAuth token and file token', async function () {
229 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.PRIVATE })
230 await waitJobs([ server ])
232 await checkVideoFiles({
234 expectedStatus: HttpStatusCode.FORBIDDEN_403,
236 videoFileToken: unrelatedFileToken
240 it('Should be able to access a private video files with appropriate OAuth token or file token', async function () {
243 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.PRIVATE })
244 const videoFileToken = await server.videoToken.getVideoFileToken({ videoId: uuid })
246 await waitJobs([ server ])
248 await checkVideoFiles({ id: uuid, expectedStatus: HttpStatusCode.OK_200, token: server.accessToken, videoFileToken })
251 it('Should reinject video file token', async function () {
254 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.PRIVATE })
256 const videoFileToken = await server.videoToken.getVideoFileToken({ videoId: uuid })
257 await waitJobs([ server ])
259 const video = await server.videos.getWithToken({ id: uuid })
260 const hls = video.streamingPlaylists[0]
263 const query = { videoFileToken }
264 const { text } = await makeRawRequest({ url: hls.playlistUrl, query, expectedStatus: HttpStatusCode.OK_200 })
266 expect(text).to.not.include(videoFileToken)
270 await checkVideoFileTokenReinjection({
274 resolutions: [ 240, 720 ],
280 it('Should be able to access a private video of another user with an admin OAuth token or file token', async function () {
283 const { uuid } = await server.videos.quickUpload({ name: 'video', token: userToken, privacy: VideoPrivacy.PRIVATE })
284 const videoFileToken = await server.videoToken.getVideoFileToken({ videoId: uuid })
286 await waitJobs([ server ])
288 await checkVideoFiles({ id: uuid, expectedStatus: HttpStatusCode.OK_200, token: server.accessToken, videoFileToken })
292 describe('Live static file path and check', function () {
293 let normalLiveId: string
294 let normalLive: LiveVideo
296 let permanentLiveId: string
297 let permanentLive: LiveVideo
299 let unrelatedFileToken: string
301 async function checkLiveFiles (live: LiveVideo, liveId: string) {
302 const ffmpegCommand = sendRTMPStream({ rtmpBaseUrl: live.rtmpUrl, streamKey: live.streamKey })
303 await server.live.waitUntilPublished({ videoId: liveId })
305 const video = await server.videos.getWithToken({ id: liveId })
306 const fileToken = await server.videoToken.getVideoFileToken({ videoId: video.uuid })
308 const hls = video.streamingPlaylists[0]
310 for (const url of [ hls.playlistUrl, hls.segmentsSha256Url ]) {
311 expectStartWith(url, server.url + '/static/streaming-playlists/hls/private/')
313 await makeRawRequest({ url, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
314 await makeRawRequest({ url, query: { videoFileToken: fileToken }, expectedStatus: HttpStatusCode.OK_200 })
316 await makeRawRequest({ url, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
317 await makeRawRequest({ url, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
318 await makeRawRequest({ url, query: { videoFileToken: unrelatedFileToken }, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
321 await stopFfmpeg(ffmpegCommand)
324 async function checkReplay (replay: VideoDetails) {
325 const fileToken = await server.videoToken.getVideoFileToken({ videoId: replay.uuid })
327 const hls = replay.streamingPlaylists[0]
328 expect(hls.files).to.not.have.lengthOf(0)
330 for (const file of hls.files) {
331 await makeRawRequest({ url: file.fileUrl, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
332 await makeRawRequest({ url: file.fileUrl, query: { videoFileToken: fileToken }, expectedStatus: HttpStatusCode.OK_200 })
334 await makeRawRequest({ url: file.fileUrl, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
335 await makeRawRequest({ url: file.fileUrl, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
336 await makeRawRequest({
338 query: { videoFileToken: unrelatedFileToken },
339 expectedStatus: HttpStatusCode.FORBIDDEN_403
343 for (const url of [ hls.playlistUrl, hls.segmentsSha256Url ]) {
344 expectStartWith(url, server.url + '/static/streaming-playlists/hls/private/')
346 await makeRawRequest({ url, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
347 await makeRawRequest({ url, query: { videoFileToken: fileToken }, expectedStatus: HttpStatusCode.OK_200 })
349 await makeRawRequest({ url, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
350 await makeRawRequest({ url, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
351 await makeRawRequest({ url, query: { videoFileToken: unrelatedFileToken }, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
355 before(async function () {
356 await server.config.enableMinimumTranscoding()
358 const { uuid } = await server.videos.quickUpload({ name: 'another video' })
359 unrelatedFileToken = await server.videoToken.getVideoFileToken({ videoId: uuid })
361 await server.config.enableLive({
368 const { video, live } = await server.live.quickCreate({ saveReplay: true, permanentLive: false, privacy: VideoPrivacy.PRIVATE })
369 normalLiveId = video.uuid
374 const { video, live } = await server.live.quickCreate({ saveReplay: true, permanentLive: true, privacy: VideoPrivacy.PRIVATE })
375 permanentLiveId = video.uuid
380 it('Should create a private normal live and have a private static path', async function () {
383 await checkLiveFiles(normalLive, normalLiveId)
386 it('Should create a private permanent live and have a private static path', async function () {
389 await checkLiveFiles(permanentLive, permanentLiveId)
392 it('Should reinject video file token on permanent live', async function () {
395 const ffmpegCommand = sendRTMPStream({ rtmpBaseUrl: permanentLive.rtmpUrl, streamKey: permanentLive.streamKey })
396 await server.live.waitUntilPublished({ videoId: permanentLiveId })
398 const video = await server.videos.getWithToken({ id: permanentLiveId })
399 const videoFileToken = await server.videoToken.getVideoFileToken({ videoId: video.uuid })
400 const hls = video.streamingPlaylists[0]
403 const query = { videoFileToken }
404 const { text } = await makeRawRequest({ url: hls.playlistUrl, query, expectedStatus: HttpStatusCode.OK_200 })
406 expect(text).to.not.include(videoFileToken)
410 await checkVideoFileTokenReinjection({
412 videoUUID: permanentLiveId,
414 resolutions: [ 720 ],
419 await stopFfmpeg(ffmpegCommand)
422 it('Should have created a replay of the normal live with a private static path', async function () {
425 await server.live.waitUntilReplacedByReplay({ videoId: normalLiveId })
427 const replay = await server.videos.getWithToken({ id: normalLiveId })
428 await checkReplay(replay)
431 it('Should have created a replay of the permanent live with a private static path', async function () {
434 await server.live.waitUntilWaiting({ videoId: permanentLiveId })
435 await waitJobs([ server ])
437 const live = await server.videos.getWithToken({ id: permanentLiveId })
438 const replayFromList = await findExternalSavedVideo(server, live)
439 const replay = await server.videos.getWithToken({ id: replayFromList.id })
441 await checkReplay(replay)
445 describe('With static file right check disabled', function () {
446 let videoUUID: string
448 before(async function () {
455 private_files_require_auth: false
459 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.INTERNAL })
462 await waitJobs([ server ])
465 it('Should not check auth for private static files', async function () {
466 const video = await server.videos.getWithToken({ id: videoUUID })
468 for (const file of getAllFiles(video)) {
469 await makeRawRequest({ url: file.fileUrl, expectedStatus: HttpStatusCode.OK_200 })
472 const hls = video.streamingPlaylists[0]
473 await makeRawRequest({ url: hls.playlistUrl, expectedStatus: HttpStatusCode.OK_200 })
474 await makeRawRequest({ url: hls.segmentsSha256Url, expectedStatus: HttpStatusCode.OK_200 })
478 after(async function () {
479 await cleanupTests([ server ])