aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/tests/api/videos/video-static-file-privacy.ts
diff options
context:
space:
mode:
Diffstat (limited to 'server/tests/api/videos/video-static-file-privacy.ts')
-rw-r--r--server/tests/api/videos/video-static-file-privacy.ts600
1 files changed, 0 insertions, 600 deletions
diff --git a/server/tests/api/videos/video-static-file-privacy.ts b/server/tests/api/videos/video-static-file-privacy.ts
deleted file mode 100644
index 0a9864134..000000000
--- a/server/tests/api/videos/video-static-file-privacy.ts
+++ /dev/null
@@ -1,600 +0,0 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { expect } from 'chai'
4import { decode } from 'magnet-uri'
5import { checkVideoFileTokenReinjection, expectStartWith, parseTorrentVideo } from '@server/tests/shared'
6import { getAllFiles, wait } from '@shared/core-utils'
7import { HttpStatusCode, LiveVideo, VideoDetails, VideoPrivacy } from '@shared/models'
8import {
9 cleanupTests,
10 createSingleServer,
11 findExternalSavedVideo,
12 makeRawRequest,
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
39 async function checkPrivateFiles (uuid: string) {
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/web-videos/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
65 async function checkPublicFiles (uuid: string) {
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/password protected 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
100 await checkPrivateFiles(uuid)
101 }
102
103 const { uuid } = await server.videos.quickUpload({
104 name: 'video',
105 privacy: VideoPrivacy.PASSWORD_PROTECTED,
106 videoPasswords: [ 'my super password' ]
107 })
108 await waitJobs([ server ])
109
110 await checkPrivateFiles(uuid)
111 })
112
113 it('Should upload a public video and update it as private/internal to have a private static path', async function () {
114 this.timeout(120000)
115
116 for (const privacy of [ VideoPrivacy.PRIVATE, VideoPrivacy.INTERNAL ]) {
117 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.PUBLIC })
118 await waitJobs([ server ])
119
120 await server.videos.update({ id: uuid, attributes: { privacy } })
121 await waitJobs([ server ])
122
123 await checkPrivateFiles(uuid)
124 }
125 })
126
127 it('Should upload a private video and update it to unlisted to have a public static path', async function () {
128 this.timeout(120000)
129
130 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.PRIVATE })
131 await waitJobs([ server ])
132
133 await server.videos.update({ id: uuid, attributes: { privacy: VideoPrivacy.UNLISTED } })
134 await waitJobs([ server ])
135
136 await checkPublicFiles(uuid)
137 })
138
139 it('Should upload an internal video and update it to public to have a public static path', async function () {
140 this.timeout(120000)
141
142 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.INTERNAL })
143 await waitJobs([ server ])
144
145 await server.videos.update({ id: uuid, attributes: { privacy: VideoPrivacy.PUBLIC } })
146 await waitJobs([ server ])
147
148 await checkPublicFiles(uuid)
149 })
150
151 it('Should upload an internal video and schedule a public publish', async function () {
152 this.timeout(120000)
153
154 const attributes = {
155 name: 'video',
156 privacy: VideoPrivacy.PRIVATE,
157 scheduleUpdate: {
158 updateAt: new Date(Date.now() + 1000).toISOString(),
159 privacy: VideoPrivacy.PUBLIC as VideoPrivacy.PUBLIC
160 }
161 }
162
163 const { uuid } = await server.videos.upload({ attributes })
164
165 await waitJobs([ server ])
166 await wait(1000)
167 await server.debug.sendCommand({ body: { command: 'process-update-videos-scheduler' } })
168
169 await waitJobs([ server ])
170
171 await checkPublicFiles(uuid)
172 })
173 }
174
175 describe('Without transcoding', function () {
176 runSuite()
177 })
178
179 describe('With transcoding', function () {
180
181 before(async function () {
182 await server.config.enableMinimumTranscoding()
183 })
184
185 runSuite()
186 })
187 })
188
189 describe('VOD static file right check', function () {
190 let unrelatedFileToken: string
191
192 async function checkVideoFiles (options: {
193 id: string
194 expectedStatus: HttpStatusCode
195 token: string
196 videoFileToken: string
197 videoPassword?: string
198 }) {
199 const { id, expectedStatus, token, videoFileToken, videoPassword } = options
200
201 const video = await server.videos.getWithToken({ id })
202
203 for (const file of getAllFiles(video)) {
204 await makeRawRequest({ url: file.fileUrl, token, expectedStatus })
205 await makeRawRequest({ url: file.fileDownloadUrl, token, expectedStatus })
206
207 await makeRawRequest({ url: file.fileUrl, query: { videoFileToken }, expectedStatus })
208 await makeRawRequest({ url: file.fileDownloadUrl, query: { videoFileToken }, expectedStatus })
209
210 if (videoPassword) {
211 const headers = { 'x-peertube-video-password': videoPassword }
212 await makeRawRequest({ url: file.fileUrl, headers, expectedStatus })
213 await makeRawRequest({ url: file.fileDownloadUrl, headers, expectedStatus })
214 }
215 }
216
217 const hls = video.streamingPlaylists[0]
218 await makeRawRequest({ url: hls.playlistUrl, token, expectedStatus })
219 await makeRawRequest({ url: hls.segmentsSha256Url, token, expectedStatus })
220
221 await makeRawRequest({ url: hls.playlistUrl, query: { videoFileToken }, expectedStatus })
222 await makeRawRequest({ url: hls.segmentsSha256Url, query: { videoFileToken }, expectedStatus })
223
224 if (videoPassword) {
225 const headers = { 'x-peertube-video-password': videoPassword }
226 await makeRawRequest({ url: hls.playlistUrl, token: null, headers, expectedStatus })
227 await makeRawRequest({ url: hls.segmentsSha256Url, token: null, headers, expectedStatus })
228 }
229 }
230
231 before(async function () {
232 await server.config.enableMinimumTranscoding()
233
234 const { uuid } = await server.videos.quickUpload({ name: 'another video' })
235 unrelatedFileToken = await server.videoToken.getVideoFileToken({ videoId: uuid })
236 })
237
238 it('Should not be able to access a private video files without OAuth token and file token', async function () {
239 this.timeout(120000)
240
241 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.PRIVATE })
242 await waitJobs([ server ])
243
244 await checkVideoFiles({ id: uuid, expectedStatus: HttpStatusCode.FORBIDDEN_403, token: null, videoFileToken: null })
245 })
246
247 it('Should not be able to access password protected video files without OAuth token, file token and password', async function () {
248 this.timeout(120000)
249 const videoPassword = 'my super password'
250
251 const { uuid } = await server.videos.quickUpload({
252 name: 'password protected video',
253 privacy: VideoPrivacy.PASSWORD_PROTECTED,
254 videoPasswords: [ videoPassword ]
255 })
256 await waitJobs([ server ])
257
258 await checkVideoFiles({
259 id: uuid,
260 expectedStatus: HttpStatusCode.FORBIDDEN_403,
261 token: null,
262 videoFileToken: null,
263 videoPassword: null
264 })
265 })
266
267 it('Should not be able to access an password video files with incorrect OAuth token, file token and password', async function () {
268 this.timeout(120000)
269 const videoPassword = 'my super password'
270
271 const { uuid } = await server.videos.quickUpload({
272 name: 'password protected video',
273 privacy: VideoPrivacy.PASSWORD_PROTECTED,
274 videoPasswords: [ videoPassword ]
275 })
276 await waitJobs([ server ])
277
278 await checkVideoFiles({
279 id: uuid,
280 expectedStatus: HttpStatusCode.FORBIDDEN_403,
281 token: userToken,
282 videoFileToken: unrelatedFileToken,
283 videoPassword: 'incorrectPassword'
284 })
285 })
286
287 it('Should not be able to access an private video files without appropriate OAuth token and file token', async function () {
288 this.timeout(120000)
289
290 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.PRIVATE })
291 await waitJobs([ server ])
292
293 await checkVideoFiles({
294 id: uuid,
295 expectedStatus: HttpStatusCode.FORBIDDEN_403,
296 token: userToken,
297 videoFileToken: unrelatedFileToken
298 })
299 })
300
301 it('Should be able to access a private video files with appropriate OAuth token or file token', async function () {
302 this.timeout(120000)
303
304 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.PRIVATE })
305 const videoFileToken = await server.videoToken.getVideoFileToken({ videoId: uuid })
306
307 await waitJobs([ server ])
308
309 await checkVideoFiles({ id: uuid, expectedStatus: HttpStatusCode.OK_200, token: server.accessToken, videoFileToken })
310 })
311
312 it('Should be able to access a password protected video files with appropriate OAuth token or file token', async function () {
313 this.timeout(120000)
314 const videoPassword = 'my super password'
315
316 const { uuid } = await server.videos.quickUpload({
317 name: 'video',
318 privacy: VideoPrivacy.PASSWORD_PROTECTED,
319 videoPasswords: [ videoPassword ]
320 })
321
322 const videoFileToken = await server.videoToken.getVideoFileToken({ token: null, videoId: uuid, videoPassword })
323
324 await waitJobs([ server ])
325
326 await checkVideoFiles({ id: uuid, expectedStatus: HttpStatusCode.OK_200, token: server.accessToken, videoFileToken, videoPassword })
327 })
328
329 it('Should reinject video file token', async function () {
330 this.timeout(120000)
331
332 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.PRIVATE })
333
334 const videoFileToken = await server.videoToken.getVideoFileToken({ videoId: uuid })
335 await waitJobs([ server ])
336
337 {
338 const video = await server.videos.getWithToken({ id: uuid })
339 const hls = video.streamingPlaylists[0]
340 const query = { videoFileToken }
341 const { text } = await makeRawRequest({ url: hls.playlistUrl, query, expectedStatus: HttpStatusCode.OK_200 })
342
343 expect(text).to.not.include(videoFileToken)
344 }
345
346 {
347 await checkVideoFileTokenReinjection({
348 server,
349 videoUUID: uuid,
350 videoFileToken,
351 resolutions: [ 240, 720 ],
352 isLive: false
353 })
354 }
355 })
356
357 it('Should be able to access a private video of another user with an admin OAuth token or file token', async function () {
358 this.timeout(120000)
359
360 const { uuid } = await server.videos.quickUpload({ name: 'video', token: userToken, privacy: VideoPrivacy.PRIVATE })
361 const videoFileToken = await server.videoToken.getVideoFileToken({ videoId: uuid })
362
363 await waitJobs([ server ])
364
365 await checkVideoFiles({ id: uuid, expectedStatus: HttpStatusCode.OK_200, token: server.accessToken, videoFileToken })
366 })
367 })
368
369 describe('Live static file path and check', function () {
370 let normalLiveId: string
371 let normalLive: LiveVideo
372
373 let permanentLiveId: string
374 let permanentLive: LiveVideo
375
376 let passwordProtectedLiveId: string
377 let passwordProtectedLive: LiveVideo
378
379 const correctPassword = 'my super password'
380
381 let unrelatedFileToken: string
382
383 async function checkLiveFiles (options: { live: LiveVideo, liveId: string, videoPassword?: string }) {
384 const { live, liveId, videoPassword } = options
385 const ffmpegCommand = sendRTMPStream({ rtmpBaseUrl: live.rtmpUrl, streamKey: live.streamKey })
386 await server.live.waitUntilPublished({ videoId: liveId })
387
388 const video = await server.videos.getWithToken({ id: liveId })
389
390 const fileToken = await server.videoToken.getVideoFileToken({ videoId: video.uuid })
391
392 const hls = video.streamingPlaylists[0]
393
394 for (const url of [ hls.playlistUrl, hls.segmentsSha256Url ]) {
395 expectStartWith(url, server.url + '/static/streaming-playlists/hls/private/')
396
397 await makeRawRequest({ url, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
398 await makeRawRequest({ url, query: { videoFileToken: fileToken }, expectedStatus: HttpStatusCode.OK_200 })
399
400 await makeRawRequest({ url, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
401 await makeRawRequest({ url, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
402 await makeRawRequest({ url, query: { videoFileToken: unrelatedFileToken }, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
403
404 if (videoPassword) {
405 await makeRawRequest({ url, headers: { 'x-peertube-video-password': videoPassword }, expectedStatus: HttpStatusCode.OK_200 })
406 await makeRawRequest({
407 url,
408 headers: { 'x-peertube-video-password': 'incorrectPassword' },
409 expectedStatus: HttpStatusCode.FORBIDDEN_403
410 })
411 }
412
413 }
414
415 await stopFfmpeg(ffmpegCommand)
416 }
417
418 async function checkReplay (replay: VideoDetails) {
419 const fileToken = await server.videoToken.getVideoFileToken({ videoId: replay.uuid })
420
421 const hls = replay.streamingPlaylists[0]
422 expect(hls.files).to.not.have.lengthOf(0)
423
424 for (const file of hls.files) {
425 await makeRawRequest({ url: file.fileUrl, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
426 await makeRawRequest({ url: file.fileUrl, query: { videoFileToken: fileToken }, expectedStatus: HttpStatusCode.OK_200 })
427
428 await makeRawRequest({ url: file.fileUrl, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
429 await makeRawRequest({ url: file.fileUrl, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
430 await makeRawRequest({
431 url: file.fileUrl,
432 query: { videoFileToken: unrelatedFileToken },
433 expectedStatus: HttpStatusCode.FORBIDDEN_403
434 })
435 }
436
437 for (const url of [ hls.playlistUrl, hls.segmentsSha256Url ]) {
438 expectStartWith(url, server.url + '/static/streaming-playlists/hls/private/')
439
440 await makeRawRequest({ url, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
441 await makeRawRequest({ url, query: { videoFileToken: fileToken }, expectedStatus: HttpStatusCode.OK_200 })
442
443 await makeRawRequest({ url, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
444 await makeRawRequest({ url, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
445 await makeRawRequest({ url, query: { videoFileToken: unrelatedFileToken }, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
446 }
447 }
448
449 before(async function () {
450 await server.config.enableMinimumTranscoding()
451
452 const { uuid } = await server.videos.quickUpload({ name: 'another video' })
453 unrelatedFileToken = await server.videoToken.getVideoFileToken({ videoId: uuid })
454
455 await server.config.enableLive({
456 allowReplay: true,
457 transcoding: true,
458 resolutions: 'min'
459 })
460
461 {
462 const { video, live } = await server.live.quickCreate({
463 saveReplay: true,
464 permanentLive: false,
465 privacy: VideoPrivacy.PRIVATE
466 })
467 normalLiveId = video.uuid
468 normalLive = live
469 }
470
471 {
472 const { video, live } = await server.live.quickCreate({
473 saveReplay: true,
474 permanentLive: true,
475 privacy: VideoPrivacy.PRIVATE
476 })
477 permanentLiveId = video.uuid
478 permanentLive = live
479 }
480
481 {
482 const { video, live } = await server.live.quickCreate({
483 saveReplay: false,
484 permanentLive: false,
485 privacy: VideoPrivacy.PASSWORD_PROTECTED,
486 videoPasswords: [ correctPassword ]
487 })
488 passwordProtectedLiveId = video.uuid
489 passwordProtectedLive = live
490 }
491 })
492
493 it('Should create a private normal live and have a private static path', async function () {
494 this.timeout(240000)
495
496 await checkLiveFiles({ live: normalLive, liveId: normalLiveId })
497 })
498
499 it('Should create a private permanent live and have a private static path', async function () {
500 this.timeout(240000)
501
502 await checkLiveFiles({ live: permanentLive, liveId: permanentLiveId })
503 })
504
505 it('Should create a password protected live and have a private static path', async function () {
506 this.timeout(240000)
507
508 await checkLiveFiles({ live: passwordProtectedLive, liveId: passwordProtectedLiveId, videoPassword: correctPassword })
509 })
510
511 it('Should reinject video file token on permanent live', async function () {
512 this.timeout(240000)
513
514 const ffmpegCommand = sendRTMPStream({ rtmpBaseUrl: permanentLive.rtmpUrl, streamKey: permanentLive.streamKey })
515 await server.live.waitUntilPublished({ videoId: permanentLiveId })
516
517 const video = await server.videos.getWithToken({ id: permanentLiveId })
518 const videoFileToken = await server.videoToken.getVideoFileToken({ videoId: video.uuid })
519 const hls = video.streamingPlaylists[0]
520
521 {
522 const query = { videoFileToken }
523 const { text } = await makeRawRequest({ url: hls.playlistUrl, query, expectedStatus: HttpStatusCode.OK_200 })
524
525 expect(text).to.not.include(videoFileToken)
526 }
527
528 {
529 await checkVideoFileTokenReinjection({
530 server,
531 videoUUID: permanentLiveId,
532 videoFileToken,
533 resolutions: [ 720 ],
534 isLive: true
535 })
536 }
537
538 await stopFfmpeg(ffmpegCommand)
539 })
540
541 it('Should have created a replay of the normal live with a private static path', async function () {
542 this.timeout(240000)
543
544 await server.live.waitUntilReplacedByReplay({ videoId: normalLiveId })
545
546 const replay = await server.videos.getWithToken({ id: normalLiveId })
547 await checkReplay(replay)
548 })
549
550 it('Should have created a replay of the permanent live with a private static path', async function () {
551 this.timeout(240000)
552
553 await server.live.waitUntilWaiting({ videoId: permanentLiveId })
554 await waitJobs([ server ])
555
556 const live = await server.videos.getWithToken({ id: permanentLiveId })
557 const replayFromList = await findExternalSavedVideo(server, live)
558 const replay = await server.videos.getWithToken({ id: replayFromList.id })
559
560 await checkReplay(replay)
561 })
562 })
563
564 describe('With static file right check disabled', function () {
565 let videoUUID: string
566
567 before(async function () {
568 this.timeout(240000)
569
570 await server.kill()
571
572 await server.run({
573 static_files: {
574 private_files_require_auth: false
575 }
576 })
577
578 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.INTERNAL })
579 videoUUID = uuid
580
581 await waitJobs([ server ])
582 })
583
584 it('Should not check auth for private static files', async function () {
585 const video = await server.videos.getWithToken({ id: videoUUID })
586
587 for (const file of getAllFiles(video)) {
588 await makeRawRequest({ url: file.fileUrl, expectedStatus: HttpStatusCode.OK_200 })
589 }
590
591 const hls = video.streamingPlaylists[0]
592 await makeRawRequest({ url: hls.playlistUrl, expectedStatus: HttpStatusCode.OK_200 })
593 await makeRawRequest({ url: hls.segmentsSha256Url, expectedStatus: HttpStatusCode.OK_200 })
594 })
595 })
596
597 after(async function () {
598 await cleanupTests([ server ])
599 })
600})