1 /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
3 import { expect } from 'chai'
4 import { basename } from 'path'
5 import { expectStartWith } from '@server/tests/shared'
6 import { areScalewayObjectStorageTestsDisabled, getAllFiles, getHLS } 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('Object storage for video static file privacy', function () {
23 // We need real world object storage to check ACL
24 if (areScalewayObjectStorageTestsDisabled()) return
26 let server: PeerTubeServer
29 before(async function () {
32 server = await createSingleServer(1, ObjectStorageCommand.getDefaultScalewayConfig(1))
33 await setAccessTokensToServers([ server ])
34 await setDefaultVideoChannel([ server ])
36 await server.config.enableMinimumTranscoding()
38 userToken = await server.users.generateUserAndToken('user1')
41 describe('VOD', function () {
42 let privateVideoUUID: string
43 let publicVideoUUID: string
44 let userPrivateVideoUUID: string
46 async function checkPrivateFiles (uuid: string) {
47 const video = await server.videos.getWithToken({ id: uuid })
49 for (const file of video.files) {
50 expectStartWith(file.fileUrl, server.url + '/object-storage-proxy/webseed/private/')
52 await makeRawRequest({ url: file.fileUrl, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
55 for (const file of getAllFiles(video)) {
56 const internalFileUrl = await server.sql.getInternalFileUrl(file.id)
57 expectStartWith(internalFileUrl, ObjectStorageCommand.getScalewayBaseUrl())
58 await makeRawRequest({ url: internalFileUrl, token: server.accessToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
61 const hls = getHLS(video)
64 for (const url of [ hls.playlistUrl, hls.segmentsSha256Url ]) {
65 expectStartWith(url, server.url + '/object-storage-proxy/streaming-playlists/hls/private/')
68 await makeRawRequest({ url: hls.playlistUrl, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
69 await makeRawRequest({ url: hls.segmentsSha256Url, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
71 for (const file of hls.files) {
72 expectStartWith(file.fileUrl, server.url + '/object-storage-proxy/streaming-playlists/hls/private/')
74 await makeRawRequest({ url: file.fileUrl, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
79 async function checkPublicFiles (uuid: string) {
80 const video = await server.videos.getWithToken({ id: uuid })
82 for (const file of getAllFiles(video)) {
83 expectStartWith(file.fileUrl, ObjectStorageCommand.getScalewayBaseUrl())
85 await makeRawRequest({ url: file.fileUrl, expectedStatus: HttpStatusCode.OK_200 })
88 const hls = getHLS(video)
91 expectStartWith(hls.playlistUrl, ObjectStorageCommand.getScalewayBaseUrl())
92 expectStartWith(hls.segmentsSha256Url, ObjectStorageCommand.getScalewayBaseUrl())
94 await makeRawRequest({ url: hls.playlistUrl, expectedStatus: HttpStatusCode.OK_200 })
95 await makeRawRequest({ url: hls.segmentsSha256Url, expectedStatus: HttpStatusCode.OK_200 })
99 async function getSampleFileUrls (videoId: string) {
100 const video = await server.videos.getWithToken({ id: videoId })
103 webTorrentFile: video.files[0].fileUrl,
104 hlsFile: getHLS(video).files[0].fileUrl
108 it('Should upload a private video and have appropriate object storage ACL', async function () {
112 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.PRIVATE })
113 privateVideoUUID = uuid
117 const { uuid } = await server.videos.quickUpload({ name: 'user video', token: userToken, privacy: VideoPrivacy.PRIVATE })
118 userPrivateVideoUUID = uuid
121 await waitJobs([ server ])
123 await checkPrivateFiles(privateVideoUUID)
126 it('Should upload a public video and have appropriate object storage ACL', async function () {
129 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.UNLISTED })
130 await waitJobs([ server ])
132 publicVideoUUID = uuid
134 await checkPublicFiles(publicVideoUUID)
137 it('Should not get files without appropriate OAuth token', async function () {
140 const { webTorrentFile, hlsFile } = await getSampleFileUrls(privateVideoUUID)
142 await makeRawRequest({ url: webTorrentFile, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
143 await makeRawRequest({ url: webTorrentFile, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
145 await makeRawRequest({ url: hlsFile, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
146 await makeRawRequest({ url: hlsFile, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
149 it('Should not get HLS file of another video', async function () {
152 const privateVideo = await server.videos.getWithToken({ id: privateVideoUUID })
153 const hlsFilename = basename(getHLS(privateVideo).files[0].fileUrl)
155 const badUrl = server.url + '/object-storage-proxy/streaming-playlists/hls/private/' + userPrivateVideoUUID + '/' + hlsFilename
156 const goodUrl = server.url + '/object-storage-proxy/streaming-playlists/hls/private/' + privateVideoUUID + '/' + hlsFilename
158 await makeRawRequest({ url: badUrl, token: server.accessToken, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
159 await makeRawRequest({ url: goodUrl, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
162 it('Should correctly check OAuth or video file token', async function () {
165 const badVideoFileToken = await server.videoToken.getVideoFileToken({ token: userToken, videoId: userPrivateVideoUUID })
166 const goodVideoFileToken = await server.videoToken.getVideoFileToken({ videoId: privateVideoUUID })
168 const { webTorrentFile, hlsFile } = await getSampleFileUrls(privateVideoUUID)
170 for (const url of [ webTorrentFile, hlsFile ]) {
171 await makeRawRequest({ url, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
172 await makeRawRequest({ url, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
173 await makeRawRequest({ url, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
175 await makeRawRequest({ url, query: { videoFileToken: badVideoFileToken }, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
176 await makeRawRequest({ url, query: { videoFileToken: goodVideoFileToken }, expectedStatus: HttpStatusCode.OK_200 })
180 it('Should update public video to private', async function () {
183 await server.videos.update({ id: publicVideoUUID, attributes: { privacy: VideoPrivacy.INTERNAL } })
185 await checkPrivateFiles(publicVideoUUID)
188 it('Should update private video to public', async function () {
191 await server.videos.update({ id: publicVideoUUID, attributes: { privacy: VideoPrivacy.PUBLIC } })
193 await checkPublicFiles(publicVideoUUID)
197 describe('Live', function () {
198 let normalLiveId: string
199 let normalLive: LiveVideo
201 let permanentLiveId: string
202 let permanentLive: LiveVideo
204 let unrelatedFileToken: string
206 async function checkLiveFiles (live: LiveVideo, liveId: string) {
207 const ffmpegCommand = sendRTMPStream({ rtmpBaseUrl: live.rtmpUrl, streamKey: live.streamKey })
208 await server.live.waitUntilPublished({ videoId: liveId })
210 const video = await server.videos.getWithToken({ id: liveId })
211 const fileToken = await server.videoToken.getVideoFileToken({ videoId: video.uuid })
213 const hls = video.streamingPlaylists[0]
215 for (const url of [ hls.playlistUrl, hls.segmentsSha256Url ]) {
216 expectStartWith(url, server.url + '/object-storage-proxy/streaming-playlists/hls/private/')
218 await makeRawRequest({ url: hls.playlistUrl, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
219 await makeRawRequest({ url: hls.segmentsSha256Url, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
221 await makeRawRequest({ url, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
222 await makeRawRequest({ url, query: { videoFileToken: fileToken }, expectedStatus: HttpStatusCode.OK_200 })
224 await makeRawRequest({ url, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
225 await makeRawRequest({ url, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
226 await makeRawRequest({ url, query: { videoFileToken: unrelatedFileToken }, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
229 await stopFfmpeg(ffmpegCommand)
232 async function checkReplay (replay: VideoDetails) {
233 const fileToken = await server.videoToken.getVideoFileToken({ videoId: replay.uuid })
235 const hls = replay.streamingPlaylists[0]
236 expect(hls.files).to.not.have.lengthOf(0)
238 for (const file of hls.files) {
239 await makeRawRequest({ url: file.fileUrl, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
240 await makeRawRequest({ url: file.fileUrl, query: { videoFileToken: fileToken }, expectedStatus: HttpStatusCode.OK_200 })
242 await makeRawRequest({ url: file.fileUrl, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
243 await makeRawRequest({ url: file.fileUrl, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
244 await makeRawRequest({
246 query: { videoFileToken: unrelatedFileToken },
247 expectedStatus: HttpStatusCode.FORBIDDEN_403
251 for (const url of [ hls.playlistUrl, hls.segmentsSha256Url ]) {
252 expectStartWith(url, server.url + '/object-storage-proxy/streaming-playlists/hls/private/')
254 await makeRawRequest({ url, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
255 await makeRawRequest({ url, query: { videoFileToken: fileToken }, expectedStatus: HttpStatusCode.OK_200 })
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 })
263 before(async function () {
264 await server.config.enableMinimumTranscoding()
266 const { uuid } = await server.videos.quickUpload({ name: 'another video' })
267 unrelatedFileToken = await server.videoToken.getVideoFileToken({ videoId: uuid })
269 await server.config.enableLive({
276 const { video, live } = await server.live.quickCreate({ saveReplay: true, permanentLive: false, privacy: VideoPrivacy.PRIVATE })
277 normalLiveId = video.uuid
282 const { video, live } = await server.live.quickCreate({ saveReplay: true, permanentLive: true, privacy: VideoPrivacy.PRIVATE })
283 permanentLiveId = video.uuid
288 it('Should create a private normal live and have a private static path', async function () {
291 await checkLiveFiles(normalLive, normalLiveId)
294 it('Should create a private permanent live and have a private static path', async function () {
297 await checkLiveFiles(permanentLive, permanentLiveId)
300 it('Should have created a replay of the normal live with a private static path', async function () {
303 await server.live.waitUntilReplacedByReplay({ videoId: normalLiveId })
305 const replay = await server.videos.getWithToken({ id: normalLiveId })
306 await checkReplay(replay)
309 it('Should have created a replay of the permanent live with a private static path', async function () {
312 await server.live.waitUntilWaiting({ videoId: permanentLiveId })
313 await waitJobs([ server ])
315 const live = await server.videos.getWithToken({ id: permanentLiveId })
316 const replayFromList = await findExternalSavedVideo(server, live)
317 const replay = await server.videos.getWithToken({ id: replayFromList.id })
319 await checkReplay(replay)
323 after(async function () {
326 const { data } = await server.videos.listAllForAdmin()
328 for (const v of data) {
329 await server.videos.remove({ id: v.uuid })
332 for (const v of data) {
333 await server.servers.waitUntilLog('Removed files of video ' + v.url, 1, true)
336 await cleanupTests([ server ])