diff options
-rw-r--r-- | server/controllers/api/videos/live.ts | 18 | ||||
-rw-r--r-- | server/middlewares/validators/videos/video-live.ts | 6 | ||||
-rw-r--r-- | server/models/video/video-live.ts | 25 | ||||
-rw-r--r-- | server/tests/api/check-params/live.ts | 28 | ||||
-rw-r--r-- | shared/models/users/user-right.enum.ts | 56 | ||||
-rw-r--r-- | shared/models/videos/live/live-video.model.ts | 8 | ||||
-rw-r--r-- | support/doc/api/openapi.yaml | 4 |
7 files changed, 89 insertions, 56 deletions
diff --git a/server/controllers/api/videos/live.ts b/server/controllers/api/videos/live.ts index c6f038079..e51658927 100644 --- a/server/controllers/api/videos/live.ts +++ b/server/controllers/api/videos/live.ts | |||
@@ -10,11 +10,11 @@ import { videoLiveAddValidator, videoLiveGetValidator, videoLiveUpdateValidator | |||
10 | import { VideoLiveModel } from '@server/models/video/video-live' | 10 | import { VideoLiveModel } from '@server/models/video/video-live' |
11 | import { MVideoDetails, MVideoFullLight } from '@server/types/models' | 11 | import { MVideoDetails, MVideoFullLight } from '@server/types/models' |
12 | import { buildUUID, uuidToShort } from '@shared/extra-utils' | 12 | import { buildUUID, uuidToShort } from '@shared/extra-utils' |
13 | import { HttpStatusCode, LiveVideoCreate, LiveVideoLatencyMode, LiveVideoUpdate, VideoState } from '@shared/models' | 13 | import { HttpStatusCode, LiveVideoCreate, LiveVideoLatencyMode, LiveVideoUpdate, UserRight, VideoState } from '@shared/models' |
14 | import { logger } from '../../../helpers/logger' | 14 | import { logger } from '../../../helpers/logger' |
15 | import { sequelizeTypescript } from '../../../initializers/database' | 15 | import { sequelizeTypescript } from '../../../initializers/database' |
16 | import { updateVideoMiniatureFromExisting } from '../../../lib/thumbnail' | 16 | import { updateVideoMiniatureFromExisting } from '../../../lib/thumbnail' |
17 | import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate } from '../../../middlewares' | 17 | import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, optionalAuthenticate } from '../../../middlewares' |
18 | import { VideoModel } from '../../../models/video/video' | 18 | import { VideoModel } from '../../../models/video/video' |
19 | 19 | ||
20 | const liveRouter = express.Router() | 20 | const liveRouter = express.Router() |
@@ -29,7 +29,7 @@ liveRouter.post('/live', | |||
29 | ) | 29 | ) |
30 | 30 | ||
31 | liveRouter.get('/live/:videoId', | 31 | liveRouter.get('/live/:videoId', |
32 | authenticate, | 32 | optionalAuthenticate, |
33 | asyncMiddleware(videoLiveGetValidator), | 33 | asyncMiddleware(videoLiveGetValidator), |
34 | getLiveVideo | 34 | getLiveVideo |
35 | ) | 35 | ) |
@@ -52,7 +52,17 @@ export { | |||
52 | function getLiveVideo (req: express.Request, res: express.Response) { | 52 | function getLiveVideo (req: express.Request, res: express.Response) { |
53 | const videoLive = res.locals.videoLive | 53 | const videoLive = res.locals.videoLive |
54 | 54 | ||
55 | return res.json(videoLive.toFormattedJSON()) | 55 | return res.json(videoLive.toFormattedJSON(canSeePrivateLiveInformation(res))) |
56 | } | ||
57 | |||
58 | function canSeePrivateLiveInformation (res: express.Response) { | ||
59 | const user = res.locals.oauth?.token.User | ||
60 | if (!user) return false | ||
61 | |||
62 | if (user.hasRight(UserRight.GET_ANY_LIVE)) return true | ||
63 | |||
64 | const video = res.locals.videoAll | ||
65 | return video.VideoChannel.Account.userId === user.id | ||
56 | } | 66 | } |
57 | 67 | ||
58 | async function updateLiveVideo (req: express.Request, res: express.Response) { | 68 | async function updateLiveVideo (req: express.Request, res: express.Response) { |
diff --git a/server/middlewares/validators/videos/video-live.ts b/server/middlewares/validators/videos/video-live.ts index b756c0bf1..8f821c5f9 100644 --- a/server/middlewares/validators/videos/video-live.ts +++ b/server/middlewares/validators/videos/video-live.ts | |||
@@ -33,15 +33,11 @@ const videoLiveGetValidator = [ | |||
33 | isValidVideoIdParam('videoId'), | 33 | isValidVideoIdParam('videoId'), |
34 | 34 | ||
35 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | 35 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { |
36 | logger.debug('Checking videoLiveGetValidator parameters', { parameters: req.params, user: res.locals.oauth.token.User.username }) | 36 | logger.debug('Checking videoLiveGetValidator parameters', { parameters: req.params }) |
37 | 37 | ||
38 | if (areValidationErrors(req, res)) return | 38 | if (areValidationErrors(req, res)) return |
39 | if (!await doesVideoExist(req.params.videoId, res, 'all')) return | 39 | if (!await doesVideoExist(req.params.videoId, res, 'all')) return |
40 | 40 | ||
41 | // Check if the user who did the request is able to get the live info | ||
42 | const user = res.locals.oauth.token.User | ||
43 | if (!checkUserCanManageVideo(user, res.locals.videoAll, UserRight.GET_ANY_LIVE, res, false)) return | ||
44 | |||
45 | const videoLive = await VideoLiveModel.loadByVideoId(res.locals.videoAll.id) | 41 | const videoLive = await VideoLiveModel.loadByVideoId(res.locals.videoAll.id) |
46 | if (!videoLive) { | 42 | if (!videoLive) { |
47 | return res.fail({ | 43 | return res.fail({ |
diff --git a/server/models/video/video-live.ts b/server/models/video/video-live.ts index 96c0bf7f7..68e381105 100644 --- a/server/models/video/video-live.ts +++ b/server/models/video/video-live.ts | |||
@@ -101,21 +101,28 @@ export class VideoLiveModel extends Model<Partial<AttributesOnly<VideoLiveModel> | |||
101 | return VideoLiveModel.findOne<MVideoLive>(query) | 101 | return VideoLiveModel.findOne<MVideoLive>(query) |
102 | } | 102 | } |
103 | 103 | ||
104 | toFormattedJSON (): LiveVideo { | 104 | toFormattedJSON (canSeePrivateInformation: boolean): LiveVideo { |
105 | let rtmpUrl: string = null | 105 | let privateInformation: Pick<LiveVideo, 'rtmpUrl' | 'rtmpsUrl' | 'streamKey'> | {} = {} |
106 | let rtmpsUrl: string = null | ||
107 | 106 | ||
108 | // If we don't have a stream key, it means this is a remote live so we don't specify the rtmp URL | 107 | // If we don't have a stream key, it means this is a remote live so we don't specify the rtmp URL |
109 | if (this.streamKey) { | 108 | // We also display these private information only to the live owne/moderators |
110 | if (CONFIG.LIVE.RTMP.ENABLED) rtmpUrl = WEBSERVER.RTMP_URL | 109 | if (this.streamKey && canSeePrivateInformation === true) { |
111 | if (CONFIG.LIVE.RTMPS.ENABLED) rtmpsUrl = WEBSERVER.RTMPS_URL | 110 | privateInformation = { |
111 | streamKey: this.streamKey, | ||
112 | |||
113 | rtmpUrl: CONFIG.LIVE.RTMP.ENABLED | ||
114 | ? WEBSERVER.RTMP_URL | ||
115 | : null, | ||
116 | |||
117 | rtmpsUrl: CONFIG.LIVE.RTMPS.ENABLED | ||
118 | ? WEBSERVER.RTMPS_URL | ||
119 | : null | ||
120 | } | ||
112 | } | 121 | } |
113 | 122 | ||
114 | return { | 123 | return { |
115 | rtmpUrl, | 124 | ...privateInformation, |
116 | rtmpsUrl, | ||
117 | 125 | ||
118 | streamKey: this.streamKey, | ||
119 | permanentLive: this.permanentLive, | 126 | permanentLive: this.permanentLive, |
120 | saveReplay: this.saveReplay, | 127 | saveReplay: this.saveReplay, |
121 | latencyMode: this.latencyMode | 128 | latencyMode: this.latencyMode |
diff --git a/server/tests/api/check-params/live.ts b/server/tests/api/check-params/live.ts index 2f1c1257e..d4a81c4f6 100644 --- a/server/tests/api/check-params/live.ts +++ b/server/tests/api/check-params/live.ts | |||
@@ -1,6 +1,7 @@ | |||
1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ | 1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ |
2 | 2 | ||
3 | import 'mocha' | 3 | import 'mocha' |
4 | import { expect } from 'chai' | ||
4 | import { omit } from 'lodash' | 5 | import { omit } from 'lodash' |
5 | import { buildAbsoluteFixturePath } from '@shared/core-utils' | 6 | import { buildAbsoluteFixturePath } from '@shared/core-utils' |
6 | import { HttpStatusCode, LiveVideoLatencyMode, VideoCreateResult, VideoPrivacy } from '@shared/models' | 7 | import { HttpStatusCode, LiveVideoLatencyMode, VideoCreateResult, VideoPrivacy } from '@shared/models' |
@@ -340,16 +341,33 @@ describe('Test video lives API validator', function () { | |||
340 | 341 | ||
341 | describe('When getting live information', function () { | 342 | describe('When getting live information', function () { |
342 | 343 | ||
343 | it('Should fail without access token', async function () { | ||
344 | await command.get({ token: '', videoId: video.id, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 }) | ||
345 | }) | ||
346 | 344 | ||
347 | it('Should fail with a bad access token', async function () { | 345 | it('Should fail with a bad access token', async function () { |
348 | await command.get({ token: 'toto', videoId: video.id, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 }) | 346 | await command.get({ token: 'toto', videoId: video.id, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 }) |
349 | }) | 347 | }) |
350 | 348 | ||
351 | it('Should fail with access token of another user', async function () { | 349 | it('Should not display private information without access token', async function () { |
352 | await command.get({ token: userAccessToken, videoId: video.id, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) | 350 | const live = await command.get({ token: '', videoId: video.id }) |
351 | |||
352 | expect(live.rtmpUrl).to.not.exist | ||
353 | expect(live.streamKey).to.not.exist | ||
354 | expect(live.latencyMode).to.exist | ||
355 | }) | ||
356 | |||
357 | it('Should not display private information with token of another user', async function () { | ||
358 | const live = await command.get({ token: userAccessToken, videoId: video.id }) | ||
359 | |||
360 | expect(live.rtmpUrl).to.not.exist | ||
361 | expect(live.streamKey).to.not.exist | ||
362 | expect(live.latencyMode).to.exist | ||
363 | }) | ||
364 | |||
365 | it('Should display private information with appropriate token', async function () { | ||
366 | const live = await command.get({ videoId: video.id }) | ||
367 | |||
368 | expect(live.rtmpUrl).to.exist | ||
369 | expect(live.streamKey).to.exist | ||
370 | expect(live.latencyMode).to.exist | ||
353 | }) | 371 | }) |
354 | 372 | ||
355 | it('Should fail with a bad video id', async function () { | 373 | it('Should fail with a bad video id', async function () { |
diff --git a/shared/models/users/user-right.enum.ts b/shared/models/users/user-right.enum.ts index d3f793d8b..9c6828aa5 100644 --- a/shared/models/users/user-right.enum.ts +++ b/shared/models/users/user-right.enum.ts | |||
@@ -1,47 +1,47 @@ | |||
1 | export const enum UserRight { | 1 | export const enum UserRight { |
2 | ALL, | 2 | ALL = 0, |
3 | 3 | ||
4 | MANAGE_USERS, | 4 | MANAGE_USERS = 1, |
5 | 5 | ||
6 | MANAGE_SERVER_FOLLOW, | 6 | MANAGE_SERVER_FOLLOW = 2, |
7 | 7 | ||
8 | MANAGE_LOGS, | 8 | MANAGE_LOGS = 3, |
9 | 9 | ||
10 | MANAGE_DEBUG, | 10 | MANAGE_DEBUG = 4, |
11 | 11 | ||
12 | MANAGE_SERVER_REDUNDANCY, | 12 | MANAGE_SERVER_REDUNDANCY = 5, |
13 | 13 | ||
14 | MANAGE_ABUSES, | 14 | MANAGE_ABUSES = 6, |
15 | 15 | ||
16 | MANAGE_JOBS, | 16 | MANAGE_JOBS = 7, |
17 | 17 | ||
18 | MANAGE_CONFIGURATION, | 18 | MANAGE_CONFIGURATION = 8, |
19 | MANAGE_INSTANCE_CUSTOM_PAGE, | 19 | MANAGE_INSTANCE_CUSTOM_PAGE = 9, |
20 | 20 | ||
21 | MANAGE_ACCOUNTS_BLOCKLIST, | 21 | MANAGE_ACCOUNTS_BLOCKLIST = 10, |
22 | MANAGE_SERVERS_BLOCKLIST, | 22 | MANAGE_SERVERS_BLOCKLIST = 11, |
23 | 23 | ||
24 | MANAGE_VIDEO_BLACKLIST, | 24 | MANAGE_VIDEO_BLACKLIST = 12, |
25 | MANAGE_ANY_VIDEO_CHANNEL, | 25 | MANAGE_ANY_VIDEO_CHANNEL = 13, |
26 | 26 | ||
27 | REMOVE_ANY_VIDEO, | 27 | REMOVE_ANY_VIDEO = 14, |
28 | REMOVE_ANY_VIDEO_PLAYLIST, | 28 | REMOVE_ANY_VIDEO_PLAYLIST = 15, |
29 | REMOVE_ANY_VIDEO_COMMENT, | 29 | REMOVE_ANY_VIDEO_COMMENT = 16, |
30 | 30 | ||
31 | UPDATE_ANY_VIDEO, | 31 | UPDATE_ANY_VIDEO = 17, |
32 | UPDATE_ANY_VIDEO_PLAYLIST, | 32 | UPDATE_ANY_VIDEO_PLAYLIST = 18, |
33 | 33 | ||
34 | GET_ANY_LIVE, | 34 | GET_ANY_LIVE = 19, |
35 | SEE_ALL_VIDEOS, | 35 | SEE_ALL_VIDEOS = 20, |
36 | SEE_ALL_COMMENTS, | 36 | SEE_ALL_COMMENTS = 21, |
37 | CHANGE_VIDEO_OWNERSHIP, | 37 | CHANGE_VIDEO_OWNERSHIP = 22, |
38 | 38 | ||
39 | MANAGE_PLUGINS, | 39 | MANAGE_PLUGINS = 23, |
40 | 40 | ||
41 | MANAGE_VIDEOS_REDUNDANCIES, | 41 | MANAGE_VIDEOS_REDUNDANCIES = 24, |
42 | 42 | ||
43 | MANAGE_VIDEO_FILES, | 43 | MANAGE_VIDEO_FILES = 25, |
44 | RUN_VIDEO_TRANSCODING, | 44 | RUN_VIDEO_TRANSCODING = 26, |
45 | 45 | ||
46 | MANAGE_VIDEO_IMPORTS | 46 | MANAGE_VIDEO_IMPORTS = 27 |
47 | } | 47 | } |
diff --git a/shared/models/videos/live/live-video.model.ts b/shared/models/videos/live/live-video.model.ts index 2d3169941..d0f57f883 100644 --- a/shared/models/videos/live/live-video.model.ts +++ b/shared/models/videos/live/live-video.model.ts | |||
@@ -1,10 +1,10 @@ | |||
1 | import { LiveVideoLatencyMode } from './live-video-latency-mode.enum' | 1 | import { LiveVideoLatencyMode } from './live-video-latency-mode.enum' |
2 | 2 | ||
3 | export interface LiveVideo { | 3 | export interface LiveVideo { |
4 | rtmpUrl: string | 4 | // If owner |
5 | rtmpsUrl: string | 5 | rtmpUrl?: string |
6 | 6 | rtmpsUrl?: string | |
7 | streamKey: string | 7 | streamKey?: string |
8 | 8 | ||
9 | saveReplay: boolean | 9 | saveReplay: boolean |
10 | permanentLive: boolean | 10 | permanentLive: boolean |
diff --git a/support/doc/api/openapi.yaml b/support/doc/api/openapi.yaml index 012f6d135..123e54f47 100644 --- a/support/doc/api/openapi.yaml +++ b/support/doc/api/openapi.yaml | |||
@@ -7657,11 +7657,13 @@ components: | |||
7657 | properties: | 7657 | properties: |
7658 | rtmpUrl: | 7658 | rtmpUrl: |
7659 | type: string | 7659 | type: string |
7660 | description: Included in the response if an appropriate token is provided | ||
7660 | rtmpsUrl: | 7661 | rtmpsUrl: |
7661 | type: string | 7662 | type: string |
7663 | description: Included in the response if an appropriate token is provided | ||
7662 | streamKey: | 7664 | streamKey: |
7663 | type: string | 7665 | type: string |
7664 | description: RTMP stream key to use to stream into this live video | 7666 | description: RTMP stream key to use to stream into this live video. Included in the response if an appropriate token is provided |
7665 | saveReplay: | 7667 | saveReplay: |
7666 | type: boolean | 7668 | type: boolean |
7667 | permanentLive: | 7669 | permanentLive: |