aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/models/video
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2022-10-19 10:43:53 +0200
committerChocobozzz <chocobozzz@cpy.re>2022-10-24 14:48:24 +0200
commit9ab330b90decf4edf152ff8e1d2948c065766b2c (patch)
tree29d924f50f7307e8e828a57ecb9ea78623487ce0 /server/models/video
parent3545e72c686ff1725bbdfd8d16d693e2f4aa75a3 (diff)
downloadPeerTube-9ab330b90decf4edf152ff8e1d2948c065766b2c.tar.gz
PeerTube-9ab330b90decf4edf152ff8e1d2948c065766b2c.tar.zst
PeerTube-9ab330b90decf4edf152ff8e1d2948c065766b2c.zip
Use private ACL for private videos in s3
Diffstat (limited to 'server/models/video')
-rw-r--r--server/models/video/video-file.ts62
-rw-r--r--server/models/video/video-streaming-playlist.ts28
-rw-r--r--server/models/video/video.ts34
3 files changed, 102 insertions, 22 deletions
diff --git a/server/models/video/video-file.ts b/server/models/video/video-file.ts
index 1a608932f..c20c90c1b 100644
--- a/server/models/video/video-file.ts
+++ b/server/models/video/video-file.ts
@@ -22,7 +22,12 @@ import validator from 'validator'
22import { logger } from '@server/helpers/logger' 22import { logger } from '@server/helpers/logger'
23import { extractVideo } from '@server/helpers/video' 23import { extractVideo } from '@server/helpers/video'
24import { buildRemoteVideoBaseUrl } from '@server/lib/activitypub/url' 24import { buildRemoteVideoBaseUrl } from '@server/lib/activitypub/url'
25import { getHLSPublicFileUrl, getWebTorrentPublicFileUrl } from '@server/lib/object-storage' 25import {
26 getHLSPrivateFileUrl,
27 getHLSPublicFileUrl,
28 getWebTorrentPrivateFileUrl,
29 getWebTorrentPublicFileUrl
30} from '@server/lib/object-storage'
26import { getFSTorrentFilePath } from '@server/lib/paths' 31import { getFSTorrentFilePath } from '@server/lib/paths'
27import { isVideoInPrivateDirectory } from '@server/lib/video-privacy' 32import { isVideoInPrivateDirectory } from '@server/lib/video-privacy'
28import { isStreamingPlaylist, MStreamingPlaylistVideo, MVideo, MVideoWithHost } from '@server/types/models' 33import { isStreamingPlaylist, MStreamingPlaylistVideo, MVideo, MVideoWithHost } from '@server/types/models'
@@ -503,7 +508,25 @@ export class VideoFileModel extends Model<Partial<AttributesOnly<VideoFileModel>
503 return !!this.videoStreamingPlaylistId 508 return !!this.videoStreamingPlaylistId
504 } 509 }
505 510
506 getObjectStorageUrl () { 511 // ---------------------------------------------------------------------------
512
513 getObjectStorageUrl (video: MVideo) {
514 if (video.hasPrivateStaticPath()) {
515 return this.getPrivateObjectStorageUrl(video)
516 }
517
518 return this.getPublicObjectStorageUrl()
519 }
520
521 private getPrivateObjectStorageUrl (video: MVideo) {
522 if (this.isHLS()) {
523 return getHLSPrivateFileUrl(video, this.filename)
524 }
525
526 return getWebTorrentPrivateFileUrl(this.filename)
527 }
528
529 private getPublicObjectStorageUrl () {
507 if (this.isHLS()) { 530 if (this.isHLS()) {
508 return getHLSPublicFileUrl(this.fileUrl) 531 return getHLSPublicFileUrl(this.fileUrl)
509 } 532 }
@@ -511,26 +534,29 @@ export class VideoFileModel extends Model<Partial<AttributesOnly<VideoFileModel>
511 return getWebTorrentPublicFileUrl(this.fileUrl) 534 return getWebTorrentPublicFileUrl(this.fileUrl)
512 } 535 }
513 536
537 // ---------------------------------------------------------------------------
538
514 getFileUrl (video: MVideo) { 539 getFileUrl (video: MVideo) {
515 if (this.storage === VideoStorage.OBJECT_STORAGE) { 540 if (video.isOwned()) {
516 return this.getObjectStorageUrl() 541 if (this.storage === VideoStorage.OBJECT_STORAGE) {
517 } 542 return this.getObjectStorageUrl(video)
543 }
518 544
519 if (!this.Video) this.Video = video as VideoModel 545 return WEBSERVER.URL + this.getFileStaticPath(video)
520 if (video.isOwned()) return WEBSERVER.URL + this.getFileStaticPath(video) 546 }
521 547
522 return this.fileUrl 548 return this.fileUrl
523 } 549 }
524 550
551 // ---------------------------------------------------------------------------
552
525 getFileStaticPath (video: MVideo) { 553 getFileStaticPath (video: MVideo) {
526 if (this.isHLS()) { 554 if (this.isHLS()) return this.getHLSFileStaticPath(video)
527 if (isVideoInPrivateDirectory(video.privacy)) {
528 return join(STATIC_PATHS.STREAMING_PLAYLISTS.PRIVATE_HLS, video.uuid, this.filename)
529 }
530 555
531 return join(STATIC_PATHS.STREAMING_PLAYLISTS.HLS, video.uuid, this.filename) 556 return this.getWebTorrentFileStaticPath(video)
532 } 557 }
533 558
559 private getWebTorrentFileStaticPath (video: MVideo) {
534 if (isVideoInPrivateDirectory(video.privacy)) { 560 if (isVideoInPrivateDirectory(video.privacy)) {
535 return join(STATIC_PATHS.PRIVATE_WEBSEED, this.filename) 561 return join(STATIC_PATHS.PRIVATE_WEBSEED, this.filename)
536 } 562 }
@@ -538,6 +564,16 @@ export class VideoFileModel extends Model<Partial<AttributesOnly<VideoFileModel>
538 return join(STATIC_PATHS.WEBSEED, this.filename) 564 return join(STATIC_PATHS.WEBSEED, this.filename)
539 } 565 }
540 566
567 private getHLSFileStaticPath (video: MVideo) {
568 if (isVideoInPrivateDirectory(video.privacy)) {
569 return join(STATIC_PATHS.STREAMING_PLAYLISTS.PRIVATE_HLS, video.uuid, this.filename)
570 }
571
572 return join(STATIC_PATHS.STREAMING_PLAYLISTS.HLS, video.uuid, this.filename)
573 }
574
575 // ---------------------------------------------------------------------------
576
541 getFileDownloadUrl (video: MVideoWithHost) { 577 getFileDownloadUrl (video: MVideoWithHost) {
542 const path = this.isHLS() 578 const path = this.isHLS()
543 ? join(STATIC_DOWNLOAD_PATHS.HLS_VIDEOS, `${video.uuid}-${this.resolution}-fragmented${this.extname}`) 579 ? join(STATIC_DOWNLOAD_PATHS.HLS_VIDEOS, `${video.uuid}-${this.resolution}-fragmented${this.extname}`)
diff --git a/server/models/video/video-streaming-playlist.ts b/server/models/video/video-streaming-playlist.ts
index b919046ed..1318a4dae 100644
--- a/server/models/video/video-streaming-playlist.ts
+++ b/server/models/video/video-streaming-playlist.ts
@@ -15,7 +15,7 @@ import {
15 Table, 15 Table,
16 UpdatedAt 16 UpdatedAt
17} from 'sequelize-typescript' 17} from 'sequelize-typescript'
18import { getHLSPublicFileUrl } from '@server/lib/object-storage' 18import { getHLSPrivateFileUrl, getHLSPublicFileUrl } from '@server/lib/object-storage'
19import { generateHLSMasterPlaylistFilename, generateHlsSha256SegmentsFilename } from '@server/lib/paths' 19import { generateHLSMasterPlaylistFilename, generateHlsSha256SegmentsFilename } from '@server/lib/paths'
20import { isVideoInPrivateDirectory } from '@server/lib/video-privacy' 20import { isVideoInPrivateDirectory } from '@server/lib/video-privacy'
21import { VideoFileModel } from '@server/models/video/video-file' 21import { VideoFileModel } from '@server/models/video/video-file'
@@ -245,10 +245,12 @@ export class VideoStreamingPlaylistModel extends Model<Partial<AttributesOnly<Vi
245 this.p2pMediaLoaderInfohashes = VideoStreamingPlaylistModel.buildP2PMediaLoaderInfoHashes(masterPlaylistUrl, files) 245 this.p2pMediaLoaderInfohashes = VideoStreamingPlaylistModel.buildP2PMediaLoaderInfoHashes(masterPlaylistUrl, files)
246 } 246 }
247 247
248 // ---------------------------------------------------------------------------
249
248 getMasterPlaylistUrl (video: MVideo) { 250 getMasterPlaylistUrl (video: MVideo) {
249 if (video.isOwned()) { 251 if (video.isOwned()) {
250 if (this.storage === VideoStorage.OBJECT_STORAGE) { 252 if (this.storage === VideoStorage.OBJECT_STORAGE) {
251 return getHLSPublicFileUrl(this.playlistUrl) 253 return this.getMasterPlaylistObjectStorageUrl(video)
252 } 254 }
253 255
254 return WEBSERVER.URL + this.getMasterPlaylistStaticPath(video) 256 return WEBSERVER.URL + this.getMasterPlaylistStaticPath(video)
@@ -257,10 +259,20 @@ export class VideoStreamingPlaylistModel extends Model<Partial<AttributesOnly<Vi
257 return this.playlistUrl 259 return this.playlistUrl
258 } 260 }
259 261
262 private getMasterPlaylistObjectStorageUrl (video: MVideo) {
263 if (video.hasPrivateStaticPath()) {
264 return getHLSPrivateFileUrl(video, this.playlistFilename)
265 }
266
267 return getHLSPublicFileUrl(this.playlistUrl)
268 }
269
270 // ---------------------------------------------------------------------------
271
260 getSha256SegmentsUrl (video: MVideo) { 272 getSha256SegmentsUrl (video: MVideo) {
261 if (video.isOwned()) { 273 if (video.isOwned()) {
262 if (this.storage === VideoStorage.OBJECT_STORAGE) { 274 if (this.storage === VideoStorage.OBJECT_STORAGE) {
263 return getHLSPublicFileUrl(this.segmentsSha256Url) 275 return this.getSha256SegmentsObjectStorageUrl(video)
264 } 276 }
265 277
266 return WEBSERVER.URL + this.getSha256SegmentsStaticPath(video) 278 return WEBSERVER.URL + this.getSha256SegmentsStaticPath(video)
@@ -269,6 +281,16 @@ export class VideoStreamingPlaylistModel extends Model<Partial<AttributesOnly<Vi
269 return this.segmentsSha256Url 281 return this.segmentsSha256Url
270 } 282 }
271 283
284 private getSha256SegmentsObjectStorageUrl (video: MVideo) {
285 if (video.hasPrivateStaticPath()) {
286 return getHLSPrivateFileUrl(video, this.segmentsSha256Filename)
287 }
288
289 return getHLSPublicFileUrl(this.segmentsSha256Url)
290 }
291
292 // ---------------------------------------------------------------------------
293
272 getStringType () { 294 getStringType () {
273 if (this.type === VideoStreamingPlaylistType.HLS) return 'hls' 295 if (this.type === VideoStreamingPlaylistType.HLS) return 'hls'
274 296
diff --git a/server/models/video/video.ts b/server/models/video/video.ts
index 82362917e..c568075d8 100644
--- a/server/models/video/video.ts
+++ b/server/models/video/video.ts
@@ -30,6 +30,7 @@ import { removeHLSFileObjectStorage, removeHLSObjectStorage, removeWebTorrentObj
30import { tracer } from '@server/lib/opentelemetry/tracing' 30import { tracer } from '@server/lib/opentelemetry/tracing'
31import { getHLSDirectory, getHLSRedundancyDirectory, getHlsResolutionPlaylistFilename } from '@server/lib/paths' 31import { getHLSDirectory, getHLSRedundancyDirectory, getHlsResolutionPlaylistFilename } from '@server/lib/paths'
32import { VideoPathManager } from '@server/lib/video-path-manager' 32import { VideoPathManager } from '@server/lib/video-path-manager'
33import { isVideoInPrivateDirectory } from '@server/lib/video-privacy'
33import { getServerActor } from '@server/models/application/application' 34import { getServerActor } from '@server/models/application/application'
34import { ModelCache } from '@server/models/model-cache' 35import { ModelCache } from '@server/models/model-cache'
35import { buildVideoEmbedPath, buildVideoWatchPath, pick } from '@shared/core-utils' 36import { buildVideoEmbedPath, buildVideoWatchPath, pick } from '@shared/core-utils'
@@ -1764,9 +1765,7 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> {
1764 const playlist = this.VideoStreamingPlaylists.find(p => p.type === VideoStreamingPlaylistType.HLS) 1765 const playlist = this.VideoStreamingPlaylists.find(p => p.type === VideoStreamingPlaylistType.HLS)
1765 if (!playlist) return undefined 1766 if (!playlist) return undefined
1766 1767
1767 playlist.Video = this 1768 return playlist.withVideo(this)
1768
1769 return playlist
1770 } 1769 }
1771 1770
1772 setHLSPlaylist (playlist: MStreamingPlaylist) { 1771 setHLSPlaylist (playlist: MStreamingPlaylist) {
@@ -1868,16 +1867,39 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> {
1868 return setAsUpdated('video', this.id, transaction) 1867 return setAsUpdated('video', this.id, transaction)
1869 } 1868 }
1870 1869
1871 requiresAuth (paramId: string) { 1870 // ---------------------------------------------------------------------------
1871
1872 requiresAuth (options: {
1873 urlParamId: string
1874 checkBlacklist: boolean
1875 }) {
1876 const { urlParamId, checkBlacklist } = options
1877
1878 if (this.privacy === VideoPrivacy.PRIVATE || this.privacy === VideoPrivacy.INTERNAL) {
1879 return true
1880 }
1881
1872 if (this.privacy === VideoPrivacy.UNLISTED) { 1882 if (this.privacy === VideoPrivacy.UNLISTED) {
1873 if (!isUUIDValid(paramId)) return true 1883 if (urlParamId && !isUUIDValid(urlParamId)) return true
1874 1884
1875 return false 1885 return false
1876 } 1886 }
1877 1887
1878 return this.privacy === VideoPrivacy.PRIVATE || this.privacy === VideoPrivacy.INTERNAL || !!this.VideoBlacklist 1888 if (checkBlacklist && this.VideoBlacklist) return true
1889
1890 if (this.privacy !== VideoPrivacy.PUBLIC) {
1891 throw new Error(`Unknown video privacy ${this.privacy} to know if the video requires auth`)
1892 }
1893
1894 return false
1879 } 1895 }
1880 1896
1897 hasPrivateStaticPath () {
1898 return isVideoInPrivateDirectory(this.privacy)
1899 }
1900
1901 // ---------------------------------------------------------------------------
1902
1881 async setNewState (newState: VideoState, isNewVideo: boolean, transaction: Transaction) { 1903 async setNewState (newState: VideoState, isNewVideo: boolean, transaction: Transaction) {
1882 if (this.state === newState) throw new Error('Cannot use same state ' + newState) 1904 if (this.state === newState) throw new Error('Cannot use same state ' + newState)
1883 1905