1
0
Fork 0

Refractor validators

This commit is contained in:
Chocobozzz 2017-11-27 17:30:46 +01:00
parent fcaf1e0aa8
commit a2431b7dcb
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
33 changed files with 478 additions and 643 deletions

View File

@ -19,22 +19,22 @@ import { VideoShareInstance } from '../../models/video/video-share-interface'
const activityPubClientRouter = express.Router() const activityPubClientRouter = express.Router()
activityPubClientRouter.get('/account/:name', activityPubClientRouter.get('/account/:name',
executeIfActivityPub(localAccountValidator), executeIfActivityPub(asyncMiddleware(localAccountValidator)),
executeIfActivityPub(accountController) executeIfActivityPub(accountController)
) )
activityPubClientRouter.get('/account/:name/followers', activityPubClientRouter.get('/account/:name/followers',
executeIfActivityPub(localAccountValidator), executeIfActivityPub(asyncMiddleware(localAccountValidator)),
executeIfActivityPub(asyncMiddleware(accountFollowersController)) executeIfActivityPub(asyncMiddleware(accountFollowersController))
) )
activityPubClientRouter.get('/account/:name/following', activityPubClientRouter.get('/account/:name/following',
executeIfActivityPub(localAccountValidator), executeIfActivityPub(asyncMiddleware(localAccountValidator)),
executeIfActivityPub(asyncMiddleware(accountFollowingController)) executeIfActivityPub(asyncMiddleware(accountFollowingController))
) )
activityPubClientRouter.get('/videos/watch/:id', activityPubClientRouter.get('/videos/watch/:id',
executeIfActivityPub(videosGetValidator), executeIfActivityPub(asyncMiddleware(videosGetValidator)),
executeIfActivityPub(videoController) executeIfActivityPub(videoController)
) )
@ -44,7 +44,7 @@ activityPubClientRouter.get('/videos/watch/:id/announces/:accountId',
) )
activityPubClientRouter.get('/video-channels/:id', activityPubClientRouter.get('/video-channels/:id',
executeIfActivityPub(videoChannelsGetValidator), executeIfActivityPub(asyncMiddleware(videoChannelsGetValidator)),
executeIfActivityPub(asyncMiddleware(videoChannelController)) executeIfActivityPub(asyncMiddleware(videoChannelController))
) )

View File

@ -41,7 +41,7 @@ serverFollowsRouter.post('/following',
serverFollowsRouter.delete('/following/:accountId', serverFollowsRouter.delete('/following/:accountId',
authenticate, authenticate,
ensureUserHasRight(UserRight.MANAGE_SERVER_FOLLOW), ensureUserHasRight(UserRight.MANAGE_SERVER_FOLLOW),
removeFollowingValidator, asyncMiddleware(removeFollowingValidator),
asyncMiddleware(removeFollow) asyncMiddleware(removeFollow)
) )

View File

@ -43,7 +43,7 @@ usersRouter.get('/me/videos',
usersRouter.get('/me/videos/:videoId/rating', usersRouter.get('/me/videos/:videoId/rating',
authenticate, authenticate,
usersVideoRatingValidator, asyncMiddleware(usersVideoRatingValidator),
asyncMiddleware(getUserVideoRating) asyncMiddleware(getUserVideoRating)
) )
@ -56,20 +56,20 @@ usersRouter.get('/',
) )
usersRouter.get('/:id', usersRouter.get('/:id',
usersGetValidator, asyncMiddleware(usersGetValidator),
getUser getUser
) )
usersRouter.post('/', usersRouter.post('/',
authenticate, authenticate,
ensureUserHasRight(UserRight.MANAGE_USERS), ensureUserHasRight(UserRight.MANAGE_USERS),
usersAddValidator, asyncMiddleware(usersAddValidator),
createUserRetryWrapper asyncMiddleware(createUserRetryWrapper)
) )
usersRouter.post('/register', usersRouter.post('/register',
ensureUserRegistrationAllowed, asyncMiddleware(ensureUserRegistrationAllowed),
usersRegisterValidator, asyncMiddleware(usersRegisterValidator),
asyncMiddleware(registerUserRetryWrapper) asyncMiddleware(registerUserRetryWrapper)
) )
@ -82,14 +82,14 @@ usersRouter.put('/me',
usersRouter.put('/:id', usersRouter.put('/:id',
authenticate, authenticate,
ensureUserHasRight(UserRight.MANAGE_USERS), ensureUserHasRight(UserRight.MANAGE_USERS),
usersUpdateValidator, asyncMiddleware(usersUpdateValidator),
asyncMiddleware(updateUser) asyncMiddleware(updateUser)
) )
usersRouter.delete('/:id', usersRouter.delete('/:id',
authenticate, authenticate,
ensureUserHasRight(UserRight.MANAGE_USERS), ensureUserHasRight(UserRight.MANAGE_USERS),
usersRemoveValidator, asyncMiddleware(usersRemoveValidator),
asyncMiddleware(removeUser) asyncMiddleware(removeUser)
) )

View File

@ -33,7 +33,7 @@ abuseVideoRouter.get('/abuse',
) )
abuseVideoRouter.post('/:id/abuse', abuseVideoRouter.post('/:id/abuse',
authenticate, authenticate,
videoAbuseReportValidator, asyncMiddleware(videoAbuseReportValidator),
asyncMiddleware(reportVideoAbuseRetryWrapper) asyncMiddleware(reportVideoAbuseRetryWrapper)
) )

View File

@ -21,7 +21,7 @@ const blacklistRouter = express.Router()
blacklistRouter.post('/:videoId/blacklist', blacklistRouter.post('/:videoId/blacklist',
authenticate, authenticate,
ensureUserHasRight(UserRight.MANAGE_VIDEO_BLACKLIST), ensureUserHasRight(UserRight.MANAGE_VIDEO_BLACKLIST),
videosBlacklistAddValidator, asyncMiddleware(videosBlacklistAddValidator),
asyncMiddleware(addVideoToBlacklist) asyncMiddleware(addVideoToBlacklist)
) )
@ -38,7 +38,7 @@ blacklistRouter.get('/blacklist',
blacklistRouter.delete('/:videoId/blacklist', blacklistRouter.delete('/:videoId/blacklist',
authenticate, authenticate,
ensureUserHasRight(UserRight.MANAGE_VIDEO_BLACKLIST), ensureUserHasRight(UserRight.MANAGE_VIDEO_BLACKLIST),
videosBlacklistRemoveValidator, asyncMiddleware(videosBlacklistRemoveValidator),
asyncMiddleware(removeVideoFromBlacklistController) asyncMiddleware(removeVideoFromBlacklistController)
) )

View File

@ -3,6 +3,7 @@ import { VideoChannelCreate, VideoChannelUpdate } from '../../../../shared'
import { getFormattedObjects, logger, resetSequelizeInstance, retryTransactionWrapper } from '../../../helpers' import { getFormattedObjects, logger, resetSequelizeInstance, retryTransactionWrapper } from '../../../helpers'
import { database as db } from '../../../initializers' import { database as db } from '../../../initializers'
import { createVideoChannel } from '../../../lib' import { createVideoChannel } from '../../../lib'
import { sendUpdateVideoChannel } from '../../../lib/activitypub/send/send-update'
import { import {
asyncMiddleware, asyncMiddleware,
authenticate, authenticate,
@ -10,14 +11,13 @@ import {
paginationValidator, paginationValidator,
setPagination, setPagination,
setVideoChannelsSort, setVideoChannelsSort,
videoChannelsGetValidator,
videoChannelsAddValidator, videoChannelsAddValidator,
videoChannelsGetValidator,
videoChannelsRemoveValidator, videoChannelsRemoveValidator,
videoChannelsSortValidator, videoChannelsSortValidator,
videoChannelsUpdateValidator videoChannelsUpdateValidator
} from '../../../middlewares' } from '../../../middlewares'
import { AccountInstance, VideoChannelInstance } from '../../../models' import { AccountInstance, VideoChannelInstance } from '../../../models'
import { sendUpdateVideoChannel } from '../../../lib/activitypub/send/send-update'
const videoChannelRouter = express.Router() const videoChannelRouter = express.Router()
@ -30,7 +30,7 @@ videoChannelRouter.get('/channels',
) )
videoChannelRouter.get('/accounts/:accountId/channels', videoChannelRouter.get('/accounts/:accountId/channels',
listVideoAccountChannelsValidator, asyncMiddleware(listVideoAccountChannelsValidator),
asyncMiddleware(listVideoAccountChannels) asyncMiddleware(listVideoAccountChannels)
) )
@ -42,18 +42,18 @@ videoChannelRouter.post('/channels',
videoChannelRouter.put('/channels/:id', videoChannelRouter.put('/channels/:id',
authenticate, authenticate,
videoChannelsUpdateValidator, asyncMiddleware(videoChannelsUpdateValidator),
updateVideoChannelRetryWrapper updateVideoChannelRetryWrapper
) )
videoChannelRouter.delete('/channels/:id', videoChannelRouter.delete('/channels/:id',
authenticate, authenticate,
videoChannelsRemoveValidator, asyncMiddleware(videoChannelsRemoveValidator),
asyncMiddleware(removeVideoChannelRetryWrapper) asyncMiddleware(removeVideoChannelRetryWrapper)
) )
videoChannelRouter.get('/channels/:id', videoChannelRouter.get('/channels/:id',
videoChannelsGetValidator, asyncMiddleware(videoChannelsGetValidator),
asyncMiddleware(getVideoChannel) asyncMiddleware(getVideoChannel)
) )

View File

@ -86,7 +86,7 @@ videosRouter.get('/',
) )
videosRouter.put('/:id', videosRouter.put('/:id',
authenticate, authenticate,
videosUpdateValidator, asyncMiddleware(videosUpdateValidator),
asyncMiddleware(updateVideoRetryWrapper) asyncMiddleware(updateVideoRetryWrapper)
) )
videosRouter.post('/upload', videosRouter.post('/upload',
@ -97,17 +97,17 @@ videosRouter.post('/upload',
) )
videosRouter.get('/:id/description', videosRouter.get('/:id/description',
videosGetValidator, asyncMiddleware(videosGetValidator),
asyncMiddleware(getVideoDescription) asyncMiddleware(getVideoDescription)
) )
videosRouter.get('/:id', videosRouter.get('/:id',
videosGetValidator, asyncMiddleware(videosGetValidator),
getVideo getVideo
) )
videosRouter.delete('/:id', videosRouter.delete('/:id',
authenticate, authenticate,
videosRemoveValidator, asyncMiddleware(videosRemoveValidator),
asyncMiddleware(removeVideoRetryWrapper) asyncMiddleware(removeVideoRetryWrapper)
) )

View File

@ -12,7 +12,7 @@ const rateVideoRouter = express.Router()
rateVideoRouter.put('/:id/rate', rateVideoRouter.put('/:id/rate',
authenticate, authenticate,
videoRateValidator, asyncMiddleware(videoRateValidator),
asyncMiddleware(rateVideoRetryWrapper) asyncMiddleware(rateVideoRetryWrapper)
) )

View File

@ -1,12 +1,16 @@
import * as express from 'express' import * as express from 'express'
import { CONFIG, PREVIEWS_SIZE, EMBED_SIZE } from '../initializers' import { CONFIG, EMBED_SIZE, PREVIEWS_SIZE } from '../initializers'
import { oembedValidator } from '../middlewares' import { oembedValidator } from '../middlewares'
import { asyncMiddleware } from '../middlewares/async'
import { VideoInstance } from '../models' import { VideoInstance } from '../models'
const servicesRouter = express.Router() const servicesRouter = express.Router()
servicesRouter.use('/oembed', oembedValidator, generateOEmbed) servicesRouter.use('/oembed',
asyncMiddleware(oembedValidator),
generateOEmbed
)
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------

View File

@ -1,11 +1,12 @@
import * as express from 'express' import * as express from 'express'
import { asyncMiddleware } from '../middlewares/async'
import { webfingerValidator } from '../middlewares/validators/webfinger' import { webfingerValidator } from '../middlewares/validators/webfinger'
import { AccountInstance } from '../models/account/account-interface' import { AccountInstance } from '../models/account/account-interface'
const webfingerRouter = express.Router() const webfingerRouter = express.Router()
webfingerRouter.get('/.well-known/webfinger', webfingerRouter.get('/.well-known/webfinger',
webfingerValidator, asyncMiddleware(webfingerValidator),
webfingerController webfingerController
) )

View File

@ -1,17 +1,16 @@
import * as Bluebird from 'bluebird' import * as Bluebird from 'bluebird'
import * as express from 'express' import { Response } from 'express'
import 'express-validator' import 'express-validator'
import * as validator from 'validator' import * as validator from 'validator'
import { database as db } from '../../initializers' import { database as db } from '../../initializers'
import { AccountInstance } from '../../models' import { AccountInstance } from '../../models'
import { logger } from '../logger'
import { isUserUsernameValid } from './users' import { isUserUsernameValid } from './users'
function isAccountNameValid (value: string) { function isAccountNameValid (value: string) {
return isUserUsernameValid(value) return isUserUsernameValid(value)
} }
function checkAccountIdExists (id: number | string, res: express.Response, callback: (err: Error, account: AccountInstance) => any) { function isAccountIdExist (id: number | string, res: Response) {
let promise: Bluebird<AccountInstance> let promise: Bluebird<AccountInstance>
if (validator.isInt('' + id)) { if (validator.isInt('' + id)) {
@ -20,36 +19,35 @@ function checkAccountIdExists (id: number | string, res: express.Response, callb
promise = db.Account.loadByUUID('' + id) promise = db.Account.loadByUUID('' + id)
} }
return checkAccountExists(promise, res, callback) return isAccountExist(promise, res)
} }
function checkLocalAccountNameExists (name: string, res: express.Response, callback: (err: Error, account: AccountInstance) => any) { function isLocalAccountNameExist (name: string, res: Response) {
const p = db.Account.loadLocalByName(name) const promise = db.Account.loadLocalByName(name)
return checkAccountExists(p, res, callback) return isAccountExist(promise, res)
} }
function checkAccountExists (p: Bluebird<AccountInstance>, res: express.Response, callback: (err: Error, account: AccountInstance) => any) { async function isAccountExist (p: Bluebird<AccountInstance>, res: Response) {
p.then(account => { const account = await p
if (!account) {
return res.status(404)
.send({ error: 'Account not found' })
.end()
}
res.locals.account = account if (!account) {
return callback(null, account) res.status(404)
}) .send({ error: 'Account not found' })
.catch(err => { .end()
logger.error('Error in account request validator.', err)
return res.sendStatus(500) return false
}) }
res.locals.account = account
return true
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
export { export {
checkAccountIdExists, isAccountIdExist,
checkLocalAccountNameExists, isLocalAccountNameExist,
isAccountNameValid isAccountNameValid
} }

View File

@ -5,31 +5,19 @@ import { exists, isUUIDValid } from '../misc'
import { isActivityPubUrlValid, isBaseActivityValid } from './misc' import { isActivityPubUrlValid, isBaseActivityValid } from './misc'
function isAccountEndpointsObjectValid (endpointObject: any) { function isAccountEndpointsObjectValid (endpointObject: any) {
return isAccountSharedInboxValid(endpointObject.sharedInbox) return isActivityPubUrlValid(endpointObject.sharedInbox)
}
function isAccountSharedInboxValid (sharedInbox: string) {
return isActivityPubUrlValid(sharedInbox)
} }
function isAccountPublicKeyObjectValid (publicKeyObject: any) { function isAccountPublicKeyObjectValid (publicKeyObject: any) {
return isAccountPublicKeyIdValid(publicKeyObject.id) && return isActivityPubUrlValid(publicKeyObject.id) &&
isAccountPublicKeyOwnerValid(publicKeyObject.owner) && isActivityPubUrlValid(publicKeyObject.owner) &&
isAccountPublicKeyValid(publicKeyObject.publicKeyPem) isAccountPublicKeyValid(publicKeyObject.publicKeyPem)
} }
function isAccountPublicKeyIdValid (id: string) {
return isActivityPubUrlValid(id)
}
function isAccountTypeValid (type: string) { function isAccountTypeValid (type: string) {
return type === 'Person' || type === 'Application' return type === 'Person' || type === 'Application'
} }
function isAccountPublicKeyOwnerValid (owner: string) {
return isActivityPubUrlValid(owner)
}
function isAccountPublicKeyValid (publicKey: string) { function isAccountPublicKeyValid (publicKey: string) {
return exists(publicKey) && return exists(publicKey) &&
typeof publicKey === 'string' && typeof publicKey === 'string' &&
@ -38,34 +26,10 @@ function isAccountPublicKeyValid (publicKey: string) {
validator.isLength(publicKey, CONSTRAINTS_FIELDS.ACCOUNTS.PUBLIC_KEY) validator.isLength(publicKey, CONSTRAINTS_FIELDS.ACCOUNTS.PUBLIC_KEY)
} }
function isAccountIdValid (id: string) {
return isActivityPubUrlValid(id)
}
function isAccountFollowingValid (id: string) {
return isActivityPubUrlValid(id)
}
function isAccountFollowersValid (id: string) {
return isActivityPubUrlValid(id)
}
function isAccountInboxValid (inbox: string) {
return isActivityPubUrlValid(inbox)
}
function isAccountOutboxValid (outbox: string) {
return isActivityPubUrlValid(outbox)
}
function isAccountPreferredUsernameValid (preferredUsername: string) { function isAccountPreferredUsernameValid (preferredUsername: string) {
return isAccountNameValid(preferredUsername) return isAccountNameValid(preferredUsername)
} }
function isAccountUrlValid (url: string) {
return isActivityPubUrlValid(url)
}
function isAccountPrivateKeyValid (privateKey: string) { function isAccountPrivateKeyValid (privateKey: string) {
return exists(privateKey) && return exists(privateKey) &&
typeof privateKey === 'string' && typeof privateKey === 'string' &&
@ -75,15 +39,15 @@ function isAccountPrivateKeyValid (privateKey: string) {
} }
function isRemoteAccountValid (remoteAccount: any) { function isRemoteAccountValid (remoteAccount: any) {
return isAccountIdValid(remoteAccount.id) && return isActivityPubUrlValid(remoteAccount.id) &&
isUUIDValid(remoteAccount.uuid) && isUUIDValid(remoteAccount.uuid) &&
isAccountTypeValid(remoteAccount.type) && isAccountTypeValid(remoteAccount.type) &&
isAccountFollowingValid(remoteAccount.following) && isActivityPubUrlValid(remoteAccount.following) &&
isAccountFollowersValid(remoteAccount.followers) && isActivityPubUrlValid(remoteAccount.followers) &&
isAccountInboxValid(remoteAccount.inbox) && isActivityPubUrlValid(remoteAccount.inbox) &&
isAccountOutboxValid(remoteAccount.outbox) && isActivityPubUrlValid(remoteAccount.outbox) &&
isAccountPreferredUsernameValid(remoteAccount.preferredUsername) && isAccountPreferredUsernameValid(remoteAccount.preferredUsername) &&
isAccountUrlValid(remoteAccount.url) && isActivityPubUrlValid(remoteAccount.url) &&
isAccountPublicKeyObjectValid(remoteAccount.publicKey) && isAccountPublicKeyObjectValid(remoteAccount.publicKey) &&
isAccountEndpointsObjectValid(remoteAccount.endpoints) isAccountEndpointsObjectValid(remoteAccount.endpoints)
} }
@ -113,19 +77,10 @@ function isAccountAcceptActivityValid (activity: any) {
export { export {
isAccountEndpointsObjectValid, isAccountEndpointsObjectValid,
isAccountSharedInboxValid,
isAccountPublicKeyObjectValid, isAccountPublicKeyObjectValid,
isAccountPublicKeyIdValid,
isAccountTypeValid, isAccountTypeValid,
isAccountPublicKeyOwnerValid,
isAccountPublicKeyValid, isAccountPublicKeyValid,
isAccountIdValid,
isAccountFollowingValid,
isAccountFollowersValid,
isAccountInboxValid,
isAccountOutboxValid,
isAccountPreferredUsernameValid, isAccountPreferredUsernameValid,
isAccountUrlValid,
isAccountPrivateKeyValid, isAccountPrivateKeyValid,
isRemoteAccountValid, isRemoteAccountValid,
isAccountFollowingCountValid, isAccountFollowingCountValid,

View File

@ -8,7 +8,6 @@ import {
isVideoNSFWValid, isVideoNSFWValid,
isVideoTagValid, isVideoTagValid,
isVideoTruncatedDescriptionValid, isVideoTruncatedDescriptionValid,
isVideoUrlValid,
isVideoViewsValid isVideoViewsValid
} from '../videos' } from '../videos'
import { isActivityPubUrlValid, isBaseActivityValid } from './misc' import { isActivityPubUrlValid, isBaseActivityValid } from './misc'
@ -77,12 +76,11 @@ export {
function setValidRemoteTags (video: any) { function setValidRemoteTags (video: any) {
if (Array.isArray(video.tag) === false) return false if (Array.isArray(video.tag) === false) return false
const newTag = video.tag.filter(t => { video.tag = video.tag.filter(t => {
return t.type === 'Hashtag' && return t.type === 'Hashtag' &&
isVideoTagValid(t.name) isVideoTagValid(t.name)
}) })
video.tag = newTag
return true return true
} }
@ -96,7 +94,7 @@ function isRemoteVideoContentValid (mediaType: string, content: string) {
function isRemoteVideoIconValid (icon: any) { function isRemoteVideoIconValid (icon: any) {
return icon.type === 'Image' && return icon.type === 'Image' &&
isVideoUrlValid(icon.url) && isActivityPubUrlValid(icon.url) &&
icon.mediaType === 'image/jpeg' && icon.mediaType === 'image/jpeg' &&
validator.isInt(icon.width + '', { min: 0 }) && validator.isInt(icon.width + '', { min: 0 }) &&
validator.isInt(icon.height + '', { min: 0 }) validator.isInt(icon.height + '', { min: 0 })
@ -105,8 +103,7 @@ function isRemoteVideoIconValid (icon: any) {
function setValidRemoteVideoUrls (video: any) { function setValidRemoteVideoUrls (video: any) {
if (Array.isArray(video.url) === false) return false if (Array.isArray(video.url) === false) return false
const newUrl = video.url.filter(u => isRemoteVideoUrlValid(u)) video.url = video.url.filter(u => isRemoteVideoUrlValid(u))
video.url = newUrl
return true return true
} }
@ -115,13 +112,13 @@ function isRemoteVideoUrlValid (url: any) {
return url.type === 'Link' && return url.type === 'Link' &&
( (
ACTIVITY_PUB.URL_MIME_TYPES.VIDEO.indexOf(url.mimeType) !== -1 && ACTIVITY_PUB.URL_MIME_TYPES.VIDEO.indexOf(url.mimeType) !== -1 &&
isVideoUrlValid(url.url) && isActivityPubUrlValid(url.url) &&
validator.isInt(url.width + '', { min: 0 }) && validator.isInt(url.width + '', { min: 0 }) &&
validator.isInt(url.size + '', { min: 0 }) validator.isInt(url.size + '', { min: 0 })
) || ) ||
( (
ACTIVITY_PUB.URL_MIME_TYPES.TORRENT.indexOf(url.mimeType) !== -1 && ACTIVITY_PUB.URL_MIME_TYPES.TORRENT.indexOf(url.mimeType) !== -1 &&
isVideoUrlValid(url.url) && isActivityPubUrlValid(url.url) &&
validator.isInt(url.width + '', { min: 0 }) validator.isInt(url.width + '', { min: 0 })
) || ) ||
( (

View File

@ -1,21 +1,13 @@
import * as Bluebird from 'bluebird'
import * as express from 'express' import * as express from 'express'
import 'express-validator' import 'express-validator'
import 'multer' import 'multer'
import * as validator from 'validator' import * as validator from 'validator'
import { CONSTRAINTS_FIELDS, database as db } from '../../initializers' import { CONSTRAINTS_FIELDS, database as db } from '../../initializers'
import { VideoChannelInstance } from '../../models' import { VideoChannelInstance } from '../../models'
import { logger } from '../logger'
import { isActivityPubUrlValid } from './index'
import { exists } from './misc' import { exists } from './misc'
const VIDEO_CHANNELS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_CHANNELS const VIDEO_CHANNELS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_CHANNELS
function isVideoChannelUrlValid (value: string) {
return isActivityPubUrlValid(value)
}
function isVideoChannelDescriptionValid (value: string) { function isVideoChannelDescriptionValid (value: string) {
return value === null || validator.isLength(value, VIDEO_CHANNELS_CONSTRAINTS_FIELDS.DESCRIPTION) return value === null || validator.isLength(value, VIDEO_CHANNELS_CONSTRAINTS_FIELDS.DESCRIPTION)
} }
@ -24,31 +16,7 @@ function isVideoChannelNameValid (value: string) {
return exists(value) && validator.isLength(value, VIDEO_CHANNELS_CONSTRAINTS_FIELDS.NAME) return exists(value) && validator.isLength(value, VIDEO_CHANNELS_CONSTRAINTS_FIELDS.NAME)
} }
function checkVideoChannelExists (id: string, res: express.Response, callback: () => void) { async function isVideoChannelExist (id: string, res: express.Response) {
let promise: Bluebird<VideoChannelInstance>
if (validator.isInt(id)) {
promise = db.VideoChannel.loadAndPopulateAccount(+id)
} else { // UUID
promise = db.VideoChannel.loadByUUIDAndPopulateAccount(id)
}
promise.then(videoChannel => {
if (!videoChannel) {
return res.status(404)
.json({ error: 'Video channel not found' })
.end()
}
res.locals.videoChannel = videoChannel
callback()
})
.catch(err => {
logger.error('Error in video channel request validator.', err)
return res.sendStatus(500)
})
}
async function isVideoChannelExistsPromise (id: string, res: express.Response) {
let videoChannel: VideoChannelInstance let videoChannel: VideoChannelInstance
if (validator.isInt(id)) { if (validator.isInt(id)) {
videoChannel = await db.VideoChannel.loadAndPopulateAccount(+id) videoChannel = await db.VideoChannel.loadAndPopulateAccount(+id)
@ -72,8 +40,6 @@ async function isVideoChannelExistsPromise (id: string, res: express.Response) {
export { export {
isVideoChannelDescriptionValid, isVideoChannelDescriptionValid,
checkVideoChannelExists,
isVideoChannelNameValid, isVideoChannelNameValid,
isVideoChannelExistsPromise, isVideoChannelExist
isVideoChannelUrlValid
} }

View File

@ -1,4 +1,3 @@
import * as Bluebird from 'bluebird'
import { Response } from 'express' import { Response } from 'express'
import 'express-validator' import 'express-validator'
import { values } from 'lodash' import { values } from 'lodash'
@ -6,12 +5,10 @@ import 'multer'
import * as validator from 'validator' import * as validator from 'validator'
import { VideoRateType } from '../../../shared' import { VideoRateType } from '../../../shared'
import { CONSTRAINTS_FIELDS, VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_RATE_TYPES } from '../../initializers' import { CONSTRAINTS_FIELDS, VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_RATE_TYPES } from '../../initializers'
import { VIDEO_PRIVACIES } from '../../initializers/constants'
import { database as db } from '../../initializers/database' import { database as db } from '../../initializers/database'
import { VideoInstance } from '../../models/video/video-interface' import { VideoInstance } from '../../models/video/video-interface'
import { logger } from '../logger'
import { isActivityPubUrlValid } from './activitypub/misc'
import { exists, isArray } from './misc' import { exists, isArray } from './misc'
import { VIDEO_PRIVACIES } from '../../initializers/constants'
const VIDEOS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEOS const VIDEOS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEOS
const VIDEO_ABUSES_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_ABUSES const VIDEO_ABUSES_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_ABUSES
@ -20,10 +17,6 @@ function isVideoCategoryValid (value: number) {
return VIDEO_CATEGORIES[value] !== undefined return VIDEO_CATEGORIES[value] !== undefined
} }
function isVideoUrlValid (value: string) {
return isActivityPubUrlValid(value)
}
function isVideoLicenceValid (value: number) { function isVideoLicenceValid (value: number) {
return VIDEO_LICENCES[value] !== undefined return VIDEO_LICENCES[value] !== undefined
} }
@ -106,31 +99,7 @@ function isVideoFileSizeValid (value: string) {
return exists(value) && validator.isInt(value + '', VIDEOS_CONSTRAINTS_FIELDS.FILE_SIZE) return exists(value) && validator.isInt(value + '', VIDEOS_CONSTRAINTS_FIELDS.FILE_SIZE)
} }
function checkVideoExists (id: string, res: Response, callback: () => void) { async function isVideoExist (id: string, res: Response) {
let promise: Bluebird<VideoInstance>
if (validator.isInt(id)) {
promise = db.Video.loadAndPopulateAccountAndServerAndTags(+id)
} else { // UUID
promise = db.Video.loadByUUIDAndPopulateAccountAndServerAndTags(id)
}
promise.then(video => {
if (!video) {
return res.status(404)
.json({ error: 'Video not found' })
.end()
}
res.locals.video = video
callback()
})
.catch(err => {
logger.error('Error in video request validator.', err)
return res.sendStatus(500)
})
}
async function isVideoExistsPromise (id: string, res: Response) {
let video: VideoInstance let video: VideoInstance
if (validator.isInt(id)) { if (validator.isInt(id)) {
@ -169,10 +138,8 @@ export {
isVideoRatingTypeValid, isVideoRatingTypeValid,
isVideoDurationValid, isVideoDurationValid,
isVideoTagValid, isVideoTagValid,
isVideoUrlValid,
isVideoPrivacyValid, isVideoPrivacyValid,
isVideoFileResolutionValid, isVideoFileResolutionValid,
isVideoFileSizeValid, isVideoFileSizeValid,
checkVideoExists, isVideoExist
isVideoExistsPromise
} }

View File

@ -1,9 +1,12 @@
import { Request, Response, NextFunction, RequestHandler } from 'express'
import { eachSeries } from 'async' import { eachSeries } from 'async'
import { NextFunction, Request, RequestHandler, Response } from 'express'
// Syntactic sugar to avoid try/catch in express controllers // Syntactic sugar to avoid try/catch in express controllers
// Thanks: https://medium.com/@Abazhenov/using-async-await-in-express-with-node-8-b8af872c0016 // Thanks: https://medium.com/@Abazhenov/using-async-await-in-express-with-node-8-b8af872c0016
function asyncMiddleware (fun: RequestHandler | RequestHandler[]) {
export type RequestPromiseHandler = (req: Request, res: Response, next: NextFunction) => Promise<any>
function asyncMiddleware (fun: RequestPromiseHandler | RequestPromiseHandler[]) {
return (req: Request, res: Response, next: NextFunction) => { return (req: Request, res: Response, next: NextFunction) => {
if (Array.isArray(fun) === true) { if (Array.isArray(fun) === true) {
return eachSeries(fun as RequestHandler[], (f, cb) => { return eachSeries(fun as RequestHandler[], (f, cb) => {

View File

@ -1,18 +1,19 @@
import * as express from 'express' import * as express from 'express'
import { param } from 'express-validator/check' import { param } from 'express-validator/check'
import { logger } from '../../helpers' import { logger, isLocalAccountNameExist } from '../../helpers'
import { checkLocalAccountNameExists, isAccountNameValid } from '../../helpers/custom-validators/accounts' import { isAccountNameValid } from '../../helpers/custom-validators/accounts'
import { checkErrors } from './utils' import { areValidationErrors } from './utils'
const localAccountValidator = [ const localAccountValidator = [
param('name').custom(isAccountNameValid).withMessage('Should have a valid account name'), param('name').custom(isAccountNameValid).withMessage('Should have a valid account name'),
(req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking localAccountValidator parameters', { parameters: req.params }) logger.debug('Checking localAccountValidator parameters', { parameters: req.params })
checkErrors(req, res, () => { if (areValidationErrors(req, res)) return
checkLocalAccountNameExists(req.params.name, res, next) if (!await isLocalAccountNameExist(req.params.name, res)) return
})
return next()
} }
] ]

View File

@ -1,7 +1,7 @@
import * as express from 'express' import * as express from 'express'
import { body } from 'express-validator/check' import { body } from 'express-validator/check'
import { isRootActivityValid, logger } from '../../../helpers' import { isRootActivityValid, logger } from '../../../helpers'
import { checkErrors } from '../utils' import { areValidationErrors } from '../utils'
const activityPubValidator = [ const activityPubValidator = [
body('').custom((value, { req }) => isRootActivityValid(req.body)), body('').custom((value, { req }) => isRootActivityValid(req.body)),
@ -9,7 +9,9 @@ const activityPubValidator = [
(req: express.Request, res: express.Response, next: express.NextFunction) => { (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking activity pub parameters', { parameters: req.body }) logger.debug('Checking activity pub parameters', { parameters: req.body })
checkErrors(req, res, next) if (areValidationErrors(req, res)) return
return next()
} }
] ]

View File

@ -1,14 +1,7 @@
import { body } from 'express-validator/check'
import * as express from 'express' import * as express from 'express'
import { body } from 'express-validator/check'
import { import { isDateValid, isSignatureCreatorValid, isSignatureTypeValid, isSignatureValueValid, logger } from '../../../helpers'
logger, import { areValidationErrors } from '../utils'
isDateValid,
isSignatureTypeValid,
isSignatureCreatorValid,
isSignatureValueValid
} from '../../../helpers'
import { checkErrors } from '../utils'
const signatureValidator = [ const signatureValidator = [
body('signature.type').custom(isSignatureTypeValid).withMessage('Should have a valid signature type'), body('signature.type').custom(isSignatureTypeValid).withMessage('Should have a valid signature type'),
@ -19,7 +12,9 @@ const signatureValidator = [
(req: express.Request, res: express.Response, next: express.NextFunction) => { (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking activitypub signature parameter', { parameters: { signature: req.body.signature } }) logger.debug('Checking activitypub signature parameter', { parameters: { signature: req.body.signature } })
checkErrors(req, res, next) if (areValidationErrors(req, res)) return
return next()
} }
] ]

View File

@ -4,7 +4,7 @@ import { isTestInstance } from '../../helpers/core-utils'
import { isEachUniqueHostValid } from '../../helpers/custom-validators/servers' import { isEachUniqueHostValid } from '../../helpers/custom-validators/servers'
import { logger } from '../../helpers/logger' import { logger } from '../../helpers/logger'
import { CONFIG, database as db } from '../../initializers' import { CONFIG, database as db } from '../../initializers'
import { checkErrors } from './utils' import { areValidationErrors } from './utils'
import { getServerAccount } from '../../helpers/utils' import { getServerAccount } from '../../helpers/utils'
import { isIdOrUUIDValid } from '../../helpers/custom-validators/misc' import { isIdOrUUIDValid } from '../../helpers/custom-validators/misc'
@ -23,34 +23,30 @@ const followValidator = [
logger.debug('Checking follow parameters', { parameters: req.body }) logger.debug('Checking follow parameters', { parameters: req.body })
checkErrors(req, res, next) if (areValidationErrors(req, res)) return
return next()
} }
] ]
const removeFollowingValidator = [ const removeFollowingValidator = [
param('accountId').custom(isIdOrUUIDValid).withMessage('Should have a valid account id'), param('accountId').custom(isIdOrUUIDValid).withMessage('Should have a valid account id'),
(req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking unfollow parameters', { parameters: req.params }) logger.debug('Checking unfollow parameters', { parameters: req.params })
checkErrors(req, res, async () => { if (areValidationErrors(req, res)) return
try {
const serverAccount = await getServerAccount()
const follow = await db.AccountFollow.loadByAccountAndTarget(serverAccount.id, req.params.accountId)
if (!follow) { const serverAccount = await getServerAccount()
return res.status(404) const follow = await db.AccountFollow.loadByAccountAndTarget(serverAccount.id, req.params.accountId)
.end()
}
res.locals.follow = follow if (!follow) {
return res.status(404)
.end()
}
return next() res.locals.follow = follow
} catch (err) { return next()
logger.error('Error in remove following validator.', err)
return res.sendStatus(500)
}
})
} }
] ]

View File

@ -1,15 +1,10 @@
import { query } from 'express-validator/check'
import * as express from 'express' import * as express from 'express'
import { query } from 'express-validator/check'
import { join } from 'path' import { join } from 'path'
import { isIdOrUUIDValid, isTestInstance, logger } from '../../helpers'
import { checkErrors } from './utils'
import { CONFIG } from '../../initializers' import { CONFIG } from '../../initializers'
import { import { areValidationErrors } from './utils'
logger, import { isVideoExist } from '../../helpers/custom-validators/videos'
isTestInstance,
checkVideoExists,
isIdOrUUIDValid
} from '../../helpers'
const urlShouldStartWith = CONFIG.WEBSERVER.SCHEME + '://' + join(CONFIG.WEBSERVER.HOST, 'videos', 'watch') + '/' const urlShouldStartWith = CONFIG.WEBSERVER.SCHEME + '://' + join(CONFIG.WEBSERVER.HOST, 'videos', 'watch') + '/'
const videoWatchRegex = new RegExp('([^/]+)$') const videoWatchRegex = new RegExp('([^/]+)$')
@ -29,33 +24,35 @@ const oembedValidator = [
query('maxheight').optional().isInt().withMessage('Should have a valid max height'), query('maxheight').optional().isInt().withMessage('Should have a valid max height'),
query('format').optional().isIn([ 'xml', 'json' ]).withMessage('Should have a valid format'), query('format').optional().isIn([ 'xml', 'json' ]).withMessage('Should have a valid format'),
(req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking oembed parameters', { parameters: req.query }) logger.debug('Checking oembed parameters', { parameters: req.query })
checkErrors(req, res, () => { if (areValidationErrors(req, res)) return
if (req.query.format !== undefined && req.query.format !== 'json') {
return res.status(501)
.json({ error: 'Requested format is not implemented on server.' })
.end()
}
const startIsOk = req.query.url.startsWith(urlShouldStartWith) if (req.query.format !== undefined && req.query.format !== 'json') {
const matches = videoWatchRegex.exec(req.query.url) return res.status(501)
if (startIsOk === false || matches === null) { .json({ error: 'Requested format is not implemented on server.' })
return res.status(400) .end()
.json({ error: 'Invalid url.' }) }
.end()
}
const videoId = matches[1] const startIsOk = req.query.url.startsWith(urlShouldStartWith)
if (isIdOrUUIDValid(videoId) === false) { const matches = videoWatchRegex.exec(req.query.url)
return res.status(400) if (startIsOk === false || matches === null) {
.json({ error: 'Invalid video id.' }) return res.status(400)
.end() .json({ error: 'Invalid url.' })
} .end()
}
checkVideoExists(videoId, res, next) const videoId = matches[1]
}) if (isIdOrUUIDValid(videoId) === false) {
return res.status(400)
.json({ error: 'Invalid video id.' })
.end()
}
if (!await isVideoExist(videoId, res)) return
return next()
} }
] ]

View File

@ -1,8 +1,7 @@
import { query } from 'express-validator/check'
import * as express from 'express' import * as express from 'express'
import { query } from 'express-validator/check'
import { checkErrors } from './utils'
import { logger } from '../../helpers' import { logger } from '../../helpers'
import { areValidationErrors } from './utils'
const paginationValidator = [ const paginationValidator = [
query('start').optional().isInt().withMessage('Should have a number start'), query('start').optional().isInt().withMessage('Should have a number start'),
@ -11,7 +10,9 @@ const paginationValidator = [
(req: express.Request, res: express.Response, next: express.NextFunction) => { (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking pagination parameters', { parameters: req.query }) logger.debug('Checking pagination parameters', { parameters: req.query })
checkErrors(req, res, next) if (areValidationErrors(req, res)) return
return next()
} }
] ]

View File

@ -1,9 +1,9 @@
import { query } from 'express-validator/check' import { query } from 'express-validator/check'
import * as express from 'express' import * as express from 'express'
import { checkErrors } from './utils'
import { logger } from '../../helpers' import { logger } from '../../helpers'
import { SORTABLE_COLUMNS } from '../../initializers' import { SORTABLE_COLUMNS } from '../../initializers'
import { areValidationErrors } from './utils'
// Initialize constants here for better performances // Initialize constants here for better performances
const SORTABLE_USERS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.USERS) const SORTABLE_USERS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.USERS)
@ -43,7 +43,9 @@ function checkSort (sortableColumns: string[]) {
(req: express.Request, res: express.Response, next: express.NextFunction) => { (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking sort parameters', { parameters: req.query }) logger.debug('Checking sort parameters', { parameters: req.query })
checkErrors(req, res, next) if (areValidationErrors(req, res)) return
return next()
} }
] ]
} }

View File

@ -1,22 +1,19 @@
import { body, param } from 'express-validator/check'
import 'express-validator'
import * as express from 'express' import * as express from 'express'
import * as Promise from 'bluebird' import 'express-validator'
import * as validator from 'validator' import { body, param } from 'express-validator/check'
import { database as db } from '../../initializers/database'
import { checkErrors } from './utils'
import { import {
isSignupAllowed,
logger,
isUserUsernameValid,
isUserPasswordValid,
isUserVideoQuotaValid,
isUserDisplayNSFWValid,
isIdOrUUIDValid, isIdOrUUIDValid,
isUserRoleValid isSignupAllowed,
isUserDisplayNSFWValid,
isUserPasswordValid,
isUserRoleValid,
isUserUsernameValid,
isUserVideoQuotaValid,
logger
} from '../../helpers' } from '../../helpers'
import { UserInstance, VideoInstance } from '../../models' import { isVideoExist } from '../../helpers/custom-validators/videos'
import { database as db } from '../../initializers/database'
import { areValidationErrors } from './utils'
const usersAddValidator = [ const usersAddValidator = [
body('username').custom(isUserUsernameValid).withMessage('Should have a valid username (lowercase alphanumeric characters)'), body('username').custom(isUserUsernameValid).withMessage('Should have a valid username (lowercase alphanumeric characters)'),
@ -25,12 +22,13 @@ const usersAddValidator = [
body('videoQuota').custom(isUserVideoQuotaValid).withMessage('Should have a valid user quota'), body('videoQuota').custom(isUserVideoQuotaValid).withMessage('Should have a valid user quota'),
body('role').custom(isUserRoleValid).withMessage('Should have a valid role'), body('role').custom(isUserRoleValid).withMessage('Should have a valid role'),
(req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking usersAdd parameters', { parameters: req.body }) logger.debug('Checking usersAdd parameters', { parameters: req.body })
checkErrors(req, res, () => { if (areValidationErrors(req, res)) return
checkUserDoesNotAlreadyExist(req.body.username, req.body.email, res, next) if (!await checkUserNameOrEmailDoesNotAlreadyExist(req.body.username, req.body.email, res)) return
})
return next()
} }
] ]
@ -39,37 +37,33 @@ const usersRegisterValidator = [
body('password').custom(isUserPasswordValid).withMessage('Should have a valid password'), body('password').custom(isUserPasswordValid).withMessage('Should have a valid password'),
body('email').isEmail().withMessage('Should have a valid email'), body('email').isEmail().withMessage('Should have a valid email'),
(req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking usersRegister parameters', { parameters: req.body }) logger.debug('Checking usersRegister parameters', { parameters: req.body })
checkErrors(req, res, () => { if (areValidationErrors(req, res)) return
checkUserDoesNotAlreadyExist(req.body.username, req.body.email, res, next) if (!await checkUserNameOrEmailDoesNotAlreadyExist(req.body.username, req.body.email, res)) return
})
return next()
} }
] ]
const usersRemoveValidator = [ const usersRemoveValidator = [
param('id').isInt().not().isEmpty().withMessage('Should have a valid id'), param('id').isInt().not().isEmpty().withMessage('Should have a valid id'),
(req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking usersRemove parameters', { parameters: req.params }) logger.debug('Checking usersRemove parameters', { parameters: req.params })
checkErrors(req, res, () => { if (areValidationErrors(req, res)) return
checkUserExists(req.params.id, res, (err, user) => { if (!await checkUserIdExist(req.params.id, res)) return
if (err) {
logger.error('Error in usersRemoveValidator.', err)
return res.sendStatus(500)
}
if (user.username === 'root') { const user = res.locals.user
return res.status(400) if (user.username === 'root') {
.send({ error: 'Cannot remove the root user' }) return res.status(400)
.end() .send({ error: 'Cannot remove the root user' })
} .end()
}
return next() return next()
})
})
} }
] ]
@ -79,12 +73,13 @@ const usersUpdateValidator = [
body('videoQuota').optional().custom(isUserVideoQuotaValid).withMessage('Should have a valid user quota'), body('videoQuota').optional().custom(isUserVideoQuotaValid).withMessage('Should have a valid user quota'),
body('role').optional().custom(isUserRoleValid).withMessage('Should have a valid role'), body('role').optional().custom(isUserRoleValid).withMessage('Should have a valid role'),
(req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking usersUpdate parameters', { parameters: req.body }) logger.debug('Checking usersUpdate parameters', { parameters: req.body })
checkErrors(req, res, () => { if (areValidationErrors(req, res)) return
checkUserExists(req.params.id, res, next) if (!await checkUserIdExist(req.params.id, res)) return
})
return next()
} }
] ]
@ -97,64 +92,48 @@ const usersUpdateMeValidator = [
// TODO: Add old password verification // TODO: Add old password verification
logger.debug('Checking usersUpdateMe parameters', { parameters: req.body }) logger.debug('Checking usersUpdateMe parameters', { parameters: req.body })
checkErrors(req, res, next) if (areValidationErrors(req, res)) return
return next()
} }
] ]
const usersGetValidator = [ const usersGetValidator = [
param('id').isInt().not().isEmpty().withMessage('Should have a valid id'), param('id').isInt().not().isEmpty().withMessage('Should have a valid id'),
(req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
checkErrors(req, res, () => { logger.debug('Checking usersGet parameters', { parameters: req.body })
checkUserExists(req.params.id, res, next)
}) if (areValidationErrors(req, res)) return
if (!await checkUserIdExist(req.params.id, res)) return
return next()
} }
] ]
const usersVideoRatingValidator = [ const usersVideoRatingValidator = [
param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid video id'), param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid video id'),
(req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking usersVideoRating parameters', { parameters: req.params }) logger.debug('Checking usersVideoRating parameters', { parameters: req.params })
checkErrors(req, res, () => { if (areValidationErrors(req, res)) return
let videoPromise: Promise<VideoInstance> if (!await isVideoExist(req.params.videoId, res)) return
if (validator.isUUID(req.params.videoId)) { return next()
videoPromise = db.Video.loadByUUID(req.params.videoId)
} else {
videoPromise = db.Video.load(req.params.videoId)
}
videoPromise
.then(video => {
if (!video) {
return res.status(404)
.json({ error: 'Video not found' })
.end()
}
return next()
})
.catch(err => {
logger.error('Error in user request validator.', err)
return res.sendStatus(500)
})
})
} }
] ]
const ensureUserRegistrationAllowed = [ const ensureUserRegistrationAllowed = [
(req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
isSignupAllowed().then(allowed => { const allowed = await isSignupAllowed()
if (allowed === false) { if (allowed === false) {
return res.status(403) return res.status(403)
.send({ error: 'User registration is not enabled or user limit is reached.' }) .send({ error: 'User registration is not enabled or user limit is reached.' })
.end() .end()
} }
return next() return next()
})
} }
] ]
@ -173,37 +152,30 @@ export {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
function checkUserExists (id: number, res: express.Response, callback: (err: Error, user: UserInstance) => void) { async function checkUserIdExist (id: number, res: express.Response) {
db.User.loadById(id) const user = await db.User.loadById(id)
.then(user => {
if (!user) {
return res.status(404)
.send({ error: 'User not found' })
.end()
}
res.locals.user = user if (!user) {
return callback(null, user) res.status(404)
}) .send({ error: 'User not found' })
.catch(err => { .end()
logger.error('Error in user request validator.', err)
return res.sendStatus(500) return false
}) }
res.locals.user = user
return true
} }
function checkUserDoesNotAlreadyExist (username: string, email: string, res: express.Response, callback: () => void) { async function checkUserNameOrEmailDoesNotAlreadyExist (username: string, email: string, res: express.Response) {
db.User.loadByUsernameOrEmail(username, email) const user = await db.User.loadByUsernameOrEmail(username, email)
.then(user => {
if (user) {
return res.status(409)
.send({ error: 'User with this username of email already exists.' })
.end()
}
return callback() if (user) {
}) res.status(409)
.catch(err => { .send({ error: 'User with this username of email already exists.' })
logger.error('Error in usersAdd request validator.', err) .end()
return res.sendStatus(500) return false
}) }
return true
} }

View File

@ -1,19 +1,8 @@
import { validationResult } from 'express-validator/check'
import * as express from 'express' import * as express from 'express'
import { validationResult } from 'express-validator/check'
import { logger } from '../../helpers' import { logger } from '../../helpers'
function checkErrors (req: express.Request, res: express.Response, next: express.NextFunction) {
const errors = validationResult(req)
if (!errors.isEmpty()) {
logger.warn('Incorrect request parameters', { path: req.originalUrl, err: errors.mapped() })
return res.status(400).json({ errors: errors.mapped() })
}
return next()
}
function areValidationErrors (req: express.Request, res: express.Response) { function areValidationErrors (req: express.Request, res: express.Response) {
const errors = validationResult(req) const errors = validationResult(req)
@ -30,6 +19,5 @@ function areValidationErrors (req: express.Request, res: express.Response) {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
export { export {
checkErrors,
areValidationErrors areValidationErrors
} }

View File

@ -1,35 +1,36 @@
import { param } from 'express-validator/check'
import * as express from 'express' import * as express from 'express'
import { param } from 'express-validator/check'
import { isIdOrUUIDValid, logger } from '../../helpers'
import { isVideoExist } from '../../helpers/custom-validators/videos'
import { database as db } from '../../initializers/database' import { database as db } from '../../initializers/database'
import { checkErrors } from './utils' import { VideoInstance } from '../../models/video/video-interface'
import { logger, isIdOrUUIDValid, checkVideoExists } from '../../helpers' import { areValidationErrors } from './utils'
const videosBlacklistRemoveValidator = [ const videosBlacklistRemoveValidator = [
param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'), param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'),
(req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking blacklistRemove parameters.', { parameters: req.params }) logger.debug('Checking blacklistRemove parameters.', { parameters: req.params })
checkErrors(req, res, () => { if (areValidationErrors(req, res)) return
checkVideoExists(req.params.videoId, res, () => { if (!await isVideoExist(req.params.videoId, res)) return
checkVideoIsBlacklisted(req, res, next) if (!await checkVideoIsBlacklisted(res.locals.video, res)) return
})
}) return next()
} }
] ]
const videosBlacklistAddValidator = [ const videosBlacklistAddValidator = [
param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'), param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'),
(req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking videosBlacklist parameters', { parameters: req.params }) logger.debug('Checking videosBlacklist parameters', { parameters: req.params })
checkErrors(req, res, () => { if (areValidationErrors(req, res)) return
checkVideoExists(req.params.videoId, res, () => { if (!await isVideoExist(req.params.videoId, res)) return
checkVideoIsBlacklistable(req, res, next) if (!checkVideoIsBlacklistable(res.locals.video, res)) return
})
}) return next()
} }
] ]
@ -41,27 +42,27 @@ export {
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
function checkVideoIsBlacklistable (req: express.Request, res: express.Response, callback: () => void) { function checkVideoIsBlacklistable (video: VideoInstance, res: express.Response) {
if (res.locals.video.isOwned() === true) { if (video.isOwned() === true) {
return res.status(403) res.status(403)
.json({ error: 'Cannot blacklist a local video' }) .json({ error: 'Cannot blacklist a local video' })
.end() .end()
return false
} }
callback() return true
} }
function checkVideoIsBlacklisted (req: express.Request, res: express.Response, callback: () => void) { async function checkVideoIsBlacklisted (video: VideoInstance, res: express.Response) {
db.BlacklistedVideo.loadByVideoId(res.locals.video.id) const blacklistedVideo = await db.BlacklistedVideo.loadByVideoId(video.id)
.then(blacklistedVideo => { if (!blacklistedVideo) {
if (!blacklistedVideo) return res.status(404).send('Blacklisted video not found') res.status(404)
.send('Blacklisted video not found')
res.locals.blacklistedVideo = blacklistedVideo return false
}
callback() res.locals.blacklistedVideo = blacklistedVideo
}) return true
.catch(err => {
logger.error('Error in blacklistRemove request validator', { error: err })
return res.sendStatus(500)
})
} }

View File

@ -1,29 +1,30 @@
import * as express from 'express' import * as express from 'express'
import { body, param } from 'express-validator/check' import { body, param } from 'express-validator/check'
import { UserRight } from '../../../shared' import { UserRight } from '../../../shared'
import { checkAccountIdExists } from '../../helpers/custom-validators/accounts'
import { isIdValid } from '../../helpers/custom-validators/misc' import { isIdValid } from '../../helpers/custom-validators/misc'
import { import {
checkVideoChannelExists,
isVideoChannelDescriptionValid, isVideoChannelDescriptionValid,
isVideoChannelExistsPromise, isVideoChannelExist,
isVideoChannelNameValid isVideoChannelNameValid
} from '../../helpers/custom-validators/video-channels' } from '../../helpers/custom-validators/video-channels'
import { isIdOrUUIDValid } from '../../helpers/index' import { isIdOrUUIDValid } from '../../helpers/index'
import { logger } from '../../helpers/logger' import { logger } from '../../helpers/logger'
import { database as db } from '../../initializers' import { database as db } from '../../initializers'
import { UserInstance } from '../../models' import { UserInstance } from '../../models'
import { areValidationErrors, checkErrors } from './utils' import { areValidationErrors } from './utils'
import { isAccountIdExist } from '../../helpers/custom-validators/accounts'
import { VideoChannelInstance } from '../../models/video/video-channel-interface'
const listVideoAccountChannelsValidator = [ const listVideoAccountChannelsValidator = [
param('accountId').custom(isIdOrUUIDValid).withMessage('Should have a valid account id'), param('accountId').custom(isIdOrUUIDValid).withMessage('Should have a valid account id'),
(req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking listVideoAccountChannelsValidator parameters', { parameters: req.body }) logger.debug('Checking listVideoAccountChannelsValidator parameters', { parameters: req.body })
checkErrors(req, res, () => { if (areValidationErrors(req, res)) return
checkAccountIdExists(req.params.accountId, res, next) if (!await isAccountIdExist(req.params.accountId, res)) return
})
return next()
} }
] ]
@ -34,7 +35,9 @@ const videoChannelsAddValidator = [
(req: express.Request, res: express.Response, next: express.NextFunction) => { (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking videoChannelsAdd parameters', { parameters: req.body }) logger.debug('Checking videoChannelsAdd parameters', { parameters: req.body })
checkErrors(req, res, next) if (areValidationErrors(req, res)) return
return next()
} }
] ]
@ -43,56 +46,56 @@ const videoChannelsUpdateValidator = [
body('name').optional().custom(isVideoChannelNameValid).withMessage('Should have a valid name'), body('name').optional().custom(isVideoChannelNameValid).withMessage('Should have a valid name'),
body('description').optional().custom(isVideoChannelDescriptionValid).withMessage('Should have a valid description'), body('description').optional().custom(isVideoChannelDescriptionValid).withMessage('Should have a valid description'),
(req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking videoChannelsUpdate parameters', { parameters: req.body }) logger.debug('Checking videoChannelsUpdate parameters', { parameters: req.body })
checkErrors(req, res, () => { if (areValidationErrors(req, res)) return
checkVideoChannelExists(req.params.id, res, () => { if (!await isVideoChannelExist(req.params.id, res)) return
// We need to make additional checks
if (res.locals.videoChannel.isOwned() === false) {
return res.status(403)
.json({ error: 'Cannot update video channel of another server' })
.end()
}
if (res.locals.videoChannel.Account.userId !== res.locals.oauth.token.User.id) { // We need to make additional checks
return res.status(403) if (res.locals.videoChannel.isOwned() === false) {
.json({ error: 'Cannot update video channel of another user' }) return res.status(403)
.end() .json({ error: 'Cannot update video channel of another server' })
} .end()
}
next() if (res.locals.videoChannel.Account.userId !== res.locals.oauth.token.User.id) {
}) return res.status(403)
}) .json({ error: 'Cannot update video channel of another user' })
.end()
}
return next()
} }
] ]
const videoChannelsRemoveValidator = [ const videoChannelsRemoveValidator = [
param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'),
(req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking videoChannelsRemove parameters', { parameters: req.params }) logger.debug('Checking videoChannelsRemove parameters', { parameters: req.params })
checkErrors(req, res, () => { if (areValidationErrors(req, res)) return
checkVideoChannelExists(req.params.id, res, () => { if (!await isVideoChannelExist(req.params.id, res)) return
// Check if the user who did the request is able to delete the video
checkUserCanDeleteVideoChannel(res, () => { // Check if the user who did the request is able to delete the video
checkVideoChannelIsNotTheLastOne(res, next) if (!checkUserCanDeleteVideoChannel(res.locals.user, res.locals.videoChannel, res)) return
}) if (!await checkVideoChannelIsNotTheLastOne(res)) return
})
}) return next()
} }
] ]
const videoChannelsGetValidator = [ const videoChannelsGetValidator = [
param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'),
(req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking videoChannelsGet parameters', { parameters: req.params }) logger.debug('Checking videoChannelsGet parameters', { parameters: req.params })
checkErrors(req, res, () => { if (areValidationErrors(req, res)) return
checkVideoChannelExists(req.params.id, res, next) if (!await isVideoChannelExist(req.params.id, res)) return
})
return next()
} }
] ]
@ -104,7 +107,7 @@ const videoChannelsShareValidator = [
logger.debug('Checking videoChannelShare parameters', { parameters: req.params }) logger.debug('Checking videoChannelShare parameters', { parameters: req.params })
if (areValidationErrors(req, res)) return if (areValidationErrors(req, res)) return
if (!await isVideoChannelExistsPromise(req.params.id, res)) return if (!await isVideoChannelExist(req.params.id, res)) return
const share = await db.VideoChannelShare.load(res.locals.video.id, req.params.accountId) const share = await db.VideoChannelShare.load(res.locals.video.id, req.params.accountId)
if (!share) { if (!share) {
@ -131,38 +134,40 @@ export {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
function checkUserCanDeleteVideoChannel (res: express.Response, callback: () => void) { function checkUserCanDeleteVideoChannel (user: UserInstance, videoChannel: VideoChannelInstance, res: express.Response) {
const user: UserInstance = res.locals.oauth.token.User
// Retrieve the user who did the request // Retrieve the user who did the request
if (res.locals.videoChannel.isOwned() === false) { if (videoChannel.isOwned() === false) {
return res.status(403) res.status(403)
.json({ error: 'Cannot remove video channel of another server.' }) .json({ error: 'Cannot remove video channel of another server.' })
.end() .end()
return false
} }
// Check if the user can delete the video channel // Check if the user can delete the video channel
// The user can delete it if s/he is an admin // The user can delete it if s/he is an admin
// Or if s/he is the video channel's account // Or if s/he is the video channel's account
if (user.hasRight(UserRight.REMOVE_ANY_VIDEO_CHANNEL) === false && res.locals.videoChannel.Account.userId !== user.id) { if (user.hasRight(UserRight.REMOVE_ANY_VIDEO_CHANNEL) === false && videoChannel.Account.userId !== user.id) {
return res.status(403) res.status(403)
.json({ error: 'Cannot remove video channel of another user' }) .json({ error: 'Cannot remove video channel of another user' })
.end() .end()
return false
} }
// If we reach this comment, we can delete the video return true
callback()
} }
function checkVideoChannelIsNotTheLastOne (res: express.Response, callback: () => void) { async function checkVideoChannelIsNotTheLastOne (res: express.Response) {
db.VideoChannel.countByAccount(res.locals.oauth.token.User.Account.id) const count = await db.VideoChannel.countByAccount(res.locals.oauth.token.User.Account.id)
.then(count => {
if (count <= 1) {
return res.status(409)
.json({ error: 'Cannot remove the last channel of this user' })
.end()
}
callback() if (count <= 1) {
}) res.status(409)
.json({ error: 'Cannot remove the last channel of this user' })
.end()
return false
}
return true
} }

View File

@ -3,11 +3,11 @@ import { body, param, query } from 'express-validator/check'
import { UserRight, VideoPrivacy } from '../../../shared' import { UserRight, VideoPrivacy } from '../../../shared'
import { isIdOrUUIDValid, isIdValid } from '../../helpers/custom-validators/misc' import { isIdOrUUIDValid, isIdValid } from '../../helpers/custom-validators/misc'
import { import {
checkVideoExists,
isVideoAbuseReasonValid, isVideoAbuseReasonValid,
isVideoCategoryValid, isVideoCategoryValid,
isVideoDescriptionValid, isVideoDescriptionValid,
isVideoDurationValid, isVideoDurationValid,
isVideoExist,
isVideoFile, isVideoFile,
isVideoLanguageValid, isVideoLanguageValid,
isVideoLicenceValid, isVideoLicenceValid,
@ -20,12 +20,11 @@ import {
import { getDurationFromVideoFile } from '../../helpers/ffmpeg-utils' import { getDurationFromVideoFile } from '../../helpers/ffmpeg-utils'
import { logger } from '../../helpers/logger' import { logger } from '../../helpers/logger'
import { CONSTRAINTS_FIELDS, SEARCHABLE_COLUMNS } from '../../initializers' import { CONSTRAINTS_FIELDS, SEARCHABLE_COLUMNS } from '../../initializers'
import { database as db } from '../../initializers/database' import { database as db } from '../../initializers/database'
import { UserInstance } from '../../models/account/user-interface' import { UserInstance } from '../../models/account/user-interface'
import { VideoInstance } from '../../models/video/video-interface'
import { authenticate } from '../oauth' import { authenticate } from '../oauth'
import { areValidationErrors, checkErrors } from './utils' import { areValidationErrors } from './utils'
import { isVideoExistsPromise } from '../../helpers/index'
const videosAddValidator = [ const videosAddValidator = [
body('videofile').custom((value, { req }) => isVideoFile(req.files)).withMessage( body('videofile').custom((value, { req }) => isVideoFile(req.files)).withMessage(
@ -42,68 +41,58 @@ const videosAddValidator = [
body('privacy').custom(isVideoPrivacyValid).withMessage('Should have correct video privacy'), body('privacy').custom(isVideoPrivacyValid).withMessage('Should have correct video privacy'),
body('tags').optional().custom(isVideoTagsValid).withMessage('Should have correct tags'), body('tags').optional().custom(isVideoTagsValid).withMessage('Should have correct tags'),
(req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking videosAdd parameters', { parameters: req.body, files: req.files }) logger.debug('Checking videosAdd parameters', { parameters: req.body, files: req.files })
checkErrors(req, res, () => { if (areValidationErrors(req, res)) return
const videoFile: Express.Multer.File = req.files['videofile'][0]
const user = res.locals.oauth.token.User
return db.VideoChannel.loadByIdAndAccount(req.body.channelId, user.Account.id) const videoFile: Express.Multer.File = req.files['videofile'][0]
.then(videoChannel => { const user = res.locals.oauth.token.User
if (!videoChannel) {
res.status(400)
.json({ error: 'Unknown video video channel for this account.' })
.end()
return undefined const videoChannel = await db.VideoChannel.loadByIdAndAccount(req.body.channelId, user.Account.id)
} if (!videoChannel) {
res.status(400)
.json({ error: 'Unknown video video channel for this account.' })
.end()
res.locals.videoChannel = videoChannel return
}
return user.isAbleToUploadVideo(videoFile) res.locals.videoChannel = videoChannel
})
.then(isAble => {
if (isAble === false) {
res.status(403)
.json({ error: 'The user video quota is exceeded with this video.' })
.end()
return undefined const isAble = await user.isAbleToUploadVideo(videoFile)
} if (isAble === false) {
res.status(403)
.json({ error: 'The user video quota is exceeded with this video.' })
.end()
return getDurationFromVideoFile(videoFile.path) return
.catch(err => { }
logger.error('Invalid input file in videosAddValidator.', err)
res.status(400)
.json({ error: 'Invalid input file.' })
.end()
return undefined let duration: number
})
})
.then(duration => {
// Previous test failed, abort
if (duration === undefined) return undefined
if (!isVideoDurationValid('' + duration)) { try {
return res.status(400) duration = await getDurationFromVideoFile(videoFile.path)
.json({ } catch (err) {
error: 'Duration of the video file is too big (max: ' + CONSTRAINTS_FIELDS.VIDEOS.DURATION.max + 's).' logger.error('Invalid input file in videosAddValidator.', err)
}) res.status(400)
.end() .json({ error: 'Invalid input file.' })
} .end()
videoFile['duration'] = duration return
next() }
})
.catch(err => {
logger.error('Error in video add validator', err)
res.sendStatus(500)
return undefined if (!isVideoDurationValid('' + duration)) {
}) return res.status(400)
}) .json({
error: 'Duration of the video file is too big (max: ' + CONSTRAINTS_FIELDS.VIDEOS.DURATION.max + 's).'
})
.end()
}
videoFile['duration'] = duration
return next()
} }
] ]
@ -118,61 +107,59 @@ const videosUpdateValidator = [
body('description').optional().custom(isVideoDescriptionValid).withMessage('Should have a valid description'), body('description').optional().custom(isVideoDescriptionValid).withMessage('Should have a valid description'),
body('tags').optional().custom(isVideoTagsValid).withMessage('Should have correct tags'), body('tags').optional().custom(isVideoTagsValid).withMessage('Should have correct tags'),
(req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking videosUpdate parameters', { parameters: req.body }) logger.debug('Checking videosUpdate parameters', { parameters: req.body })
checkErrors(req, res, () => { if (areValidationErrors(req, res)) return
checkVideoExists(req.params.id, res, () => { if (!await isVideoExist(req.params.id, res)) return
const video = res.locals.video
// We need to make additional checks const video = res.locals.video
if (video.isOwned() === false) {
return res.status(403)
.json({ error: 'Cannot update video of another server' })
.end()
}
if (video.VideoChannel.Account.userId !== res.locals.oauth.token.User.id) { // We need to make additional checks
return res.status(403) if (video.isOwned() === false) {
.json({ error: 'Cannot update video of another user' }) return res.status(403)
.end() .json({ error: 'Cannot update video of another server' })
} .end()
}
if (video.privacy !== VideoPrivacy.PRIVATE && req.body.privacy === VideoPrivacy.PRIVATE) { if (video.VideoChannel.Account.userId !== res.locals.oauth.token.User.id) {
return res.status(409) return res.status(403)
.json({ error: 'Cannot set "private" a video that was not private anymore.' }) .json({ error: 'Cannot update video of another user' })
.end() .end()
} }
next() if (video.privacy !== VideoPrivacy.PRIVATE && req.body.privacy === VideoPrivacy.PRIVATE) {
}) return res.status(409)
}) .json({ error: 'Cannot set "private" a video that was not private anymore.' })
.end()
}
return next()
} }
] ]
const videosGetValidator = [ const videosGetValidator = [
param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'),
(req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking videosGet parameters', { parameters: req.params }) logger.debug('Checking videosGet parameters', { parameters: req.params })
checkErrors(req, res, () => { if (areValidationErrors(req, res)) return
checkVideoExists(req.params.id, res, () => { if (!await isVideoExist(req.params.id, res)) return
const video = res.locals.video
// Video is not private, anyone can access it const video = res.locals.video
if (video.privacy !== VideoPrivacy.PRIVATE) return next()
authenticate(req, res, () => { // Video is not private, anyone can access it
if (video.VideoChannel.Account.userId !== res.locals.oauth.token.User.id) { if (video.privacy !== VideoPrivacy.PRIVATE) return next()
return res.status(403)
.json({ error: 'Cannot get this private video of another user' })
.end()
}
next() authenticate(req, res, () => {
}) if (video.VideoChannel.Account.userId !== res.locals.oauth.token.User.id) {
}) return res.status(403)
.json({ error: 'Cannot get this private video of another user' })
.end()
}
return next()
}) })
} }
] ]
@ -180,17 +167,16 @@ const videosGetValidator = [
const videosRemoveValidator = [ const videosRemoveValidator = [
param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'),
(req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking videosRemove parameters', { parameters: req.params }) logger.debug('Checking videosRemove parameters', { parameters: req.params })
checkErrors(req, res, () => { if (areValidationErrors(req, res)) return
checkVideoExists(req.params.id, res, () => { if (!await isVideoExist(req.params.id, res)) return
// Check if the user who did the request is able to delete the video
checkUserCanDeleteVideo(res.locals.oauth.token.User, res, () => { // Check if the user who did the request is able to delete the video
next() if (!checkUserCanDeleteVideo(res.locals.oauth.token.User, res.locals.video, res)) return
})
}) return next()
})
} }
] ]
@ -201,7 +187,9 @@ const videosSearchValidator = [
(req: express.Request, res: express.Response, next: express.NextFunction) => { (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking videosSearch parameters', { parameters: req.params }) logger.debug('Checking videosSearch parameters', { parameters: req.params })
checkErrors(req, res, next) if (areValidationErrors(req, res)) return
return next()
} }
] ]
@ -209,12 +197,13 @@ const videoAbuseReportValidator = [
param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'),
body('reason').custom(isVideoAbuseReasonValid).withMessage('Should have a valid reason'), body('reason').custom(isVideoAbuseReasonValid).withMessage('Should have a valid reason'),
(req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking videoAbuseReport parameters', { parameters: req.body }) logger.debug('Checking videoAbuseReport parameters', { parameters: req.body })
checkErrors(req, res, () => { if (areValidationErrors(req, res)) return
checkVideoExists(req.params.id, res, next) if (!await isVideoExist(req.params.id, res)) return
})
return next()
} }
] ]
@ -222,12 +211,13 @@ const videoRateValidator = [
param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'),
body('rating').custom(isVideoRatingTypeValid).withMessage('Should have a valid rate type'), body('rating').custom(isVideoRatingTypeValid).withMessage('Should have a valid rate type'),
(req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking videoRate parameters', { parameters: req.body }) logger.debug('Checking videoRate parameters', { parameters: req.body })
checkErrors(req, res, () => { if (areValidationErrors(req, res)) return
checkVideoExists(req.params.id, res, next) if (!await isVideoExist(req.params.id, res)) return
})
return next()
} }
] ]
@ -239,7 +229,7 @@ const videosShareValidator = [
logger.debug('Checking videoShare parameters', { parameters: req.params }) logger.debug('Checking videoShare parameters', { parameters: req.params })
if (areValidationErrors(req, res)) return if (areValidationErrors(req, res)) return
if (!await isVideoExistsPromise(req.params.id, res)) return if (!await isVideoExist(req.params.id, res)) return
const share = await db.VideoShare.load(req.params.accountId, res.locals.video.id) const share = await db.VideoShare.load(req.params.accountId, res.locals.video.id)
if (!share) { if (!share) {
@ -248,7 +238,6 @@ const videosShareValidator = [
} }
res.locals.videoShare = share res.locals.videoShare = share
return next() return next()
} }
] ]
@ -270,24 +259,25 @@ export {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
function checkUserCanDeleteVideo (user: UserInstance, res: express.Response, callback: () => void) { function checkUserCanDeleteVideo (user: UserInstance, video: VideoInstance, res: express.Response) {
// Retrieve the user who did the request // Retrieve the user who did the request
if (res.locals.video.isOwned() === false) { if (video.isOwned() === false) {
return res.status(403) res.status(403)
.json({ error: 'Cannot remove video of another server, blacklist it' }) .json({ error: 'Cannot remove video of another server, blacklist it' })
.end() .end()
return false
} }
// Check if the user can delete the video // Check if the user can delete the video
// The user can delete it if s/he is an admin // The user can delete it if s/he is an admin
// Or if s/he is the video's account // Or if s/he is the video's account
const account = res.locals.video.VideoChannel.Account const account = video.VideoChannel.Account
if (user.hasRight(UserRight.REMOVE_ANY_VIDEO) === false && account.userId !== user.id) { if (user.hasRight(UserRight.REMOVE_ANY_VIDEO) === false && account.userId !== user.id) {
return res.status(403) res.status(403)
.json({ error: 'Cannot remove video of another user' }) .json({ error: 'Cannot remove video of another user' })
.end() .end()
return false
} }
// If we reach this comment, we can delete the video return true
callback()
} }

View File

@ -1,37 +1,31 @@
import * as express from 'express' import * as express from 'express'
import { query } from 'express-validator/check' import { query } from 'express-validator/check'
import { isWebfingerResourceValid } from '../../helpers/custom-validators/webfinger' import { isWebfingerResourceValid } from '../../helpers/custom-validators/webfinger'
import { database as db } from '../../initializers'
import { checkErrors } from './utils'
import { logger } from '../../helpers/logger' import { logger } from '../../helpers/logger'
import { database as db } from '../../initializers'
import { areValidationErrors } from './utils'
const webfingerValidator = [ const webfingerValidator = [
query('resource').custom(isWebfingerResourceValid).withMessage('Should have a valid webfinger resource'), query('resource').custom(isWebfingerResourceValid).withMessage('Should have a valid webfinger resource'),
(req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking webfinger parameters', { parameters: req.query }) logger.debug('Checking webfinger parameters', { parameters: req.query })
checkErrors(req, res, () => { if (areValidationErrors(req, res)) return
// Remove 'acct:' from the beginning of the string
const nameWithHost = req.query.resource.substr(5)
const [ name ] = nameWithHost.split('@')
db.Account.loadLocalByName(name) // Remove 'acct:' from the beginning of the string
.then(account => { const nameWithHost = req.query.resource.substr(5)
if (!account) { const [ name ] = nameWithHost.split('@')
return res.status(404)
.send({ error: 'Account not found' })
.end()
}
res.locals.account = account const account = await db.Account.loadLocalByName(name)
return next() if (!account) {
}) return res.status(404)
.catch(err => { .send({ error: 'Account not found' })
logger.error('Error in webfinger validator.', err) .end()
return res.sendStatus(500) }
})
}) res.locals.account = account
return next()
} }
] ]

View File

@ -2,17 +2,12 @@ import * as Sequelize from 'sequelize'
import { import {
activityPubContextify, activityPubContextify,
isAccountFollowersCountValid, isAccountFollowersCountValid,
isAccountFollowersValid,
isAccountFollowingCountValid, isAccountFollowingCountValid,
isAccountFollowingValid,
isAccountInboxValid,
isAccountOutboxValid,
isAccountPrivateKeyValid, isAccountPrivateKeyValid,
isAccountPublicKeyValid, isAccountPublicKeyValid,
isAccountSharedInboxValid,
isAccountUrlValid,
isUserUsernameValid isUserUsernameValid
} from '../../helpers' } from '../../helpers'
import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
import { CONFIG, CONSTRAINTS_FIELDS } from '../../initializers/constants' import { CONFIG, CONSTRAINTS_FIELDS } from '../../initializers/constants'
import { sendDeleteAccount } from '../../lib/activitypub/send/send-delete' import { sendDeleteAccount } from '../../lib/activitypub/send/send-delete'
@ -61,7 +56,7 @@ export default function defineAccount (sequelize: Sequelize.Sequelize, DataTypes
allowNull: false, allowNull: false,
validate: { validate: {
urlValid: value => { urlValid: value => {
const res = isAccountUrlValid(value) const res = isActivityPubUrlValid(value)
if (res === false) throw new Error('URL is not valid.') if (res === false) throw new Error('URL is not valid.')
} }
} }
@ -111,7 +106,7 @@ export default function defineAccount (sequelize: Sequelize.Sequelize, DataTypes
allowNull: false, allowNull: false,
validate: { validate: {
inboxUrlValid: value => { inboxUrlValid: value => {
const res = isAccountInboxValid(value) const res = isActivityPubUrlValid(value)
if (res === false) throw new Error('Inbox URL is not valid.') if (res === false) throw new Error('Inbox URL is not valid.')
} }
} }
@ -121,7 +116,7 @@ export default function defineAccount (sequelize: Sequelize.Sequelize, DataTypes
allowNull: false, allowNull: false,
validate: { validate: {
outboxUrlValid: value => { outboxUrlValid: value => {
const res = isAccountOutboxValid(value) const res = isActivityPubUrlValid(value)
if (res === false) throw new Error('Outbox URL is not valid.') if (res === false) throw new Error('Outbox URL is not valid.')
} }
} }
@ -131,7 +126,7 @@ export default function defineAccount (sequelize: Sequelize.Sequelize, DataTypes
allowNull: false, allowNull: false,
validate: { validate: {
sharedInboxUrlValid: value => { sharedInboxUrlValid: value => {
const res = isAccountSharedInboxValid(value) const res = isActivityPubUrlValid(value)
if (res === false) throw new Error('Shared inbox URL is not valid.') if (res === false) throw new Error('Shared inbox URL is not valid.')
} }
} }
@ -141,7 +136,7 @@ export default function defineAccount (sequelize: Sequelize.Sequelize, DataTypes
allowNull: false, allowNull: false,
validate: { validate: {
followersUrlValid: value => { followersUrlValid: value => {
const res = isAccountFollowersValid(value) const res = isActivityPubUrlValid(value)
if (res === false) throw new Error('Followers URL is not valid.') if (res === false) throw new Error('Followers URL is not valid.')
} }
} }
@ -151,7 +146,7 @@ export default function defineAccount (sequelize: Sequelize.Sequelize, DataTypes
allowNull: false, allowNull: false,
validate: { validate: {
followingUrlValid: value => { followingUrlValid: value => {
const res = isAccountFollowingValid(value) const res = isActivityPubUrlValid(value)
if (res === false) throw new Error('Following URL is not valid.') if (res === false) throw new Error('Following URL is not valid.')
} }
} }

View File

@ -1,6 +1,5 @@
import * as Sequelize from 'sequelize' import * as Sequelize from 'sequelize'
import { isVideoChannelDescriptionValid, isVideoChannelNameValid } from '../../helpers' import { isVideoChannelDescriptionValid, isVideoChannelNameValid } from '../../helpers'
import { isVideoChannelUrlValid } from '../../helpers/custom-validators/video-channels'
import { CONSTRAINTS_FIELDS } from '../../initializers/constants' import { CONSTRAINTS_FIELDS } from '../../initializers/constants'
import { sendDeleteVideoChannel } from '../../lib/activitypub/send/send-delete' import { sendDeleteVideoChannel } from '../../lib/activitypub/send/send-delete'
@ -8,6 +7,7 @@ import { addMethodsToModel, getSort } from '../utils'
import { VideoChannelAttributes, VideoChannelInstance, VideoChannelMethods } from './video-channel-interface' import { VideoChannelAttributes, VideoChannelInstance, VideoChannelMethods } from './video-channel-interface'
import { getAnnounceActivityPubUrl } from '../../lib/activitypub/url' import { getAnnounceActivityPubUrl } from '../../lib/activitypub/url'
import { activityPubCollection } from '../../helpers/activitypub' import { activityPubCollection } from '../../helpers/activitypub'
import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
let VideoChannel: Sequelize.Model<VideoChannelInstance, VideoChannelAttributes> let VideoChannel: Sequelize.Model<VideoChannelInstance, VideoChannelAttributes>
let toFormattedJSON: VideoChannelMethods.ToFormattedJSON let toFormattedJSON: VideoChannelMethods.ToFormattedJSON
@ -66,7 +66,7 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
allowNull: false, allowNull: false,
validate: { validate: {
urlValid: value => { urlValid: value => {
const res = isVideoChannelUrlValid(value) const res = isActivityPubUrlValid(value)
if (res === false) throw new Error('Video channel URL is not valid.') if (res === false) throw new Error('Video channel URL is not valid.')
} }
} }

View File

@ -7,7 +7,18 @@ import * as Sequelize from 'sequelize'
import { VideoPrivacy, VideoResolution } from '../../../shared' import { VideoPrivacy, VideoResolution } from '../../../shared'
import { VideoTorrentObject } from '../../../shared/models/activitypub/objects/video-torrent-object' import { VideoTorrentObject } from '../../../shared/models/activitypub/objects/video-torrent-object'
import { activityPubCollection } from '../../helpers/activitypub' import { activityPubCollection } from '../../helpers/activitypub'
import { isVideoCategoryValid, isVideoLanguageValid, isVideoPrivacyValid, isVideoUrlValid } from '../../helpers/custom-validators/videos' import { createTorrentPromise, renamePromise, statPromise, unlinkPromise, writeFilePromise } from '../../helpers/core-utils'
import { isVideoCategoryValid, isVideoLanguageValid, isVideoPrivacyValid } from '../../helpers/custom-validators/videos'
import { generateImageFromVideoFile, getVideoFileHeight, transcode } from '../../helpers/ffmpeg-utils'
import {
isActivityPubUrlValid,
isVideoDescriptionValid,
isVideoDurationValid,
isVideoLicenceValid,
isVideoNameValid,
isVideoNSFWValid
} from '../../helpers/index'
import { logger } from '../../helpers/logger'
import { import {
API_VERSION, API_VERSION,
CONFIG, CONFIG,
@ -21,18 +32,12 @@ import {
VIDEO_LICENCES, VIDEO_LICENCES,
VIDEO_PRIVACIES VIDEO_PRIVACIES
} from '../../initializers/constants' } from '../../initializers/constants'
import { getAnnounceActivityPubUrl } from '../../lib/activitypub/url'
import { sendDeleteVideo } from '../../lib/index' import { sendDeleteVideo } from '../../lib/index'
import { addMethodsToModel, getSort } from '../utils' import { addMethodsToModel, getSort } from '../utils'
import { TagInstance } from './tag-interface' import { TagInstance } from './tag-interface'
import { VideoFileInstance, VideoFileModel } from './video-file-interface' import { VideoFileInstance, VideoFileModel } from './video-file-interface'
import { VideoAttributes, VideoInstance, VideoMethods } from './video-interface' import { VideoAttributes, VideoInstance, VideoMethods } from './video-interface'
import { isVideoNameValid, isVideoLicenceValid, isVideoNSFWValid, isVideoDescriptionValid, isVideoDurationValid } from '../../helpers/index'
import { logger } from '../../helpers/logger'
import { generateImageFromVideoFile, transcode, getVideoFileHeight } from '../../helpers/ffmpeg-utils'
import { createTorrentPromise, writeFilePromise, unlinkPromise, renamePromise, statPromise } from '../../helpers/core-utils'
import { getAnnounceActivityPubUrl } from '../../lib/activitypub/url'
let Video: Sequelize.Model<VideoInstance, VideoAttributes> let Video: Sequelize.Model<VideoInstance, VideoAttributes>
let getOriginalFile: VideoMethods.GetOriginalFile let getOriginalFile: VideoMethods.GetOriginalFile
@ -205,7 +210,7 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
allowNull: false, allowNull: false,
validate: { validate: {
urlValid: value => { urlValid: value => {
const res = isVideoUrlValid(value) const res = isActivityPubUrlValid(value)
if (res === false) throw new Error('Video URL is not valid.') if (res === false) throw new Error('Video URL is not valid.')
} }
} }

View File

@ -55,10 +55,10 @@ If you want to test the decentralization feature, you can easily run 3 instances
The server is composed by: The server is composed by:
* a REST API (throught Express framework) * a REST API (Express framework)
* a WebTorrent Tracker * a WebTorrent Tracker
A video is seeded by the server throught the [WebSeed](http://www.bittorrent.org/beps/bep_0019.html) protocol (HTTP). A video is seeded by the server with the [WebSeed](http://www.bittorrent.org/beps/bep_0019.html) protocol (HTTP).
![Architecture scheme](https://github.com/Chocobozzz/PeerTube/blob/master/support/doc/server/upload-video.png) ![Architecture scheme](https://github.com/Chocobozzz/PeerTube/blob/master/support/doc/server/upload-video.png)