]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blobdiff - server/middlewares/validators/oembed.ts
Merge branch 'release/5.0.0' into develop
[github/Chocobozzz/PeerTube.git] / server / middlewares / validators / oembed.ts
index ab117635eb04799ee3ab95de7d95b4b4bff6530c..ef9a227a0446a1513727858624dcea3e404765a8 100644 (file)
@@ -1,13 +1,12 @@
-import * as express from 'express'
+import express from 'express'
 import { query } from 'express-validator'
 import { join } from 'path'
-import { fetchVideo } from '@server/lib/model-loaders'
+import { loadVideo } from '@server/lib/model-loaders'
 import { VideoPlaylistModel } from '@server/models/video/video-playlist'
 import { VideoPlaylistPrivacy, VideoPrivacy } from '@shared/models'
-import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
-import { isTestInstance } from '../../helpers/core-utils'
-import { isIdOrUUIDValid } from '../../helpers/custom-validators/misc'
-import { logger } from '../../helpers/logger'
+import { HttpStatusCode } from '../../../shared/models/http/http-error-codes'
+import { isTestOrDevInstance } from '../../helpers/core-utils'
+import { isIdOrUUIDValid, isUUIDValid, toCompleteUUID } from '../../helpers/custom-validators/misc'
 import { WEBSERVER } from '../../initializers/constants'
 import { areValidationErrors } from './shared'
 
@@ -28,26 +27,30 @@ function buildUrls (paths: string[]) {
 const startPlaylistURLs = buildUrls(playlistPaths)
 const startVideoURLs = buildUrls(videoPaths)
 
-const watchRegex = /([^/]+)$/
 const isURLOptions = {
   require_host: true,
   require_tld: true
 }
 
 // We validate 'localhost', so we don't have the top level domain
-if (isTestInstance()) {
+if (isTestOrDevInstance()) {
   isURLOptions.require_tld = false
 }
 
 const oembedValidator = [
-  query('url').isURL(isURLOptions).withMessage('Should have a valid url'),
-  query('maxwidth').optional().isInt().withMessage('Should have a valid max width'),
-  query('maxheight').optional().isInt().withMessage('Should have a valid max height'),
-  query('format').optional().isIn([ 'xml', 'json' ]).withMessage('Should have a valid format'),
+  query('url')
+    .isURL(isURLOptions),
+  query('maxwidth')
+    .optional()
+    .isInt(),
+  query('maxheight')
+    .optional()
+    .isInt(),
+  query('format')
+    .optional()
+    .isIn([ 'xml', 'json' ]),
 
   async (req: express.Request, res: express.Response, next: express.NextFunction) => {
-    logger.debug('Checking oembed parameters', { parameters: req.query })
-
     if (areValidationErrors(req, res)) return
 
     if (req.query.format !== undefined && req.query.format !== 'json') {
@@ -62,14 +65,28 @@ const oembedValidator = [
 
     const url = req.query.url as string
 
+    let urlPath: string
+
+    try {
+      urlPath = new URL(url).pathname
+    } catch (err) {
+      return res.fail({
+        status: HttpStatusCode.BAD_REQUEST_400,
+        message: err.message,
+        data: {
+          url
+        }
+      })
+    }
+
     const isPlaylist = startPlaylistURLs.some(u => url.startsWith(u))
     const isVideo = isPlaylist ? false : startVideoURLs.some(u => url.startsWith(u))
 
     const startIsOk = isVideo || isPlaylist
 
-    const matches = watchRegex.exec(url)
+    const parts = urlPath.split('/')
 
-    if (startIsOk === false || matches === null) {
+    if (startIsOk === false || parts.length === 0) {
       return res.fail({
         status: HttpStatusCode.BAD_REQUEST_400,
         message: 'Invalid url.',
@@ -79,13 +96,13 @@ const oembedValidator = [
       })
     }
 
-    const elementId = matches[1]
+    const elementId = toCompleteUUID(parts.pop())
     if (isIdOrUUIDValid(elementId) === false) {
       return res.fail({ message: 'Invalid video or playlist id.' })
     }
 
     if (isVideo) {
-      const video = await fetchVideo(elementId, 'all')
+      const video = await loadVideo(elementId, 'all')
 
       if (!video) {
         return res.fail({
@@ -94,15 +111,18 @@ const oembedValidator = [
         })
       }
 
-      if (video.privacy !== VideoPrivacy.PUBLIC) {
-        return res.fail({
-          status: HttpStatusCode.FORBIDDEN_403,
-          message: 'Video is not public'
-        })
+      if (
+        video.privacy === VideoPrivacy.PUBLIC ||
+        (video.privacy === VideoPrivacy.UNLISTED && isUUIDValid(elementId) === true)
+      ) {
+        res.locals.videoAll = video
+        return next()
       }
 
-      res.locals.videoAll = video
-      return next()
+      return res.fail({
+        status: HttpStatusCode.FORBIDDEN_403,
+        message: 'Video is not publicly available'
+      })
     }
 
     // Is playlist
@@ -115,15 +135,18 @@ const oembedValidator = [
       })
     }
 
-    if (videoPlaylist.privacy !== VideoPlaylistPrivacy.PUBLIC) {
-      return res.fail({
-        status: HttpStatusCode.FORBIDDEN_403,
-        message: 'Playlist is not public'
-      })
+    if (
+      videoPlaylist.privacy === VideoPlaylistPrivacy.PUBLIC ||
+      (videoPlaylist.privacy === VideoPlaylistPrivacy.UNLISTED && isUUIDValid(elementId))
+    ) {
+      res.locals.videoPlaylistSummary = videoPlaylist
+      return next()
     }
 
-    res.locals.videoPlaylistSummary = videoPlaylist
-    return next()
+    return res.fail({
+      status: HttpStatusCode.FORBIDDEN_403,
+      message: 'Playlist is not public'
+    })
   }
 
 ]