diff options
author | Chocobozzz <me@florianbigard.com> | 2022-10-19 10:43:53 +0200 |
---|---|---|
committer | Chocobozzz <chocobozzz@cpy.re> | 2022-10-24 14:48:24 +0200 |
commit | 9ab330b90decf4edf152ff8e1d2948c065766b2c (patch) | |
tree | 29d924f50f7307e8e828a57ecb9ea78623487ce0 /server/models/video | |
parent | 3545e72c686ff1725bbdfd8d16d693e2f4aa75a3 (diff) | |
download | PeerTube-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.ts | 62 | ||||
-rw-r--r-- | server/models/video/video-streaming-playlist.ts | 28 | ||||
-rw-r--r-- | server/models/video/video.ts | 34 |
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' | |||
22 | import { logger } from '@server/helpers/logger' | 22 | import { logger } from '@server/helpers/logger' |
23 | import { extractVideo } from '@server/helpers/video' | 23 | import { extractVideo } from '@server/helpers/video' |
24 | import { buildRemoteVideoBaseUrl } from '@server/lib/activitypub/url' | 24 | import { buildRemoteVideoBaseUrl } from '@server/lib/activitypub/url' |
25 | import { getHLSPublicFileUrl, getWebTorrentPublicFileUrl } from '@server/lib/object-storage' | 25 | import { |
26 | getHLSPrivateFileUrl, | ||
27 | getHLSPublicFileUrl, | ||
28 | getWebTorrentPrivateFileUrl, | ||
29 | getWebTorrentPublicFileUrl | ||
30 | } from '@server/lib/object-storage' | ||
26 | import { getFSTorrentFilePath } from '@server/lib/paths' | 31 | import { getFSTorrentFilePath } from '@server/lib/paths' |
27 | import { isVideoInPrivateDirectory } from '@server/lib/video-privacy' | 32 | import { isVideoInPrivateDirectory } from '@server/lib/video-privacy' |
28 | import { isStreamingPlaylist, MStreamingPlaylistVideo, MVideo, MVideoWithHost } from '@server/types/models' | 33 | import { 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' |
18 | import { getHLSPublicFileUrl } from '@server/lib/object-storage' | 18 | import { getHLSPrivateFileUrl, getHLSPublicFileUrl } from '@server/lib/object-storage' |
19 | import { generateHLSMasterPlaylistFilename, generateHlsSha256SegmentsFilename } from '@server/lib/paths' | 19 | import { generateHLSMasterPlaylistFilename, generateHlsSha256SegmentsFilename } from '@server/lib/paths' |
20 | import { isVideoInPrivateDirectory } from '@server/lib/video-privacy' | 20 | import { isVideoInPrivateDirectory } from '@server/lib/video-privacy' |
21 | import { VideoFileModel } from '@server/models/video/video-file' | 21 | import { 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 | |||
30 | import { tracer } from '@server/lib/opentelemetry/tracing' | 30 | import { tracer } from '@server/lib/opentelemetry/tracing' |
31 | import { getHLSDirectory, getHLSRedundancyDirectory, getHlsResolutionPlaylistFilename } from '@server/lib/paths' | 31 | import { getHLSDirectory, getHLSRedundancyDirectory, getHlsResolutionPlaylistFilename } from '@server/lib/paths' |
32 | import { VideoPathManager } from '@server/lib/video-path-manager' | 32 | import { VideoPathManager } from '@server/lib/video-path-manager' |
33 | import { isVideoInPrivateDirectory } from '@server/lib/video-privacy' | ||
33 | import { getServerActor } from '@server/models/application/application' | 34 | import { getServerActor } from '@server/models/application/application' |
34 | import { ModelCache } from '@server/models/model-cache' | 35 | import { ModelCache } from '@server/models/model-cache' |
35 | import { buildVideoEmbedPath, buildVideoWatchPath, pick } from '@shared/core-utils' | 36 | import { 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 | ||