Add ability to get user from file token
This commit is contained in:
parent
93293ca788
commit
868314e8bf
|
@ -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: {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 () {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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' }
|
||||
}
|
||||
|
|
|
@ -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 })
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue