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: {
},
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)
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'
// ---------------------------------------------------------------------------
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 }
}
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 () {
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
}
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' }
}
describe('Download hooks', function () {
const downloadVideos: VideoDetails[] = []
+ let downloadVideo2Token: string
before(async function () {
this.timeout(120000)
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 () {
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 })
+ }
}
})
})
MChannelBannerAccountDefault,
MChannelSyncChannel,
MStreamingPlaylist,
+ MUserAccountUrl,
MVideoChangeOwnershipFull,
MVideoFile,
MVideoFormattableDetails,
actor: MActorAccountChannelId
}
+ videoFileToken?: {
+ user: MUserAccountUrl
+ }
+
authenticated?: boolean
registeredPlugin?: RegisteredPlugin