]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/commitdiff
Fix getting live by anonymous user
authorChocobozzz <me@florianbigard.com>
Fri, 22 Apr 2022 07:50:20 +0000 (09:50 +0200)
committerChocobozzz <me@florianbigard.com>
Fri, 22 Apr 2022 07:50:20 +0000 (09:50 +0200)
server/controllers/api/videos/live.ts
server/middlewares/validators/videos/video-live.ts
server/models/video/video-live.ts
server/tests/api/check-params/live.ts
shared/models/users/user-right.enum.ts
shared/models/videos/live/live-video.model.ts
support/doc/api/openapi.yaml

index c6f038079750faf157af60c7785b236cbe982691..e516589278968fa4c695feb4242479e182468def 100644 (file)
@@ -10,11 +10,11 @@ import { videoLiveAddValidator, videoLiveGetValidator, videoLiveUpdateValidator
 import { VideoLiveModel } from '@server/models/video/video-live'
 import { MVideoDetails, MVideoFullLight } from '@server/types/models'
 import { buildUUID, uuidToShort } from '@shared/extra-utils'
-import { HttpStatusCode, LiveVideoCreate, LiveVideoLatencyMode, LiveVideoUpdate, VideoState } from '@shared/models'
+import { HttpStatusCode, LiveVideoCreate, LiveVideoLatencyMode, LiveVideoUpdate, UserRight, VideoState } from '@shared/models'
 import { logger } from '../../../helpers/logger'
 import { sequelizeTypescript } from '../../../initializers/database'
 import { updateVideoMiniatureFromExisting } from '../../../lib/thumbnail'
-import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate } from '../../../middlewares'
+import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, optionalAuthenticate } from '../../../middlewares'
 import { VideoModel } from '../../../models/video/video'
 
 const liveRouter = express.Router()
@@ -29,7 +29,7 @@ liveRouter.post('/live',
 )
 
 liveRouter.get('/live/:videoId',
-  authenticate,
+  optionalAuthenticate,
   asyncMiddleware(videoLiveGetValidator),
   getLiveVideo
 )
@@ -52,7 +52,17 @@ export {
 function getLiveVideo (req: express.Request, res: express.Response) {
   const videoLive = res.locals.videoLive
 
-  return res.json(videoLive.toFormattedJSON())
+  return res.json(videoLive.toFormattedJSON(canSeePrivateLiveInformation(res)))
+}
+
+function canSeePrivateLiveInformation (res: express.Response) {
+  const user = res.locals.oauth?.token.User
+  if (!user) return false
+
+  if (user.hasRight(UserRight.GET_ANY_LIVE)) return true
+
+  const video = res.locals.videoAll
+  return video.VideoChannel.Account.userId === user.id
 }
 
 async function updateLiveVideo (req: express.Request, res: express.Response) {
index b756c0bf10540389cf9058f81a5183098065631a..8f821c5f9a5b3e2c39a33dca14fd96057c6890da 100644 (file)
@@ -33,15 +33,11 @@ const videoLiveGetValidator = [
   isValidVideoIdParam('videoId'),
 
   async (req: express.Request, res: express.Response, next: express.NextFunction) => {
-    logger.debug('Checking videoLiveGetValidator parameters', { parameters: req.params, user: res.locals.oauth.token.User.username })
+    logger.debug('Checking videoLiveGetValidator parameters', { parameters: req.params })
 
     if (areValidationErrors(req, res)) return
     if (!await doesVideoExist(req.params.videoId, res, 'all')) return
 
-    // Check if the user who did the request is able to get the live info
-    const user = res.locals.oauth.token.User
-    if (!checkUserCanManageVideo(user, res.locals.videoAll, UserRight.GET_ANY_LIVE, res, false)) return
-
     const videoLive = await VideoLiveModel.loadByVideoId(res.locals.videoAll.id)
     if (!videoLive) {
       return res.fail({
index 96c0bf7f770d1ce40f262b03d31de12387afdbbe..68e3811059acef2e3f0119b8befb8ea28c3b11d4 100644 (file)
@@ -101,21 +101,28 @@ export class VideoLiveModel extends Model<Partial<AttributesOnly<VideoLiveModel>
     return VideoLiveModel.findOne<MVideoLive>(query)
   }
 
-  toFormattedJSON (): LiveVideo {
-    let rtmpUrl: string = null
-    let rtmpsUrl: string = null
+  toFormattedJSON (canSeePrivateInformation: boolean): LiveVideo {
+    let privateInformation: Pick<LiveVideo, 'rtmpUrl' | 'rtmpsUrl' | 'streamKey'> | {} = {}
 
     // If we don't have a stream key, it means this is a remote live so we don't specify the rtmp URL
-    if (this.streamKey) {
-      if (CONFIG.LIVE.RTMP.ENABLED) rtmpUrl = WEBSERVER.RTMP_URL
-      if (CONFIG.LIVE.RTMPS.ENABLED) rtmpsUrl = WEBSERVER.RTMPS_URL
+    // We also display these private information only to the live owne/moderators
+    if (this.streamKey && canSeePrivateInformation === true) {
+      privateInformation = {
+        streamKey: this.streamKey,
+
+        rtmpUrl: CONFIG.LIVE.RTMP.ENABLED
+          ? WEBSERVER.RTMP_URL
+          : null,
+
+        rtmpsUrl: CONFIG.LIVE.RTMPS.ENABLED
+          ? WEBSERVER.RTMPS_URL
+          : null
+      }
     }
 
     return {
-      rtmpUrl,
-      rtmpsUrl,
+      ...privateInformation,
 
-      streamKey: this.streamKey,
       permanentLive: this.permanentLive,
       saveReplay: this.saveReplay,
       latencyMode: this.latencyMode
index 2f1c1257e6a44fe1acd20efc477a2f9bbcd29575..d4a81c4f6c917ac5a38bc72d8ae606506391bf3e 100644 (file)
@@ -1,6 +1,7 @@
 /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
 
 import 'mocha'
+import { expect } from 'chai'
 import { omit } from 'lodash'
 import { buildAbsoluteFixturePath } from '@shared/core-utils'
 import { HttpStatusCode, LiveVideoLatencyMode, VideoCreateResult, VideoPrivacy } from '@shared/models'
@@ -340,16 +341,33 @@ describe('Test video lives API validator', function () {
 
   describe('When getting live information', function () {
 
-    it('Should fail without access token', async function () {
-      await command.get({ token: '', videoId: video.id, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
-    })
 
     it('Should fail with a bad access token', async function () {
       await command.get({ token: 'toto', videoId: video.id, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
     })
 
-    it('Should fail with access token of another user', async function () {
-      await command.get({ token: userAccessToken, videoId: video.id, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
+    it('Should not display private information without access token', async function () {
+      const live = await command.get({ token: '', videoId: video.id })
+
+      expect(live.rtmpUrl).to.not.exist
+      expect(live.streamKey).to.not.exist
+      expect(live.latencyMode).to.exist
+    })
+
+    it('Should not display private information with token of another user', async function () {
+      const live = await command.get({ token: userAccessToken, videoId: video.id })
+
+      expect(live.rtmpUrl).to.not.exist
+      expect(live.streamKey).to.not.exist
+      expect(live.latencyMode).to.exist
+    })
+
+    it('Should display private information with appropriate token', async function () {
+      const live = await command.get({ videoId: video.id })
+
+      expect(live.rtmpUrl).to.exist
+      expect(live.streamKey).to.exist
+      expect(live.latencyMode).to.exist
     })
 
     it('Should fail with a bad video id', async function () {
index d3f793d8b496fe72427707b8171befa35511a3fd..9c6828aa5f90b822190105ae99147aa9386b4fd6 100644 (file)
@@ -1,47 +1,47 @@
 export const enum UserRight {
-  ALL,
+  ALL = 0,
 
-  MANAGE_USERS,
+  MANAGE_USERS = 1,
 
-  MANAGE_SERVER_FOLLOW,
+  MANAGE_SERVER_FOLLOW = 2,
 
-  MANAGE_LOGS,
+  MANAGE_LOGS = 3,
 
-  MANAGE_DEBUG,
+  MANAGE_DEBUG = 4,
 
-  MANAGE_SERVER_REDUNDANCY,
+  MANAGE_SERVER_REDUNDANCY = 5,
 
-  MANAGE_ABUSES,
+  MANAGE_ABUSES = 6,
 
-  MANAGE_JOBS,
+  MANAGE_JOBS = 7,
 
-  MANAGE_CONFIGURATION,
-  MANAGE_INSTANCE_CUSTOM_PAGE,
+  MANAGE_CONFIGURATION = 8,
+  MANAGE_INSTANCE_CUSTOM_PAGE = 9,
 
-  MANAGE_ACCOUNTS_BLOCKLIST,
-  MANAGE_SERVERS_BLOCKLIST,
+  MANAGE_ACCOUNTS_BLOCKLIST = 10,
+  MANAGE_SERVERS_BLOCKLIST = 11,
 
-  MANAGE_VIDEO_BLACKLIST,
-  MANAGE_ANY_VIDEO_CHANNEL,
+  MANAGE_VIDEO_BLACKLIST = 12,
+  MANAGE_ANY_VIDEO_CHANNEL = 13,
 
-  REMOVE_ANY_VIDEO,
-  REMOVE_ANY_VIDEO_PLAYLIST,
-  REMOVE_ANY_VIDEO_COMMENT,
+  REMOVE_ANY_VIDEO = 14,
+  REMOVE_ANY_VIDEO_PLAYLIST = 15,
+  REMOVE_ANY_VIDEO_COMMENT = 16,
 
-  UPDATE_ANY_VIDEO,
-  UPDATE_ANY_VIDEO_PLAYLIST,
+  UPDATE_ANY_VIDEO = 17,
+  UPDATE_ANY_VIDEO_PLAYLIST = 18,
 
-  GET_ANY_LIVE,
-  SEE_ALL_VIDEOS,
-  SEE_ALL_COMMENTS,
-  CHANGE_VIDEO_OWNERSHIP,
+  GET_ANY_LIVE = 19,
+  SEE_ALL_VIDEOS = 20,
+  SEE_ALL_COMMENTS = 21,
+  CHANGE_VIDEO_OWNERSHIP = 22,
 
-  MANAGE_PLUGINS,
+  MANAGE_PLUGINS = 23,
 
-  MANAGE_VIDEOS_REDUNDANCIES,
+  MANAGE_VIDEOS_REDUNDANCIES = 24,
 
-  MANAGE_VIDEO_FILES,
-  RUN_VIDEO_TRANSCODING,
+  MANAGE_VIDEO_FILES = 25,
+  RUN_VIDEO_TRANSCODING = 26,
 
-  MANAGE_VIDEO_IMPORTS
+  MANAGE_VIDEO_IMPORTS = 27
 }
index 2d31699413077eeadc2dc0996d9a23d8cbb2a1c2..d0f57f8834aedda7a0ef2a17e513c910967c1df3 100644 (file)
@@ -1,10 +1,10 @@
 import { LiveVideoLatencyMode } from './live-video-latency-mode.enum'
 
 export interface LiveVideo {
-  rtmpUrl: string
-  rtmpsUrl: string
-
-  streamKey: string
+  // If owner
+  rtmpUrl?: string
+  rtmpsUrl?: string
+  streamKey?: string
 
   saveReplay: boolean
   permanentLive: boolean
index 012f6d135e21ea5727c45b8eb05dd85460121190..123e54f47ca549865ba2f8234a154d9b286a84d2 100644 (file)
@@ -7657,11 +7657,13 @@ components:
       properties:
         rtmpUrl:
           type: string
+          description: Included in the response if an appropriate token is provided
         rtmpsUrl:
           type: string
+          description: Included in the response if an appropriate token is provided
         streamKey:
           type: string
-          description: RTMP stream key to use to stream into this live video
+          description: RTMP stream key to use to stream into this live video. Included in the response if an appropriate token is provided
         saveReplay:
           type: boolean
         permanentLive: