Add ability to get user from file token

pull/5486/head
Chocobozzz 2022-12-20 09:15:49 +01:00
parent 93293ca788
commit 868314e8bf
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
7 changed files with 64 additions and 31 deletions

View 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: {

View 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)

View 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 () {

View 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
}

View 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' }
}

View 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
const allowed = [
downloadVideos[2].files[0].fileDownloadUrl,
downloadVideos[0].streamingPlaylists[0].files[0].fileDownloadUrl,
downloadVideos[1].streamingPlaylists[0].files[0].fileDownloadUrl
]
// 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[2].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[0].streamingPlaylists[0].files[0].fileDownloadUrl,
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 })
}
}
})
})

View 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