aboutsummaryrefslogtreecommitdiffhomepage
path: root/server
diff options
context:
space:
mode:
Diffstat (limited to 'server')
-rw-r--r--server/controllers/api/videos/token.ts2
-rw-r--r--server/controllers/feeds.ts4
-rw-r--r--server/controllers/tracker.ts34
-rw-r--r--server/helpers/custom-validators/misc.ts8
-rw-r--r--server/helpers/youtube-dl/youtube-dl-cli.ts12
-rw-r--r--server/initializers/constants.ts5
-rw-r--r--server/lib/opentelemetry/metric-helpers/bittorrent-tracker-observers-builder.ts51
-rw-r--r--server/lib/opentelemetry/metric-helpers/index.ts1
-rw-r--r--server/lib/opentelemetry/metrics.ts6
-rw-r--r--server/lib/plugins/plugin-helpers-builder.ts6
-rw-r--r--server/lib/redis.ts15
-rw-r--r--server/lib/sync-channel.ts2
-rw-r--r--server/lib/video-tokens-manager.ts22
-rw-r--r--server/middlewares/validators/shared/videos.ts12
-rw-r--r--server/tests/api/activitypub/cleaner.ts8
-rw-r--r--server/tests/api/check-params/redundancy.ts2
-rw-r--r--server/tests/api/live/live-fast-restream.ts14
-rw-r--r--server/tests/api/notifications/moderation-notifications.ts28
-rw-r--r--server/tests/api/object-storage/video-static-file-privacy.ts4
-rw-r--r--server/tests/api/videos/video-channel-syncs.ts3
-rw-r--r--server/tests/feeds/feeds.ts8
-rw-r--r--server/tests/fixtures/peertube-plugin-test-four/main.js6
-rw-r--r--server/tests/fixtures/peertube-plugin-test/main.js5
-rw-r--r--server/tests/plugins/filter-hooks.ts47
-rw-r--r--server/tests/plugins/plugin-helpers.ts12
-rw-r--r--server/types/express.d.ts5
-rw-r--r--server/types/plugins/register-server-option.model.ts3
27 files changed, 229 insertions, 96 deletions
diff --git a/server/controllers/api/videos/token.ts b/server/controllers/api/videos/token.ts
index 009b6dfb6..22387c3e8 100644
--- a/server/controllers/api/videos/token.ts
+++ b/server/controllers/api/videos/token.ts
@@ -22,7 +22,7 @@ export {
22function generateToken (req: express.Request, res: express.Response) { 22function generateToken (req: express.Request, res: express.Response) {
23 const video = res.locals.onlyVideo 23 const video = res.locals.onlyVideo
24 24
25 const { token, expires } = VideoTokensManager.Instance.create(video.uuid) 25 const { token, expires } = VideoTokensManager.Instance.create({ videoUUID: video.uuid, user: res.locals.oauth.token.User })
26 26
27 return res.json({ 27 return res.json({
28 files: { 28 files: {
diff --git a/server/controllers/feeds.ts b/server/controllers/feeds.ts
index 772fe734d..ef810a842 100644
--- a/server/controllers/feeds.ts
+++ b/server/controllers/feeds.ts
@@ -285,8 +285,8 @@ function addVideosToFeed (feed: Feed, videos: VideoModel[]) {
285 content: toSafeHtml(video.description), 285 content: toSafeHtml(video.description),
286 author: [ 286 author: [
287 { 287 {
288 name: video.VideoChannel.Account.getDisplayName(), 288 name: video.VideoChannel.getDisplayName(),
289 link: video.VideoChannel.Account.Actor.url 289 link: video.VideoChannel.Actor.url
290 } 290 }
291 ], 291 ],
292 date: video.publishedAt, 292 date: video.publishedAt,
diff --git a/server/controllers/tracker.ts b/server/controllers/tracker.ts
index 19a8b2bc9..c4f3a8889 100644
--- a/server/controllers/tracker.ts
+++ b/server/controllers/tracker.ts
@@ -1,17 +1,22 @@
1import { Server as TrackerServer } from 'bittorrent-tracker' 1import { Server as TrackerServer } from 'bittorrent-tracker'
2import express from 'express' 2import express from 'express'
3import { createServer } from 'http' 3import { createServer } from 'http'
4import LRUCache from 'lru-cache'
4import proxyAddr from 'proxy-addr' 5import proxyAddr from 'proxy-addr'
5import { WebSocketServer } from 'ws' 6import { WebSocketServer } from 'ws'
6import { Redis } from '@server/lib/redis'
7import { logger } from '../helpers/logger' 7import { logger } from '../helpers/logger'
8import { CONFIG } from '../initializers/config' 8import { CONFIG } from '../initializers/config'
9import { TRACKER_RATE_LIMITS } from '../initializers/constants' 9import { LRU_CACHE, TRACKER_RATE_LIMITS } from '../initializers/constants'
10import { VideoFileModel } from '../models/video/video-file' 10import { VideoFileModel } from '../models/video/video-file'
11import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist' 11import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist'
12 12
13const trackerRouter = express.Router() 13const trackerRouter = express.Router()
14 14
15const blockedIPs = new LRUCache<string, boolean>({
16 max: LRU_CACHE.TRACKER_IPS.MAX_SIZE,
17 ttl: TRACKER_RATE_LIMITS.BLOCK_IP_LIFETIME
18})
19
15let peersIps = {} 20let peersIps = {}
16let peersIpInfoHash = {} 21let peersIpInfoHash = {}
17runPeersChecker() 22runPeersChecker()
@@ -55,8 +60,7 @@ const trackerServer = new TrackerServer({
55 60
56 // Close socket connection and block IP for a few time 61 // Close socket connection and block IP for a few time
57 if (params.type === 'ws') { 62 if (params.type === 'ws') {
58 Redis.Instance.setTrackerBlockIP(ip) 63 blockedIPs.set(ip, true)
59 .catch(err => logger.error('Cannot set tracker block ip.', { err }))
60 64
61 // setTimeout to wait filter response 65 // setTimeout to wait filter response
62 setTimeout(() => params.socket.close(), 0) 66 setTimeout(() => params.socket.close(), 0)
@@ -102,26 +106,22 @@ function createWebsocketTrackerServer (app: express.Application) {
102 if (request.url === '/tracker/socket') { 106 if (request.url === '/tracker/socket') {
103 const ip = proxyAddr(request, CONFIG.TRUST_PROXY) 107 const ip = proxyAddr(request, CONFIG.TRUST_PROXY)
104 108
105 Redis.Instance.doesTrackerBlockIPExist(ip) 109 if (blockedIPs.has(ip)) {
106 .then(result => { 110 logger.debug('Blocking IP %s from tracker.', ip)
107 if (result === true) {
108 logger.debug('Blocking IP %s from tracker.', ip)
109 111
110 socket.write('HTTP/1.1 403 Forbidden\r\n\r\n') 112 socket.write('HTTP/1.1 403 Forbidden\r\n\r\n')
111 socket.destroy() 113 socket.destroy()
112 return 114 return
113 } 115 }
114 116
115 // FIXME: typings 117 // FIXME: typings
116 return wss.handleUpgrade(request, socket as any, head, ws => wss.emit('connection', ws, request)) 118 return wss.handleUpgrade(request, socket as any, head, ws => wss.emit('connection', ws, request))
117 })
118 .catch(err => logger.error('Cannot check if tracker block ip exists.', { err }))
119 } 119 }
120 120
121 // Don't destroy socket, we have Socket.IO too 121 // Don't destroy socket, we have Socket.IO too
122 }) 122 })
123 123
124 return server 124 return { server, trackerServer }
125} 125}
126 126
127// --------------------------------------------------------------------------- 127// ---------------------------------------------------------------------------
diff --git a/server/helpers/custom-validators/misc.ts b/server/helpers/custom-validators/misc.ts
index 3dc5504e3..b3ab3ac64 100644
--- a/server/helpers/custom-validators/misc.ts
+++ b/server/helpers/custom-validators/misc.ts
@@ -103,7 +103,13 @@ function checkMimetypeRegex (fileMimeType: string, mimeTypeRegex: string) {
103// --------------------------------------------------------------------------- 103// ---------------------------------------------------------------------------
104 104
105function toCompleteUUID (value: string) { 105function toCompleteUUID (value: string) {
106 if (isShortUUID(value)) return shortToUUID(value) 106 if (isShortUUID(value)) {
107 try {
108 return shortToUUID(value)
109 } catch {
110 return null
111 }
112 }
107 113
108 return value 114 return value
109} 115}
diff --git a/server/helpers/youtube-dl/youtube-dl-cli.ts b/server/helpers/youtube-dl/youtube-dl-cli.ts
index a2f630953..765038cea 100644
--- a/server/helpers/youtube-dl/youtube-dl-cli.ts
+++ b/server/helpers/youtube-dl/youtube-dl-cli.ts
@@ -6,6 +6,7 @@ import { VideoResolution } from '@shared/models'
6import { logger, loggerTagsFactory } from '../logger' 6import { logger, loggerTagsFactory } from '../logger'
7import { getProxy, isProxyEnabled } from '../proxy' 7import { getProxy, isProxyEnabled } from '../proxy'
8import { isBinaryResponse, peertubeGot } from '../requests' 8import { isBinaryResponse, peertubeGot } from '../requests'
9import { OptionsOfBufferResponseBody } from 'got/dist/source'
9 10
10const lTags = loggerTagsFactory('youtube-dl') 11const lTags = loggerTagsFactory('youtube-dl')
11 12
@@ -28,7 +29,16 @@ export class YoutubeDLCLI {
28 29
29 logger.info('Updating youtubeDL binary from %s.', url, lTags()) 30 logger.info('Updating youtubeDL binary from %s.', url, lTags())
30 31
31 const gotOptions = { context: { bodyKBLimit: 20_000 }, responseType: 'buffer' as 'buffer' } 32 const gotOptions: OptionsOfBufferResponseBody = {
33 context: { bodyKBLimit: 20_000 },
34 responseType: 'buffer' as 'buffer'
35 }
36
37 if (process.env.YOUTUBE_DL_DOWNLOAD_BEARER_TOKEN) {
38 gotOptions.headers = {
39 authorization: 'Bearer ' + process.env.YOUTUBE_DL_DOWNLOAD_BEARER_TOKEN
40 }
41 }
32 42
33 try { 43 try {
34 let gotResult = await peertubeGot(url, gotOptions) 44 let gotResult = await peertubeGot(url, gotOptions)
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts
index 0e56f0c9f..ec5045078 100644
--- a/server/initializers/constants.ts
+++ b/server/initializers/constants.ts
@@ -781,6 +781,9 @@ const LRU_CACHE = {
781 VIDEO_TOKENS: { 781 VIDEO_TOKENS: {
782 MAX_SIZE: 100_000, 782 MAX_SIZE: 100_000,
783 TTL: parseDurationToMs('8 hours') 783 TTL: parseDurationToMs('8 hours')
784 },
785 TRACKER_IPS: {
786 MAX_SIZE: 100_000
784 } 787 }
785} 788}
786 789
@@ -884,7 +887,7 @@ const TRACKER_RATE_LIMITS = {
884 INTERVAL: 60000 * 5, // 5 minutes 887 INTERVAL: 60000 * 5, // 5 minutes
885 ANNOUNCES_PER_IP_PER_INFOHASH: 15, // maximum announces per torrent in the interval 888 ANNOUNCES_PER_IP_PER_INFOHASH: 15, // maximum announces per torrent in the interval
886 ANNOUNCES_PER_IP: 30, // maximum announces for all our torrents in the interval 889 ANNOUNCES_PER_IP: 30, // maximum announces for all our torrents in the interval
887 BLOCK_IP_LIFETIME: 60000 * 3 // 3 minutes 890 BLOCK_IP_LIFETIME: parseDurationToMs('3 minutes')
888} 891}
889 892
890const P2P_MEDIA_LOADER_PEER_VERSION = 2 893const P2P_MEDIA_LOADER_PEER_VERSION = 2
diff --git a/server/lib/opentelemetry/metric-helpers/bittorrent-tracker-observers-builder.ts b/server/lib/opentelemetry/metric-helpers/bittorrent-tracker-observers-builder.ts
new file mode 100644
index 000000000..ef40c0fa9
--- /dev/null
+++ b/server/lib/opentelemetry/metric-helpers/bittorrent-tracker-observers-builder.ts
@@ -0,0 +1,51 @@
1import { Meter } from '@opentelemetry/api'
2
3export class BittorrentTrackerObserversBuilder {
4
5 constructor (private readonly meter: Meter, private readonly trackerServer: any) {
6
7 }
8
9 buildObservers () {
10 const activeInfohashes = this.meter.createObservableGauge('peertube_bittorrent_tracker_active_infohashes_total', {
11 description: 'Total active infohashes in the PeerTube BitTorrent Tracker'
12 })
13 const inactiveInfohashes = this.meter.createObservableGauge('peertube_bittorrent_tracker_inactive_infohashes_total', {
14 description: 'Total inactive infohashes in the PeerTube BitTorrent Tracker'
15 })
16 const peers = this.meter.createObservableGauge('peertube_bittorrent_tracker_peers_total', {
17 description: 'Total peers in the PeerTube BitTorrent Tracker'
18 })
19
20 this.meter.addBatchObservableCallback(observableResult => {
21 const infohashes = Object.keys(this.trackerServer.torrents)
22
23 const counters = {
24 activeInfohashes: 0,
25 inactiveInfohashes: 0,
26 peers: 0,
27 uncompletedPeers: 0
28 }
29
30 for (const infohash of infohashes) {
31 const content = this.trackerServer.torrents[infohash]
32
33 const peers = content.peers
34 if (peers.keys.length !== 0) counters.activeInfohashes++
35 else counters.inactiveInfohashes++
36
37 for (const peerId of peers.keys) {
38 const peer = peers.peek(peerId)
39 if (peer == null) return
40
41 counters.peers++
42 }
43 }
44
45 observableResult.observe(activeInfohashes, counters.activeInfohashes)
46 observableResult.observe(inactiveInfohashes, counters.inactiveInfohashes)
47 observableResult.observe(peers, counters.peers)
48 }, [ activeInfohashes, inactiveInfohashes, peers ])
49 }
50
51}
diff --git a/server/lib/opentelemetry/metric-helpers/index.ts b/server/lib/opentelemetry/metric-helpers/index.ts
index 775d954ba..47b24a54f 100644
--- a/server/lib/opentelemetry/metric-helpers/index.ts
+++ b/server/lib/opentelemetry/metric-helpers/index.ts
@@ -1,3 +1,4 @@
1export * from './bittorrent-tracker-observers-builder'
1export * from './lives-observers-builder' 2export * from './lives-observers-builder'
2export * from './job-queue-observers-builder' 3export * from './job-queue-observers-builder'
3export * from './nodejs-observers-builder' 4export * from './nodejs-observers-builder'
diff --git a/server/lib/opentelemetry/metrics.ts b/server/lib/opentelemetry/metrics.ts
index 226d514c0..9cc067e4a 100644
--- a/server/lib/opentelemetry/metrics.ts
+++ b/server/lib/opentelemetry/metrics.ts
@@ -7,6 +7,7 @@ import { CONFIG } from '@server/initializers/config'
7import { MVideoImmutable } from '@server/types/models' 7import { MVideoImmutable } from '@server/types/models'
8import { PlaybackMetricCreate } from '@shared/models' 8import { PlaybackMetricCreate } from '@shared/models'
9import { 9import {
10 BittorrentTrackerObserversBuilder,
10 JobQueueObserversBuilder, 11 JobQueueObserversBuilder,
11 LivesObserversBuilder, 12 LivesObserversBuilder,
12 NodeJSObserversBuilder, 13 NodeJSObserversBuilder,
@@ -41,7 +42,7 @@ class OpenTelemetryMetrics {
41 }) 42 })
42 } 43 }
43 44
44 registerMetrics () { 45 registerMetrics (options: { trackerServer: any }) {
45 if (CONFIG.OPEN_TELEMETRY.METRICS.ENABLED !== true) return 46 if (CONFIG.OPEN_TELEMETRY.METRICS.ENABLED !== true) return
46 47
47 logger.info('Registering Open Telemetry metrics') 48 logger.info('Registering Open Telemetry metrics')
@@ -80,6 +81,9 @@ class OpenTelemetryMetrics {
80 81
81 const viewersObserversBuilder = new ViewersObserversBuilder(this.meter) 82 const viewersObserversBuilder = new ViewersObserversBuilder(this.meter)
82 viewersObserversBuilder.buildObservers() 83 viewersObserversBuilder.buildObservers()
84
85 const bittorrentTrackerObserversBuilder = new BittorrentTrackerObserversBuilder(this.meter, options.trackerServer)
86 bittorrentTrackerObserversBuilder.buildObservers()
83 } 87 }
84 88
85 observePlaybackMetric (video: MVideoImmutable, metrics: PlaybackMetricCreate) { 89 observePlaybackMetric (video: MVideoImmutable, metrics: PlaybackMetricCreate) {
diff --git a/server/lib/plugins/plugin-helpers-builder.ts b/server/lib/plugins/plugin-helpers-builder.ts
index 7b1def6e3..66383af46 100644
--- a/server/lib/plugins/plugin-helpers-builder.ts
+++ b/server/lib/plugins/plugin-helpers-builder.ts
@@ -209,6 +209,10 @@ function buildConfigHelpers () {
209 return WEBSERVER.URL 209 return WEBSERVER.URL
210 }, 210 },
211 211
212 getServerListeningConfig () {
213 return { hostname: CONFIG.LISTEN.HOSTNAME, port: CONFIG.LISTEN.PORT }
214 },
215
212 getServerConfig () { 216 getServerConfig () {
213 return ServerConfigManager.Instance.getServerConfig() 217 return ServerConfigManager.Instance.getServerConfig()
214 } 218 }
@@ -245,7 +249,7 @@ function buildUserHelpers () {
245 }, 249 },
246 250
247 getAuthUser: (res: express.Response) => { 251 getAuthUser: (res: express.Response) => {
248 const user = res.locals.oauth?.token?.User 252 const user = res.locals.oauth?.token?.User || res.locals.videoFileToken?.user
249 if (!user) return undefined 253 if (!user) return undefined
250 254
251 return UserModel.loadByIdFull(user.id) 255 return UserModel.loadByIdFull(user.id)
diff --git a/server/lib/redis.ts b/server/lib/redis.ts
index c0e9aece7..451ddd0b6 100644
--- a/server/lib/redis.ts
+++ b/server/lib/redis.ts
@@ -8,7 +8,6 @@ import {
8 AP_CLEANER, 8 AP_CLEANER,
9 CONTACT_FORM_LIFETIME, 9 CONTACT_FORM_LIFETIME,
10 RESUMABLE_UPLOAD_SESSION_LIFETIME, 10 RESUMABLE_UPLOAD_SESSION_LIFETIME,
11 TRACKER_RATE_LIMITS,
12 TWO_FACTOR_AUTH_REQUEST_TOKEN_LIFETIME, 11 TWO_FACTOR_AUTH_REQUEST_TOKEN_LIFETIME,
13 USER_EMAIL_VERIFY_LIFETIME, 12 USER_EMAIL_VERIFY_LIFETIME,
14 USER_PASSWORD_CREATE_LIFETIME, 13 USER_PASSWORD_CREATE_LIFETIME,
@@ -157,16 +156,6 @@ class Redis {
157 return this.exists(this.generateIPViewKey(ip, videoUUID)) 156 return this.exists(this.generateIPViewKey(ip, videoUUID))
158 } 157 }
159 158
160 /* ************ Tracker IP block ************ */
161
162 setTrackerBlockIP (ip: string) {
163 return this.setValue(this.generateTrackerBlockIPKey(ip), '1', TRACKER_RATE_LIMITS.BLOCK_IP_LIFETIME)
164 }
165
166 async doesTrackerBlockIPExist (ip: string) {
167 return this.exists(this.generateTrackerBlockIPKey(ip))
168 }
169
170 /* ************ Video views stats ************ */ 159 /* ************ Video views stats ************ */
171 160
172 addVideoViewStats (videoId: number) { 161 addVideoViewStats (videoId: number) {
@@ -365,10 +354,6 @@ class Redis {
365 return `views-${videoUUID}-${ip}` 354 return `views-${videoUUID}-${ip}`
366 } 355 }
367 356
368 private generateTrackerBlockIPKey (ip: string) {
369 return `tracker-block-ip-${ip}`
370 }
371
372 private generateContactFormKey (ip: string) { 357 private generateContactFormKey (ip: string) {
373 return 'contact-form-' + ip 358 return 'contact-form-' + ip
374 } 359 }
diff --git a/server/lib/sync-channel.ts b/server/lib/sync-channel.ts
index 10167ee38..3a805a943 100644
--- a/server/lib/sync-channel.ts
+++ b/server/lib/sync-channel.ts
@@ -76,7 +76,7 @@ export async function synchronizeChannel (options: {
76 76
77 await JobQueue.Instance.createJobWithChildren(parent, children) 77 await JobQueue.Instance.createJobWithChildren(parent, children)
78 } catch (err) { 78 } catch (err) {
79 logger.error(`Failed to import channel ${channel.name}`, { err }) 79 logger.error(`Failed to import ${externalChannelUrl} in channel ${channel.name}`, { err })
80 channelSync.state = VideoChannelSyncState.FAILED 80 channelSync.state = VideoChannelSyncState.FAILED
81 await channelSync.save() 81 await channelSync.save()
82 } 82 }
diff --git a/server/lib/video-tokens-manager.ts b/server/lib/video-tokens-manager.ts
index c43085d16..17aa29cdd 100644
--- a/server/lib/video-tokens-manager.ts
+++ b/server/lib/video-tokens-manager.ts
@@ -1,5 +1,7 @@
1import LRUCache from 'lru-cache' 1import LRUCache from 'lru-cache'
2import { LRU_CACHE } from '@server/initializers/constants' 2import { LRU_CACHE } from '@server/initializers/constants'
3import { MUserAccountUrl } from '@server/types/models'
4import { pick } from '@shared/core-utils'
3import { buildUUID } from '@shared/extra-utils' 5import { buildUUID } from '@shared/extra-utils'
4 6
5// --------------------------------------------------------------------------- 7// ---------------------------------------------------------------------------
@@ -10,19 +12,22 @@ class VideoTokensManager {
10 12
11 private static instance: VideoTokensManager 13 private static instance: VideoTokensManager
12 14
13 private readonly lruCache = new LRUCache<string, string>({ 15 private readonly lruCache = new LRUCache<string, { videoUUID: string, user: MUserAccountUrl }>({
14 max: LRU_CACHE.VIDEO_TOKENS.MAX_SIZE, 16 max: LRU_CACHE.VIDEO_TOKENS.MAX_SIZE,
15 ttl: LRU_CACHE.VIDEO_TOKENS.TTL 17 ttl: LRU_CACHE.VIDEO_TOKENS.TTL
16 }) 18 })
17 19
18 private constructor () {} 20 private constructor () {}
19 21
20 create (videoUUID: string) { 22 create (options: {
23 user: MUserAccountUrl
24 videoUUID: string
25 }) {
21 const token = buildUUID() 26 const token = buildUUID()
22 27
23 const expires = new Date(new Date().getTime() + LRU_CACHE.VIDEO_TOKENS.TTL) 28 const expires = new Date(new Date().getTime() + LRU_CACHE.VIDEO_TOKENS.TTL)
24 29
25 this.lruCache.set(token, videoUUID) 30 this.lruCache.set(token, pick(options, [ 'user', 'videoUUID' ]))
26 31
27 return { token, expires } 32 return { token, expires }
28 } 33 }
@@ -34,7 +39,16 @@ class VideoTokensManager {
34 const value = this.lruCache.get(options.token) 39 const value = this.lruCache.get(options.token)
35 if (!value) return false 40 if (!value) return false
36 41
37 return value === options.videoUUID 42 return value.videoUUID === options.videoUUID
43 }
44
45 getUserFromToken (options: {
46 token: string
47 }) {
48 const value = this.lruCache.get(options.token)
49 if (!value) return undefined
50
51 return value.user
38 } 52 }
39 53
40 static get Instance () { 54 static get Instance () {
diff --git a/server/middlewares/validators/shared/videos.ts b/server/middlewares/validators/shared/videos.ts
index ebbfc0a0a..0033a32ff 100644
--- a/server/middlewares/validators/shared/videos.ts
+++ b/server/middlewares/validators/shared/videos.ts
@@ -180,18 +180,16 @@ async function checkCanAccessVideoStaticFiles (options: {
180 return checkCanSeeVideo(options) 180 return checkCanSeeVideo(options)
181 } 181 }
182 182
183 if (!video.hasPrivateStaticPath()) return true
184
185 const videoFileToken = req.query.videoFileToken 183 const videoFileToken = req.query.videoFileToken
186 if (!videoFileToken) { 184 if (videoFileToken && VideoTokensManager.Instance.hasToken({ token: videoFileToken, videoUUID: video.uuid })) {
187 res.sendStatus(HttpStatusCode.FORBIDDEN_403) 185 const user = VideoTokensManager.Instance.getUserFromToken({ token: videoFileToken })
188 return false
189 }
190 186
191 if (VideoTokensManager.Instance.hasToken({ token: videoFileToken, videoUUID: video.uuid })) { 187 res.locals.videoFileToken = { user }
192 return true 188 return true
193 } 189 }
194 190
191 if (!video.hasPrivateStaticPath()) return true
192
195 res.sendStatus(HttpStatusCode.FORBIDDEN_403) 193 res.sendStatus(HttpStatusCode.FORBIDDEN_403)
196 return false 194 return false
197} 195}
diff --git a/server/tests/api/activitypub/cleaner.ts b/server/tests/api/activitypub/cleaner.ts
index eb6779123..1c1495022 100644
--- a/server/tests/api/activitypub/cleaner.ts
+++ b/server/tests/api/activitypub/cleaner.ts
@@ -148,7 +148,7 @@ describe('Test AP cleaner', function () {
148 it('Should destroy server 3 internal shares and correctly clean them', async function () { 148 it('Should destroy server 3 internal shares and correctly clean them', async function () {
149 this.timeout(20000) 149 this.timeout(20000)
150 150
151 const preCount = await servers[0].sql.getCount('videoShare') 151 const preCount = await servers[0].sql.getVideoShareCount()
152 expect(preCount).to.equal(6) 152 expect(preCount).to.equal(6)
153 153
154 await servers[2].sql.deleteAll('videoShare') 154 await servers[2].sql.deleteAll('videoShare')
@@ -156,7 +156,7 @@ describe('Test AP cleaner', function () {
156 await waitJobs(servers) 156 await waitJobs(servers)
157 157
158 // Still 6 because we don't have remote shares on local videos 158 // Still 6 because we don't have remote shares on local videos
159 const postCount = await servers[0].sql.getCount('videoShare') 159 const postCount = await servers[0].sql.getVideoShareCount()
160 expect(postCount).to.equal(6) 160 expect(postCount).to.equal(6)
161 }) 161 })
162 162
@@ -185,7 +185,7 @@ describe('Test AP cleaner', function () {
185 async function check (like: string, ofServerUrl: string, urlSuffix: string, remote: 'true' | 'false') { 185 async function check (like: string, ofServerUrl: string, urlSuffix: string, remote: 'true' | 'false') {
186 const query = `SELECT "videoId", "accountVideoRate".url FROM "accountVideoRate" ` + 186 const query = `SELECT "videoId", "accountVideoRate".url FROM "accountVideoRate" ` +
187 `INNER JOIN video ON "accountVideoRate"."videoId" = video.id AND remote IS ${remote} WHERE "accountVideoRate"."url" LIKE '${like}'` 187 `INNER JOIN video ON "accountVideoRate"."videoId" = video.id AND remote IS ${remote} WHERE "accountVideoRate"."url" LIKE '${like}'`
188 const res = await servers[0].sql.selectQuery(query) 188 const res = await servers[0].sql.selectQuery<{ url: string }>(query)
189 189
190 for (const rate of res) { 190 for (const rate of res) {
191 const matcher = new RegExp(`^${ofServerUrl}/accounts/root/dislikes/\\d+${urlSuffix}$`) 191 const matcher = new RegExp(`^${ofServerUrl}/accounts/root/dislikes/\\d+${urlSuffix}$`)
@@ -231,7 +231,7 @@ describe('Test AP cleaner', function () {
231 const query = `SELECT "videoId", "videoComment".url, uuid as "videoUUID" FROM "videoComment" ` + 231 const query = `SELECT "videoId", "videoComment".url, uuid as "videoUUID" FROM "videoComment" ` +
232 `INNER JOIN video ON "videoComment"."videoId" = video.id AND remote IS ${remote} WHERE "videoComment"."url" LIKE '${like}'` 232 `INNER JOIN video ON "videoComment"."videoId" = video.id AND remote IS ${remote} WHERE "videoComment"."url" LIKE '${like}'`
233 233
234 const res = await servers[0].sql.selectQuery(query) 234 const res = await servers[0].sql.selectQuery<{ url: string, videoUUID: string }>(query)
235 235
236 for (const comment of res) { 236 for (const comment of res) {
237 const matcher = new RegExp(`${ofServerUrl}/videos/watch/${comment.videoUUID}/comments/\\d+${urlSuffix}`) 237 const matcher = new RegExp(`${ofServerUrl}/videos/watch/${comment.videoUUID}/comments/\\d+${urlSuffix}`)
diff --git a/server/tests/api/check-params/redundancy.ts b/server/tests/api/check-params/redundancy.ts
index 908407b9a..73dfd489d 100644
--- a/server/tests/api/check-params/redundancy.ts
+++ b/server/tests/api/check-params/redundancy.ts
@@ -24,7 +24,7 @@ describe('Test server redundancy API validators', function () {
24 // --------------------------------------------------------------- 24 // ---------------------------------------------------------------
25 25
26 before(async function () { 26 before(async function () {
27 this.timeout(80000) 27 this.timeout(160000)
28 28
29 servers = await createMultipleServers(2) 29 servers = await createMultipleServers(2)
30 30
diff --git a/server/tests/api/live/live-fast-restream.ts b/server/tests/api/live/live-fast-restream.ts
index c0bb8d529..f6959b83c 100644
--- a/server/tests/api/live/live-fast-restream.ts
+++ b/server/tests/api/live/live-fast-restream.ts
@@ -78,9 +78,15 @@ describe('Fast restream in live', function () {
78 const video = await server.videos.get({ id: liveId }) 78 const video = await server.videos.get({ id: liveId })
79 expect(video.streamingPlaylists).to.have.lengthOf(1) 79 expect(video.streamingPlaylists).to.have.lengthOf(1)
80 80
81 await server.live.getSegmentFile({ videoUUID: liveId, segment: 0, playlistNumber: 0 }) 81 try {
82 await makeRawRequest({ url: video.streamingPlaylists[0].playlistUrl, expectedStatus: HttpStatusCode.OK_200 }) 82 await server.live.getSegmentFile({ videoUUID: liveId, segment: 0, playlistNumber: 0 })
83 await makeRawRequest({ url: video.streamingPlaylists[0].segmentsSha256Url, expectedStatus: HttpStatusCode.OK_200 }) 83 await makeRawRequest({ url: video.streamingPlaylists[0].playlistUrl, expectedStatus: HttpStatusCode.OK_200 })
84 await makeRawRequest({ url: video.streamingPlaylists[0].segmentsSha256Url, expectedStatus: HttpStatusCode.OK_200 })
85 } catch (err) {
86 // FIXME: try to debug error in CI "Unexpected end of JSON input"
87 console.error(err)
88 throw err
89 }
84 90
85 await wait(100) 91 await wait(100)
86 } 92 }
@@ -129,7 +135,7 @@ describe('Fast restream in live', function () {
129 await server.config.enableLive({ allowReplay: true, transcoding: true, resolutions: 'min' }) 135 await server.config.enableLive({ allowReplay: true, transcoding: true, resolutions: 'min' })
130 }) 136 })
131 137
132 it('Should correctly fast reastream in a permanent live with and without save replay', async function () { 138 it('Should correctly fast restream in a permanent live with and without save replay', async function () {
133 this.timeout(480000) 139 this.timeout(480000)
134 140
135 // A test can take a long time, so prefer to run them in parallel 141 // A test can take a long time, so prefer to run them in parallel
diff --git a/server/tests/api/notifications/moderation-notifications.ts b/server/tests/api/notifications/moderation-notifications.ts
index b127a7a31..c7b9b5fb0 100644
--- a/server/tests/api/notifications/moderation-notifications.ts
+++ b/server/tests/api/notifications/moderation-notifications.ts
@@ -34,7 +34,7 @@ describe('Test moderation notifications', function () {
34 let emails: object[] = [] 34 let emails: object[] = []
35 35
36 before(async function () { 36 before(async function () {
37 this.timeout(120000) 37 this.timeout(50000)
38 38
39 const res = await prepareNotificationsTest(3) 39 const res = await prepareNotificationsTest(3)
40 emails = res.emails 40 emails = res.emails
@@ -60,7 +60,7 @@ describe('Test moderation notifications', function () {
60 }) 60 })
61 61
62 it('Should not send a notification to moderators on local abuse reported by an admin', async function () { 62 it('Should not send a notification to moderators on local abuse reported by an admin', async function () {
63 this.timeout(20000) 63 this.timeout(50000)
64 64
65 const name = 'video for abuse ' + buildUUID() 65 const name = 'video for abuse ' + buildUUID()
66 const video = await servers[0].videos.upload({ token: userToken1, attributes: { name } }) 66 const video = await servers[0].videos.upload({ token: userToken1, attributes: { name } })
@@ -72,7 +72,7 @@ describe('Test moderation notifications', function () {
72 }) 72 })
73 73
74 it('Should send a notification to moderators on local video abuse', async function () { 74 it('Should send a notification to moderators on local video abuse', async function () {
75 this.timeout(20000) 75 this.timeout(50000)
76 76
77 const name = 'video for abuse ' + buildUUID() 77 const name = 'video for abuse ' + buildUUID()
78 const video = await servers[0].videos.upload({ token: userToken1, attributes: { name } }) 78 const video = await servers[0].videos.upload({ token: userToken1, attributes: { name } })
@@ -84,7 +84,7 @@ describe('Test moderation notifications', function () {
84 }) 84 })
85 85
86 it('Should send a notification to moderators on remote video abuse', async function () { 86 it('Should send a notification to moderators on remote video abuse', async function () {
87 this.timeout(20000) 87 this.timeout(50000)
88 88
89 const name = 'video for abuse ' + buildUUID() 89 const name = 'video for abuse ' + buildUUID()
90 const video = await servers[0].videos.upload({ token: userToken1, attributes: { name } }) 90 const video = await servers[0].videos.upload({ token: userToken1, attributes: { name } })
@@ -99,7 +99,7 @@ describe('Test moderation notifications', function () {
99 }) 99 })
100 100
101 it('Should send a notification to moderators on local comment abuse', async function () { 101 it('Should send a notification to moderators on local comment abuse', async function () {
102 this.timeout(20000) 102 this.timeout(50000)
103 103
104 const name = 'video for abuse ' + buildUUID() 104 const name = 'video for abuse ' + buildUUID()
105 const video = await servers[0].videos.upload({ token: userToken1, attributes: { name } }) 105 const video = await servers[0].videos.upload({ token: userToken1, attributes: { name } })
@@ -118,7 +118,7 @@ describe('Test moderation notifications', function () {
118 }) 118 })
119 119
120 it('Should send a notification to moderators on remote comment abuse', async function () { 120 it('Should send a notification to moderators on remote comment abuse', async function () {
121 this.timeout(20000) 121 this.timeout(50000)
122 122
123 const name = 'video for abuse ' + buildUUID() 123 const name = 'video for abuse ' + buildUUID()
124 const video = await servers[0].videos.upload({ token: userToken1, attributes: { name } }) 124 const video = await servers[0].videos.upload({ token: userToken1, attributes: { name } })
@@ -140,7 +140,7 @@ describe('Test moderation notifications', function () {
140 }) 140 })
141 141
142 it('Should send a notification to moderators on local account abuse', async function () { 142 it('Should send a notification to moderators on local account abuse', async function () {
143 this.timeout(20000) 143 this.timeout(50000)
144 144
145 const username = 'user' + new Date().getTime() 145 const username = 'user' + new Date().getTime()
146 const { account } = await servers[0].users.create({ username, password: 'donald' }) 146 const { account } = await servers[0].users.create({ username, password: 'donald' })
@@ -153,7 +153,7 @@ describe('Test moderation notifications', function () {
153 }) 153 })
154 154
155 it('Should send a notification to moderators on remote account abuse', async function () { 155 it('Should send a notification to moderators on remote account abuse', async function () {
156 this.timeout(20000) 156 this.timeout(50000)
157 157
158 const username = 'user' + new Date().getTime() 158 const username = 'user' + new Date().getTime()
159 const tmpToken = await servers[0].users.generateUserAndToken(username) 159 const tmpToken = await servers[0].users.generateUserAndToken(username)
@@ -512,10 +512,14 @@ describe('Test moderation notifications', function () {
512 }) 512 })
513 513
514 it('Should not send video publish notification if auto-blacklisted', async function () { 514 it('Should not send video publish notification if auto-blacklisted', async function () {
515 this.timeout(120000)
516
515 await checkVideoIsPublished({ ...userBaseParams, videoName, shortUUID, checkType: 'absence' }) 517 await checkVideoIsPublished({ ...userBaseParams, videoName, shortUUID, checkType: 'absence' })
516 }) 518 })
517 519
518 it('Should not send a local user subscription notification if auto-blacklisted', async function () { 520 it('Should not send a local user subscription notification if auto-blacklisted', async function () {
521 this.timeout(120000)
522
519 await checkNewVideoFromSubscription({ ...adminBaseParamsServer1, videoName, shortUUID, checkType: 'absence' }) 523 await checkNewVideoFromSubscription({ ...adminBaseParamsServer1, videoName, shortUUID, checkType: 'absence' })
520 }) 524 })
521 525
@@ -524,7 +528,7 @@ describe('Test moderation notifications', function () {
524 }) 528 })
525 529
526 it('Should send video published and unblacklist after video unblacklisted', async function () { 530 it('Should send video published and unblacklist after video unblacklisted', async function () {
527 this.timeout(40000) 531 this.timeout(120000)
528 532
529 await servers[0].blacklist.remove({ videoId: uuid }) 533 await servers[0].blacklist.remove({ videoId: uuid })
530 534
@@ -537,10 +541,14 @@ describe('Test moderation notifications', function () {
537 }) 541 })
538 542
539 it('Should send a local user subscription notification after removed from blacklist', async function () { 543 it('Should send a local user subscription notification after removed from blacklist', async function () {
544 this.timeout(120000)
545
540 await checkNewVideoFromSubscription({ ...adminBaseParamsServer1, videoName, shortUUID, checkType: 'presence' }) 546 await checkNewVideoFromSubscription({ ...adminBaseParamsServer1, videoName, shortUUID, checkType: 'presence' })
541 }) 547 })
542 548
543 it('Should send a remote user subscription notification after removed from blacklist', async function () { 549 it('Should send a remote user subscription notification after removed from blacklist', async function () {
550 this.timeout(120000)
551
544 await checkNewVideoFromSubscription({ ...adminBaseParamsServer2, videoName, shortUUID, checkType: 'presence' }) 552 await checkNewVideoFromSubscription({ ...adminBaseParamsServer2, videoName, shortUUID, checkType: 'presence' })
545 }) 553 })
546 554
@@ -576,7 +584,7 @@ describe('Test moderation notifications', function () {
576 }) 584 })
577 585
578 it('Should not send publish/subscription notifications after scheduled update if video still auto-blacklisted', async function () { 586 it('Should not send publish/subscription notifications after scheduled update if video still auto-blacklisted', async function () {
579 this.timeout(40000) 587 this.timeout(120000)
580 588
581 // In 2 seconds 589 // In 2 seconds
582 const updateAt = new Date(new Date().getTime() + 2000) 590 const updateAt = new Date(new Date().getTime() + 2000)
diff --git a/server/tests/api/object-storage/video-static-file-privacy.ts b/server/tests/api/object-storage/video-static-file-privacy.ts
index 71ad35a43..869d437d5 100644
--- a/server/tests/api/object-storage/video-static-file-privacy.ts
+++ b/server/tests/api/object-storage/video-static-file-privacy.ts
@@ -120,7 +120,7 @@ describe('Object storage for video static file privacy', function () {
120 // --------------------------------------------------------------------------- 120 // ---------------------------------------------------------------------------
121 121
122 it('Should upload a private video and have appropriate object storage ACL', async function () { 122 it('Should upload a private video and have appropriate object storage ACL', async function () {
123 this.timeout(60000) 123 this.timeout(120000)
124 124
125 { 125 {
126 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.PRIVATE }) 126 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.PRIVATE })
@@ -138,7 +138,7 @@ describe('Object storage for video static file privacy', function () {
138 }) 138 })
139 139
140 it('Should upload a public video and have appropriate object storage ACL', async function () { 140 it('Should upload a public video and have appropriate object storage ACL', async function () {
141 this.timeout(60000) 141 this.timeout(120000)
142 142
143 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.UNLISTED }) 143 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.UNLISTED })
144 await waitJobs([ server ]) 144 await waitJobs([ server ])
diff --git a/server/tests/api/videos/video-channel-syncs.ts b/server/tests/api/videos/video-channel-syncs.ts
index 91291524d..dd483f95e 100644
--- a/server/tests/api/videos/video-channel-syncs.ts
+++ b/server/tests/api/videos/video-channel-syncs.ts
@@ -307,6 +307,7 @@ describe('Test channel synchronizations', function () {
307 }) 307 })
308 } 308 }
309 309
310 runSuite('youtube-dl') 310 // FIXME: suite is broken with youtube-dl
311 // runSuite('youtube-dl')
311 runSuite('yt-dlp') 312 runSuite('yt-dlp')
312}) 313})
diff --git a/server/tests/feeds/feeds.ts b/server/tests/feeds/feeds.ts
index 906dab1a3..7345f728a 100644
--- a/server/tests/feeds/feeds.ts
+++ b/server/tests/feeds/feeds.ts
@@ -189,7 +189,7 @@ describe('Test syndication feeds', () => {
189 const jsonObj = JSON.parse(json) 189 const jsonObj = JSON.parse(json)
190 expect(jsonObj.items.length).to.be.equal(1) 190 expect(jsonObj.items.length).to.be.equal(1)
191 expect(jsonObj.items[0].title).to.equal('my super name for server 1') 191 expect(jsonObj.items[0].title).to.equal('my super name for server 1')
192 expect(jsonObj.items[0].author.name).to.equal('root') 192 expect(jsonObj.items[0].author.name).to.equal('Main root channel')
193 } 193 }
194 194
195 { 195 {
@@ -197,7 +197,7 @@ describe('Test syndication feeds', () => {
197 const jsonObj = JSON.parse(json) 197 const jsonObj = JSON.parse(json)
198 expect(jsonObj.items.length).to.be.equal(1) 198 expect(jsonObj.items.length).to.be.equal(1)
199 expect(jsonObj.items[0].title).to.equal('user video') 199 expect(jsonObj.items[0].title).to.equal('user video')
200 expect(jsonObj.items[0].author.name).to.equal('john') 200 expect(jsonObj.items[0].author.name).to.equal('Main john channel')
201 } 201 }
202 202
203 for (const server of servers) { 203 for (const server of servers) {
@@ -223,7 +223,7 @@ describe('Test syndication feeds', () => {
223 const jsonObj = JSON.parse(json) 223 const jsonObj = JSON.parse(json)
224 expect(jsonObj.items.length).to.be.equal(1) 224 expect(jsonObj.items.length).to.be.equal(1)
225 expect(jsonObj.items[0].title).to.equal('my super name for server 1') 225 expect(jsonObj.items[0].title).to.equal('my super name for server 1')
226 expect(jsonObj.items[0].author.name).to.equal('root') 226 expect(jsonObj.items[0].author.name).to.equal('Main root channel')
227 } 227 }
228 228
229 { 229 {
@@ -231,7 +231,7 @@ describe('Test syndication feeds', () => {
231 const jsonObj = JSON.parse(json) 231 const jsonObj = JSON.parse(json)
232 expect(jsonObj.items.length).to.be.equal(1) 232 expect(jsonObj.items.length).to.be.equal(1)
233 expect(jsonObj.items[0].title).to.equal('user video') 233 expect(jsonObj.items[0].title).to.equal('user video')
234 expect(jsonObj.items[0].author.name).to.equal('john') 234 expect(jsonObj.items[0].author.name).to.equal('Main john channel')
235 } 235 }
236 236
237 for (const server of servers) { 237 for (const server of servers) {
diff --git a/server/tests/fixtures/peertube-plugin-test-four/main.js b/server/tests/fixtures/peertube-plugin-test-four/main.js
index 3e848c49e..b10177b45 100644
--- a/server/tests/fixtures/peertube-plugin-test-four/main.js
+++ b/server/tests/fixtures/peertube-plugin-test-four/main.js
@@ -76,6 +76,12 @@ async function register ({
76 return res.json({ serverConfig }) 76 return res.json({ serverConfig })
77 }) 77 })
78 78
79 router.get('/server-listening-config', async (req, res) => {
80 const config = await peertubeHelpers.config.getServerListeningConfig()
81
82 return res.json({ config })
83 })
84
79 router.get('/static-route', async (req, res) => { 85 router.get('/static-route', async (req, res) => {
80 const staticRoute = peertubeHelpers.plugin.getBaseStaticRoute() 86 const staticRoute = peertubeHelpers.plugin.getBaseStaticRoute()
81 87
diff --git a/server/tests/fixtures/peertube-plugin-test/main.js b/server/tests/fixtures/peertube-plugin-test/main.js
index 19dccf26e..19ba9f278 100644
--- a/server/tests/fixtures/peertube-plugin-test/main.js
+++ b/server/tests/fixtures/peertube-plugin-test/main.js
@@ -250,7 +250,10 @@ async function register ({ registerHook, registerSetting, settingsManager, stora
250 250
251 registerHook({ 251 registerHook({
252 target: 'filter:api.download.video.allowed.result', 252 target: 'filter:api.download.video.allowed.result',
253 handler: (result, params) => { 253 handler: async (result, params) => {
254 const loggedInUser = await peertubeHelpers.user.getAuthUser(params.res)
255 if (loggedInUser) return { allowed: true }
256
254 if (params && !params.streamingPlaylist && params.video.name.includes('bad file')) { 257 if (params && !params.streamingPlaylist && params.video.name.includes('bad file')) {
255 return { allowed: false, errorMessage: 'Cao Cao' } 258 return { allowed: false, errorMessage: 'Cao Cao' }
256 } 259 }
diff --git a/server/tests/plugins/filter-hooks.ts b/server/tests/plugins/filter-hooks.ts
index 083fd43ca..6724b3bf8 100644
--- a/server/tests/plugins/filter-hooks.ts
+++ b/server/tests/plugins/filter-hooks.ts
@@ -430,6 +430,7 @@ describe('Test plugin filter hooks', function () {
430 430
431 describe('Download hooks', function () { 431 describe('Download hooks', function () {
432 const downloadVideos: VideoDetails[] = [] 432 const downloadVideos: VideoDetails[] = []
433 let downloadVideo2Token: string
433 434
434 before(async function () { 435 before(async function () {
435 this.timeout(120000) 436 this.timeout(120000)
@@ -459,6 +460,8 @@ describe('Test plugin filter hooks', function () {
459 for (const uuid of uuids) { 460 for (const uuid of uuids) {
460 downloadVideos.push(await servers[0].videos.get({ id: uuid })) 461 downloadVideos.push(await servers[0].videos.get({ id: uuid }))
461 } 462 }
463
464 downloadVideo2Token = await servers[0].videoToken.getVideoFileToken({ videoId: downloadVideos[2].uuid })
462 }) 465 })
463 466
464 it('Should run filter:api.download.torrent.allowed.result', async function () { 467 it('Should run filter:api.download.torrent.allowed.result', async function () {
@@ -471,32 +474,42 @@ describe('Test plugin filter hooks', function () {
471 474
472 it('Should run filter:api.download.video.allowed.result', async function () { 475 it('Should run filter:api.download.video.allowed.result', async function () {
473 { 476 {
474 const res = await makeRawRequest({ url: downloadVideos[1].files[0].fileDownloadUrl, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) 477 const refused = downloadVideos[1].files[0].fileDownloadUrl
478 const allowed = [
479 downloadVideos[0].files[0].fileDownloadUrl,
480 downloadVideos[2].files[0].fileDownloadUrl
481 ]
482
483 const res = await makeRawRequest({ url: refused, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
475 expect(res.body.error).to.equal('Cao Cao') 484 expect(res.body.error).to.equal('Cao Cao')
476 485
477 await makeRawRequest({ url: downloadVideos[0].files[0].fileDownloadUrl, expectedStatus: HttpStatusCode.OK_200 }) 486 for (const url of allowed) {
478 await makeRawRequest({ url: downloadVideos[2].files[0].fileDownloadUrl, expectedStatus: HttpStatusCode.OK_200 }) 487 await makeRawRequest({ url, expectedStatus: HttpStatusCode.OK_200 })
488 await makeRawRequest({ url, expectedStatus: HttpStatusCode.OK_200 })
489 }
479 } 490 }
480 491
481 { 492 {
482 const res = await makeRawRequest({ 493 const refused = downloadVideos[2].streamingPlaylists[0].files[0].fileDownloadUrl
483 url: downloadVideos[2].streamingPlaylists[0].files[0].fileDownloadUrl,
484 expectedStatus: HttpStatusCode.FORBIDDEN_403
485 })
486 494
487 expect(res.body.error).to.equal('Sun Jian') 495 const allowed = [
496 downloadVideos[2].files[0].fileDownloadUrl,
497 downloadVideos[0].streamingPlaylists[0].files[0].fileDownloadUrl,
498 downloadVideos[1].streamingPlaylists[0].files[0].fileDownloadUrl
499 ]
488 500
489 await makeRawRequest({ url: downloadVideos[2].files[0].fileDownloadUrl, expectedStatus: HttpStatusCode.OK_200 }) 501 // Only streaming playlist is refuse
502 const res = await makeRawRequest({ url: refused, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
503 expect(res.body.error).to.equal('Sun Jian')
490 504
491 await makeRawRequest({ 505 // But not we there is a user in res
492 url: downloadVideos[0].streamingPlaylists[0].files[0].fileDownloadUrl, 506 await makeRawRequest({ url: refused, token: servers[0].accessToken, expectedStatus: HttpStatusCode.OK_200 })
493 expectedStatus: HttpStatusCode.OK_200 507 await makeRawRequest({ url: refused, query: { videoFileToken: downloadVideo2Token }, expectedStatus: HttpStatusCode.OK_200 })
494 })
495 508
496 await makeRawRequest({ 509 // Other files work
497 url: downloadVideos[1].streamingPlaylists[0].files[0].fileDownloadUrl, 510 for (const url of allowed) {
498 expectedStatus: HttpStatusCode.OK_200 511 await makeRawRequest({ url, expectedStatus: HttpStatusCode.OK_200 })
499 }) 512 }
500 } 513 }
501 }) 514 })
502 }) 515 })
diff --git a/server/tests/plugins/plugin-helpers.ts b/server/tests/plugins/plugin-helpers.ts
index 038e3f0d6..e25992723 100644
--- a/server/tests/plugins/plugin-helpers.ts
+++ b/server/tests/plugins/plugin-helpers.ts
@@ -64,6 +64,18 @@ describe('Test plugin helpers', function () {
64 await servers[0].servers.waitUntilLog(`server url is ${servers[0].url}`) 64 await servers[0].servers.waitUntilLog(`server url is ${servers[0].url}`)
65 }) 65 })
66 66
67 it('Should have the correct listening config', async function () {
68 const res = await makeGetRequest({
69 url: servers[0].url,
70 path: '/plugins/test-four/router/server-listening-config',
71 expectedStatus: HttpStatusCode.OK_200
72 })
73
74 expect(res.body.config).to.exist
75 expect(res.body.config.hostname).to.equal('::')
76 expect(res.body.config.port).to.equal(servers[0].port)
77 })
78
67 it('Should have the correct config', async function () { 79 it('Should have the correct config', async function () {
68 const res = await makeGetRequest({ 80 const res = await makeGetRequest({
69 url: servers[0].url, 81 url: servers[0].url,
diff --git a/server/types/express.d.ts b/server/types/express.d.ts
index 3738ffc47..99244d2a0 100644
--- a/server/types/express.d.ts
+++ b/server/types/express.d.ts
@@ -10,6 +10,7 @@ import {
10 MChannelBannerAccountDefault, 10 MChannelBannerAccountDefault,
11 MChannelSyncChannel, 11 MChannelSyncChannel,
12 MStreamingPlaylist, 12 MStreamingPlaylist,
13 MUserAccountUrl,
13 MVideoChangeOwnershipFull, 14 MVideoChangeOwnershipFull,
14 MVideoFile, 15 MVideoFile,
15 MVideoFormattableDetails, 16 MVideoFormattableDetails,
@@ -187,6 +188,10 @@ declare module 'express' {
187 actor: MActorAccountChannelId 188 actor: MActorAccountChannelId
188 } 189 }
189 190
191 videoFileToken?: {
192 user: MUserAccountUrl
193 }
194
190 authenticated?: boolean 195 authenticated?: boolean
191 196
192 registeredPlugin?: RegisteredPlugin 197 registeredPlugin?: RegisteredPlugin
diff --git a/server/types/plugins/register-server-option.model.ts b/server/types/plugins/register-server-option.model.ts
index 1e2bd830e..df419fff4 100644
--- a/server/types/plugins/register-server-option.model.ts
+++ b/server/types/plugins/register-server-option.model.ts
@@ -71,6 +71,9 @@ export type PeerTubeHelpers = {
71 config: { 71 config: {
72 getWebserverUrl: () => string 72 getWebserverUrl: () => string
73 73
74 // PeerTube >= 5.1
75 getServerListeningConfig: () => { hostname: string, port: number }
76
74 getServerConfig: () => Promise<ServerConfig> 77 getServerConfig: () => Promise<ServerConfig>
75 } 78 }
76 79