diff options
author | Wicklow <123956049+wickloww@users.noreply.github.com> | 2023-06-29 07:48:55 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-06-29 09:48:55 +0200 |
commit | 40346ead2b0b7afa475aef057d3673b6c7574b7a (patch) | |
tree | 24ffdc23c3a9d987334842e0d400b5bd44500cf7 /server/tests | |
parent | ae22c59f14d0d553f60b281948b6c232c2aca178 (diff) | |
download | PeerTube-40346ead2b0b7afa475aef057d3673b6c7574b7a.tar.gz PeerTube-40346ead2b0b7afa475aef057d3673b6c7574b7a.tar.zst PeerTube-40346ead2b0b7afa475aef057d3673b6c7574b7a.zip |
Feature/password protected videos (#5836)
* Add server endpoints
* Refactoring test suites
* Update server and add openapi documentation
* fix compliation and tests
* upload/import password protected video on client
* add server error code
* Add video password to update resolver
* add custom message when sharing pw protected video
* improve confirm component
* Add new alert in component
* Add ability to watch protected video on client
* Cannot have password protected replay privacy
* Add migration
* Add tests
* update after review
* Update check params tests
* Add live videos test
* Add more filter test
* Update static file privacy test
* Update object storage tests
* Add test on feeds
* Add missing word
* Fix tests
* Fix tests on live videos
* add embed support on password protected videos
* fix style
* Correcting data leaks
* Unable to add password protected privacy on replay
* Updated code based on review comments
* fix validator and command
* Updated code based on review comments
Diffstat (limited to 'server/tests')
-rw-r--r-- | server/tests/api/check-params/live.ts | 4 | ||||
-rw-r--r-- | server/tests/api/check-params/video-passwords.ts | 609 | ||||
-rw-r--r-- | server/tests/api/check-params/video-token.ts | 44 | ||||
-rw-r--r-- | server/tests/api/object-storage/video-static-file-privacy.ts | 132 | ||||
-rw-r--r-- | server/tests/api/videos/video-passwords.ts | 97 | ||||
-rw-r--r-- | server/tests/api/videos/video-playlists.ts | 17 | ||||
-rw-r--r-- | server/tests/api/videos/video-static-file-privacy.ts | 127 | ||||
-rw-r--r-- | server/tests/client.ts | 13 | ||||
-rw-r--r-- | server/tests/feeds/feeds.ts | 9 |
9 files changed, 1023 insertions, 29 deletions
diff --git a/server/tests/api/check-params/live.ts b/server/tests/api/check-params/live.ts index 2dc735c23..406a96824 100644 --- a/server/tests/api/check-params/live.ts +++ b/server/tests/api/check-params/live.ts | |||
@@ -143,7 +143,7 @@ describe('Test video lives API validator', function () { | |||
143 | }) | 143 | }) |
144 | 144 | ||
145 | it('Should fail with a bad privacy for replay settings', async function () { | 145 | it('Should fail with a bad privacy for replay settings', async function () { |
146 | const fields = { ...baseCorrectParams, replaySettings: { privacy: 5 } } | 146 | const fields = { ...baseCorrectParams, saveReplay: true, replaySettings: { privacy: 999 } } |
147 | 147 | ||
148 | await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) | 148 | await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) |
149 | }) | 149 | }) |
@@ -472,7 +472,7 @@ describe('Test video lives API validator', function () { | |||
472 | }) | 472 | }) |
473 | 473 | ||
474 | it('Should fail with a bad privacy for replay settings', async function () { | 474 | it('Should fail with a bad privacy for replay settings', async function () { |
475 | const fields = { saveReplay: true, replaySettings: { privacy: 5 } } | 475 | const fields = { saveReplay: true, replaySettings: { privacy: 999 } } |
476 | 476 | ||
477 | await command.update({ videoId: video.id, fields, expectedStatus: HttpStatusCode.BAD_REQUEST_400 }) | 477 | await command.update({ videoId: video.id, fields, expectedStatus: HttpStatusCode.BAD_REQUEST_400 }) |
478 | }) | 478 | }) |
diff --git a/server/tests/api/check-params/video-passwords.ts b/server/tests/api/check-params/video-passwords.ts new file mode 100644 index 000000000..4e936b5d2 --- /dev/null +++ b/server/tests/api/check-params/video-passwords.ts | |||
@@ -0,0 +1,609 @@ | |||
1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ | ||
2 | import { | ||
3 | FIXTURE_URLS, | ||
4 | checkBadCountPagination, | ||
5 | checkBadSortPagination, | ||
6 | checkBadStartPagination, | ||
7 | checkUploadVideoParam | ||
8 | } from '@server/tests/shared' | ||
9 | import { root } from '@shared/core-utils' | ||
10 | import { | ||
11 | HttpStatusCode, | ||
12 | PeerTubeProblemDocument, | ||
13 | ServerErrorCode, | ||
14 | VideoCreateResult, | ||
15 | VideoPrivacy | ||
16 | } from '@shared/models' | ||
17 | import { | ||
18 | cleanupTests, | ||
19 | createSingleServer, | ||
20 | makePostBodyRequest, | ||
21 | PeerTubeServer, | ||
22 | setAccessTokensToServers | ||
23 | } from '@shared/server-commands' | ||
24 | import { expect } from 'chai' | ||
25 | import { join } from 'path' | ||
26 | |||
27 | describe('Test video passwords validator', function () { | ||
28 | let path: string | ||
29 | let server: PeerTubeServer | ||
30 | let userAccessToken = '' | ||
31 | let video: VideoCreateResult | ||
32 | let channelId: number | ||
33 | let publicVideo: VideoCreateResult | ||
34 | let commentId: number | ||
35 | // --------------------------------------------------------------- | ||
36 | |||
37 | before(async function () { | ||
38 | this.timeout(50000) | ||
39 | |||
40 | server = await createSingleServer(1) | ||
41 | |||
42 | await setAccessTokensToServers([ server ]) | ||
43 | |||
44 | await server.config.updateCustomSubConfig({ | ||
45 | newConfig: { | ||
46 | live: { | ||
47 | enabled: true, | ||
48 | latencySetting: { | ||
49 | enabled: false | ||
50 | }, | ||
51 | allowReplay: false | ||
52 | }, | ||
53 | import: { | ||
54 | videos: { | ||
55 | http:{ | ||
56 | enabled: true | ||
57 | } | ||
58 | } | ||
59 | } | ||
60 | } | ||
61 | }) | ||
62 | |||
63 | userAccessToken = await server.users.generateUserAndToken('user1') | ||
64 | |||
65 | { | ||
66 | const body = await server.users.getMyInfo() | ||
67 | channelId = body.videoChannels[0].id | ||
68 | } | ||
69 | |||
70 | { | ||
71 | video = await server.videos.quickUpload({ | ||
72 | name: 'password protected video', | ||
73 | privacy: VideoPrivacy.PASSWORD_PROTECTED, | ||
74 | videoPasswords: [ 'password1', 'password2' ] | ||
75 | }) | ||
76 | } | ||
77 | path = '/api/v1/videos/' | ||
78 | }) | ||
79 | |||
80 | async function checkVideoPasswordOptions (options: { | ||
81 | server: PeerTubeServer | ||
82 | token: string | ||
83 | videoPasswords: string[] | ||
84 | expectedStatus: HttpStatusCode | ||
85 | mode: 'uploadLegacy' | 'uploadResumable' | 'import' | 'updateVideo' | 'updatePasswords' | 'live' | ||
86 | }) { | ||
87 | const { server, token, videoPasswords, expectedStatus = HttpStatusCode.OK_200, mode } = options | ||
88 | const attaches = { | ||
89 | fixture: join(root(), 'server', 'tests', 'fixtures', 'video_short.webm') | ||
90 | } | ||
91 | const baseCorrectParams = { | ||
92 | name: 'my super name', | ||
93 | category: 5, | ||
94 | licence: 1, | ||
95 | language: 'pt', | ||
96 | nsfw: false, | ||
97 | commentsEnabled: true, | ||
98 | downloadEnabled: true, | ||
99 | waitTranscoding: true, | ||
100 | description: 'my super description', | ||
101 | support: 'my super support text', | ||
102 | tags: [ 'tag1', 'tag2' ], | ||
103 | privacy: VideoPrivacy.PASSWORD_PROTECTED, | ||
104 | channelId, | ||
105 | originallyPublishedAt: new Date().toISOString() | ||
106 | } | ||
107 | if (mode === 'uploadLegacy') { | ||
108 | const fields = { ...baseCorrectParams, videoPasswords } | ||
109 | return checkUploadVideoParam(server, token, { ...fields, ...attaches }, expectedStatus, 'legacy') | ||
110 | } | ||
111 | |||
112 | if (mode === 'uploadResumable') { | ||
113 | const fields = { ...baseCorrectParams, videoPasswords } | ||
114 | return checkUploadVideoParam(server, token, { ...fields, ...attaches }, expectedStatus, 'resumable') | ||
115 | } | ||
116 | |||
117 | if (mode === 'import') { | ||
118 | const attributes = { ...baseCorrectParams, targetUrl: FIXTURE_URLS.goodVideo, videoPasswords } | ||
119 | return server.imports.importVideo({ attributes, expectedStatus }) | ||
120 | } | ||
121 | |||
122 | if (mode === 'updateVideo') { | ||
123 | const attributes = { ...baseCorrectParams, videoPasswords } | ||
124 | return server.videos.update({ token, expectedStatus, id: video.id, attributes }) | ||
125 | } | ||
126 | |||
127 | if (mode === 'updatePasswords') { | ||
128 | return server.videoPasswords.updateAll({ token, expectedStatus, videoId: video.id, passwords: videoPasswords }) | ||
129 | } | ||
130 | |||
131 | if (mode === 'live') { | ||
132 | const fields = { ...baseCorrectParams, videoPasswords } | ||
133 | |||
134 | return server.live.create({ fields, expectedStatus }) | ||
135 | } | ||
136 | } | ||
137 | |||
138 | function validateVideoPasswordList (mode: 'uploadLegacy' | 'uploadResumable' | 'import' | 'updateVideo' | 'updatePasswords' | 'live') { | ||
139 | |||
140 | it('Should fail with a password protected privacy without providing a password', async function () { | ||
141 | await checkVideoPasswordOptions({ | ||
142 | server, | ||
143 | token: server.accessToken, | ||
144 | videoPasswords: undefined, | ||
145 | expectedStatus: HttpStatusCode.BAD_REQUEST_400, | ||
146 | mode | ||
147 | }) | ||
148 | }) | ||
149 | |||
150 | it('Should fail with a password protected privacy and an empty password list', async function () { | ||
151 | const videoPasswords = [] | ||
152 | |||
153 | await checkVideoPasswordOptions({ | ||
154 | server, | ||
155 | token: server.accessToken, | ||
156 | videoPasswords, | ||
157 | expectedStatus: HttpStatusCode.BAD_REQUEST_400, | ||
158 | mode | ||
159 | }) | ||
160 | }) | ||
161 | |||
162 | it('Should fail with a password protected privacy and a too short password', async function () { | ||
163 | const videoPasswords = [ 'p' ] | ||
164 | |||
165 | await checkVideoPasswordOptions({ | ||
166 | server, | ||
167 | token: server.accessToken, | ||
168 | videoPasswords, | ||
169 | expectedStatus: HttpStatusCode.BAD_REQUEST_400, | ||
170 | mode | ||
171 | }) | ||
172 | }) | ||
173 | |||
174 | it('Should fail with a password protected privacy and a too long password', async function () { | ||
175 | const videoPasswords = [ 'Very very very very very very very very very very very very very very very very very very long password' ] | ||
176 | |||
177 | await checkVideoPasswordOptions({ | ||
178 | server, | ||
179 | token: server.accessToken, | ||
180 | videoPasswords, | ||
181 | expectedStatus: HttpStatusCode.BAD_REQUEST_400, | ||
182 | mode | ||
183 | }) | ||
184 | }) | ||
185 | |||
186 | it('Should fail with a password protected privacy and an empty password', async function () { | ||
187 | const videoPasswords = [ '' ] | ||
188 | |||
189 | await checkVideoPasswordOptions({ | ||
190 | server, | ||
191 | token: server.accessToken, | ||
192 | videoPasswords, | ||
193 | expectedStatus: HttpStatusCode.BAD_REQUEST_400, | ||
194 | mode | ||
195 | }) | ||
196 | }) | ||
197 | |||
198 | it('Should fail with a password protected privacy and duplicated passwords', async function () { | ||
199 | const videoPasswords = [ 'password', 'password' ] | ||
200 | |||
201 | await checkVideoPasswordOptions({ | ||
202 | server, | ||
203 | token: server.accessToken, | ||
204 | videoPasswords, | ||
205 | expectedStatus: HttpStatusCode.BAD_REQUEST_400, | ||
206 | mode | ||
207 | }) | ||
208 | }) | ||
209 | |||
210 | if (mode === 'updatePasswords') { | ||
211 | it('Should fail for an unauthenticated user', async function () { | ||
212 | const videoPasswords = [ 'password' ] | ||
213 | await checkVideoPasswordOptions({ | ||
214 | server, | ||
215 | token: null, | ||
216 | videoPasswords, | ||
217 | expectedStatus: HttpStatusCode.UNAUTHORIZED_401, | ||
218 | mode | ||
219 | }) | ||
220 | }) | ||
221 | |||
222 | it('Should fail for an unauthorized user', async function () { | ||
223 | const videoPasswords = [ 'password' ] | ||
224 | await checkVideoPasswordOptions({ | ||
225 | server, | ||
226 | token: userAccessToken, | ||
227 | videoPasswords, | ||
228 | expectedStatus: HttpStatusCode.FORBIDDEN_403, | ||
229 | mode | ||
230 | }) | ||
231 | }) | ||
232 | } | ||
233 | |||
234 | it('Should succeed with a password protected privacy and correct passwords', async function () { | ||
235 | const videoPasswords = [ 'password1', 'password2' ] | ||
236 | const expectedStatus = mode === 'updatePasswords' || mode === 'updateVideo' | ||
237 | ? HttpStatusCode.NO_CONTENT_204 | ||
238 | : HttpStatusCode.OK_200 | ||
239 | |||
240 | await checkVideoPasswordOptions({ server, token: server.accessToken, videoPasswords, expectedStatus, mode }) | ||
241 | }) | ||
242 | } | ||
243 | |||
244 | describe('When adding or updating a video', function () { | ||
245 | describe('Resumable upload', function () { | ||
246 | validateVideoPasswordList('uploadResumable') | ||
247 | }) | ||
248 | |||
249 | describe('Legacy upload', function () { | ||
250 | validateVideoPasswordList('uploadLegacy') | ||
251 | }) | ||
252 | |||
253 | describe('When importing a video', function () { | ||
254 | validateVideoPasswordList('import') | ||
255 | }) | ||
256 | |||
257 | describe('When updating a video', function () { | ||
258 | validateVideoPasswordList('updateVideo') | ||
259 | }) | ||
260 | |||
261 | describe('When updating the password list of a video', function () { | ||
262 | validateVideoPasswordList('updatePasswords') | ||
263 | }) | ||
264 | |||
265 | describe('When creating a live', function () { | ||
266 | validateVideoPasswordList('live') | ||
267 | }) | ||
268 | }) | ||
269 | |||
270 | async function checkVideoAccessOptions (options: { | ||
271 | server: PeerTubeServer | ||
272 | token?: string | ||
273 | videoPassword?: string | ||
274 | expectedStatus: HttpStatusCode | ||
275 | mode: 'get' | 'getWithPassword' | 'getWithToken' | 'listCaptions' | 'createThread' | 'listThreads' | 'replyThread' | 'rate' | 'token' | ||
276 | }) { | ||
277 | const { server, token = null, videoPassword, expectedStatus, mode } = options | ||
278 | |||
279 | if (mode === 'get') { | ||
280 | return server.videos.get({ id: video.id, expectedStatus }) | ||
281 | } | ||
282 | |||
283 | if (mode === 'getWithToken') { | ||
284 | return server.videos.getWithToken({ | ||
285 | id: video.id, | ||
286 | token, | ||
287 | expectedStatus | ||
288 | }) | ||
289 | } | ||
290 | |||
291 | if (mode === 'getWithPassword') { | ||
292 | return server.videos.getWithPassword({ | ||
293 | id: video.id, | ||
294 | token, | ||
295 | expectedStatus, | ||
296 | password: videoPassword | ||
297 | }) | ||
298 | } | ||
299 | |||
300 | if (mode === 'rate') { | ||
301 | return server.videos.rate({ | ||
302 | id: video.id, | ||
303 | token, | ||
304 | expectedStatus, | ||
305 | rating: 'like', | ||
306 | videoPassword | ||
307 | }) | ||
308 | } | ||
309 | |||
310 | if (mode === 'createThread') { | ||
311 | const fields = { text: 'super comment' } | ||
312 | const headers = videoPassword !== undefined && videoPassword !== null | ||
313 | ? { 'x-peertube-video-password': videoPassword } | ||
314 | : undefined | ||
315 | const body = await makePostBodyRequest({ | ||
316 | url: server.url, | ||
317 | path: path + video.uuid + '/comment-threads', | ||
318 | token, | ||
319 | fields, | ||
320 | headers, | ||
321 | expectedStatus | ||
322 | }) | ||
323 | return JSON.parse(body.text) | ||
324 | } | ||
325 | |||
326 | if (mode === 'replyThread') { | ||
327 | const fields = { text: 'super reply' } | ||
328 | const headers = videoPassword !== undefined && videoPassword !== null | ||
329 | ? { 'x-peertube-video-password': videoPassword } | ||
330 | : undefined | ||
331 | return makePostBodyRequest({ | ||
332 | url: server.url, | ||
333 | path: path + video.uuid + '/comments/' + commentId, | ||
334 | token, | ||
335 | fields, | ||
336 | headers, | ||
337 | expectedStatus | ||
338 | }) | ||
339 | } | ||
340 | if (mode === 'listThreads') { | ||
341 | return server.comments.listThreads({ | ||
342 | videoId: video.id, | ||
343 | token, | ||
344 | expectedStatus, | ||
345 | videoPassword | ||
346 | }) | ||
347 | } | ||
348 | |||
349 | if (mode === 'listCaptions') { | ||
350 | return server.captions.list({ | ||
351 | videoId: video.id, | ||
352 | token, | ||
353 | expectedStatus, | ||
354 | videoPassword | ||
355 | }) | ||
356 | } | ||
357 | |||
358 | if (mode === 'token') { | ||
359 | return server.videoToken.create({ | ||
360 | videoId: video.id, | ||
361 | token, | ||
362 | expectedStatus, | ||
363 | videoPassword | ||
364 | }) | ||
365 | } | ||
366 | } | ||
367 | |||
368 | function checkVideoError (error: any, mode: 'providePassword' | 'incorrectPassword') { | ||
369 | const serverCode = mode === 'providePassword' | ||
370 | ? ServerErrorCode.VIDEO_REQUIRES_PASSWORD | ||
371 | : ServerErrorCode.INCORRECT_VIDEO_PASSWORD | ||
372 | |||
373 | const message = mode === 'providePassword' | ||
374 | ? 'Please provide a password to access this password protected video' | ||
375 | : 'Incorrect video password. Access to the video is denied.' | ||
376 | |||
377 | if (!error.code) { | ||
378 | error = JSON.parse(error.text) | ||
379 | } | ||
380 | |||
381 | expect(error.code).to.equal(serverCode) | ||
382 | expect(error.detail).to.equal(message) | ||
383 | expect(error.error).to.equal(message) | ||
384 | |||
385 | expect(error.status).to.equal(HttpStatusCode.FORBIDDEN_403) | ||
386 | } | ||
387 | |||
388 | function validateVideoAccess (mode: 'get' | 'listCaptions' | 'createThread' | 'listThreads' | 'replyThread' | 'rate' | 'token') { | ||
389 | const requiresUserAuth = [ 'createThread', 'replyThread', 'rate' ].includes(mode) | ||
390 | let tokens: string[] | ||
391 | if (!requiresUserAuth) { | ||
392 | it('Should fail without providing a password for an unlogged user', async function () { | ||
393 | const body = await checkVideoAccessOptions({ server, expectedStatus: HttpStatusCode.FORBIDDEN_403, mode }) | ||
394 | const error = body as unknown as PeerTubeProblemDocument | ||
395 | |||
396 | checkVideoError(error, 'providePassword') | ||
397 | }) | ||
398 | } | ||
399 | |||
400 | it('Should fail without providing a password for an unauthorised user', async function () { | ||
401 | const tmp = mode === 'get' ? 'getWithToken' : mode | ||
402 | |||
403 | const body = await checkVideoAccessOptions({ | ||
404 | server, | ||
405 | token: userAccessToken, | ||
406 | expectedStatus: HttpStatusCode.FORBIDDEN_403, | ||
407 | mode: tmp | ||
408 | }) | ||
409 | |||
410 | const error = body as unknown as PeerTubeProblemDocument | ||
411 | |||
412 | checkVideoError(error, 'providePassword') | ||
413 | }) | ||
414 | |||
415 | it('Should fail if a wrong password is entered', async function () { | ||
416 | const tmp = mode === 'get' ? 'getWithPassword' : mode | ||
417 | tokens = [ userAccessToken, server.accessToken ] | ||
418 | |||
419 | if (!requiresUserAuth) tokens.push(null) | ||
420 | |||
421 | for (const token of tokens) { | ||
422 | const body = await checkVideoAccessOptions({ | ||
423 | server, | ||
424 | token, | ||
425 | videoPassword: 'toto', | ||
426 | expectedStatus: HttpStatusCode.FORBIDDEN_403, | ||
427 | mode: tmp | ||
428 | }) | ||
429 | const error = body as unknown as PeerTubeProblemDocument | ||
430 | |||
431 | checkVideoError(error, 'incorrectPassword') | ||
432 | } | ||
433 | }) | ||
434 | |||
435 | it('Should fail if an empty password is entered', async function () { | ||
436 | const tmp = mode === 'get' ? 'getWithPassword' : mode | ||
437 | |||
438 | for (const token of tokens) { | ||
439 | const body = await checkVideoAccessOptions({ | ||
440 | server, | ||
441 | token, | ||
442 | videoPassword: '', | ||
443 | expectedStatus: HttpStatusCode.FORBIDDEN_403, | ||
444 | mode: tmp | ||
445 | }) | ||
446 | const error = body as unknown as PeerTubeProblemDocument | ||
447 | |||
448 | checkVideoError(error, 'incorrectPassword') | ||
449 | } | ||
450 | }) | ||
451 | |||
452 | it('Should fail if an inccorect password containing the correct password is entered', async function () { | ||
453 | const tmp = mode === 'get' ? 'getWithPassword' : mode | ||
454 | |||
455 | for (const token of tokens) { | ||
456 | const body = await checkVideoAccessOptions({ | ||
457 | server, | ||
458 | token, | ||
459 | videoPassword: 'password11', | ||
460 | expectedStatus: HttpStatusCode.FORBIDDEN_403, | ||
461 | mode: tmp | ||
462 | }) | ||
463 | const error = body as unknown as PeerTubeProblemDocument | ||
464 | |||
465 | checkVideoError(error, 'incorrectPassword') | ||
466 | } | ||
467 | }) | ||
468 | |||
469 | it('Should succeed without providing a password for an authorised user', async function () { | ||
470 | const tmp = mode === 'get' ? 'getWithToken' : mode | ||
471 | const expectedStatus = mode === 'rate' ? HttpStatusCode.NO_CONTENT_204 : HttpStatusCode.OK_200 | ||
472 | |||
473 | const body = await checkVideoAccessOptions({ server, token: server.accessToken, expectedStatus, mode: tmp }) | ||
474 | |||
475 | if (mode === 'createThread') commentId = body.comment.id | ||
476 | }) | ||
477 | |||
478 | it('Should succeed using correct passwords', async function () { | ||
479 | const tmp = mode === 'get' ? 'getWithPassword' : mode | ||
480 | const expectedStatus = mode === 'rate' ? HttpStatusCode.NO_CONTENT_204 : HttpStatusCode.OK_200 | ||
481 | |||
482 | for (const token of tokens) { | ||
483 | await checkVideoAccessOptions({ server, videoPassword: 'password1', token, expectedStatus, mode: tmp }) | ||
484 | await checkVideoAccessOptions({ server, videoPassword: 'password2', token, expectedStatus, mode: tmp }) | ||
485 | } | ||
486 | }) | ||
487 | } | ||
488 | |||
489 | describe('When accessing password protected video', function () { | ||
490 | |||
491 | describe('For getting a password protected video', function () { | ||
492 | validateVideoAccess('get') | ||
493 | }) | ||
494 | |||
495 | describe('For rating a video', function () { | ||
496 | validateVideoAccess('rate') | ||
497 | }) | ||
498 | |||
499 | describe('For creating a thread', function () { | ||
500 | validateVideoAccess('createThread') | ||
501 | }) | ||
502 | |||
503 | describe('For replying to a thread', function () { | ||
504 | validateVideoAccess('replyThread') | ||
505 | }) | ||
506 | |||
507 | describe('For listing threads', function () { | ||
508 | validateVideoAccess('listThreads') | ||
509 | }) | ||
510 | |||
511 | describe('For getting captions', function () { | ||
512 | validateVideoAccess('listCaptions') | ||
513 | }) | ||
514 | |||
515 | describe('For creating video file token', function () { | ||
516 | validateVideoAccess('token') | ||
517 | }) | ||
518 | }) | ||
519 | |||
520 | describe('When listing passwords', function () { | ||
521 | it('Should fail with a bad start pagination', async function () { | ||
522 | await checkBadStartPagination(server.url, path + video.uuid + '/passwords', server.accessToken) | ||
523 | }) | ||
524 | |||
525 | it('Should fail with a bad count pagination', async function () { | ||
526 | await checkBadCountPagination(server.url, path + video.uuid + '/passwords', server.accessToken) | ||
527 | }) | ||
528 | |||
529 | it('Should fail with an incorrect sort', async function () { | ||
530 | await checkBadSortPagination(server.url, path + video.uuid + '/passwords', server.accessToken) | ||
531 | }) | ||
532 | |||
533 | it('Should fail for unauthenticated user', async function () { | ||
534 | await server.videoPasswords.list({ | ||
535 | token: null, | ||
536 | expectedStatus: HttpStatusCode.UNAUTHORIZED_401, | ||
537 | videoId: video.id | ||
538 | }) | ||
539 | }) | ||
540 | |||
541 | it('Should fail for unauthorized user', async function () { | ||
542 | await server.videoPasswords.list({ | ||
543 | token: userAccessToken, | ||
544 | expectedStatus: HttpStatusCode.FORBIDDEN_403, | ||
545 | videoId: video.id | ||
546 | }) | ||
547 | }) | ||
548 | |||
549 | it('Should succeed with the correct parameters', async function () { | ||
550 | await server.videoPasswords.list({ | ||
551 | token: server.accessToken, | ||
552 | expectedStatus: HttpStatusCode.OK_200, | ||
553 | videoId: video.id | ||
554 | }) | ||
555 | }) | ||
556 | }) | ||
557 | |||
558 | describe('When deleting a password', async function () { | ||
559 | const passwords = (await server.videoPasswords.list({ videoId: video.id })).data | ||
560 | |||
561 | it('Should fail with wrong password id', async function () { | ||
562 | await server.videoPasswords.remove({ id: -1, videoId: video.id, expectedStatus: HttpStatusCode.NOT_FOUND_404 }) | ||
563 | }) | ||
564 | |||
565 | it('Should fail for unauthenticated user', async function () { | ||
566 | await server.videoPasswords.remove({ | ||
567 | id: passwords[0].id, | ||
568 | token: null, | ||
569 | videoId: video.id, | ||
570 | expectedStatus: HttpStatusCode.FORBIDDEN_403 | ||
571 | }) | ||
572 | }) | ||
573 | |||
574 | it('Should fail for unauthorized user', async function () { | ||
575 | await server.videoPasswords.remove({ | ||
576 | id: passwords[0].id, | ||
577 | token: userAccessToken, | ||
578 | videoId: video.id, | ||
579 | expectedStatus: HttpStatusCode.BAD_REQUEST_400 | ||
580 | }) | ||
581 | }) | ||
582 | |||
583 | it('Should fail for non password protected video', async function () { | ||
584 | publicVideo = await server.videos.quickUpload({ name: 'public video' }) | ||
585 | await server.videoPasswords.remove({ id: passwords[0].id, videoId: publicVideo.id, expectedStatus: HttpStatusCode.BAD_REQUEST_400 }) | ||
586 | }) | ||
587 | |||
588 | it('Should fail for password not linked to correct video', async function () { | ||
589 | const video2 = await server.videos.quickUpload({ | ||
590 | name: 'password protected video', | ||
591 | privacy: VideoPrivacy.PASSWORD_PROTECTED, | ||
592 | videoPasswords: [ 'password1', 'password2' ] | ||
593 | }) | ||
594 | await server.videoPasswords.remove({ id: passwords[0].id, videoId: video2.id, expectedStatus: HttpStatusCode.NOT_FOUND_404 }) | ||
595 | }) | ||
596 | |||
597 | it('Should succeed with correct parameter', async function () { | ||
598 | await server.videoPasswords.remove({ id: passwords[0].id, videoId: video.id, expectedStatus: HttpStatusCode.NO_CONTENT_204 }) | ||
599 | }) | ||
600 | |||
601 | it('Should fail for last password of a video', async function () { | ||
602 | await server.videoPasswords.remove({ id: passwords[1].id, videoId: video.id, expectedStatus: HttpStatusCode.BAD_REQUEST_400 }) | ||
603 | }) | ||
604 | }) | ||
605 | |||
606 | after(async function () { | ||
607 | await cleanupTests([ server ]) | ||
608 | }) | ||
609 | }) | ||
diff --git a/server/tests/api/check-params/video-token.ts b/server/tests/api/check-params/video-token.ts index 7acb9d580..7cb3e84a2 100644 --- a/server/tests/api/check-params/video-token.ts +++ b/server/tests/api/check-params/video-token.ts | |||
@@ -5,9 +5,12 @@ import { cleanupTests, createSingleServer, PeerTubeServer, setAccessTokensToServ | |||
5 | 5 | ||
6 | describe('Test video tokens', function () { | 6 | describe('Test video tokens', function () { |
7 | let server: PeerTubeServer | 7 | let server: PeerTubeServer |
8 | let videoId: string | 8 | let privateVideoId: string |
9 | let passwordProtectedVideoId: string | ||
9 | let userToken: string | 10 | let userToken: string |
10 | 11 | ||
12 | const videoPassword = 'password' | ||
13 | |||
11 | // --------------------------------------------------------------- | 14 | // --------------------------------------------------------------- |
12 | 15 | ||
13 | before(async function () { | 16 | before(async function () { |
@@ -15,27 +18,50 @@ describe('Test video tokens', function () { | |||
15 | 18 | ||
16 | server = await createSingleServer(1) | 19 | server = await createSingleServer(1) |
17 | await setAccessTokensToServers([ server ]) | 20 | await setAccessTokensToServers([ server ]) |
18 | 21 | { | |
19 | const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.PRIVATE }) | 22 | const { uuid } = await server.videos.quickUpload({ name: 'private video', privacy: VideoPrivacy.PRIVATE }) |
20 | videoId = uuid | 23 | privateVideoId = uuid |
21 | 24 | } | |
25 | { | ||
26 | const { uuid } = await server.videos.quickUpload({ | ||
27 | name: 'password protected video', | ||
28 | privacy: VideoPrivacy.PASSWORD_PROTECTED, | ||
29 | videoPasswords: [ videoPassword ] | ||
30 | }) | ||
31 | passwordProtectedVideoId = uuid | ||
32 | } | ||
22 | userToken = await server.users.generateUserAndToken('user1') | 33 | userToken = await server.users.generateUserAndToken('user1') |
23 | }) | 34 | }) |
24 | 35 | ||
25 | it('Should not generate tokens for unauthenticated user', async function () { | 36 | it('Should not generate tokens on private video for unauthenticated user', async function () { |
26 | await server.videoToken.create({ videoId, token: null, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 }) | 37 | await server.videoToken.create({ videoId: privateVideoId, token: null, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 }) |
27 | }) | 38 | }) |
28 | 39 | ||
29 | it('Should not generate tokens of unknown video', async function () { | 40 | it('Should not generate tokens of unknown video', async function () { |
30 | await server.videoToken.create({ videoId: 404, expectedStatus: HttpStatusCode.NOT_FOUND_404 }) | 41 | await server.videoToken.create({ videoId: 404, expectedStatus: HttpStatusCode.NOT_FOUND_404 }) |
31 | }) | 42 | }) |
32 | 43 | ||
44 | it('Should not generate tokens with incorrect password', async function () { | ||
45 | await server.videoToken.create({ | ||
46 | videoId: passwordProtectedVideoId, | ||
47 | token: null, | ||
48 | expectedStatus: HttpStatusCode.FORBIDDEN_403, | ||
49 | videoPassword: 'incorrectPassword' | ||
50 | }) | ||
51 | }) | ||
52 | |||
33 | it('Should not generate tokens of a non owned video', async function () { | 53 | it('Should not generate tokens of a non owned video', async function () { |
34 | await server.videoToken.create({ videoId, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) | 54 | await server.videoToken.create({ videoId: privateVideoId, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) |
35 | }) | 55 | }) |
36 | 56 | ||
37 | it('Should generate token', async function () { | 57 | it('Should generate token', async function () { |
38 | await server.videoToken.create({ videoId }) | 58 | await server.videoToken.create({ videoId: privateVideoId }) |
59 | }) | ||
60 | |||
61 | it('Should generate token on password protected video', async function () { | ||
62 | await server.videoToken.create({ videoId: passwordProtectedVideoId, videoPassword, token: null }) | ||
63 | await server.videoToken.create({ videoId: passwordProtectedVideoId, videoPassword, token: userToken }) | ||
64 | await server.videoToken.create({ videoId: passwordProtectedVideoId, videoPassword }) | ||
39 | }) | 65 | }) |
40 | 66 | ||
41 | after(async function () { | 67 | after(async function () { |
diff --git a/server/tests/api/object-storage/video-static-file-privacy.ts b/server/tests/api/object-storage/video-static-file-privacy.ts index af9d681b2..2a7c3381d 100644 --- a/server/tests/api/object-storage/video-static-file-privacy.ts +++ b/server/tests/api/object-storage/video-static-file-privacy.ts | |||
@@ -107,8 +107,13 @@ describe('Object storage for video static file privacy', function () { | |||
107 | describe('VOD', function () { | 107 | describe('VOD', function () { |
108 | let privateVideoUUID: string | 108 | let privateVideoUUID: string |
109 | let publicVideoUUID: string | 109 | let publicVideoUUID: string |
110 | let passwordProtectedVideoUUID: string | ||
110 | let userPrivateVideoUUID: string | 111 | let userPrivateVideoUUID: string |
111 | 112 | ||
113 | const correctPassword = 'my super password' | ||
114 | const correctPasswordHeader = { 'x-peertube-video-password': correctPassword } | ||
115 | const incorrectPasswordHeader = { 'x-peertube-video-password': correctPassword + 'toto' } | ||
116 | |||
112 | // --------------------------------------------------------------------------- | 117 | // --------------------------------------------------------------------------- |
113 | 118 | ||
114 | async function getSampleFileUrls (videoId: string) { | 119 | async function getSampleFileUrls (videoId: string) { |
@@ -140,6 +145,22 @@ describe('Object storage for video static file privacy', function () { | |||
140 | await checkPrivateVODFiles(privateVideoUUID) | 145 | await checkPrivateVODFiles(privateVideoUUID) |
141 | }) | 146 | }) |
142 | 147 | ||
148 | it('Should upload a password protected video and have appropriate object storage ACL', async function () { | ||
149 | this.timeout(120000) | ||
150 | |||
151 | { | ||
152 | const { uuid } = await server.videos.quickUpload({ | ||
153 | name: 'video', | ||
154 | privacy: VideoPrivacy.PASSWORD_PROTECTED, | ||
155 | videoPasswords: [ correctPassword ] | ||
156 | }) | ||
157 | passwordProtectedVideoUUID = uuid | ||
158 | } | ||
159 | await waitJobs([ server ]) | ||
160 | |||
161 | await checkPrivateVODFiles(passwordProtectedVideoUUID) | ||
162 | }) | ||
163 | |||
143 | it('Should upload a public video and have appropriate object storage ACL', async function () { | 164 | it('Should upload a public video and have appropriate object storage ACL', async function () { |
144 | this.timeout(120000) | 165 | this.timeout(120000) |
145 | 166 | ||
@@ -163,6 +184,42 @@ describe('Object storage for video static file privacy', function () { | |||
163 | await makeRawRequest({ url: hlsFile, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 }) | 184 | await makeRawRequest({ url: hlsFile, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 }) |
164 | }) | 185 | }) |
165 | 186 | ||
187 | it('Should not get files without appropriate password or appropriate OAuth token', async function () { | ||
188 | this.timeout(60000) | ||
189 | |||
190 | const { webTorrentFile, hlsFile } = await getSampleFileUrls(passwordProtectedVideoUUID) | ||
191 | |||
192 | await makeRawRequest({ url: webTorrentFile, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) | ||
193 | await makeRawRequest({ | ||
194 | url: webTorrentFile, | ||
195 | token: null, | ||
196 | headers: incorrectPasswordHeader, | ||
197 | expectedStatus: HttpStatusCode.FORBIDDEN_403 | ||
198 | }) | ||
199 | await makeRawRequest({ url: webTorrentFile, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 }) | ||
200 | await makeRawRequest({ | ||
201 | url: webTorrentFile, | ||
202 | token: null, | ||
203 | headers: correctPasswordHeader, | ||
204 | expectedStatus: HttpStatusCode.OK_200 | ||
205 | }) | ||
206 | |||
207 | await makeRawRequest({ url: hlsFile, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) | ||
208 | await makeRawRequest({ | ||
209 | url: hlsFile, | ||
210 | token: null, | ||
211 | headers: incorrectPasswordHeader, | ||
212 | expectedStatus: HttpStatusCode.FORBIDDEN_403 | ||
213 | }) | ||
214 | await makeRawRequest({ url: hlsFile, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 }) | ||
215 | await makeRawRequest({ | ||
216 | url: hlsFile, | ||
217 | token: null, | ||
218 | headers: correctPasswordHeader, | ||
219 | expectedStatus: HttpStatusCode.OK_200 | ||
220 | }) | ||
221 | }) | ||
222 | |||
166 | it('Should not get HLS file of another video', async function () { | 223 | it('Should not get HLS file of another video', async function () { |
167 | this.timeout(60000) | 224 | this.timeout(60000) |
168 | 225 | ||
@@ -176,7 +233,7 @@ describe('Object storage for video static file privacy', function () { | |||
176 | await makeRawRequest({ url: goodUrl, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 }) | 233 | await makeRawRequest({ url: goodUrl, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 }) |
177 | }) | 234 | }) |
178 | 235 | ||
179 | it('Should correctly check OAuth or video file token', async function () { | 236 | it('Should correctly check OAuth, video file token of private video', async function () { |
180 | this.timeout(60000) | 237 | this.timeout(60000) |
181 | 238 | ||
182 | const badVideoFileToken = await server.videoToken.getVideoFileToken({ token: userToken, videoId: userPrivateVideoUUID }) | 239 | const badVideoFileToken = await server.videoToken.getVideoFileToken({ token: userToken, videoId: userPrivateVideoUUID }) |
@@ -191,6 +248,35 @@ describe('Object storage for video static file privacy', function () { | |||
191 | 248 | ||
192 | await makeRawRequest({ url, query: { videoFileToken: badVideoFileToken }, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) | 249 | await makeRawRequest({ url, query: { videoFileToken: badVideoFileToken }, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) |
193 | await makeRawRequest({ url, query: { videoFileToken: goodVideoFileToken }, expectedStatus: HttpStatusCode.OK_200 }) | 250 | await makeRawRequest({ url, query: { videoFileToken: goodVideoFileToken }, expectedStatus: HttpStatusCode.OK_200 }) |
251 | |||
252 | } | ||
253 | }) | ||
254 | |||
255 | it('Should correctly check OAuth, video file token or video password of password protected video', async function () { | ||
256 | this.timeout(60000) | ||
257 | |||
258 | const badVideoFileToken = await server.videoToken.getVideoFileToken({ token: userToken, videoId: userPrivateVideoUUID }) | ||
259 | const goodVideoFileToken = await server.videoToken.getVideoFileToken({ | ||
260 | videoId: passwordProtectedVideoUUID, | ||
261 | videoPassword: correctPassword | ||
262 | }) | ||
263 | |||
264 | const { webTorrentFile, hlsFile } = await getSampleFileUrls(passwordProtectedVideoUUID) | ||
265 | |||
266 | for (const url of [ hlsFile, webTorrentFile ]) { | ||
267 | await makeRawRequest({ url, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) | ||
268 | await makeRawRequest({ url, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) | ||
269 | await makeRawRequest({ url, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 }) | ||
270 | |||
271 | await makeRawRequest({ url, query: { videoFileToken: badVideoFileToken }, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) | ||
272 | await makeRawRequest({ url, query: { videoFileToken: goodVideoFileToken }, expectedStatus: HttpStatusCode.OK_200 }) | ||
273 | |||
274 | await makeRawRequest({ | ||
275 | url, | ||
276 | headers: incorrectPasswordHeader, | ||
277 | expectedStatus: HttpStatusCode.FORBIDDEN_403 | ||
278 | }) | ||
279 | await makeRawRequest({ url, headers: correctPasswordHeader, expectedStatus: HttpStatusCode.OK_200 }) | ||
194 | } | 280 | } |
195 | }) | 281 | }) |
196 | 282 | ||
@@ -232,16 +318,26 @@ describe('Object storage for video static file privacy', function () { | |||
232 | let permanentLiveId: string | 318 | let permanentLiveId: string |
233 | let permanentLive: LiveVideo | 319 | let permanentLive: LiveVideo |
234 | 320 | ||
321 | let passwordProtectedLiveId: string | ||
322 | let passwordProtectedLive: LiveVideo | ||
323 | |||
324 | const correctPassword = 'my super password' | ||
325 | |||
235 | let unrelatedFileToken: string | 326 | let unrelatedFileToken: string |
236 | 327 | ||
237 | // --------------------------------------------------------------------------- | 328 | // --------------------------------------------------------------------------- |
238 | 329 | ||
239 | async function checkLiveFiles (live: LiveVideo, liveId: string) { | 330 | async function checkLiveFiles (live: LiveVideo, liveId: string, videoPassword?: string) { |
240 | const ffmpegCommand = sendRTMPStream({ rtmpBaseUrl: live.rtmpUrl, streamKey: live.streamKey }) | 331 | const ffmpegCommand = sendRTMPStream({ rtmpBaseUrl: live.rtmpUrl, streamKey: live.streamKey }) |
241 | await server.live.waitUntilPublished({ videoId: liveId }) | 332 | await server.live.waitUntilPublished({ videoId: liveId }) |
242 | 333 | ||
243 | const video = await server.videos.getWithToken({ id: liveId }) | 334 | const video = videoPassword |
244 | const fileToken = await server.videoToken.getVideoFileToken({ videoId: video.uuid }) | 335 | ? await server.videos.getWithPassword({ id: liveId, password: videoPassword }) |
336 | : await server.videos.getWithToken({ id: liveId }) | ||
337 | |||
338 | const fileToken = videoPassword | ||
339 | ? await server.videoToken.getVideoFileToken({ token: null, videoId: video.uuid, videoPassword }) | ||
340 | : await server.videoToken.getVideoFileToken({ videoId: video.uuid }) | ||
245 | 341 | ||
246 | const hls = video.streamingPlaylists[0] | 342 | const hls = video.streamingPlaylists[0] |
247 | 343 | ||
@@ -253,10 +349,19 @@ describe('Object storage for video static file privacy', function () { | |||
253 | 349 | ||
254 | await makeRawRequest({ url, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 }) | 350 | await makeRawRequest({ url, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 }) |
255 | await makeRawRequest({ url, query: { videoFileToken: fileToken }, expectedStatus: HttpStatusCode.OK_200 }) | 351 | await makeRawRequest({ url, query: { videoFileToken: fileToken }, expectedStatus: HttpStatusCode.OK_200 }) |
256 | 352 | if (videoPassword) { | |
353 | await makeRawRequest({ url, headers: { 'x-peertube-video-password': videoPassword }, expectedStatus: HttpStatusCode.OK_200 }) | ||
354 | } | ||
257 | await makeRawRequest({ url, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) | 355 | await makeRawRequest({ url, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) |
258 | await makeRawRequest({ url, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) | 356 | await makeRawRequest({ url, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) |
259 | await makeRawRequest({ url, query: { videoFileToken: unrelatedFileToken }, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) | 357 | await makeRawRequest({ url, query: { videoFileToken: unrelatedFileToken }, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) |
358 | if (videoPassword) { | ||
359 | await makeRawRequest({ | ||
360 | url, | ||
361 | headers: { 'x-peertube-video-password': 'incorrectPassword' }, | ||
362 | expectedStatus: HttpStatusCode.FORBIDDEN_403 | ||
363 | }) | ||
364 | } | ||
260 | } | 365 | } |
261 | 366 | ||
262 | await stopFfmpeg(ffmpegCommand) | 367 | await stopFfmpeg(ffmpegCommand) |
@@ -326,6 +431,17 @@ describe('Object storage for video static file privacy', function () { | |||
326 | permanentLiveId = video.uuid | 431 | permanentLiveId = video.uuid |
327 | permanentLive = live | 432 | permanentLive = live |
328 | } | 433 | } |
434 | |||
435 | { | ||
436 | const { video, live } = await server.live.quickCreate({ | ||
437 | saveReplay: false, | ||
438 | permanentLive: false, | ||
439 | privacy: VideoPrivacy.PASSWORD_PROTECTED, | ||
440 | videoPasswords: [ correctPassword ] | ||
441 | }) | ||
442 | passwordProtectedLiveId = video.uuid | ||
443 | passwordProtectedLive = live | ||
444 | } | ||
329 | }) | 445 | }) |
330 | 446 | ||
331 | it('Should create a private normal live and have a private static path', async function () { | 447 | it('Should create a private normal live and have a private static path', async function () { |
@@ -340,6 +456,12 @@ describe('Object storage for video static file privacy', function () { | |||
340 | await checkLiveFiles(permanentLive, permanentLiveId) | 456 | await checkLiveFiles(permanentLive, permanentLiveId) |
341 | }) | 457 | }) |
342 | 458 | ||
459 | it('Should create a password protected live and have a private static path', async function () { | ||
460 | this.timeout(240000) | ||
461 | |||
462 | await checkLiveFiles(passwordProtectedLive, passwordProtectedLiveId, correctPassword) | ||
463 | }) | ||
464 | |||
343 | it('Should reinject video file token in permanent live', async function () { | 465 | it('Should reinject video file token in permanent live', async function () { |
344 | this.timeout(240000) | 466 | this.timeout(240000) |
345 | 467 | ||
diff --git a/server/tests/api/videos/video-passwords.ts b/server/tests/api/videos/video-passwords.ts new file mode 100644 index 000000000..e01a93a4d --- /dev/null +++ b/server/tests/api/videos/video-passwords.ts | |||
@@ -0,0 +1,97 @@ | |||
1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ | ||
2 | |||
3 | import { expect } from 'chai' | ||
4 | import { | ||
5 | cleanupTests, | ||
6 | createSingleServer, | ||
7 | VideoPasswordsCommand, | ||
8 | PeerTubeServer, | ||
9 | setAccessTokensToServers, | ||
10 | setDefaultAccountAvatar, | ||
11 | setDefaultChannelAvatar | ||
12 | } from '@shared/server-commands' | ||
13 | import { VideoPrivacy } from '@shared/models' | ||
14 | |||
15 | describe('Test video passwords', function () { | ||
16 | let server: PeerTubeServer | ||
17 | let videoUUID: string | ||
18 | |||
19 | let userAccessTokenServer1: string | ||
20 | |||
21 | let videoPasswords: string[] = [] | ||
22 | let command: VideoPasswordsCommand | ||
23 | |||
24 | before(async function () { | ||
25 | this.timeout(30000) | ||
26 | |||
27 | server = await createSingleServer(1) | ||
28 | |||
29 | await setAccessTokensToServers([ server ]) | ||
30 | |||
31 | for (let i = 0; i < 10; i++) { | ||
32 | videoPasswords.push(`password ${i + 1}`) | ||
33 | } | ||
34 | const { uuid } = await server.videos.upload({ attributes: { privacy: VideoPrivacy.PASSWORD_PROTECTED, videoPasswords } }) | ||
35 | videoUUID = uuid | ||
36 | |||
37 | await setDefaultChannelAvatar(server) | ||
38 | await setDefaultAccountAvatar(server) | ||
39 | |||
40 | userAccessTokenServer1 = await server.users.generateUserAndToken('user1') | ||
41 | await setDefaultChannelAvatar(server, 'user1_channel') | ||
42 | await setDefaultAccountAvatar(server, userAccessTokenServer1) | ||
43 | |||
44 | command = server.videoPasswords | ||
45 | }) | ||
46 | |||
47 | it('Should list video passwords', async function () { | ||
48 | const body = await command.list({ videoId: videoUUID }) | ||
49 | |||
50 | expect(body.total).to.equal(10) | ||
51 | expect(body.data).to.be.an('array') | ||
52 | expect(body.data).to.have.lengthOf(10) | ||
53 | }) | ||
54 | |||
55 | it('Should filter passwords on this video', async function () { | ||
56 | const body = await command.list({ videoId: videoUUID, count: 2, start: 3, sort: 'createdAt' }) | ||
57 | |||
58 | expect(body.total).to.equal(10) | ||
59 | expect(body.data).to.be.an('array') | ||
60 | expect(body.data).to.have.lengthOf(2) | ||
61 | expect(body.data[0].password).to.equal('password 4') | ||
62 | expect(body.data[1].password).to.equal('password 5') | ||
63 | }) | ||
64 | |||
65 | it('Should update password for this video', async function () { | ||
66 | videoPasswords = [ 'my super new password 1', 'my super new password 2' ] | ||
67 | |||
68 | await command.updateAll({ videoId: videoUUID, passwords: videoPasswords }) | ||
69 | const body = await command.list({ videoId: videoUUID }) | ||
70 | expect(body.total).to.equal(2) | ||
71 | expect(body.data).to.be.an('array') | ||
72 | expect(body.data).to.have.lengthOf(2) | ||
73 | expect(body.data[0].password).to.equal('my super new password 2') | ||
74 | expect(body.data[1].password).to.equal('my super new password 1') | ||
75 | }) | ||
76 | |||
77 | it('Should delete one password', async function () { | ||
78 | { | ||
79 | const body = await command.list({ videoId: videoUUID }) | ||
80 | expect(body.total).to.equal(2) | ||
81 | expect(body.data).to.be.an('array') | ||
82 | expect(body.data).to.have.lengthOf(2) | ||
83 | await command.remove({ id: body.data[0].id, videoId: videoUUID }) | ||
84 | } | ||
85 | { | ||
86 | const body = await command.list({ videoId: videoUUID }) | ||
87 | |||
88 | expect(body.total).to.equal(1) | ||
89 | expect(body.data).to.be.an('array') | ||
90 | expect(body.data).to.have.lengthOf(1) | ||
91 | } | ||
92 | }) | ||
93 | |||
94 | after(async function () { | ||
95 | await cleanupTests([ server ]) | ||
96 | }) | ||
97 | }) | ||
diff --git a/server/tests/api/videos/video-playlists.ts b/server/tests/api/videos/video-playlists.ts index d9c5bdf16..9277b49f4 100644 --- a/server/tests/api/videos/video-playlists.ts +++ b/server/tests/api/videos/video-playlists.ts | |||
@@ -474,7 +474,7 @@ describe('Test video playlists', function () { | |||
474 | await servers[1].playlists.get({ playlistId: unlistedPlaylist.id, expectedStatus: 404 }) | 474 | await servers[1].playlists.get({ playlistId: unlistedPlaylist.id, expectedStatus: 404 }) |
475 | }) | 475 | }) |
476 | 476 | ||
477 | it('Should get unlisted plyaylist using uuid or shortUUID', async function () { | 477 | it('Should get unlisted playlist using uuid or shortUUID', async function () { |
478 | await servers[1].playlists.get({ playlistId: unlistedPlaylist.uuid }) | 478 | await servers[1].playlists.get({ playlistId: unlistedPlaylist.uuid }) |
479 | await servers[1].playlists.get({ playlistId: unlistedPlaylist.shortUUID }) | 479 | await servers[1].playlists.get({ playlistId: unlistedPlaylist.shortUUID }) |
480 | }) | 480 | }) |
@@ -686,7 +686,7 @@ describe('Test video playlists', function () { | |||
686 | await waitJobs(servers) | 686 | await waitJobs(servers) |
687 | }) | 687 | }) |
688 | 688 | ||
689 | it('Should update the element type if the video is private', async function () { | 689 | it('Should update the element type if the video is private/password protected', async function () { |
690 | this.timeout(20000) | 690 | this.timeout(20000) |
691 | 691 | ||
692 | const name = 'video 89' | 692 | const name = 'video 89' |
@@ -703,6 +703,19 @@ describe('Test video playlists', function () { | |||
703 | } | 703 | } |
704 | 704 | ||
705 | { | 705 | { |
706 | await servers[0].videos.update({ | ||
707 | id: video1, | ||
708 | attributes: { privacy: VideoPrivacy.PASSWORD_PROTECTED, videoPasswords: [ 'password' ] } | ||
709 | }) | ||
710 | await waitJobs(servers) | ||
711 | |||
712 | await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3) | ||
713 | await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.PRIVATE, position, name, 3) | ||
714 | await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.PRIVATE, position, name, 3) | ||
715 | await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3) | ||
716 | } | ||
717 | |||
718 | { | ||
706 | await servers[0].videos.update({ id: video1, attributes: { privacy: VideoPrivacy.PUBLIC } }) | 719 | await servers[0].videos.update({ id: video1, attributes: { privacy: VideoPrivacy.PUBLIC } }) |
707 | await waitJobs(servers) | 720 | await waitJobs(servers) |
708 | 721 | ||
diff --git a/server/tests/api/videos/video-static-file-privacy.ts b/server/tests/api/videos/video-static-file-privacy.ts index 542848533..ec4c697db 100644 --- a/server/tests/api/videos/video-static-file-privacy.ts +++ b/server/tests/api/videos/video-static-file-privacy.ts | |||
@@ -90,7 +90,7 @@ describe('Test video static file privacy', function () { | |||
90 | } | 90 | } |
91 | } | 91 | } |
92 | 92 | ||
93 | it('Should upload a private/internal video and have a private static path', async function () { | 93 | it('Should upload a private/internal/password protected video and have a private static path', async function () { |
94 | this.timeout(120000) | 94 | this.timeout(120000) |
95 | 95 | ||
96 | for (const privacy of [ VideoPrivacy.PRIVATE, VideoPrivacy.INTERNAL ]) { | 96 | for (const privacy of [ VideoPrivacy.PRIVATE, VideoPrivacy.INTERNAL ]) { |
@@ -99,6 +99,15 @@ describe('Test video static file privacy', function () { | |||
99 | 99 | ||
100 | await checkPrivateFiles(uuid) | 100 | await checkPrivateFiles(uuid) |
101 | } | 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) | ||
102 | }) | 111 | }) |
103 | 112 | ||
104 | it('Should upload a public video and update it as private/internal to have a private static path', async function () { | 113 | it('Should upload a public video and update it as private/internal to have a private static path', async function () { |
@@ -185,8 +194,9 @@ describe('Test video static file privacy', function () { | |||
185 | expectedStatus: HttpStatusCode | 194 | expectedStatus: HttpStatusCode |
186 | token: string | 195 | token: string |
187 | videoFileToken: string | 196 | videoFileToken: string |
197 | videoPassword?: string | ||
188 | }) { | 198 | }) { |
189 | const { id, expectedStatus, token, videoFileToken } = options | 199 | const { id, expectedStatus, token, videoFileToken, videoPassword } = options |
190 | 200 | ||
191 | const video = await server.videos.getWithToken({ id }) | 201 | const video = await server.videos.getWithToken({ id }) |
192 | 202 | ||
@@ -196,6 +206,12 @@ describe('Test video static file privacy', function () { | |||
196 | 206 | ||
197 | await makeRawRequest({ url: file.fileUrl, query: { videoFileToken }, expectedStatus }) | 207 | await makeRawRequest({ url: file.fileUrl, query: { videoFileToken }, expectedStatus }) |
198 | await makeRawRequest({ url: file.fileDownloadUrl, 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 | } | ||
199 | } | 215 | } |
200 | 216 | ||
201 | const hls = video.streamingPlaylists[0] | 217 | const hls = video.streamingPlaylists[0] |
@@ -204,6 +220,12 @@ describe('Test video static file privacy', function () { | |||
204 | 220 | ||
205 | await makeRawRequest({ url: hls.playlistUrl, query: { videoFileToken }, expectedStatus }) | 221 | await makeRawRequest({ url: hls.playlistUrl, query: { videoFileToken }, expectedStatus }) |
206 | await makeRawRequest({ url: hls.segmentsSha256Url, 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 | } | ||
207 | } | 229 | } |
208 | 230 | ||
209 | before(async function () { | 231 | before(async function () { |
@@ -216,13 +238,53 @@ describe('Test video static file privacy', function () { | |||
216 | it('Should not be able to access a private video files without OAuth token and file token', async function () { | 238 | it('Should not be able to access a private video files without OAuth token and file token', async function () { |
217 | this.timeout(120000) | 239 | this.timeout(120000) |
218 | 240 | ||
219 | const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.INTERNAL }) | 241 | const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.PRIVATE }) |
220 | await waitJobs([ server ]) | 242 | await waitJobs([ server ]) |
221 | 243 | ||
222 | await checkVideoFiles({ id: uuid, expectedStatus: HttpStatusCode.FORBIDDEN_403, token: null, videoFileToken: null }) | 244 | await checkVideoFiles({ id: uuid, expectedStatus: HttpStatusCode.FORBIDDEN_403, token: null, videoFileToken: null }) |
223 | }) | 245 | }) |
224 | 246 | ||
225 | it('Should not be able to access an internal video files without appropriate OAuth token and file token', async function () { | 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 () { | ||
226 | this.timeout(120000) | 288 | this.timeout(120000) |
227 | 289 | ||
228 | const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.PRIVATE }) | 290 | const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.PRIVATE }) |
@@ -247,6 +309,23 @@ describe('Test video static file privacy', function () { | |||
247 | await checkVideoFiles({ id: uuid, expectedStatus: HttpStatusCode.OK_200, token: server.accessToken, videoFileToken }) | 309 | await checkVideoFiles({ id: uuid, expectedStatus: HttpStatusCode.OK_200, token: server.accessToken, videoFileToken }) |
248 | }) | 310 | }) |
249 | 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 | |||
250 | it('Should reinject video file token', async function () { | 329 | it('Should reinject video file token', async function () { |
251 | this.timeout(120000) | 330 | this.timeout(120000) |
252 | 331 | ||
@@ -294,13 +373,20 @@ describe('Test video static file privacy', function () { | |||
294 | let permanentLiveId: string | 373 | let permanentLiveId: string |
295 | let permanentLive: LiveVideo | 374 | let permanentLive: LiveVideo |
296 | 375 | ||
376 | let passwordProtectedLiveId: string | ||
377 | let passwordProtectedLive: LiveVideo | ||
378 | |||
379 | const correctPassword = 'my super password' | ||
380 | |||
297 | let unrelatedFileToken: string | 381 | let unrelatedFileToken: string |
298 | 382 | ||
299 | async function checkLiveFiles (live: LiveVideo, liveId: string) { | 383 | async function checkLiveFiles (options: { live: LiveVideo, liveId: string, videoPassword?: string }) { |
384 | const { live, liveId, videoPassword } = options | ||
300 | const ffmpegCommand = sendRTMPStream({ rtmpBaseUrl: live.rtmpUrl, streamKey: live.streamKey }) | 385 | const ffmpegCommand = sendRTMPStream({ rtmpBaseUrl: live.rtmpUrl, streamKey: live.streamKey }) |
301 | await server.live.waitUntilPublished({ videoId: liveId }) | 386 | await server.live.waitUntilPublished({ videoId: liveId }) |
302 | 387 | ||
303 | const video = await server.videos.getWithToken({ id: liveId }) | 388 | const video = await server.videos.getWithToken({ id: liveId }) |
389 | |||
304 | const fileToken = await server.videoToken.getVideoFileToken({ videoId: video.uuid }) | 390 | const fileToken = await server.videoToken.getVideoFileToken({ videoId: video.uuid }) |
305 | 391 | ||
306 | const hls = video.streamingPlaylists[0] | 392 | const hls = video.streamingPlaylists[0] |
@@ -314,6 +400,16 @@ describe('Test video static file privacy', function () { | |||
314 | await makeRawRequest({ url, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) | 400 | await makeRawRequest({ url, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) |
315 | await makeRawRequest({ url, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) | 401 | await makeRawRequest({ url, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) |
316 | await makeRawRequest({ url, query: { videoFileToken: unrelatedFileToken }, 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 | |||
317 | } | 413 | } |
318 | 414 | ||
319 | await stopFfmpeg(ffmpegCommand) | 415 | await stopFfmpeg(ffmpegCommand) |
@@ -381,18 +477,35 @@ describe('Test video static file privacy', function () { | |||
381 | permanentLiveId = video.uuid | 477 | permanentLiveId = video.uuid |
382 | permanentLive = live | 478 | permanentLive = live |
383 | } | 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 | } | ||
384 | }) | 491 | }) |
385 | 492 | ||
386 | it('Should create a private normal live and have a private static path', async function () { | 493 | it('Should create a private normal live and have a private static path', async function () { |
387 | this.timeout(240000) | 494 | this.timeout(240000) |
388 | 495 | ||
389 | await checkLiveFiles(normalLive, normalLiveId) | 496 | await checkLiveFiles({ live: normalLive, liveId: normalLiveId }) |
390 | }) | 497 | }) |
391 | 498 | ||
392 | it('Should create a private permanent live and have a private static path', async function () { | 499 | it('Should create a private permanent live and have a private static path', async function () { |
393 | this.timeout(240000) | 500 | this.timeout(240000) |
394 | 501 | ||
395 | await checkLiveFiles(permanentLive, permanentLiveId) | 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 }) | ||
396 | }) | 509 | }) |
397 | 510 | ||
398 | it('Should reinject video file token on permanent live', async function () { | 511 | it('Should reinject video file token on permanent live', async function () { |
diff --git a/server/tests/client.ts b/server/tests/client.ts index e84251561..68f3a1d14 100644 --- a/server/tests/client.ts +++ b/server/tests/client.ts | |||
@@ -56,6 +56,7 @@ describe('Test a client controllers', function () { | |||
56 | let privateVideoId: string | 56 | let privateVideoId: string |
57 | let internalVideoId: string | 57 | let internalVideoId: string |
58 | let unlistedVideoId: string | 58 | let unlistedVideoId: string |
59 | let passwordProtectedVideoId: string | ||
59 | 60 | ||
60 | let playlistIds: (string | number)[] = [] | 61 | let playlistIds: (string | number)[] = [] |
61 | 62 | ||
@@ -92,7 +93,12 @@ describe('Test a client controllers', function () { | |||
92 | { | 93 | { |
93 | ({ uuid: privateVideoId } = await servers[0].videos.quickUpload({ name: 'private', privacy: VideoPrivacy.PRIVATE })); | 94 | ({ uuid: privateVideoId } = await servers[0].videos.quickUpload({ name: 'private', privacy: VideoPrivacy.PRIVATE })); |
94 | ({ uuid: unlistedVideoId } = await servers[0].videos.quickUpload({ name: 'unlisted', privacy: VideoPrivacy.UNLISTED })); | 95 | ({ uuid: unlistedVideoId } = await servers[0].videos.quickUpload({ name: 'unlisted', privacy: VideoPrivacy.UNLISTED })); |
95 | ({ uuid: internalVideoId } = await servers[0].videos.quickUpload({ name: 'internal', privacy: VideoPrivacy.INTERNAL })) | 96 | ({ uuid: internalVideoId } = await servers[0].videos.quickUpload({ name: 'internal', privacy: VideoPrivacy.INTERNAL })); |
97 | ({ uuid: passwordProtectedVideoId } = await servers[0].videos.quickUpload({ | ||
98 | name: 'password protected', | ||
99 | privacy: VideoPrivacy.PASSWORD_PROTECTED, | ||
100 | videoPasswords: [ 'password' ] | ||
101 | })) | ||
96 | } | 102 | } |
97 | 103 | ||
98 | // Playlist | 104 | // Playlist |
@@ -502,9 +508,9 @@ describe('Test a client controllers', function () { | |||
502 | } | 508 | } |
503 | }) | 509 | }) |
504 | 510 | ||
505 | it('Should not display internal/private video', async function () { | 511 | it('Should not display internal/private/password protected video', async function () { |
506 | for (const basePath of watchVideoBasePaths) { | 512 | for (const basePath of watchVideoBasePaths) { |
507 | for (const id of [ privateVideoId, internalVideoId ]) { | 513 | for (const id of [ privateVideoId, internalVideoId, passwordProtectedVideoId ]) { |
508 | const res = await makeGetRequest({ | 514 | const res = await makeGetRequest({ |
509 | url: servers[0].url, | 515 | url: servers[0].url, |
510 | path: basePath + id, | 516 | path: basePath + id, |
@@ -514,6 +520,7 @@ describe('Test a client controllers', function () { | |||
514 | 520 | ||
515 | expect(res.text).to.not.contain('internal') | 521 | expect(res.text).to.not.contain('internal') |
516 | expect(res.text).to.not.contain('private') | 522 | expect(res.text).to.not.contain('private') |
523 | expect(res.text).to.not.contain('password protected') | ||
517 | } | 524 | } |
518 | } | 525 | } |
519 | }) | 526 | }) |
diff --git a/server/tests/feeds/feeds.ts b/server/tests/feeds/feeds.ts index 8433c873e..83a85be58 100644 --- a/server/tests/feeds/feeds.ts +++ b/server/tests/feeds/feeds.ts | |||
@@ -99,6 +99,13 @@ describe('Test syndication feeds', () => { | |||
99 | await servers[0].comments.createThread({ videoId: id, text: 'comment on unlisted video' }) | 99 | await servers[0].comments.createThread({ videoId: id, text: 'comment on unlisted video' }) |
100 | } | 100 | } |
101 | 101 | ||
102 | { | ||
103 | const attributes = { name: 'password protected video', privacy: VideoPrivacy.PASSWORD_PROTECTED, videoPasswords: [ 'password' ] } | ||
104 | const { id } = await servers[0].videos.upload({ attributes }) | ||
105 | |||
106 | await servers[0].comments.createThread({ videoId: id, text: 'comment on password protected video' }) | ||
107 | } | ||
108 | |||
102 | await serverHLSOnly.videos.upload({ attributes: { name: 'hls only video' } }) | 109 | await serverHLSOnly.videos.upload({ attributes: { name: 'hls only video' } }) |
103 | 110 | ||
104 | await waitJobs([ ...servers, serverHLSOnly ]) | 111 | await waitJobs([ ...servers, serverHLSOnly ]) |
@@ -445,7 +452,7 @@ describe('Test syndication feeds', () => { | |||
445 | 452 | ||
446 | describe('Video comments feed', function () { | 453 | describe('Video comments feed', function () { |
447 | 454 | ||
448 | it('Should contain valid comments (covers JSON feed 1.0 endpoint) and not from unlisted videos', async function () { | 455 | it('Should contain valid comments (covers JSON feed 1.0 endpoint) and not from unlisted/password protected videos', async function () { |
449 | for (const server of servers) { | 456 | for (const server of servers) { |
450 | const json = await server.feed.getJSON({ feed: 'video-comments', ignoreCache: true }) | 457 | const json = await server.feed.getJSON({ feed: 'video-comments', ignoreCache: true }) |
451 | 458 | ||