]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/commitdiff
Add ability to get user from file token
authorChocobozzz <me@florianbigard.com>
Tue, 20 Dec 2022 08:15:49 +0000 (09:15 +0100)
committerChocobozzz <me@florianbigard.com>
Tue, 20 Dec 2022 08:15:49 +0000 (09:15 +0100)
server/controllers/api/videos/token.ts
server/lib/plugins/plugin-helpers-builder.ts
server/lib/video-tokens-manager.ts
server/middlewares/validators/shared/videos.ts
server/tests/fixtures/peertube-plugin-test/main.js
server/tests/plugins/filter-hooks.ts
server/types/express.d.ts

index 009b6dfb653c56c5ed9a9b93f2ee34006c2d94b1..22387c3e827ae1dc9d5de1f3ca8e923099960dc5 100644 (file)
@@ -22,7 +22,7 @@ export {
 function generateToken (req: express.Request, res: express.Response) {
   const video = res.locals.onlyVideo
 
-  const { token, expires } = VideoTokensManager.Instance.create(video.uuid)
+  const { token, expires } = VideoTokensManager.Instance.create({ videoUUID: video.uuid, user: res.locals.oauth.token.User })
 
   return res.json({
     files: {
index 7b1def6e30c9e171f8703f4751047374f8fbbfed..e75c0b99429c8d8da2f892761ce318429d007cee 100644 (file)
@@ -245,7 +245,7 @@ function buildUserHelpers () {
     },
 
     getAuthUser: (res: express.Response) => {
-      const user = res.locals.oauth?.token?.User
+      const user = res.locals.oauth?.token?.User || res.locals.videoFileToken?.user
       if (!user) return undefined
 
       return UserModel.loadByIdFull(user.id)
index c43085d167631d5bf08956a31c20aceb7dda922f..17aa29cdda707f9dc2e3893557d3e8aae3d389aa 100644 (file)
@@ -1,5 +1,7 @@
 import LRUCache from 'lru-cache'
 import { LRU_CACHE } from '@server/initializers/constants'
+import { MUserAccountUrl } from '@server/types/models'
+import { pick } from '@shared/core-utils'
 import { buildUUID } from '@shared/extra-utils'
 
 // ---------------------------------------------------------------------------
@@ -10,19 +12,22 @@ class VideoTokensManager {
 
   private static instance: VideoTokensManager
 
-  private readonly lruCache = new LRUCache<string, string>({
+  private readonly lruCache = new LRUCache<string, { videoUUID: string, user: MUserAccountUrl }>({
     max: LRU_CACHE.VIDEO_TOKENS.MAX_SIZE,
     ttl: LRU_CACHE.VIDEO_TOKENS.TTL
   })
 
   private constructor () {}
 
-  create (videoUUID: string) {
+  create (options: {
+    user: MUserAccountUrl
+    videoUUID: string
+  }) {
     const token = buildUUID()
 
     const expires = new Date(new Date().getTime() + LRU_CACHE.VIDEO_TOKENS.TTL)
 
-    this.lruCache.set(token, videoUUID)
+    this.lruCache.set(token, pick(options, [ 'user', 'videoUUID' ]))
 
     return { token, expires }
   }
@@ -34,7 +39,16 @@ class VideoTokensManager {
     const value = this.lruCache.get(options.token)
     if (!value) return false
 
-    return value === options.videoUUID
+    return value.videoUUID === options.videoUUID
+  }
+
+  getUserFromToken (options: {
+    token: string
+  }) {
+    const value = this.lruCache.get(options.token)
+    if (!value) return undefined
+
+    return value.user
   }
 
   static get Instance () {
index ebbfc0a0a9242232c349dd070c612b4ac5d51549..0033a32ff1af454f6a7794ce3403e49965b5526a 100644 (file)
@@ -180,18 +180,16 @@ async function checkCanAccessVideoStaticFiles (options: {
     return checkCanSeeVideo(options)
   }
 
-  if (!video.hasPrivateStaticPath()) return true
-
   const videoFileToken = req.query.videoFileToken
-  if (!videoFileToken) {
-    res.sendStatus(HttpStatusCode.FORBIDDEN_403)
-    return false
-  }
+  if (videoFileToken && VideoTokensManager.Instance.hasToken({ token: videoFileToken, videoUUID: video.uuid })) {
+    const user = VideoTokensManager.Instance.getUserFromToken({ token: videoFileToken })
 
-  if (VideoTokensManager.Instance.hasToken({ token: videoFileToken, videoUUID: video.uuid })) {
+    res.locals.videoFileToken = { user }
     return true
   }
 
+  if (!video.hasPrivateStaticPath()) return true
+
   res.sendStatus(HttpStatusCode.FORBIDDEN_403)
   return false
 }
index 19dccf26e8bff975ff23360b281071ab96749db3..19ba9f2784c01ff4e56cd506bee5b3fd1674331a 100644 (file)
@@ -250,7 +250,10 @@ async function register ({ registerHook, registerSetting, settingsManager, stora
 
   registerHook({
     target: 'filter:api.download.video.allowed.result',
-    handler: (result, params) => {
+    handler: async (result, params) => {
+      const loggedInUser = await peertubeHelpers.user.getAuthUser(params.res)
+      if (loggedInUser) return { allowed: true }
+
       if (params && !params.streamingPlaylist && params.video.name.includes('bad file')) {
         return { allowed: false, errorMessage: 'Cao Cao' }
       }
index 083fd43ca4bb5e573aa7f5931cd09bd11d926cd2..6724b3bf86dcfcc7b6fd3946be8fbc4bd70b8ad9 100644 (file)
@@ -430,6 +430,7 @@ describe('Test plugin filter hooks', function () {
 
   describe('Download hooks', function () {
     const downloadVideos: VideoDetails[] = []
+    let downloadVideo2Token: string
 
     before(async function () {
       this.timeout(120000)
@@ -459,6 +460,8 @@ describe('Test plugin filter hooks', function () {
       for (const uuid of uuids) {
         downloadVideos.push(await servers[0].videos.get({ id: uuid }))
       }
+
+      downloadVideo2Token = await servers[0].videoToken.getVideoFileToken({ videoId: downloadVideos[2].uuid })
     })
 
     it('Should run filter:api.download.torrent.allowed.result', async function () {
@@ -471,32 +474,42 @@ describe('Test plugin filter hooks', function () {
 
     it('Should run filter:api.download.video.allowed.result', async function () {
       {
-        const res = await makeRawRequest({ url: downloadVideos[1].files[0].fileDownloadUrl, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
+        const refused = downloadVideos[1].files[0].fileDownloadUrl
+        const allowed = [
+          downloadVideos[0].files[0].fileDownloadUrl,
+          downloadVideos[2].files[0].fileDownloadUrl
+        ]
+
+        const res = await makeRawRequest({ url: refused, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
         expect(res.body.error).to.equal('Cao Cao')
 
-        await makeRawRequest({ url: downloadVideos[0].files[0].fileDownloadUrl, expectedStatus: HttpStatusCode.OK_200 })
-        await makeRawRequest({ url: downloadVideos[2].files[0].fileDownloadUrl, expectedStatus: HttpStatusCode.OK_200 })
+        for (const url of allowed) {
+          await makeRawRequest({ url, expectedStatus: HttpStatusCode.OK_200 })
+          await makeRawRequest({ url, expectedStatus: HttpStatusCode.OK_200 })
+        }
       }
 
       {
-        const res = await makeRawRequest({
-          url: downloadVideos[2].streamingPlaylists[0].files[0].fileDownloadUrl,
-          expectedStatus: HttpStatusCode.FORBIDDEN_403
-        })
+        const refused = downloadVideos[2].streamingPlaylists[0].files[0].fileDownloadUrl
 
-        expect(res.body.error).to.equal('Sun Jian')
+        const allowed = [
+          downloadVideos[2].files[0].fileDownloadUrl,
+          downloadVideos[0].streamingPlaylists[0].files[0].fileDownloadUrl,
+          downloadVideos[1].streamingPlaylists[0].files[0].fileDownloadUrl
+        ]
 
-        await makeRawRequest({ url: downloadVideos[2].files[0].fileDownloadUrl, expectedStatus: HttpStatusCode.OK_200 })
+        // Only streaming playlist is refuse
+        const res = await makeRawRequest({ url: refused, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
+        expect(res.body.error).to.equal('Sun Jian')
 
-        await makeRawRequest({
-          url: downloadVideos[0].streamingPlaylists[0].files[0].fileDownloadUrl,
-          expectedStatus: HttpStatusCode.OK_200
-        })
+        // But not we there is a user in res
+        await makeRawRequest({ url: refused, token: servers[0].accessToken, expectedStatus: HttpStatusCode.OK_200 })
+        await makeRawRequest({ url: refused, query: { videoFileToken: downloadVideo2Token }, expectedStatus: HttpStatusCode.OK_200 })
 
-        await makeRawRequest({
-          url: downloadVideos[1].streamingPlaylists[0].files[0].fileDownloadUrl,
-          expectedStatus: HttpStatusCode.OK_200
-        })
+        // Other files work
+        for (const url of allowed) {
+          await makeRawRequest({ url, expectedStatus: HttpStatusCode.OK_200 })
+        }
       }
     })
   })
index 3738ffc47dd8d716078831e17cbff1405a072347..99244d2a04c5c63326a087e4f70f224216419ecf 100644 (file)
@@ -10,6 +10,7 @@ import {
   MChannelBannerAccountDefault,
   MChannelSyncChannel,
   MStreamingPlaylist,
+  MUserAccountUrl,
   MVideoChangeOwnershipFull,
   MVideoFile,
   MVideoFormattableDetails,
@@ -187,6 +188,10 @@ declare module 'express' {
         actor: MActorAccountChannelId
       }
 
+      videoFileToken?: {
+        user: MUserAccountUrl
+      }
+
       authenticated?: boolean
 
       registeredPlugin?: RegisteredPlugin