aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/lib
diff options
context:
space:
mode:
authorWicklow <123956049+wickloww@users.noreply.github.com>2023-06-29 07:48:55 +0000
committerGitHub <noreply@github.com>2023-06-29 09:48:55 +0200
commit40346ead2b0b7afa475aef057d3673b6c7574b7a (patch)
tree24ffdc23c3a9d987334842e0d400b5bd44500cf7 /server/lib
parentae22c59f14d0d553f60b281948b6c232c2aca178 (diff)
downloadPeerTube-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/lib')
-rw-r--r--server/lib/client-html.ts3
-rw-r--r--server/lib/video-pre-import.ts11
-rw-r--r--server/lib/video-privacy.ts10
-rw-r--r--server/lib/video-tokens-manager.ts25
4 files changed, 39 insertions, 10 deletions
diff --git a/server/lib/client-html.ts b/server/lib/client-html.ts
index 18b16bee1..be6df1792 100644
--- a/server/lib/client-html.ts
+++ b/server/lib/client-html.ts
@@ -32,6 +32,7 @@ import { getActivityStreamDuration } from './activitypub/activity'
32import { getBiggestActorImage } from './actor-image' 32import { getBiggestActorImage } from './actor-image'
33import { Hooks } from './plugins/hooks' 33import { Hooks } from './plugins/hooks'
34import { ServerConfigManager } from './server-config-manager' 34import { ServerConfigManager } from './server-config-manager'
35import { isVideoInPrivateDirectory } from './video-privacy'
35 36
36type Tags = { 37type Tags = {
37 ogType: string 38 ogType: string
@@ -106,7 +107,7 @@ class ClientHtml {
106 ]) 107 ])
107 108
108 // Let Angular application handle errors 109 // Let Angular application handle errors
109 if (!video || video.privacy === VideoPrivacy.PRIVATE || video.privacy === VideoPrivacy.INTERNAL || video.VideoBlacklist) { 110 if (!video || isVideoInPrivateDirectory(video.privacy) || video.VideoBlacklist) {
110 res.status(HttpStatusCode.NOT_FOUND_404) 111 res.status(HttpStatusCode.NOT_FOUND_404)
111 return html 112 return html
112 } 113 }
diff --git a/server/lib/video-pre-import.ts b/server/lib/video-pre-import.ts
index df67dc953..0ac667ba3 100644
--- a/server/lib/video-pre-import.ts
+++ b/server/lib/video-pre-import.ts
@@ -30,6 +30,7 @@ import {
30import { ThumbnailType, VideoImportCreate, VideoImportPayload, VideoImportState, VideoPrivacy, VideoState } from '@shared/models' 30import { ThumbnailType, VideoImportCreate, VideoImportPayload, VideoImportState, VideoPrivacy, VideoState } from '@shared/models'
31import { getLocalVideoActivityPubUrl } from './activitypub/url' 31import { getLocalVideoActivityPubUrl } from './activitypub/url'
32import { updateVideoMiniatureFromExisting, updateVideoMiniatureFromUrl } from './thumbnail' 32import { updateVideoMiniatureFromExisting, updateVideoMiniatureFromUrl } from './thumbnail'
33import { VideoPasswordModel } from '@server/models/video/video-password'
33 34
34class YoutubeDlImportError extends Error { 35class YoutubeDlImportError extends Error {
35 code: YoutubeDlImportError.CODE 36 code: YoutubeDlImportError.CODE
@@ -64,8 +65,9 @@ async function insertFromImportIntoDB (parameters: {
64 tags: string[] 65 tags: string[]
65 videoImportAttributes: FilteredModelAttributes<VideoImportModel> 66 videoImportAttributes: FilteredModelAttributes<VideoImportModel>
66 user: MUser 67 user: MUser
68 videoPasswords?: string[]
67}): Promise<MVideoImportFormattable> { 69}): Promise<MVideoImportFormattable> {
68 const { video, thumbnailModel, previewModel, videoChannel, tags, videoImportAttributes, user } = parameters 70 const { video, thumbnailModel, previewModel, videoChannel, tags, videoImportAttributes, user, videoPasswords } = parameters
69 71
70 const videoImport = await sequelizeTypescript.transaction(async t => { 72 const videoImport = await sequelizeTypescript.transaction(async t => {
71 const sequelizeOptions = { transaction: t } 73 const sequelizeOptions = { transaction: t }
@@ -77,6 +79,10 @@ async function insertFromImportIntoDB (parameters: {
77 if (thumbnailModel) await videoCreated.addAndSaveThumbnail(thumbnailModel, t) 79 if (thumbnailModel) await videoCreated.addAndSaveThumbnail(thumbnailModel, t)
78 if (previewModel) await videoCreated.addAndSaveThumbnail(previewModel, t) 80 if (previewModel) await videoCreated.addAndSaveThumbnail(previewModel, t)
79 81
82 if (videoCreated.privacy === VideoPrivacy.PASSWORD_PROTECTED) {
83 await VideoPasswordModel.addPasswords(videoPasswords, video.id, t)
84 }
85
80 await autoBlacklistVideoIfNeeded({ 86 await autoBlacklistVideoIfNeeded({
81 video: videoCreated, 87 video: videoCreated,
82 user, 88 user,
@@ -208,7 +214,8 @@ async function buildYoutubeDLImport (options: {
208 state: VideoImportState.PENDING, 214 state: VideoImportState.PENDING,
209 userId: user.id, 215 userId: user.id,
210 videoChannelSyncId: channelSync?.id 216 videoChannelSyncId: channelSync?.id
211 } 217 },
218 videoPasswords: importDataOverride.videoPasswords
212 }) 219 })
213 220
214 // Get video subtitles 221 // Get video subtitles
diff --git a/server/lib/video-privacy.ts b/server/lib/video-privacy.ts
index 41f9d62b3..39430ef1e 100644
--- a/server/lib/video-privacy.ts
+++ b/server/lib/video-privacy.ts
@@ -6,6 +6,12 @@ import { MVideo, MVideoFile, MVideoFullLight } from '@server/types/models'
6import { VideoPrivacy, VideoStorage } from '@shared/models' 6import { VideoPrivacy, VideoStorage } from '@shared/models'
7import { updateHLSFilesACL, updateWebTorrentFileACL } from './object-storage' 7import { updateHLSFilesACL, updateWebTorrentFileACL } from './object-storage'
8 8
9const validPrivacySet = new Set([
10 VideoPrivacy.PRIVATE,
11 VideoPrivacy.INTERNAL,
12 VideoPrivacy.PASSWORD_PROTECTED
13])
14
9function setVideoPrivacy (video: MVideo, newPrivacy: VideoPrivacy) { 15function setVideoPrivacy (video: MVideo, newPrivacy: VideoPrivacy) {
10 if (video.privacy === VideoPrivacy.PRIVATE && newPrivacy !== VideoPrivacy.PRIVATE) { 16 if (video.privacy === VideoPrivacy.PRIVATE && newPrivacy !== VideoPrivacy.PRIVATE) {
11 video.publishedAt = new Date() 17 video.publishedAt = new Date()
@@ -14,8 +20,8 @@ function setVideoPrivacy (video: MVideo, newPrivacy: VideoPrivacy) {
14 video.privacy = newPrivacy 20 video.privacy = newPrivacy
15} 21}
16 22
17function isVideoInPrivateDirectory (privacy: VideoPrivacy) { 23function isVideoInPrivateDirectory (privacy) {
18 return privacy === VideoPrivacy.PRIVATE || privacy === VideoPrivacy.INTERNAL 24 return validPrivacySet.has(privacy)
19} 25}
20 26
21function isVideoInPublicDirectory (privacy: VideoPrivacy) { 27function isVideoInPublicDirectory (privacy: VideoPrivacy) {
diff --git a/server/lib/video-tokens-manager.ts b/server/lib/video-tokens-manager.ts
index 660533528..e28e55cf7 100644
--- a/server/lib/video-tokens-manager.ts
+++ b/server/lib/video-tokens-manager.ts
@@ -12,26 +12,34 @@ class VideoTokensManager {
12 12
13 private static instance: VideoTokensManager 13 private static instance: VideoTokensManager
14 14
15 private readonly lruCache = new LRUCache<string, { videoUUID: string, user: MUserAccountUrl }>({ 15 private readonly lruCache = new LRUCache<string, { videoUUID: string, user?: MUserAccountUrl }>({
16 max: LRU_CACHE.VIDEO_TOKENS.MAX_SIZE, 16 max: LRU_CACHE.VIDEO_TOKENS.MAX_SIZE,
17 ttl: LRU_CACHE.VIDEO_TOKENS.TTL 17 ttl: LRU_CACHE.VIDEO_TOKENS.TTL
18 }) 18 })
19 19
20 private constructor () {} 20 private constructor () {}
21 21
22 create (options: { 22 createForAuthUser (options: {
23 user: MUserAccountUrl 23 user: MUserAccountUrl
24 videoUUID: string 24 videoUUID: string
25 }) { 25 }) {
26 const token = buildUUID() 26 const { token, expires } = this.generateVideoToken()
27
28 const expires = new Date(new Date().getTime() + LRU_CACHE.VIDEO_TOKENS.TTL)
29 27
30 this.lruCache.set(token, pick(options, [ 'user', 'videoUUID' ])) 28 this.lruCache.set(token, pick(options, [ 'user', 'videoUUID' ]))
31 29
32 return { token, expires } 30 return { token, expires }
33 } 31 }
34 32
33 createForPasswordProtectedVideo (options: {
34 videoUUID: string
35 }) {
36 const { token, expires } = this.generateVideoToken()
37
38 this.lruCache.set(token, pick(options, [ 'videoUUID' ]))
39
40 return { token, expires }
41 }
42
35 hasToken (options: { 43 hasToken (options: {
36 token: string 44 token: string
37 videoUUID: string 45 videoUUID: string
@@ -54,6 +62,13 @@ class VideoTokensManager {
54 static get Instance () { 62 static get Instance () {
55 return this.instance || (this.instance = new this()) 63 return this.instance || (this.instance = new this())
56 } 64 }
65
66 private generateVideoToken () {
67 const token = buildUUID()
68 const expires = new Date(new Date().getTime() + LRU_CACHE.VIDEO_TOKENS.TTL)
69
70 return { token, expires }
71 }
57} 72}
58 73
59// --------------------------------------------------------------------------- 74// ---------------------------------------------------------------------------