]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/tests/api/object-storage/video-static-file-privacy.ts
Add runner server tests
[github/Chocobozzz/PeerTube.git] / server / tests / api / object-storage / video-static-file-privacy.ts
CommitLineData
9ab330b9
C
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { expect } from 'chai'
4import { basename } from 'path'
d102de1b 5import { checkVideoFileTokenReinjection, expectStartWith, SQLCommand } from '@server/tests/shared'
9ab330b9
C
6import { areScalewayObjectStorageTestsDisabled, getAllFiles, getHLS } from '@shared/core-utils'
7import { HttpStatusCode, LiveVideo, VideoDetails, VideoPrivacy } from '@shared/models'
8import {
9 cleanupTests,
10 createSingleServer,
11 findExternalSavedVideo,
12 makeRawRequest,
13 ObjectStorageCommand,
14 PeerTubeServer,
15 sendRTMPStream,
16 setAccessTokensToServers,
17 setDefaultVideoChannel,
18 stopFfmpeg,
19 waitJobs
20} from '@shared/server-commands'
21
5a122ddd
C
22function extractFilenameFromUrl (url: string) {
23 const parts = basename(url).split(':')
24
25 return parts[parts.length - 1]
26}
27
9ab330b9
C
28describe('Object storage for video static file privacy', function () {
29 // We need real world object storage to check ACL
30 if (areScalewayObjectStorageTestsDisabled()) return
31
32 let server: PeerTubeServer
d102de1b 33 let sqlCommand: SQLCommand
9ab330b9
C
34 let userToken: string
35
5a122ddd 36 // ---------------------------------------------------------------------------
9ab330b9 37
5a122ddd
C
38 async function checkPrivateVODFiles (uuid: string) {
39 const video = await server.videos.getWithToken({ id: uuid })
9ab330b9 40
5a122ddd
C
41 for (const file of video.files) {
42 expectStartWith(file.fileUrl, server.url + '/object-storage-proxy/webseed/private/')
9ab330b9 43
5a122ddd
C
44 await makeRawRequest({ url: file.fileUrl, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
45 }
9ab330b9 46
5a122ddd 47 for (const file of getAllFiles(video)) {
d102de1b 48 const internalFileUrl = await sqlCommand.getInternalFileUrl(file.id)
5a122ddd
C
49 expectStartWith(internalFileUrl, ObjectStorageCommand.getScalewayBaseUrl())
50 await makeRawRequest({ url: internalFileUrl, token: server.accessToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
51 }
52
53 const hls = getHLS(video)
54
55 if (hls) {
56 for (const url of [ hls.playlistUrl, hls.segmentsSha256Url ]) {
57 expectStartWith(url, server.url + '/object-storage-proxy/streaming-playlists/hls/private/')
58 }
9ab330b9 59
5a122ddd
C
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 })
9ab330b9 62
5a122ddd
C
63 for (const file of hls.files) {
64 expectStartWith(file.fileUrl, server.url + '/object-storage-proxy/streaming-playlists/hls/private/')
9ab330b9
C
65
66 await makeRawRequest({ url: file.fileUrl, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
67 }
5a122ddd
C
68 }
69 }
9ab330b9 70
5a122ddd
C
71 async function checkPublicVODFiles (uuid: string) {
72 const video = await server.videos.getWithToken({ id: uuid })
9ab330b9 73
5a122ddd
C
74 for (const file of getAllFiles(video)) {
75 expectStartWith(file.fileUrl, ObjectStorageCommand.getScalewayBaseUrl())
9ab330b9 76
5a122ddd
C
77 await makeRawRequest({ url: file.fileUrl, expectedStatus: HttpStatusCode.OK_200 })
78 }
9ab330b9 79
5a122ddd 80 const hls = getHLS(video)
9ab330b9 81
5a122ddd
C
82 if (hls) {
83 expectStartWith(hls.playlistUrl, ObjectStorageCommand.getScalewayBaseUrl())
84 expectStartWith(hls.segmentsSha256Url, ObjectStorageCommand.getScalewayBaseUrl())
9ab330b9 85
5a122ddd
C
86 await makeRawRequest({ url: hls.playlistUrl, expectedStatus: HttpStatusCode.OK_200 })
87 await makeRawRequest({ url: hls.segmentsSha256Url, expectedStatus: HttpStatusCode.OK_200 })
9ab330b9 88 }
5a122ddd 89 }
9ab330b9 90
5a122ddd 91 // ---------------------------------------------------------------------------
9ab330b9 92
5a122ddd
C
93 before(async function () {
94 this.timeout(120000)
9ab330b9 95
5a122ddd
C
96 server = await createSingleServer(1, ObjectStorageCommand.getDefaultScalewayConfig({ serverNumber: 1 }))
97 await setAccessTokensToServers([ server ])
98 await setDefaultVideoChannel([ server ])
9ab330b9 99
5a122ddd 100 await server.config.enableMinimumTranscoding()
9ab330b9 101
5a122ddd 102 userToken = await server.users.generateUserAndToken('user1')
d102de1b
C
103
104 sqlCommand = new SQLCommand(server)
5a122ddd 105 })
9ab330b9 106
5a122ddd
C
107 describe('VOD', function () {
108 let privateVideoUUID: string
109 let publicVideoUUID: string
110 let userPrivateVideoUUID: string
111
112 // ---------------------------------------------------------------------------
9ab330b9
C
113
114 async function getSampleFileUrls (videoId: string) {
115 const video = await server.videos.getWithToken({ id: videoId })
116
117 return {
118 webTorrentFile: video.files[0].fileUrl,
119 hlsFile: getHLS(video).files[0].fileUrl
120 }
121 }
122
5a122ddd
C
123 // ---------------------------------------------------------------------------
124
9ab330b9 125 it('Should upload a private video and have appropriate object storage ACL', async function () {
4fe7cde2 126 this.timeout(120000)
9ab330b9
C
127
128 {
129 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.PRIVATE })
130 privateVideoUUID = uuid
131 }
132
133 {
134 const { uuid } = await server.videos.quickUpload({ name: 'user video', token: userToken, privacy: VideoPrivacy.PRIVATE })
135 userPrivateVideoUUID = uuid
136 }
137
138 await waitJobs([ server ])
139
5a122ddd 140 await checkPrivateVODFiles(privateVideoUUID)
9ab330b9
C
141 })
142
143 it('Should upload a public video and have appropriate object storage ACL', async function () {
4fe7cde2 144 this.timeout(120000)
9ab330b9
C
145
146 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.UNLISTED })
147 await waitJobs([ server ])
148
149 publicVideoUUID = uuid
150
5a122ddd 151 await checkPublicVODFiles(publicVideoUUID)
9ab330b9
C
152 })
153
154 it('Should not get files without appropriate OAuth token', async function () {
155 this.timeout(60000)
156
157 const { webTorrentFile, hlsFile } = await getSampleFileUrls(privateVideoUUID)
158
159 await makeRawRequest({ url: webTorrentFile, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
160 await makeRawRequest({ url: webTorrentFile, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
161
162 await makeRawRequest({ url: hlsFile, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
163 await makeRawRequest({ url: hlsFile, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
164 })
165
166 it('Should not get HLS file of another video', async function () {
167 this.timeout(60000)
168
169 const privateVideo = await server.videos.getWithToken({ id: privateVideoUUID })
170 const hlsFilename = basename(getHLS(privateVideo).files[0].fileUrl)
171
172 const badUrl = server.url + '/object-storage-proxy/streaming-playlists/hls/private/' + userPrivateVideoUUID + '/' + hlsFilename
173 const goodUrl = server.url + '/object-storage-proxy/streaming-playlists/hls/private/' + privateVideoUUID + '/' + hlsFilename
174
175 await makeRawRequest({ url: badUrl, token: server.accessToken, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
176 await makeRawRequest({ url: goodUrl, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
177 })
178
179 it('Should correctly check OAuth or video file token', async function () {
180 this.timeout(60000)
181
182 const badVideoFileToken = await server.videoToken.getVideoFileToken({ token: userToken, videoId: userPrivateVideoUUID })
183 const goodVideoFileToken = await server.videoToken.getVideoFileToken({ videoId: privateVideoUUID })
184
185 const { webTorrentFile, hlsFile } = await getSampleFileUrls(privateVideoUUID)
186
187 for (const url of [ webTorrentFile, hlsFile ]) {
188 await makeRawRequest({ url, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
189 await makeRawRequest({ url, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
190 await makeRawRequest({ url, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
191
192 await makeRawRequest({ url, query: { videoFileToken: badVideoFileToken }, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
193 await makeRawRequest({ url, query: { videoFileToken: goodVideoFileToken }, expectedStatus: HttpStatusCode.OK_200 })
194 }
195 })
196
71e3e879
C
197 it('Should reinject video file token', async function () {
198 this.timeout(120000)
199
200 const videoFileToken = await server.videoToken.getVideoFileToken({ videoId: privateVideoUUID })
201
202 await checkVideoFileTokenReinjection({
203 server,
204 videoUUID: privateVideoUUID,
205 videoFileToken,
206 resolutions: [ 240, 720 ],
207 isLive: false
208 })
209 })
210
9ab330b9
C
211 it('Should update public video to private', async function () {
212 this.timeout(60000)
213
214 await server.videos.update({ id: publicVideoUUID, attributes: { privacy: VideoPrivacy.INTERNAL } })
215
5a122ddd 216 await checkPrivateVODFiles(publicVideoUUID)
9ab330b9
C
217 })
218
219 it('Should update private video to public', async function () {
220 this.timeout(60000)
221
222 await server.videos.update({ id: publicVideoUUID, attributes: { privacy: VideoPrivacy.PUBLIC } })
223
5a122ddd 224 await checkPublicVODFiles(publicVideoUUID)
9ab330b9 225 })
9ab330b9
C
226 })
227
228 describe('Live', function () {
229 let normalLiveId: string
230 let normalLive: LiveVideo
231
232 let permanentLiveId: string
233 let permanentLive: LiveVideo
234
235 let unrelatedFileToken: string
236
5a122ddd
C
237 // ---------------------------------------------------------------------------
238
9ab330b9
C
239 async function checkLiveFiles (live: LiveVideo, liveId: string) {
240 const ffmpegCommand = sendRTMPStream({ rtmpBaseUrl: live.rtmpUrl, streamKey: live.streamKey })
241 await server.live.waitUntilPublished({ videoId: liveId })
242
243 const video = await server.videos.getWithToken({ id: liveId })
244 const fileToken = await server.videoToken.getVideoFileToken({ videoId: video.uuid })
245
246 const hls = video.streamingPlaylists[0]
247
248 for (const url of [ hls.playlistUrl, hls.segmentsSha256Url ]) {
249 expectStartWith(url, server.url + '/object-storage-proxy/streaming-playlists/hls/private/')
250
251 await makeRawRequest({ url: hls.playlistUrl, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
252 await makeRawRequest({ url: hls.segmentsSha256Url, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
253
254 await makeRawRequest({ url, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
255 await makeRawRequest({ url, query: { videoFileToken: fileToken }, expectedStatus: HttpStatusCode.OK_200 })
256
257 await makeRawRequest({ url, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
258 await makeRawRequest({ url, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
259 await makeRawRequest({ url, query: { videoFileToken: unrelatedFileToken }, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
260 }
261
262 await stopFfmpeg(ffmpegCommand)
263 }
264
265 async function checkReplay (replay: VideoDetails) {
266 const fileToken = await server.videoToken.getVideoFileToken({ videoId: replay.uuid })
267
268 const hls = replay.streamingPlaylists[0]
269 expect(hls.files).to.not.have.lengthOf(0)
270
271 for (const file of hls.files) {
272 await makeRawRequest({ url: file.fileUrl, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
273 await makeRawRequest({ url: file.fileUrl, query: { videoFileToken: fileToken }, expectedStatus: HttpStatusCode.OK_200 })
274
275 await makeRawRequest({ url: file.fileUrl, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
276 await makeRawRequest({ url: file.fileUrl, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
277 await makeRawRequest({
278 url: file.fileUrl,
279 query: { videoFileToken: unrelatedFileToken },
280 expectedStatus: HttpStatusCode.FORBIDDEN_403
281 })
282 }
283
284 for (const url of [ hls.playlistUrl, hls.segmentsSha256Url ]) {
285 expectStartWith(url, server.url + '/object-storage-proxy/streaming-playlists/hls/private/')
286
287 await makeRawRequest({ url, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
288 await makeRawRequest({ url, query: { videoFileToken: fileToken }, expectedStatus: HttpStatusCode.OK_200 })
289
290 await makeRawRequest({ url, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
291 await makeRawRequest({ url, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
292 await makeRawRequest({ url, query: { videoFileToken: unrelatedFileToken }, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
293 }
294 }
295
5a122ddd
C
296 // ---------------------------------------------------------------------------
297
9ab330b9
C
298 before(async function () {
299 await server.config.enableMinimumTranscoding()
300
301 const { uuid } = await server.videos.quickUpload({ name: 'another video' })
302 unrelatedFileToken = await server.videoToken.getVideoFileToken({ videoId: uuid })
303
304 await server.config.enableLive({
305 allowReplay: true,
306 transcoding: true,
307 resolutions: 'min'
308 })
309
310 {
05a60d85
W
311 const { video, live } = await server.live.quickCreate({
312 saveReplay: true,
313 permanentLive: false,
314 privacy: VideoPrivacy.PRIVATE
315 })
9ab330b9
C
316 normalLiveId = video.uuid
317 normalLive = live
318 }
319
320 {
05a60d85
W
321 const { video, live } = await server.live.quickCreate({
322 saveReplay: true,
323 permanentLive: true,
324 privacy: VideoPrivacy.PRIVATE
325 })
9ab330b9
C
326 permanentLiveId = video.uuid
327 permanentLive = live
328 }
329 })
330
331 it('Should create a private normal live and have a private static path', async function () {
332 this.timeout(240000)
333
334 await checkLiveFiles(normalLive, normalLiveId)
335 })
336
337 it('Should create a private permanent live and have a private static path', async function () {
338 this.timeout(240000)
339
340 await checkLiveFiles(permanentLive, permanentLiveId)
341 })
342
71e3e879
C
343 it('Should reinject video file token in permanent live', async function () {
344 this.timeout(240000)
345
346 const ffmpegCommand = sendRTMPStream({ rtmpBaseUrl: permanentLive.rtmpUrl, streamKey: permanentLive.streamKey })
347 await server.live.waitUntilPublished({ videoId: permanentLiveId })
348
349 const video = await server.videos.getWithToken({ id: permanentLiveId })
350 const videoFileToken = await server.videoToken.getVideoFileToken({ videoId: video.uuid })
351
352 await checkVideoFileTokenReinjection({
353 server,
354 videoUUID: permanentLiveId,
355 videoFileToken,
356 resolutions: [ 720 ],
357 isLive: true
358 })
359
360 await stopFfmpeg(ffmpegCommand)
361 })
362
9ab330b9
C
363 it('Should have created a replay of the normal live with a private static path', async function () {
364 this.timeout(240000)
365
366 await server.live.waitUntilReplacedByReplay({ videoId: normalLiveId })
367
368 const replay = await server.videos.getWithToken({ id: normalLiveId })
369 await checkReplay(replay)
370 })
371
372 it('Should have created a replay of the permanent live with a private static path', async function () {
373 this.timeout(240000)
374
375 await server.live.waitUntilWaiting({ videoId: permanentLiveId })
376 await waitJobs([ server ])
377
378 const live = await server.videos.getWithToken({ id: permanentLiveId })
379 const replayFromList = await findExternalSavedVideo(server, live)
380 const replay = await server.videos.getWithToken({ id: replayFromList.id })
381
382 await checkReplay(replay)
383 })
384 })
385
5a122ddd
C
386 describe('With private files proxy disabled and public ACL for private files', function () {
387 let videoUUID: string
388
389 before(async function () {
390 this.timeout(240000)
391
392 await server.kill()
393
394 const config = ObjectStorageCommand.getDefaultScalewayConfig({
e8299615 395 serverNumber: 1,
5a122ddd
C
396 enablePrivateProxy: false,
397 privateACL: 'public-read'
398 })
399 await server.run(config)
400
401 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.PRIVATE })
402 videoUUID = uuid
403
404 await waitJobs([ server ])
405 })
406
407 it('Should display object storage path for a private video and be able to access them', async function () {
408 this.timeout(60000)
409
410 await checkPublicVODFiles(videoUUID)
411 })
412
413 it('Should not be able to access object storage proxy', async function () {
414 const privateVideo = await server.videos.getWithToken({ id: videoUUID })
415 const webtorrentFilename = extractFilenameFromUrl(privateVideo.files[0].fileUrl)
416 const hlsFilename = extractFilenameFromUrl(getHLS(privateVideo).files[0].fileUrl)
417
418 await makeRawRequest({
419 url: server.url + '/object-storage-proxy/webseed/private/' + webtorrentFilename,
420 token: server.accessToken,
421 expectedStatus: HttpStatusCode.BAD_REQUEST_400
422 })
423
424 await makeRawRequest({
425 url: server.url + '/object-storage-proxy/streaming-playlists/hls/private/' + videoUUID + '/' + hlsFilename,
426 token: server.accessToken,
427 expectedStatus: HttpStatusCode.BAD_REQUEST_400
428 })
429 })
430 })
431
9ab330b9 432 after(async function () {
1ebe2c2b 433 this.timeout(240000)
508c1b1e
C
434
435 const { data } = await server.videos.listAllForAdmin()
436
437 for (const v of data) {
438 await server.videos.remove({ id: v.uuid })
439 }
440
441 for (const v of data) {
e8299615 442 await server.servers.waitUntilLog('Removed files of video ' + v.url)
508c1b1e
C
443 }
444
d102de1b 445 await sqlCommand.cleanup()
9ab330b9
C
446 await cleanupTests([ server ])
447 })
448})