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