]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/tests/api/videos/video-static-file-privacy.ts
Add runner server tests
[github/Chocobozzz/PeerTube.git] / server / tests / api / videos / video-static-file-privacy.ts
CommitLineData
3545e72c
C
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { expect } from 'chai'
4import { decode } from 'magnet-uri'
d102de1b 5import { checkVideoFileTokenReinjection, expectStartWith, parseTorrentVideo } from '@server/tests/shared'
3545e72c
C
6import { getAllFiles, wait } from '@shared/core-utils'
7import { HttpStatusCode, LiveVideo, VideoDetails, VideoPrivacy } from '@shared/models'
8import {
9 cleanupTests,
10 createSingleServer,
11 findExternalSavedVideo,
12 makeRawRequest,
3545e72c
C
13 PeerTubeServer,
14 sendRTMPStream,
15 setAccessTokensToServers,
16 setDefaultVideoChannel,
17 stopFfmpeg,
18 waitJobs
19} from '@shared/server-commands'
20
21describe('Test video static file privacy', function () {
22 let server: PeerTubeServer
23 let userToken: string
24
25 before(async function () {
26 this.timeout(50000)
27
28 server = await createSingleServer(1)
29 await setAccessTokensToServers([ server ])
30 await setDefaultVideoChannel([ server ])
31
32 userToken = await server.users.generateUserAndToken('user1')
33 })
34
35 describe('VOD static file path', function () {
36
37 function runSuite () {
38
9ab330b9 39 async function checkPrivateFiles (uuid: string) {
3545e72c
C
40 const video = await server.videos.getWithToken({ id: uuid })
41
42 for (const file of video.files) {
43 expect(file.fileDownloadUrl).to.not.include('/private/')
44 expectStartWith(file.fileUrl, server.url + '/static/webseed/private/')
45
46 const torrent = await parseTorrentVideo(server, file)
47 expect(torrent.urlList).to.have.lengthOf(0)
48
49 const magnet = decode(file.magnetUri)
50 expect(magnet.urlList).to.have.lengthOf(0)
51
52 await makeRawRequest({ url: file.fileUrl, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
53 }
54
55 const hls = video.streamingPlaylists[0]
56 if (hls) {
57 expectStartWith(hls.playlistUrl, server.url + '/static/streaming-playlists/hls/private/')
58 expectStartWith(hls.segmentsSha256Url, server.url + '/static/streaming-playlists/hls/private/')
59
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 })
62 }
63 }
64
9ab330b9 65 async function checkPublicFiles (uuid: string) {
3545e72c
C
66 const video = await server.videos.get({ id: uuid })
67
68 for (const file of getAllFiles(video)) {
69 expect(file.fileDownloadUrl).to.not.include('/private/')
70 expect(file.fileUrl).to.not.include('/private/')
71
72 const torrent = await parseTorrentVideo(server, file)
73 expect(torrent.urlList[0]).to.not.include('private')
74
75 const magnet = decode(file.magnetUri)
76 expect(magnet.urlList[0]).to.not.include('private')
77
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 })
81 }
82
83 const hls = video.streamingPlaylists[0]
84 if (hls) {
85 expect(hls.playlistUrl).to.not.include('private')
86 expect(hls.segmentsSha256Url).to.not.include('private')
87
88 await makeRawRequest({ url: hls.playlistUrl, expectedStatus: HttpStatusCode.OK_200 })
89 await makeRawRequest({ url: hls.segmentsSha256Url, expectedStatus: HttpStatusCode.OK_200 })
90 }
91 }
92
93 it('Should upload a private/internal video and have a private static path', async function () {
94 this.timeout(120000)
95
96 for (const privacy of [ VideoPrivacy.PRIVATE, VideoPrivacy.INTERNAL ]) {
97 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy })
98 await waitJobs([ server ])
99
9ab330b9 100 await checkPrivateFiles(uuid)
3545e72c
C
101 }
102 })
103
104 it('Should upload a public video and update it as private/internal to have a private static path', async function () {
105 this.timeout(120000)
106
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 ])
110
111 await server.videos.update({ id: uuid, attributes: { privacy } })
112 await waitJobs([ server ])
113
9ab330b9 114 await checkPrivateFiles(uuid)
3545e72c
C
115 }
116 })
117
118 it('Should upload a private video and update it to unlisted to have a public static path', async function () {
119 this.timeout(120000)
120
121 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.PRIVATE })
122 await waitJobs([ server ])
123
124 await server.videos.update({ id: uuid, attributes: { privacy: VideoPrivacy.UNLISTED } })
125 await waitJobs([ server ])
126
9ab330b9 127 await checkPublicFiles(uuid)
3545e72c
C
128 })
129
130 it('Should upload an internal video and update it to public to have a public static path', async function () {
131 this.timeout(120000)
132
133 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.INTERNAL })
134 await waitJobs([ server ])
135
136 await server.videos.update({ id: uuid, attributes: { privacy: VideoPrivacy.PUBLIC } })
137 await waitJobs([ server ])
138
9ab330b9 139 await checkPublicFiles(uuid)
3545e72c
C
140 })
141
142 it('Should upload an internal video and schedule a public publish', async function () {
143 this.timeout(120000)
144
145 const attributes = {
146 name: 'video',
147 privacy: VideoPrivacy.PRIVATE,
148 scheduleUpdate: {
149 updateAt: new Date(Date.now() + 1000).toISOString(),
150 privacy: VideoPrivacy.PUBLIC as VideoPrivacy.PUBLIC
151 }
152 }
153
154 const { uuid } = await server.videos.upload({ attributes })
155
156 await waitJobs([ server ])
157 await wait(1000)
158 await server.debug.sendCommand({ body: { command: 'process-update-videos-scheduler' } })
159
160 await waitJobs([ server ])
161
9ab330b9 162 await checkPublicFiles(uuid)
3545e72c
C
163 })
164 }
165
166 describe('Without transcoding', function () {
167 runSuite()
168 })
169
170 describe('With transcoding', function () {
171
172 before(async function () {
173 await server.config.enableMinimumTranscoding()
174 })
175
176 runSuite()
177 })
178 })
179
180 describe('VOD static file right check', function () {
181 let unrelatedFileToken: string
182
183 async function checkVideoFiles (options: {
184 id: string
185 expectedStatus: HttpStatusCode
186 token: string
187 videoFileToken: string
188 }) {
189 const { id, expectedStatus, token, videoFileToken } = options
190
191 const video = await server.videos.getWithToken({ id })
192
193 for (const file of getAllFiles(video)) {
194 await makeRawRequest({ url: file.fileUrl, token, expectedStatus })
195 await makeRawRequest({ url: file.fileDownloadUrl, token, expectedStatus })
196
197 await makeRawRequest({ url: file.fileUrl, query: { videoFileToken }, expectedStatus })
198 await makeRawRequest({ url: file.fileDownloadUrl, query: { videoFileToken }, expectedStatus })
199 }
200
201 const hls = video.streamingPlaylists[0]
202 await makeRawRequest({ url: hls.playlistUrl, token, expectedStatus })
203 await makeRawRequest({ url: hls.segmentsSha256Url, token, expectedStatus })
204
205 await makeRawRequest({ url: hls.playlistUrl, query: { videoFileToken }, expectedStatus })
206 await makeRawRequest({ url: hls.segmentsSha256Url, query: { videoFileToken }, expectedStatus })
207 }
208
209 before(async function () {
210 await server.config.enableMinimumTranscoding()
211
212 const { uuid } = await server.videos.quickUpload({ name: 'another video' })
213 unrelatedFileToken = await server.videoToken.getVideoFileToken({ videoId: uuid })
214 })
215
216 it('Should not be able to access a private video files without OAuth token and file token', async function () {
217 this.timeout(120000)
218
219 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.INTERNAL })
220 await waitJobs([ server ])
221
222 await checkVideoFiles({ id: uuid, expectedStatus: HttpStatusCode.FORBIDDEN_403, token: null, videoFileToken: null })
223 })
224
225 it('Should not be able to access an internal video files without appropriate OAuth token and file token', async function () {
226 this.timeout(120000)
227
228 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.PRIVATE })
229 await waitJobs([ server ])
230
231 await checkVideoFiles({
232 id: uuid,
233 expectedStatus: HttpStatusCode.FORBIDDEN_403,
234 token: userToken,
235 videoFileToken: unrelatedFileToken
236 })
237 })
238
239 it('Should be able to access a private video files with appropriate OAuth token or file token', async function () {
240 this.timeout(120000)
241
242 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.PRIVATE })
243 const videoFileToken = await server.videoToken.getVideoFileToken({ videoId: uuid })
244
245 await waitJobs([ server ])
246
247 await checkVideoFiles({ id: uuid, expectedStatus: HttpStatusCode.OK_200, token: server.accessToken, videoFileToken })
248 })
249
71e3e879
C
250 it('Should reinject video file token', async function () {
251 this.timeout(120000)
252
253 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.PRIVATE })
254
255 const videoFileToken = await server.videoToken.getVideoFileToken({ videoId: uuid })
256 await waitJobs([ server ])
257
71e3e879 258 {
73fb3dc5
W
259 const video = await server.videos.getWithToken({ id: uuid })
260 const hls = video.streamingPlaylists[0]
71e3e879
C
261 const query = { videoFileToken }
262 const { text } = await makeRawRequest({ url: hls.playlistUrl, query, expectedStatus: HttpStatusCode.OK_200 })
263
264 expect(text).to.not.include(videoFileToken)
265 }
266
267 {
268 await checkVideoFileTokenReinjection({
269 server,
270 videoUUID: uuid,
271 videoFileToken,
272 resolutions: [ 240, 720 ],
273 isLive: false
274 })
275 }
276 })
277
3545e72c
C
278 it('Should be able to access a private video of another user with an admin OAuth token or file token', async function () {
279 this.timeout(120000)
280
281 const { uuid } = await server.videos.quickUpload({ name: 'video', token: userToken, privacy: VideoPrivacy.PRIVATE })
282 const videoFileToken = await server.videoToken.getVideoFileToken({ videoId: uuid })
283
284 await waitJobs([ server ])
285
286 await checkVideoFiles({ id: uuid, expectedStatus: HttpStatusCode.OK_200, token: server.accessToken, videoFileToken })
287 })
288 })
289
290 describe('Live static file path and check', function () {
291 let normalLiveId: string
292 let normalLive: LiveVideo
293
294 let permanentLiveId: string
295 let permanentLive: LiveVideo
296
297 let unrelatedFileToken: string
298
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 })
302
303 const video = await server.videos.getWithToken({ id: liveId })
304 const fileToken = await server.videoToken.getVideoFileToken({ videoId: video.uuid })
305
306 const hls = video.streamingPlaylists[0]
307
308 for (const url of [ hls.playlistUrl, hls.segmentsSha256Url ]) {
309 expectStartWith(url, server.url + '/static/streaming-playlists/hls/private/')
310
311 await makeRawRequest({ url, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
312 await makeRawRequest({ url, query: { videoFileToken: fileToken }, expectedStatus: HttpStatusCode.OK_200 })
313
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 })
317 }
318
319 await stopFfmpeg(ffmpegCommand)
320 }
321
322 async function checkReplay (replay: VideoDetails) {
323 const fileToken = await server.videoToken.getVideoFileToken({ videoId: replay.uuid })
324
325 const hls = replay.streamingPlaylists[0]
326 expect(hls.files).to.not.have.lengthOf(0)
327
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 })
331
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({
335 url: file.fileUrl,
336 query: { videoFileToken: unrelatedFileToken },
337 expectedStatus: HttpStatusCode.FORBIDDEN_403
338 })
339 }
340
341 for (const url of [ hls.playlistUrl, hls.segmentsSha256Url ]) {
342 expectStartWith(url, server.url + '/static/streaming-playlists/hls/private/')
343
344 await makeRawRequest({ url, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
345 await makeRawRequest({ url, query: { videoFileToken: fileToken }, expectedStatus: HttpStatusCode.OK_200 })
346
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 })
350 }
351 }
352
353 before(async function () {
354 await server.config.enableMinimumTranscoding()
355
356 const { uuid } = await server.videos.quickUpload({ name: 'another video' })
357 unrelatedFileToken = await server.videoToken.getVideoFileToken({ videoId: uuid })
358
359 await server.config.enableLive({
360 allowReplay: true,
361 transcoding: true,
362 resolutions: 'min'
363 })
364
365 {
05a60d85
W
366 const { video, live } = await server.live.quickCreate({
367 saveReplay: true,
368 permanentLive: false,
369 privacy: VideoPrivacy.PRIVATE
370 })
3545e72c
C
371 normalLiveId = video.uuid
372 normalLive = live
373 }
374
375 {
05a60d85
W
376 const { video, live } = await server.live.quickCreate({
377 saveReplay: true,
378 permanentLive: true,
379 privacy: VideoPrivacy.PRIVATE
380 })
3545e72c
C
381 permanentLiveId = video.uuid
382 permanentLive = live
383 }
384 })
385
386 it('Should create a private normal live and have a private static path', async function () {
387 this.timeout(240000)
388
389 await checkLiveFiles(normalLive, normalLiveId)
390 })
391
392 it('Should create a private permanent live and have a private static path', async function () {
393 this.timeout(240000)
394
395 await checkLiveFiles(permanentLive, permanentLiveId)
396 })
397
71e3e879
C
398 it('Should reinject video file token on permanent live', async function () {
399 this.timeout(240000)
400
401 const ffmpegCommand = sendRTMPStream({ rtmpBaseUrl: permanentLive.rtmpUrl, streamKey: permanentLive.streamKey })
402 await server.live.waitUntilPublished({ videoId: permanentLiveId })
403
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]
407
408 {
409 const query = { videoFileToken }
410 const { text } = await makeRawRequest({ url: hls.playlistUrl, query, expectedStatus: HttpStatusCode.OK_200 })
411
412 expect(text).to.not.include(videoFileToken)
413 }
414
415 {
416 await checkVideoFileTokenReinjection({
417 server,
418 videoUUID: permanentLiveId,
419 videoFileToken,
420 resolutions: [ 720 ],
421 isLive: true
422 })
423 }
424
425 await stopFfmpeg(ffmpegCommand)
426 })
427
3545e72c
C
428 it('Should have created a replay of the normal live with a private static path', async function () {
429 this.timeout(240000)
430
431 await server.live.waitUntilReplacedByReplay({ videoId: normalLiveId })
432
433 const replay = await server.videos.getWithToken({ id: normalLiveId })
434 await checkReplay(replay)
435 })
436
437 it('Should have created a replay of the permanent live with a private static path', async function () {
438 this.timeout(240000)
439
440 await server.live.waitUntilWaiting({ videoId: permanentLiveId })
441 await waitJobs([ server ])
442
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 })
446
447 await checkReplay(replay)
448 })
449 })
450
5a122ddd
C
451 describe('With static file right check disabled', function () {
452 let videoUUID: string
453
454 before(async function () {
455 this.timeout(240000)
456
457 await server.kill()
458
459 await server.run({
460 static_files: {
461 private_files_require_auth: false
462 }
463 })
464
465 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.INTERNAL })
466 videoUUID = uuid
467
468 await waitJobs([ server ])
469 })
470
471 it('Should not check auth for private static files', async function () {
472 const video = await server.videos.getWithToken({ id: videoUUID })
473
474 for (const file of getAllFiles(video)) {
475 await makeRawRequest({ url: file.fileUrl, expectedStatus: HttpStatusCode.OK_200 })
476 }
477
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 })
481 })
482 })
483
3545e72c
C
484 after(async function () {
485 await cleanupTests([ server ])
486 })
487})