aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--server/controllers/activitypub/client.ts10
-rw-r--r--server/controllers/api/server/follows.ts2
-rw-r--r--server/controllers/api/users.ts16
-rw-r--r--server/controllers/api/videos/abuse.ts2
-rw-r--r--server/controllers/api/videos/blacklist.ts4
-rw-r--r--server/controllers/api/videos/channel.ts12
-rw-r--r--server/controllers/api/videos/index.ts8
-rw-r--r--server/controllers/api/videos/rate.ts2
-rw-r--r--server/controllers/services.ts8
-rw-r--r--server/controllers/webfinger.ts3
-rw-r--r--server/helpers/custom-validators/accounts.ts46
-rw-r--r--server/helpers/custom-validators/activitypub/account.ts63
-rw-r--r--server/helpers/custom-validators/activitypub/videos.ts13
-rw-r--r--server/helpers/custom-validators/video-channels.ts38
-rw-r--r--server/helpers/custom-validators/videos.ts39
-rw-r--r--server/middlewares/async.ts7
-rw-r--r--server/middlewares/validators/account.ts15
-rw-r--r--server/middlewares/validators/activitypub/activity.ts6
-rw-r--r--server/middlewares/validators/activitypub/signature.ts17
-rw-r--r--server/middlewares/validators/follows.ts32
-rw-r--r--server/middlewares/validators/oembed.ts59
-rw-r--r--server/middlewares/validators/pagination.ts9
-rw-r--r--server/middlewares/validators/sort.ts6
-rw-r--r--server/middlewares/validators/users.ts198
-rw-r--r--server/middlewares/validators/utils.ts14
-rw-r--r--server/middlewares/validators/video-blacklist.ts63
-rw-r--r--server/middlewares/validators/video-channels.ts129
-rw-r--r--server/middlewares/validators/videos.ts252
-rw-r--r--server/middlewares/validators/webfinger.ts42
-rw-r--r--server/models/account/account.ts19
-rw-r--r--server/models/video/video-channel.ts4
-rw-r--r--server/models/video/video.ts23
-rw-r--r--support/doc/server/code.md4
33 files changed, 500 insertions, 665 deletions
diff --git a/server/controllers/activitypub/client.ts b/server/controllers/activitypub/client.ts
index 41272bca0..62cde1fc7 100644
--- a/server/controllers/activitypub/client.ts
+++ b/server/controllers/activitypub/client.ts
@@ -19,22 +19,22 @@ import { VideoShareInstance } from '../../models/video/video-share-interface'
19const activityPubClientRouter = express.Router() 19const activityPubClientRouter = express.Router()
20 20
21activityPubClientRouter.get('/account/:name', 21activityPubClientRouter.get('/account/:name',
22 executeIfActivityPub(localAccountValidator), 22 executeIfActivityPub(asyncMiddleware(localAccountValidator)),
23 executeIfActivityPub(accountController) 23 executeIfActivityPub(accountController)
24) 24)
25 25
26activityPubClientRouter.get('/account/:name/followers', 26activityPubClientRouter.get('/account/:name/followers',
27 executeIfActivityPub(localAccountValidator), 27 executeIfActivityPub(asyncMiddleware(localAccountValidator)),
28 executeIfActivityPub(asyncMiddleware(accountFollowersController)) 28 executeIfActivityPub(asyncMiddleware(accountFollowersController))
29) 29)
30 30
31activityPubClientRouter.get('/account/:name/following', 31activityPubClientRouter.get('/account/:name/following',
32 executeIfActivityPub(localAccountValidator), 32 executeIfActivityPub(asyncMiddleware(localAccountValidator)),
33 executeIfActivityPub(asyncMiddleware(accountFollowingController)) 33 executeIfActivityPub(asyncMiddleware(accountFollowingController))
34) 34)
35 35
36activityPubClientRouter.get('/videos/watch/:id', 36activityPubClientRouter.get('/videos/watch/:id',
37 executeIfActivityPub(videosGetValidator), 37 executeIfActivityPub(asyncMiddleware(videosGetValidator)),
38 executeIfActivityPub(videoController) 38 executeIfActivityPub(videoController)
39) 39)
40 40
@@ -44,7 +44,7 @@ activityPubClientRouter.get('/videos/watch/:id/announces/:accountId',
44) 44)
45 45
46activityPubClientRouter.get('/video-channels/:id', 46activityPubClientRouter.get('/video-channels/:id',
47 executeIfActivityPub(videoChannelsGetValidator), 47 executeIfActivityPub(asyncMiddleware(videoChannelsGetValidator)),
48 executeIfActivityPub(asyncMiddleware(videoChannelController)) 48 executeIfActivityPub(asyncMiddleware(videoChannelController))
49) 49)
50 50
diff --git a/server/controllers/api/server/follows.ts b/server/controllers/api/server/follows.ts
index 535d530f7..c2fb37c39 100644
--- a/server/controllers/api/server/follows.ts
+++ b/server/controllers/api/server/follows.ts
@@ -41,7 +41,7 @@ serverFollowsRouter.post('/following',
41serverFollowsRouter.delete('/following/:accountId', 41serverFollowsRouter.delete('/following/:accountId',
42 authenticate, 42 authenticate,
43 ensureUserHasRight(UserRight.MANAGE_SERVER_FOLLOW), 43 ensureUserHasRight(UserRight.MANAGE_SERVER_FOLLOW),
44 removeFollowingValidator, 44 asyncMiddleware(removeFollowingValidator),
45 asyncMiddleware(removeFollow) 45 asyncMiddleware(removeFollow)
46) 46)
47 47
diff --git a/server/controllers/api/users.ts b/server/controllers/api/users.ts
index ac7c87517..721b23301 100644
--- a/server/controllers/api/users.ts
+++ b/server/controllers/api/users.ts
@@ -43,7 +43,7 @@ usersRouter.get('/me/videos',
43 43
44usersRouter.get('/me/videos/:videoId/rating', 44usersRouter.get('/me/videos/:videoId/rating',
45 authenticate, 45 authenticate,
46 usersVideoRatingValidator, 46 asyncMiddleware(usersVideoRatingValidator),
47 asyncMiddleware(getUserVideoRating) 47 asyncMiddleware(getUserVideoRating)
48) 48)
49 49
@@ -56,20 +56,20 @@ usersRouter.get('/',
56) 56)
57 57
58usersRouter.get('/:id', 58usersRouter.get('/:id',
59 usersGetValidator, 59 asyncMiddleware(usersGetValidator),
60 getUser 60 getUser
61) 61)
62 62
63usersRouter.post('/', 63usersRouter.post('/',
64 authenticate, 64 authenticate,
65 ensureUserHasRight(UserRight.MANAGE_USERS), 65 ensureUserHasRight(UserRight.MANAGE_USERS),
66 usersAddValidator, 66 asyncMiddleware(usersAddValidator),
67 createUserRetryWrapper 67 asyncMiddleware(createUserRetryWrapper)
68) 68)
69 69
70usersRouter.post('/register', 70usersRouter.post('/register',
71 ensureUserRegistrationAllowed, 71 asyncMiddleware(ensureUserRegistrationAllowed),
72 usersRegisterValidator, 72 asyncMiddleware(usersRegisterValidator),
73 asyncMiddleware(registerUserRetryWrapper) 73 asyncMiddleware(registerUserRetryWrapper)
74) 74)
75 75
@@ -82,14 +82,14 @@ usersRouter.put('/me',
82usersRouter.put('/:id', 82usersRouter.put('/:id',
83 authenticate, 83 authenticate,
84 ensureUserHasRight(UserRight.MANAGE_USERS), 84 ensureUserHasRight(UserRight.MANAGE_USERS),
85 usersUpdateValidator, 85 asyncMiddleware(usersUpdateValidator),
86 asyncMiddleware(updateUser) 86 asyncMiddleware(updateUser)
87) 87)
88 88
89usersRouter.delete('/:id', 89usersRouter.delete('/:id',
90 authenticate, 90 authenticate,
91 ensureUserHasRight(UserRight.MANAGE_USERS), 91 ensureUserHasRight(UserRight.MANAGE_USERS),
92 usersRemoveValidator, 92 asyncMiddleware(usersRemoveValidator),
93 asyncMiddleware(removeUser) 93 asyncMiddleware(removeUser)
94) 94)
95 95
diff --git a/server/controllers/api/videos/abuse.ts b/server/controllers/api/videos/abuse.ts
index 88b3d538d..29e1175c5 100644
--- a/server/controllers/api/videos/abuse.ts
+++ b/server/controllers/api/videos/abuse.ts
@@ -33,7 +33,7 @@ abuseVideoRouter.get('/abuse',
33) 33)
34abuseVideoRouter.post('/:id/abuse', 34abuseVideoRouter.post('/:id/abuse',
35 authenticate, 35 authenticate,
36 videoAbuseReportValidator, 36 asyncMiddleware(videoAbuseReportValidator),
37 asyncMiddleware(reportVideoAbuseRetryWrapper) 37 asyncMiddleware(reportVideoAbuseRetryWrapper)
38) 38)
39 39
diff --git a/server/controllers/api/videos/blacklist.ts b/server/controllers/api/videos/blacklist.ts
index be7cf6ea4..06333c271 100644
--- a/server/controllers/api/videos/blacklist.ts
+++ b/server/controllers/api/videos/blacklist.ts
@@ -21,7 +21,7 @@ const blacklistRouter = express.Router()
21blacklistRouter.post('/:videoId/blacklist', 21blacklistRouter.post('/:videoId/blacklist',
22 authenticate, 22 authenticate,
23 ensureUserHasRight(UserRight.MANAGE_VIDEO_BLACKLIST), 23 ensureUserHasRight(UserRight.MANAGE_VIDEO_BLACKLIST),
24 videosBlacklistAddValidator, 24 asyncMiddleware(videosBlacklistAddValidator),
25 asyncMiddleware(addVideoToBlacklist) 25 asyncMiddleware(addVideoToBlacklist)
26) 26)
27 27
@@ -38,7 +38,7 @@ blacklistRouter.get('/blacklist',
38blacklistRouter.delete('/:videoId/blacklist', 38blacklistRouter.delete('/:videoId/blacklist',
39 authenticate, 39 authenticate,
40 ensureUserHasRight(UserRight.MANAGE_VIDEO_BLACKLIST), 40 ensureUserHasRight(UserRight.MANAGE_VIDEO_BLACKLIST),
41 videosBlacklistRemoveValidator, 41 asyncMiddleware(videosBlacklistRemoveValidator),
42 asyncMiddleware(removeVideoFromBlacklistController) 42 asyncMiddleware(removeVideoFromBlacklistController)
43) 43)
44 44
diff --git a/server/controllers/api/videos/channel.ts b/server/controllers/api/videos/channel.ts
index ce2656e71..d99f47c32 100644
--- a/server/controllers/api/videos/channel.ts
+++ b/server/controllers/api/videos/channel.ts
@@ -3,6 +3,7 @@ import { VideoChannelCreate, VideoChannelUpdate } from '../../../../shared'
3import { getFormattedObjects, logger, resetSequelizeInstance, retryTransactionWrapper } from '../../../helpers' 3import { getFormattedObjects, logger, resetSequelizeInstance, retryTransactionWrapper } from '../../../helpers'
4import { database as db } from '../../../initializers' 4import { database as db } from '../../../initializers'
5import { createVideoChannel } from '../../../lib' 5import { createVideoChannel } from '../../../lib'
6import { sendUpdateVideoChannel } from '../../../lib/activitypub/send/send-update'
6import { 7import {
7 asyncMiddleware, 8 asyncMiddleware,
8 authenticate, 9 authenticate,
@@ -10,14 +11,13 @@ import {
10 paginationValidator, 11 paginationValidator,
11 setPagination, 12 setPagination,
12 setVideoChannelsSort, 13 setVideoChannelsSort,
13 videoChannelsGetValidator,
14 videoChannelsAddValidator, 14 videoChannelsAddValidator,
15 videoChannelsGetValidator,
15 videoChannelsRemoveValidator, 16 videoChannelsRemoveValidator,
16 videoChannelsSortValidator, 17 videoChannelsSortValidator,
17 videoChannelsUpdateValidator 18 videoChannelsUpdateValidator
18} from '../../../middlewares' 19} from '../../../middlewares'
19import { AccountInstance, VideoChannelInstance } from '../../../models' 20import { AccountInstance, VideoChannelInstance } from '../../../models'
20import { sendUpdateVideoChannel } from '../../../lib/activitypub/send/send-update'
21 21
22const videoChannelRouter = express.Router() 22const videoChannelRouter = express.Router()
23 23
@@ -30,7 +30,7 @@ videoChannelRouter.get('/channels',
30) 30)
31 31
32videoChannelRouter.get('/accounts/:accountId/channels', 32videoChannelRouter.get('/accounts/:accountId/channels',
33 listVideoAccountChannelsValidator, 33 asyncMiddleware(listVideoAccountChannelsValidator),
34 asyncMiddleware(listVideoAccountChannels) 34 asyncMiddleware(listVideoAccountChannels)
35) 35)
36 36
@@ -42,18 +42,18 @@ videoChannelRouter.post('/channels',
42 42
43videoChannelRouter.put('/channels/:id', 43videoChannelRouter.put('/channels/:id',
44 authenticate, 44 authenticate,
45 videoChannelsUpdateValidator, 45 asyncMiddleware(videoChannelsUpdateValidator),
46 updateVideoChannelRetryWrapper 46 updateVideoChannelRetryWrapper
47) 47)
48 48
49videoChannelRouter.delete('/channels/:id', 49videoChannelRouter.delete('/channels/:id',
50 authenticate, 50 authenticate,
51 videoChannelsRemoveValidator, 51 asyncMiddleware(videoChannelsRemoveValidator),
52 asyncMiddleware(removeVideoChannelRetryWrapper) 52 asyncMiddleware(removeVideoChannelRetryWrapper)
53) 53)
54 54
55videoChannelRouter.get('/channels/:id', 55videoChannelRouter.get('/channels/:id',
56 videoChannelsGetValidator, 56 asyncMiddleware(videoChannelsGetValidator),
57 asyncMiddleware(getVideoChannel) 57 asyncMiddleware(getVideoChannel)
58) 58)
59 59
diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts
index 2b5afd632..244d91914 100644
--- a/server/controllers/api/videos/index.ts
+++ b/server/controllers/api/videos/index.ts
@@ -86,7 +86,7 @@ videosRouter.get('/',
86) 86)
87videosRouter.put('/:id', 87videosRouter.put('/:id',
88 authenticate, 88 authenticate,
89 videosUpdateValidator, 89 asyncMiddleware(videosUpdateValidator),
90 asyncMiddleware(updateVideoRetryWrapper) 90 asyncMiddleware(updateVideoRetryWrapper)
91) 91)
92videosRouter.post('/upload', 92videosRouter.post('/upload',
@@ -97,17 +97,17 @@ videosRouter.post('/upload',
97) 97)
98 98
99videosRouter.get('/:id/description', 99videosRouter.get('/:id/description',
100 videosGetValidator, 100 asyncMiddleware(videosGetValidator),
101 asyncMiddleware(getVideoDescription) 101 asyncMiddleware(getVideoDescription)
102) 102)
103videosRouter.get('/:id', 103videosRouter.get('/:id',
104 videosGetValidator, 104 asyncMiddleware(videosGetValidator),
105 getVideo 105 getVideo
106) 106)
107 107
108videosRouter.delete('/:id', 108videosRouter.delete('/:id',
109 authenticate, 109 authenticate,
110 videosRemoveValidator, 110 asyncMiddleware(videosRemoveValidator),
111 asyncMiddleware(removeVideoRetryWrapper) 111 asyncMiddleware(removeVideoRetryWrapper)
112) 112)
113 113
diff --git a/server/controllers/api/videos/rate.ts b/server/controllers/api/videos/rate.ts
index 134284df7..0c6a988cf 100644
--- a/server/controllers/api/videos/rate.ts
+++ b/server/controllers/api/videos/rate.ts
@@ -12,7 +12,7 @@ const rateVideoRouter = express.Router()
12 12
13rateVideoRouter.put('/:id/rate', 13rateVideoRouter.put('/:id/rate',
14 authenticate, 14 authenticate,
15 videoRateValidator, 15 asyncMiddleware(videoRateValidator),
16 asyncMiddleware(rateVideoRetryWrapper) 16 asyncMiddleware(rateVideoRetryWrapper)
17) 17)
18 18
diff --git a/server/controllers/services.ts b/server/controllers/services.ts
index 0db6e5498..0c325678c 100644
--- a/server/controllers/services.ts
+++ b/server/controllers/services.ts
@@ -1,12 +1,16 @@
1import * as express from 'express' 1import * as express from 'express'
2 2
3import { CONFIG, PREVIEWS_SIZE, EMBED_SIZE } from '../initializers' 3import { CONFIG, EMBED_SIZE, PREVIEWS_SIZE } from '../initializers'
4import { oembedValidator } from '../middlewares' 4import { oembedValidator } from '../middlewares'
5import { asyncMiddleware } from '../middlewares/async'
5import { VideoInstance } from '../models' 6import { VideoInstance } from '../models'
6 7
7const servicesRouter = express.Router() 8const servicesRouter = express.Router()
8 9
9servicesRouter.use('/oembed', oembedValidator, generateOEmbed) 10servicesRouter.use('/oembed',
11 asyncMiddleware(oembedValidator),
12 generateOEmbed
13)
10 14
11// --------------------------------------------------------------------------- 15// ---------------------------------------------------------------------------
12 16
diff --git a/server/controllers/webfinger.ts b/server/controllers/webfinger.ts
index cc28a8909..1cea513a1 100644
--- a/server/controllers/webfinger.ts
+++ b/server/controllers/webfinger.ts
@@ -1,11 +1,12 @@
1import * as express from 'express' 1import * as express from 'express'
2import { asyncMiddleware } from '../middlewares/async'
2import { webfingerValidator } from '../middlewares/validators/webfinger' 3import { webfingerValidator } from '../middlewares/validators/webfinger'
3import { AccountInstance } from '../models/account/account-interface' 4import { AccountInstance } from '../models/account/account-interface'
4 5
5const webfingerRouter = express.Router() 6const webfingerRouter = express.Router()
6 7
7webfingerRouter.get('/.well-known/webfinger', 8webfingerRouter.get('/.well-known/webfinger',
8 webfingerValidator, 9 asyncMiddleware(webfingerValidator),
9 webfingerController 10 webfingerController
10) 11)
11 12
diff --git a/server/helpers/custom-validators/accounts.ts b/server/helpers/custom-validators/accounts.ts
index a6d7f2b82..e3c477414 100644
--- a/server/helpers/custom-validators/accounts.ts
+++ b/server/helpers/custom-validators/accounts.ts
@@ -1,17 +1,16 @@
1import * as Bluebird from 'bluebird' 1import * as Bluebird from 'bluebird'
2import * as express from 'express' 2import { Response } from 'express'
3import 'express-validator' 3import 'express-validator'
4import * as validator from 'validator' 4import * as validator from 'validator'
5import { database as db } from '../../initializers' 5import { database as db } from '../../initializers'
6import { AccountInstance } from '../../models' 6import { AccountInstance } from '../../models'
7import { logger } from '../logger'
8import { isUserUsernameValid } from './users' 7import { isUserUsernameValid } from './users'
9 8
10function isAccountNameValid (value: string) { 9function isAccountNameValid (value: string) {
11 return isUserUsernameValid(value) 10 return isUserUsernameValid(value)
12} 11}
13 12
14function checkAccountIdExists (id: number | string, res: express.Response, callback: (err: Error, account: AccountInstance) => any) { 13function isAccountIdExist (id: number | string, res: Response) {
15 let promise: Bluebird<AccountInstance> 14 let promise: Bluebird<AccountInstance>
16 15
17 if (validator.isInt('' + id)) { 16 if (validator.isInt('' + id)) {
@@ -20,36 +19,35 @@ function checkAccountIdExists (id: number | string, res: express.Response, callb
20 promise = db.Account.loadByUUID('' + id) 19 promise = db.Account.loadByUUID('' + id)
21 } 20 }
22 21
23 return checkAccountExists(promise, res, callback) 22 return isAccountExist(promise, res)
24} 23}
25 24
26function checkLocalAccountNameExists (name: string, res: express.Response, callback: (err: Error, account: AccountInstance) => any) { 25function isLocalAccountNameExist (name: string, res: Response) {
27 const p = db.Account.loadLocalByName(name) 26 const promise = db.Account.loadLocalByName(name)
28 27
29 return checkAccountExists(p, res, callback) 28 return isAccountExist(promise, res)
30} 29}
31 30
32function checkAccountExists (p: Bluebird<AccountInstance>, res: express.Response, callback: (err: Error, account: AccountInstance) => any) { 31async function isAccountExist (p: Bluebird<AccountInstance>, res: Response) {
33 p.then(account => { 32 const account = await p
34 if (!account) { 33
35 return res.status(404) 34 if (!account) {
36 .send({ error: 'Account not found' }) 35 res.status(404)
37 .end() 36 .send({ error: 'Account not found' })
38 } 37 .end()
39 38
40 res.locals.account = account 39 return false
41 return callback(null, account) 40 }
42 }) 41
43 .catch(err => { 42 res.locals.account = account
44 logger.error('Error in account request validator.', err) 43
45 return res.sendStatus(500) 44 return true
46 })
47} 45}
48 46
49// --------------------------------------------------------------------------- 47// ---------------------------------------------------------------------------
50 48
51export { 49export {
52 checkAccountIdExists, 50 isAccountIdExist,
53 checkLocalAccountNameExists, 51 isLocalAccountNameExist,
54 isAccountNameValid 52 isAccountNameValid
55} 53}
diff --git a/server/helpers/custom-validators/activitypub/account.ts b/server/helpers/custom-validators/activitypub/account.ts
index 645f55a5a..cab39a654 100644
--- a/server/helpers/custom-validators/activitypub/account.ts
+++ b/server/helpers/custom-validators/activitypub/account.ts
@@ -5,31 +5,19 @@ import { exists, isUUIDValid } from '../misc'
5import { isActivityPubUrlValid, isBaseActivityValid } from './misc' 5import { isActivityPubUrlValid, isBaseActivityValid } from './misc'
6 6
7function isAccountEndpointsObjectValid (endpointObject: any) { 7function isAccountEndpointsObjectValid (endpointObject: any) {
8 return isAccountSharedInboxValid(endpointObject.sharedInbox) 8 return isActivityPubUrlValid(endpointObject.sharedInbox)
9}
10
11function isAccountSharedInboxValid (sharedInbox: string) {
12 return isActivityPubUrlValid(sharedInbox)
13} 9}
14 10
15function isAccountPublicKeyObjectValid (publicKeyObject: any) { 11function isAccountPublicKeyObjectValid (publicKeyObject: any) {
16 return isAccountPublicKeyIdValid(publicKeyObject.id) && 12 return isActivityPubUrlValid(publicKeyObject.id) &&
17 isAccountPublicKeyOwnerValid(publicKeyObject.owner) && 13 isActivityPubUrlValid(publicKeyObject.owner) &&
18 isAccountPublicKeyValid(publicKeyObject.publicKeyPem) 14 isAccountPublicKeyValid(publicKeyObject.publicKeyPem)
19} 15}
20 16
21function isAccountPublicKeyIdValid (id: string) {
22 return isActivityPubUrlValid(id)
23}
24
25function isAccountTypeValid (type: string) { 17function isAccountTypeValid (type: string) {
26 return type === 'Person' || type === 'Application' 18 return type === 'Person' || type === 'Application'
27} 19}
28 20
29function isAccountPublicKeyOwnerValid (owner: string) {
30 return isActivityPubUrlValid(owner)
31}
32
33function isAccountPublicKeyValid (publicKey: string) { 21function isAccountPublicKeyValid (publicKey: string) {
34 return exists(publicKey) && 22 return exists(publicKey) &&
35 typeof publicKey === 'string' && 23 typeof publicKey === 'string' &&
@@ -38,34 +26,10 @@ function isAccountPublicKeyValid (publicKey: string) {
38 validator.isLength(publicKey, CONSTRAINTS_FIELDS.ACCOUNTS.PUBLIC_KEY) 26 validator.isLength(publicKey, CONSTRAINTS_FIELDS.ACCOUNTS.PUBLIC_KEY)
39} 27}
40 28
41function isAccountIdValid (id: string) {
42 return isActivityPubUrlValid(id)
43}
44
45function isAccountFollowingValid (id: string) {
46 return isActivityPubUrlValid(id)
47}
48
49function isAccountFollowersValid (id: string) {
50 return isActivityPubUrlValid(id)
51}
52
53function isAccountInboxValid (inbox: string) {
54 return isActivityPubUrlValid(inbox)
55}
56
57function isAccountOutboxValid (outbox: string) {
58 return isActivityPubUrlValid(outbox)
59}
60
61function isAccountPreferredUsernameValid (preferredUsername: string) { 29function isAccountPreferredUsernameValid (preferredUsername: string) {
62 return isAccountNameValid(preferredUsername) 30 return isAccountNameValid(preferredUsername)
63} 31}
64 32
65function isAccountUrlValid (url: string) {
66 return isActivityPubUrlValid(url)
67}
68
69function isAccountPrivateKeyValid (privateKey: string) { 33function isAccountPrivateKeyValid (privateKey: string) {
70 return exists(privateKey) && 34 return exists(privateKey) &&
71 typeof privateKey === 'string' && 35 typeof privateKey === 'string' &&
@@ -75,15 +39,15 @@ function isAccountPrivateKeyValid (privateKey: string) {
75} 39}
76 40
77function isRemoteAccountValid (remoteAccount: any) { 41function isRemoteAccountValid (remoteAccount: any) {
78 return isAccountIdValid(remoteAccount.id) && 42 return isActivityPubUrlValid(remoteAccount.id) &&
79 isUUIDValid(remoteAccount.uuid) && 43 isUUIDValid(remoteAccount.uuid) &&
80 isAccountTypeValid(remoteAccount.type) && 44 isAccountTypeValid(remoteAccount.type) &&
81 isAccountFollowingValid(remoteAccount.following) && 45 isActivityPubUrlValid(remoteAccount.following) &&
82 isAccountFollowersValid(remoteAccount.followers) && 46 isActivityPubUrlValid(remoteAccount.followers) &&
83 isAccountInboxValid(remoteAccount.inbox) && 47 isActivityPubUrlValid(remoteAccount.inbox) &&
84 isAccountOutboxValid(remoteAccount.outbox) && 48 isActivityPubUrlValid(remoteAccount.outbox) &&
85 isAccountPreferredUsernameValid(remoteAccount.preferredUsername) && 49 isAccountPreferredUsernameValid(remoteAccount.preferredUsername) &&
86 isAccountUrlValid(remoteAccount.url) && 50 isActivityPubUrlValid(remoteAccount.url) &&
87 isAccountPublicKeyObjectValid(remoteAccount.publicKey) && 51 isAccountPublicKeyObjectValid(remoteAccount.publicKey) &&
88 isAccountEndpointsObjectValid(remoteAccount.endpoints) 52 isAccountEndpointsObjectValid(remoteAccount.endpoints)
89} 53}
@@ -113,19 +77,10 @@ function isAccountAcceptActivityValid (activity: any) {
113 77
114export { 78export {
115 isAccountEndpointsObjectValid, 79 isAccountEndpointsObjectValid,
116 isAccountSharedInboxValid,
117 isAccountPublicKeyObjectValid, 80 isAccountPublicKeyObjectValid,
118 isAccountPublicKeyIdValid,
119 isAccountTypeValid, 81 isAccountTypeValid,
120 isAccountPublicKeyOwnerValid,
121 isAccountPublicKeyValid, 82 isAccountPublicKeyValid,
122 isAccountIdValid,
123 isAccountFollowingValid,
124 isAccountFollowersValid,
125 isAccountInboxValid,
126 isAccountOutboxValid,
127 isAccountPreferredUsernameValid, 83 isAccountPreferredUsernameValid,
128 isAccountUrlValid,
129 isAccountPrivateKeyValid, 84 isAccountPrivateKeyValid,
130 isRemoteAccountValid, 85 isRemoteAccountValid,
131 isAccountFollowingCountValid, 86 isAccountFollowingCountValid,
diff --git a/server/helpers/custom-validators/activitypub/videos.ts b/server/helpers/custom-validators/activitypub/videos.ts
index 55e79c4e8..12c672fd2 100644
--- a/server/helpers/custom-validators/activitypub/videos.ts
+++ b/server/helpers/custom-validators/activitypub/videos.ts
@@ -8,7 +8,6 @@ import {
8 isVideoNSFWValid, 8 isVideoNSFWValid,
9 isVideoTagValid, 9 isVideoTagValid,
10 isVideoTruncatedDescriptionValid, 10 isVideoTruncatedDescriptionValid,
11 isVideoUrlValid,
12 isVideoViewsValid 11 isVideoViewsValid
13} from '../videos' 12} from '../videos'
14import { isActivityPubUrlValid, isBaseActivityValid } from './misc' 13import { isActivityPubUrlValid, isBaseActivityValid } from './misc'
@@ -77,12 +76,11 @@ export {
77function setValidRemoteTags (video: any) { 76function setValidRemoteTags (video: any) {
78 if (Array.isArray(video.tag) === false) return false 77 if (Array.isArray(video.tag) === false) return false
79 78
80 const newTag = video.tag.filter(t => { 79 video.tag = video.tag.filter(t => {
81 return t.type === 'Hashtag' && 80 return t.type === 'Hashtag' &&
82 isVideoTagValid(t.name) 81 isVideoTagValid(t.name)
83 }) 82 })
84 83
85 video.tag = newTag
86 return true 84 return true
87} 85}
88 86
@@ -96,7 +94,7 @@ function isRemoteVideoContentValid (mediaType: string, content: string) {
96 94
97function isRemoteVideoIconValid (icon: any) { 95function isRemoteVideoIconValid (icon: any) {
98 return icon.type === 'Image' && 96 return icon.type === 'Image' &&
99 isVideoUrlValid(icon.url) && 97 isActivityPubUrlValid(icon.url) &&
100 icon.mediaType === 'image/jpeg' && 98 icon.mediaType === 'image/jpeg' &&
101 validator.isInt(icon.width + '', { min: 0 }) && 99 validator.isInt(icon.width + '', { min: 0 }) &&
102 validator.isInt(icon.height + '', { min: 0 }) 100 validator.isInt(icon.height + '', { min: 0 })
@@ -105,8 +103,7 @@ function isRemoteVideoIconValid (icon: any) {
105function setValidRemoteVideoUrls (video: any) { 103function setValidRemoteVideoUrls (video: any) {
106 if (Array.isArray(video.url) === false) return false 104 if (Array.isArray(video.url) === false) return false
107 105
108 const newUrl = video.url.filter(u => isRemoteVideoUrlValid(u)) 106 video.url = video.url.filter(u => isRemoteVideoUrlValid(u))
109 video.url = newUrl
110 107
111 return true 108 return true
112} 109}
@@ -115,13 +112,13 @@ function isRemoteVideoUrlValid (url: any) {
115 return url.type === 'Link' && 112 return url.type === 'Link' &&
116 ( 113 (
117 ACTIVITY_PUB.URL_MIME_TYPES.VIDEO.indexOf(url.mimeType) !== -1 && 114 ACTIVITY_PUB.URL_MIME_TYPES.VIDEO.indexOf(url.mimeType) !== -1 &&
118 isVideoUrlValid(url.url) && 115 isActivityPubUrlValid(url.url) &&
119 validator.isInt(url.width + '', { min: 0 }) && 116 validator.isInt(url.width + '', { min: 0 }) &&
120 validator.isInt(url.size + '', { min: 0 }) 117 validator.isInt(url.size + '', { min: 0 })
121 ) || 118 ) ||
122 ( 119 (
123 ACTIVITY_PUB.URL_MIME_TYPES.TORRENT.indexOf(url.mimeType) !== -1 && 120 ACTIVITY_PUB.URL_MIME_TYPES.TORRENT.indexOf(url.mimeType) !== -1 &&
124 isVideoUrlValid(url.url) && 121 isActivityPubUrlValid(url.url) &&
125 validator.isInt(url.width + '', { min: 0 }) 122 validator.isInt(url.width + '', { min: 0 })
126 ) || 123 ) ||
127 ( 124 (
diff --git a/server/helpers/custom-validators/video-channels.ts b/server/helpers/custom-validators/video-channels.ts
index 267d987fc..3de9f041b 100644
--- a/server/helpers/custom-validators/video-channels.ts
+++ b/server/helpers/custom-validators/video-channels.ts
@@ -1,21 +1,13 @@
1import * as Bluebird from 'bluebird'
2import * as express from 'express' 1import * as express from 'express'
3import 'express-validator' 2import 'express-validator'
4import 'multer' 3import 'multer'
5import * as validator from 'validator' 4import * as validator from 'validator'
6
7import { CONSTRAINTS_FIELDS, database as db } from '../../initializers' 5import { CONSTRAINTS_FIELDS, database as db } from '../../initializers'
8import { VideoChannelInstance } from '../../models' 6import { VideoChannelInstance } from '../../models'
9import { logger } from '../logger'
10import { isActivityPubUrlValid } from './index'
11import { exists } from './misc' 7import { exists } from './misc'
12 8
13const VIDEO_CHANNELS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_CHANNELS 9const VIDEO_CHANNELS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_CHANNELS
14 10
15function isVideoChannelUrlValid (value: string) {
16 return isActivityPubUrlValid(value)
17}
18
19function isVideoChannelDescriptionValid (value: string) { 11function isVideoChannelDescriptionValid (value: string) {
20 return value === null || validator.isLength(value, VIDEO_CHANNELS_CONSTRAINTS_FIELDS.DESCRIPTION) 12 return value === null || validator.isLength(value, VIDEO_CHANNELS_CONSTRAINTS_FIELDS.DESCRIPTION)
21} 13}
@@ -24,31 +16,7 @@ function isVideoChannelNameValid (value: string) {
24 return exists(value) && validator.isLength(value, VIDEO_CHANNELS_CONSTRAINTS_FIELDS.NAME) 16 return exists(value) && validator.isLength(value, VIDEO_CHANNELS_CONSTRAINTS_FIELDS.NAME)
25} 17}
26 18
27function checkVideoChannelExists (id: string, res: express.Response, callback: () => void) { 19async function isVideoChannelExist (id: string, res: express.Response) {
28 let promise: Bluebird<VideoChannelInstance>
29 if (validator.isInt(id)) {
30 promise = db.VideoChannel.loadAndPopulateAccount(+id)
31 } else { // UUID
32 promise = db.VideoChannel.loadByUUIDAndPopulateAccount(id)
33 }
34
35 promise.then(videoChannel => {
36 if (!videoChannel) {
37 return res.status(404)
38 .json({ error: 'Video channel not found' })
39 .end()
40 }
41
42 res.locals.videoChannel = videoChannel
43 callback()
44 })
45 .catch(err => {
46 logger.error('Error in video channel request validator.', err)
47 return res.sendStatus(500)
48 })
49}
50
51async function isVideoChannelExistsPromise (id: string, res: express.Response) {
52 let videoChannel: VideoChannelInstance 20 let videoChannel: VideoChannelInstance
53 if (validator.isInt(id)) { 21 if (validator.isInt(id)) {
54 videoChannel = await db.VideoChannel.loadAndPopulateAccount(+id) 22 videoChannel = await db.VideoChannel.loadAndPopulateAccount(+id)
@@ -72,8 +40,6 @@ async function isVideoChannelExistsPromise (id: string, res: express.Response) {
72 40
73export { 41export {
74 isVideoChannelDescriptionValid, 42 isVideoChannelDescriptionValid,
75 checkVideoChannelExists,
76 isVideoChannelNameValid, 43 isVideoChannelNameValid,
77 isVideoChannelExistsPromise, 44 isVideoChannelExist
78 isVideoChannelUrlValid
79} 45}
diff --git a/server/helpers/custom-validators/videos.ts b/server/helpers/custom-validators/videos.ts
index 276354626..f13178c54 100644
--- a/server/helpers/custom-validators/videos.ts
+++ b/server/helpers/custom-validators/videos.ts
@@ -1,4 +1,3 @@
1import * as Bluebird from 'bluebird'
2import { Response } from 'express' 1import { Response } from 'express'
3import 'express-validator' 2import 'express-validator'
4import { values } from 'lodash' 3import { values } from 'lodash'
@@ -6,12 +5,10 @@ import 'multer'
6import * as validator from 'validator' 5import * as validator from 'validator'
7import { VideoRateType } from '../../../shared' 6import { VideoRateType } from '../../../shared'
8import { CONSTRAINTS_FIELDS, VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_RATE_TYPES } from '../../initializers' 7import { CONSTRAINTS_FIELDS, VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_RATE_TYPES } from '../../initializers'
8import { VIDEO_PRIVACIES } from '../../initializers/constants'
9import { database as db } from '../../initializers/database' 9import { database as db } from '../../initializers/database'
10import { VideoInstance } from '../../models/video/video-interface' 10import { VideoInstance } from '../../models/video/video-interface'
11import { logger } from '../logger'
12import { isActivityPubUrlValid } from './activitypub/misc'
13import { exists, isArray } from './misc' 11import { exists, isArray } from './misc'
14import { VIDEO_PRIVACIES } from '../../initializers/constants'
15 12
16const VIDEOS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEOS 13const VIDEOS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEOS
17const VIDEO_ABUSES_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_ABUSES 14const VIDEO_ABUSES_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_ABUSES
@@ -20,10 +17,6 @@ function isVideoCategoryValid (value: number) {
20 return VIDEO_CATEGORIES[value] !== undefined 17 return VIDEO_CATEGORIES[value] !== undefined
21} 18}
22 19
23function isVideoUrlValid (value: string) {
24 return isActivityPubUrlValid(value)
25}
26
27function isVideoLicenceValid (value: number) { 20function isVideoLicenceValid (value: number) {
28 return VIDEO_LICENCES[value] !== undefined 21 return VIDEO_LICENCES[value] !== undefined
29} 22}
@@ -106,31 +99,7 @@ function isVideoFileSizeValid (value: string) {
106 return exists(value) && validator.isInt(value + '', VIDEOS_CONSTRAINTS_FIELDS.FILE_SIZE) 99 return exists(value) && validator.isInt(value + '', VIDEOS_CONSTRAINTS_FIELDS.FILE_SIZE)
107} 100}
108 101
109function checkVideoExists (id: string, res: Response, callback: () => void) { 102async function isVideoExist (id: string, res: Response) {
110 let promise: Bluebird<VideoInstance>
111 if (validator.isInt(id)) {
112 promise = db.Video.loadAndPopulateAccountAndServerAndTags(+id)
113 } else { // UUID
114 promise = db.Video.loadByUUIDAndPopulateAccountAndServerAndTags(id)
115 }
116
117 promise.then(video => {
118 if (!video) {
119 return res.status(404)
120 .json({ error: 'Video not found' })
121 .end()
122 }
123
124 res.locals.video = video
125 callback()
126 })
127 .catch(err => {
128 logger.error('Error in video request validator.', err)
129 return res.sendStatus(500)
130 })
131}
132
133async function isVideoExistsPromise (id: string, res: Response) {
134 let video: VideoInstance 103 let video: VideoInstance
135 104
136 if (validator.isInt(id)) { 105 if (validator.isInt(id)) {
@@ -169,10 +138,8 @@ export {
169 isVideoRatingTypeValid, 138 isVideoRatingTypeValid,
170 isVideoDurationValid, 139 isVideoDurationValid,
171 isVideoTagValid, 140 isVideoTagValid,
172 isVideoUrlValid,
173 isVideoPrivacyValid, 141 isVideoPrivacyValid,
174 isVideoFileResolutionValid, 142 isVideoFileResolutionValid,
175 isVideoFileSizeValid, 143 isVideoFileSizeValid,
176 checkVideoExists, 144 isVideoExist
177 isVideoExistsPromise
178} 145}
diff --git a/server/middlewares/async.ts b/server/middlewares/async.ts
index 9692f9be7..534891899 100644
--- a/server/middlewares/async.ts
+++ b/server/middlewares/async.ts
@@ -1,9 +1,12 @@
1import { Request, Response, NextFunction, RequestHandler } from 'express'
2import { eachSeries } from 'async' 1import { eachSeries } from 'async'
2import { NextFunction, Request, RequestHandler, Response } from 'express'
3 3
4// Syntactic sugar to avoid try/catch in express controllers 4// Syntactic sugar to avoid try/catch in express controllers
5// Thanks: https://medium.com/@Abazhenov/using-async-await-in-express-with-node-8-b8af872c0016 5// Thanks: https://medium.com/@Abazhenov/using-async-await-in-express-with-node-8-b8af872c0016
6function asyncMiddleware (fun: RequestHandler | RequestHandler[]) { 6
7export type RequestPromiseHandler = (req: Request, res: Response, next: NextFunction) => Promise<any>
8
9function asyncMiddleware (fun: RequestPromiseHandler | RequestPromiseHandler[]) {
7 return (req: Request, res: Response, next: NextFunction) => { 10 return (req: Request, res: Response, next: NextFunction) => {
8 if (Array.isArray(fun) === true) { 11 if (Array.isArray(fun) === true) {
9 return eachSeries(fun as RequestHandler[], (f, cb) => { 12 return eachSeries(fun as RequestHandler[], (f, cb) => {
diff --git a/server/middlewares/validators/account.ts b/server/middlewares/validators/account.ts
index 47ed6a7bb..70f4e4d3b 100644
--- a/server/middlewares/validators/account.ts
+++ b/server/middlewares/validators/account.ts
@@ -1,18 +1,19 @@
1import * as express from 'express' 1import * as express from 'express'
2import { param } from 'express-validator/check' 2import { param } from 'express-validator/check'
3import { logger } from '../../helpers' 3import { logger, isLocalAccountNameExist } from '../../helpers'
4import { checkLocalAccountNameExists, isAccountNameValid } from '../../helpers/custom-validators/accounts' 4import { isAccountNameValid } from '../../helpers/custom-validators/accounts'
5import { checkErrors } from './utils' 5import { areValidationErrors } from './utils'
6 6
7const localAccountValidator = [ 7const localAccountValidator = [
8 param('name').custom(isAccountNameValid).withMessage('Should have a valid account name'), 8 param('name').custom(isAccountNameValid).withMessage('Should have a valid account name'),
9 9
10 (req: express.Request, res: express.Response, next: express.NextFunction) => { 10 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
11 logger.debug('Checking localAccountValidator parameters', { parameters: req.params }) 11 logger.debug('Checking localAccountValidator parameters', { parameters: req.params })
12 12
13 checkErrors(req, res, () => { 13 if (areValidationErrors(req, res)) return
14 checkLocalAccountNameExists(req.params.name, res, next) 14 if (!await isLocalAccountNameExist(req.params.name, res)) return
15 }) 15
16 return next()
16 } 17 }
17] 18]
18 19
diff --git a/server/middlewares/validators/activitypub/activity.ts b/server/middlewares/validators/activitypub/activity.ts
index 0de8b2d85..8aa82298c 100644
--- a/server/middlewares/validators/activitypub/activity.ts
+++ b/server/middlewares/validators/activitypub/activity.ts
@@ -1,7 +1,7 @@
1import * as express from 'express' 1import * as express from 'express'
2import { body } from 'express-validator/check' 2import { body } from 'express-validator/check'
3import { isRootActivityValid, logger } from '../../../helpers' 3import { isRootActivityValid, logger } from '../../../helpers'
4import { checkErrors } from '../utils' 4import { areValidationErrors } from '../utils'
5 5
6const activityPubValidator = [ 6const activityPubValidator = [
7 body('').custom((value, { req }) => isRootActivityValid(req.body)), 7 body('').custom((value, { req }) => isRootActivityValid(req.body)),
@@ -9,7 +9,9 @@ const activityPubValidator = [
9 (req: express.Request, res: express.Response, next: express.NextFunction) => { 9 (req: express.Request, res: express.Response, next: express.NextFunction) => {
10 logger.debug('Checking activity pub parameters', { parameters: req.body }) 10 logger.debug('Checking activity pub parameters', { parameters: req.body })
11 11
12 checkErrors(req, res, next) 12 if (areValidationErrors(req, res)) return
13
14 return next()
13 } 15 }
14] 16]
15 17
diff --git a/server/middlewares/validators/activitypub/signature.ts b/server/middlewares/validators/activitypub/signature.ts
index 0ce15c1f6..360685512 100644
--- a/server/middlewares/validators/activitypub/signature.ts
+++ b/server/middlewares/validators/activitypub/signature.ts
@@ -1,14 +1,7 @@
1import { body } from 'express-validator/check'
2import * as express from 'express' 1import * as express from 'express'
3 2import { body } from 'express-validator/check'
4import { 3import { isDateValid, isSignatureCreatorValid, isSignatureTypeValid, isSignatureValueValid, logger } from '../../../helpers'
5 logger, 4import { areValidationErrors } from '../utils'
6 isDateValid,
7 isSignatureTypeValid,
8 isSignatureCreatorValid,
9 isSignatureValueValid
10} from '../../../helpers'
11import { checkErrors } from '../utils'
12 5
13const signatureValidator = [ 6const signatureValidator = [
14 body('signature.type').custom(isSignatureTypeValid).withMessage('Should have a valid signature type'), 7 body('signature.type').custom(isSignatureTypeValid).withMessage('Should have a valid signature type'),
@@ -19,7 +12,9 @@ const signatureValidator = [
19 (req: express.Request, res: express.Response, next: express.NextFunction) => { 12 (req: express.Request, res: express.Response, next: express.NextFunction) => {
20 logger.debug('Checking activitypub signature parameter', { parameters: { signature: req.body.signature } }) 13 logger.debug('Checking activitypub signature parameter', { parameters: { signature: req.body.signature } })
21 14
22 checkErrors(req, res, next) 15 if (areValidationErrors(req, res)) return
16
17 return next()
23 } 18 }
24] 19]
25 20
diff --git a/server/middlewares/validators/follows.ts b/server/middlewares/validators/follows.ts
index ddc4c1de1..605872ecf 100644
--- a/server/middlewares/validators/follows.ts
+++ b/server/middlewares/validators/follows.ts
@@ -4,7 +4,7 @@ import { isTestInstance } from '../../helpers/core-utils'
4import { isEachUniqueHostValid } from '../../helpers/custom-validators/servers' 4import { isEachUniqueHostValid } from '../../helpers/custom-validators/servers'
5import { logger } from '../../helpers/logger' 5import { logger } from '../../helpers/logger'
6import { CONFIG, database as db } from '../../initializers' 6import { CONFIG, database as db } from '../../initializers'
7import { checkErrors } from './utils' 7import { areValidationErrors } from './utils'
8import { getServerAccount } from '../../helpers/utils' 8import { getServerAccount } from '../../helpers/utils'
9import { isIdOrUUIDValid } from '../../helpers/custom-validators/misc' 9import { isIdOrUUIDValid } from '../../helpers/custom-validators/misc'
10 10
@@ -23,34 +23,30 @@ const followValidator = [
23 23
24 logger.debug('Checking follow parameters', { parameters: req.body }) 24 logger.debug('Checking follow parameters', { parameters: req.body })
25 25
26 checkErrors(req, res, next) 26 if (areValidationErrors(req, res)) return
27
28 return next()
27 } 29 }
28] 30]
29 31
30const removeFollowingValidator = [ 32const removeFollowingValidator = [
31 param('accountId').custom(isIdOrUUIDValid).withMessage('Should have a valid account id'), 33 param('accountId').custom(isIdOrUUIDValid).withMessage('Should have a valid account id'),
32 34
33 (req: express.Request, res: express.Response, next: express.NextFunction) => { 35 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
34 logger.debug('Checking unfollow parameters', { parameters: req.params }) 36 logger.debug('Checking unfollow parameters', { parameters: req.params })
35 37
36 checkErrors(req, res, async () => { 38 if (areValidationErrors(req, res)) return
37 try {
38 const serverAccount = await getServerAccount()
39 const follow = await db.AccountFollow.loadByAccountAndTarget(serverAccount.id, req.params.accountId)
40 39
41 if (!follow) { 40 const serverAccount = await getServerAccount()
42 return res.status(404) 41 const follow = await db.AccountFollow.loadByAccountAndTarget(serverAccount.id, req.params.accountId)
43 .end()
44 }
45 42
46 res.locals.follow = follow 43 if (!follow) {
44 return res.status(404)
45 .end()
46 }
47 47
48 return next() 48 res.locals.follow = follow
49 } catch (err) { 49 return next()
50 logger.error('Error in remove following validator.', err)
51 return res.sendStatus(500)
52 }
53 })
54 } 50 }
55] 51]
56 52
diff --git a/server/middlewares/validators/oembed.ts b/server/middlewares/validators/oembed.ts
index f8e34d2d4..31f06dc65 100644
--- a/server/middlewares/validators/oembed.ts
+++ b/server/middlewares/validators/oembed.ts
@@ -1,15 +1,10 @@
1import { query } from 'express-validator/check'
2import * as express from 'express' 1import * as express from 'express'
2import { query } from 'express-validator/check'
3import { join } from 'path' 3import { join } from 'path'
4 4import { isIdOrUUIDValid, isTestInstance, logger } from '../../helpers'
5import { checkErrors } from './utils'
6import { CONFIG } from '../../initializers' 5import { CONFIG } from '../../initializers'
7import { 6import { areValidationErrors } from './utils'
8 logger, 7import { isVideoExist } from '../../helpers/custom-validators/videos'
9 isTestInstance,
10 checkVideoExists,
11 isIdOrUUIDValid
12} from '../../helpers'
13 8
14const urlShouldStartWith = CONFIG.WEBSERVER.SCHEME + '://' + join(CONFIG.WEBSERVER.HOST, 'videos', 'watch') + '/' 9const urlShouldStartWith = CONFIG.WEBSERVER.SCHEME + '://' + join(CONFIG.WEBSERVER.HOST, 'videos', 'watch') + '/'
15const videoWatchRegex = new RegExp('([^/]+)$') 10const videoWatchRegex = new RegExp('([^/]+)$')
@@ -29,33 +24,35 @@ const oembedValidator = [
29 query('maxheight').optional().isInt().withMessage('Should have a valid max height'), 24 query('maxheight').optional().isInt().withMessage('Should have a valid max height'),
30 query('format').optional().isIn([ 'xml', 'json' ]).withMessage('Should have a valid format'), 25 query('format').optional().isIn([ 'xml', 'json' ]).withMessage('Should have a valid format'),
31 26
32 (req: express.Request, res: express.Response, next: express.NextFunction) => { 27 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
33 logger.debug('Checking oembed parameters', { parameters: req.query }) 28 logger.debug('Checking oembed parameters', { parameters: req.query })
34 29
35 checkErrors(req, res, () => { 30 if (areValidationErrors(req, res)) return
36 if (req.query.format !== undefined && req.query.format !== 'json') { 31
37 return res.status(501) 32 if (req.query.format !== undefined && req.query.format !== 'json') {
38 .json({ error: 'Requested format is not implemented on server.' }) 33 return res.status(501)
39 .end() 34 .json({ error: 'Requested format is not implemented on server.' })
40 } 35 .end()
36 }
37
38 const startIsOk = req.query.url.startsWith(urlShouldStartWith)
39 const matches = videoWatchRegex.exec(req.query.url)
40 if (startIsOk === false || matches === null) {
41 return res.status(400)
42 .json({ error: 'Invalid url.' })
43 .end()
44 }
41 45
42 const startIsOk = req.query.url.startsWith(urlShouldStartWith) 46 const videoId = matches[1]
43 const matches = videoWatchRegex.exec(req.query.url) 47 if (isIdOrUUIDValid(videoId) === false) {
44 if (startIsOk === false || matches === null) { 48 return res.status(400)
45 return res.status(400) 49 .json({ error: 'Invalid video id.' })
46 .json({ error: 'Invalid url.' }) 50 .end()
47 .end() 51 }
48 }
49 52
50 const videoId = matches[1] 53 if (!await isVideoExist(videoId, res)) return
51 if (isIdOrUUIDValid(videoId) === false) {
52 return res.status(400)
53 .json({ error: 'Invalid video id.' })
54 .end()
55 }
56 54
57 checkVideoExists(videoId, res, next) 55 return next()
58 })
59 } 56 }
60] 57]
61 58
diff --git a/server/middlewares/validators/pagination.ts b/server/middlewares/validators/pagination.ts
index a5a542cdf..0895b4eb8 100644
--- a/server/middlewares/validators/pagination.ts
+++ b/server/middlewares/validators/pagination.ts
@@ -1,8 +1,7 @@
1import { query } from 'express-validator/check'
2import * as express from 'express' 1import * as express from 'express'
3 2import { query } from 'express-validator/check'
4import { checkErrors } from './utils'
5import { logger } from '../../helpers' 3import { logger } from '../../helpers'
4import { areValidationErrors } from './utils'
6 5
7const paginationValidator = [ 6const paginationValidator = [
8 query('start').optional().isInt().withMessage('Should have a number start'), 7 query('start').optional().isInt().withMessage('Should have a number start'),
@@ -11,7 +10,9 @@ const paginationValidator = [
11 (req: express.Request, res: express.Response, next: express.NextFunction) => { 10 (req: express.Request, res: express.Response, next: express.NextFunction) => {
12 logger.debug('Checking pagination parameters', { parameters: req.query }) 11 logger.debug('Checking pagination parameters', { parameters: req.query })
13 12
14 checkErrors(req, res, next) 13 if (areValidationErrors(req, res)) return
14
15 return next()
15 } 16 }
16] 17]
17 18
diff --git a/server/middlewares/validators/sort.ts b/server/middlewares/validators/sort.ts
index 6fea41bb8..636f68885 100644
--- a/server/middlewares/validators/sort.ts
+++ b/server/middlewares/validators/sort.ts
@@ -1,9 +1,9 @@
1import { query } from 'express-validator/check' 1import { query } from 'express-validator/check'
2import * as express from 'express' 2import * as express from 'express'
3 3
4import { checkErrors } from './utils'
5import { logger } from '../../helpers' 4import { logger } from '../../helpers'
6import { SORTABLE_COLUMNS } from '../../initializers' 5import { SORTABLE_COLUMNS } from '../../initializers'
6import { areValidationErrors } from './utils'
7 7
8// Initialize constants here for better performances 8// Initialize constants here for better performances
9const SORTABLE_USERS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.USERS) 9const SORTABLE_USERS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.USERS)
@@ -43,7 +43,9 @@ function checkSort (sortableColumns: string[]) {
43 (req: express.Request, res: express.Response, next: express.NextFunction) => { 43 (req: express.Request, res: express.Response, next: express.NextFunction) => {
44 logger.debug('Checking sort parameters', { parameters: req.query }) 44 logger.debug('Checking sort parameters', { parameters: req.query })
45 45
46 checkErrors(req, res, next) 46 if (areValidationErrors(req, res)) return
47
48 return next()
47 } 49 }
48 ] 50 ]
49} 51}
diff --git a/server/middlewares/validators/users.ts b/server/middlewares/validators/users.ts
index 6b845f62b..ac7435b7d 100644
--- a/server/middlewares/validators/users.ts
+++ b/server/middlewares/validators/users.ts
@@ -1,22 +1,19 @@
1import { body, param } from 'express-validator/check'
2import 'express-validator'
3import * as express from 'express' 1import * as express from 'express'
4import * as Promise from 'bluebird' 2import 'express-validator'
5import * as validator from 'validator' 3import { body, param } from 'express-validator/check'
6
7import { database as db } from '../../initializers/database'
8import { checkErrors } from './utils'
9import { 4import {
5 isIdOrUUIDValid,
10 isSignupAllowed, 6 isSignupAllowed,
11 logger, 7 isUserDisplayNSFWValid,
12 isUserUsernameValid,
13 isUserPasswordValid, 8 isUserPasswordValid,
9 isUserRoleValid,
10 isUserUsernameValid,
14 isUserVideoQuotaValid, 11 isUserVideoQuotaValid,
15 isUserDisplayNSFWValid, 12 logger
16 isIdOrUUIDValid,
17 isUserRoleValid
18} from '../../helpers' 13} from '../../helpers'
19import { UserInstance, VideoInstance } from '../../models' 14import { isVideoExist } from '../../helpers/custom-validators/videos'
15import { database as db } from '../../initializers/database'
16import { areValidationErrors } from './utils'
20 17
21const usersAddValidator = [ 18const usersAddValidator = [
22 body('username').custom(isUserUsernameValid).withMessage('Should have a valid username (lowercase alphanumeric characters)'), 19 body('username').custom(isUserUsernameValid).withMessage('Should have a valid username (lowercase alphanumeric characters)'),
@@ -25,12 +22,13 @@ const usersAddValidator = [
25 body('videoQuota').custom(isUserVideoQuotaValid).withMessage('Should have a valid user quota'), 22 body('videoQuota').custom(isUserVideoQuotaValid).withMessage('Should have a valid user quota'),
26 body('role').custom(isUserRoleValid).withMessage('Should have a valid role'), 23 body('role').custom(isUserRoleValid).withMessage('Should have a valid role'),
27 24
28 (req: express.Request, res: express.Response, next: express.NextFunction) => { 25 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
29 logger.debug('Checking usersAdd parameters', { parameters: req.body }) 26 logger.debug('Checking usersAdd parameters', { parameters: req.body })
30 27
31 checkErrors(req, res, () => { 28 if (areValidationErrors(req, res)) return
32 checkUserDoesNotAlreadyExist(req.body.username, req.body.email, res, next) 29 if (!await checkUserNameOrEmailDoesNotAlreadyExist(req.body.username, req.body.email, res)) return
33 }) 30
31 return next()
34 } 32 }
35] 33]
36 34
@@ -39,37 +37,33 @@ const usersRegisterValidator = [
39 body('password').custom(isUserPasswordValid).withMessage('Should have a valid password'), 37 body('password').custom(isUserPasswordValid).withMessage('Should have a valid password'),
40 body('email').isEmail().withMessage('Should have a valid email'), 38 body('email').isEmail().withMessage('Should have a valid email'),
41 39
42 (req: express.Request, res: express.Response, next: express.NextFunction) => { 40 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
43 logger.debug('Checking usersRegister parameters', { parameters: req.body }) 41 logger.debug('Checking usersRegister parameters', { parameters: req.body })
44 42
45 checkErrors(req, res, () => { 43 if (areValidationErrors(req, res)) return
46 checkUserDoesNotAlreadyExist(req.body.username, req.body.email, res, next) 44 if (!await checkUserNameOrEmailDoesNotAlreadyExist(req.body.username, req.body.email, res)) return
47 }) 45
46 return next()
48 } 47 }
49] 48]
50 49
51const usersRemoveValidator = [ 50const usersRemoveValidator = [
52 param('id').isInt().not().isEmpty().withMessage('Should have a valid id'), 51 param('id').isInt().not().isEmpty().withMessage('Should have a valid id'),
53 52
54 (req: express.Request, res: express.Response, next: express.NextFunction) => { 53 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
55 logger.debug('Checking usersRemove parameters', { parameters: req.params }) 54 logger.debug('Checking usersRemove parameters', { parameters: req.params })
56 55
57 checkErrors(req, res, () => { 56 if (areValidationErrors(req, res)) return
58 checkUserExists(req.params.id, res, (err, user) => { 57 if (!await checkUserIdExist(req.params.id, res)) return
59 if (err) { 58
60 logger.error('Error in usersRemoveValidator.', err) 59 const user = res.locals.user
61 return res.sendStatus(500) 60 if (user.username === 'root') {
62 } 61 return res.status(400)
63 62 .send({ error: 'Cannot remove the root user' })
64 if (user.username === 'root') { 63 .end()
65 return res.status(400) 64 }
66 .send({ error: 'Cannot remove the root user' }) 65
67 .end() 66 return next()
68 }
69
70 return next()
71 })
72 })
73 } 67 }
74] 68]
75 69
@@ -79,12 +73,13 @@ const usersUpdateValidator = [
79 body('videoQuota').optional().custom(isUserVideoQuotaValid).withMessage('Should have a valid user quota'), 73 body('videoQuota').optional().custom(isUserVideoQuotaValid).withMessage('Should have a valid user quota'),
80 body('role').optional().custom(isUserRoleValid).withMessage('Should have a valid role'), 74 body('role').optional().custom(isUserRoleValid).withMessage('Should have a valid role'),
81 75
82 (req: express.Request, res: express.Response, next: express.NextFunction) => { 76 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
83 logger.debug('Checking usersUpdate parameters', { parameters: req.body }) 77 logger.debug('Checking usersUpdate parameters', { parameters: req.body })
84 78
85 checkErrors(req, res, () => { 79 if (areValidationErrors(req, res)) return
86 checkUserExists(req.params.id, res, next) 80 if (!await checkUserIdExist(req.params.id, res)) return
87 }) 81
82 return next()
88 } 83 }
89] 84]
90 85
@@ -97,64 +92,48 @@ const usersUpdateMeValidator = [
97 // TODO: Add old password verification 92 // TODO: Add old password verification
98 logger.debug('Checking usersUpdateMe parameters', { parameters: req.body }) 93 logger.debug('Checking usersUpdateMe parameters', { parameters: req.body })
99 94
100 checkErrors(req, res, next) 95 if (areValidationErrors(req, res)) return
96
97 return next()
101 } 98 }
102] 99]
103 100
104const usersGetValidator = [ 101const usersGetValidator = [
105 param('id').isInt().not().isEmpty().withMessage('Should have a valid id'), 102 param('id').isInt().not().isEmpty().withMessage('Should have a valid id'),
106 103
107 (req: express.Request, res: express.Response, next: express.NextFunction) => { 104 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
108 checkErrors(req, res, () => { 105 logger.debug('Checking usersGet parameters', { parameters: req.body })
109 checkUserExists(req.params.id, res, next) 106
110 }) 107 if (areValidationErrors(req, res)) return
108 if (!await checkUserIdExist(req.params.id, res)) return
109
110 return next()
111 } 111 }
112] 112]
113 113
114const usersVideoRatingValidator = [ 114const usersVideoRatingValidator = [
115 param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid video id'), 115 param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid video id'),
116 116
117 (req: express.Request, res: express.Response, next: express.NextFunction) => { 117 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
118 logger.debug('Checking usersVideoRating parameters', { parameters: req.params }) 118 logger.debug('Checking usersVideoRating parameters', { parameters: req.params })
119 119
120 checkErrors(req, res, () => { 120 if (areValidationErrors(req, res)) return
121 let videoPromise: Promise<VideoInstance> 121 if (!await isVideoExist(req.params.videoId, res)) return
122 122
123 if (validator.isUUID(req.params.videoId)) { 123 return next()
124 videoPromise = db.Video.loadByUUID(req.params.videoId)
125 } else {
126 videoPromise = db.Video.load(req.params.videoId)
127 }
128
129 videoPromise
130 .then(video => {
131 if (!video) {
132 return res.status(404)
133 .json({ error: 'Video not found' })
134 .end()
135 }
136
137 return next()
138 })
139 .catch(err => {
140 logger.error('Error in user request validator.', err)
141 return res.sendStatus(500)
142 })
143 })
144 } 124 }
145] 125]
146 126
147const ensureUserRegistrationAllowed = [ 127const ensureUserRegistrationAllowed = [
148 (req: express.Request, res: express.Response, next: express.NextFunction) => { 128 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
149 isSignupAllowed().then(allowed => { 129 const allowed = await isSignupAllowed()
150 if (allowed === false) { 130 if (allowed === false) {
151 return res.status(403) 131 return res.status(403)
152 .send({ error: 'User registration is not enabled or user limit is reached.' }) 132 .send({ error: 'User registration is not enabled or user limit is reached.' })
153 .end() 133 .end()
154 } 134 }
155 135
156 return next() 136 return next()
157 })
158 } 137 }
159] 138]
160 139
@@ -173,37 +152,30 @@ export {
173 152
174// --------------------------------------------------------------------------- 153// ---------------------------------------------------------------------------
175 154
176function checkUserExists (id: number, res: express.Response, callback: (err: Error, user: UserInstance) => void) { 155async function checkUserIdExist (id: number, res: express.Response) {
177 db.User.loadById(id) 156 const user = await db.User.loadById(id)
178 .then(user => { 157
179 if (!user) { 158 if (!user) {
180 return res.status(404) 159 res.status(404)
181 .send({ error: 'User not found' }) 160 .send({ error: 'User not found' })
182 .end() 161 .end()
183 } 162
184 163 return false
185 res.locals.user = user 164 }
186 return callback(null, user) 165
187 }) 166 res.locals.user = user
188 .catch(err => { 167 return true
189 logger.error('Error in user request validator.', err)
190 return res.sendStatus(500)
191 })
192} 168}
193 169
194function checkUserDoesNotAlreadyExist (username: string, email: string, res: express.Response, callback: () => void) { 170async function checkUserNameOrEmailDoesNotAlreadyExist (username: string, email: string, res: express.Response) {
195 db.User.loadByUsernameOrEmail(username, email) 171 const user = await db.User.loadByUsernameOrEmail(username, email)
196 .then(user => { 172
197 if (user) { 173 if (user) {
198 return res.status(409) 174 res.status(409)
199 .send({ error: 'User with this username of email already exists.' }) 175 .send({ error: 'User with this username of email already exists.' })
200 .end() 176 .end()
201 } 177 return false
202 178 }
203 return callback() 179
204 }) 180 return true
205 .catch(err => {
206 logger.error('Error in usersAdd request validator.', err)
207 return res.sendStatus(500)
208 })
209} 181}
diff --git a/server/middlewares/validators/utils.ts b/server/middlewares/validators/utils.ts
index 77a1a0d4b..ca80acf29 100644
--- a/server/middlewares/validators/utils.ts
+++ b/server/middlewares/validators/utils.ts
@@ -1,19 +1,8 @@
1import { validationResult } from 'express-validator/check'
2import * as express from 'express' 1import * as express from 'express'
2import { validationResult } from 'express-validator/check'
3 3
4import { logger } from '../../helpers' 4import { logger } from '../../helpers'
5 5
6function checkErrors (req: express.Request, res: express.Response, next: express.NextFunction) {
7 const errors = validationResult(req)
8
9 if (!errors.isEmpty()) {
10 logger.warn('Incorrect request parameters', { path: req.originalUrl, err: errors.mapped() })
11 return res.status(400).json({ errors: errors.mapped() })
12 }
13
14 return next()
15}
16
17function areValidationErrors (req: express.Request, res: express.Response) { 6function areValidationErrors (req: express.Request, res: express.Response) {
18 const errors = validationResult(req) 7 const errors = validationResult(req)
19 8
@@ -30,6 +19,5 @@ function areValidationErrors (req: express.Request, res: express.Response) {
30// --------------------------------------------------------------------------- 19// ---------------------------------------------------------------------------
31 20
32export { 21export {
33 checkErrors,
34 areValidationErrors 22 areValidationErrors
35} 23}
diff --git a/server/middlewares/validators/video-blacklist.ts b/server/middlewares/validators/video-blacklist.ts
index 3c8c31519..f1cc04950 100644
--- a/server/middlewares/validators/video-blacklist.ts
+++ b/server/middlewares/validators/video-blacklist.ts
@@ -1,35 +1,36 @@
1import { param } from 'express-validator/check'
2import * as express from 'express' 1import * as express from 'express'
3 2import { param } from 'express-validator/check'
3import { isIdOrUUIDValid, logger } from '../../helpers'
4import { isVideoExist } from '../../helpers/custom-validators/videos'
4import { database as db } from '../../initializers/database' 5import { database as db } from '../../initializers/database'
5import { checkErrors } from './utils' 6import { VideoInstance } from '../../models/video/video-interface'
6import { logger, isIdOrUUIDValid, checkVideoExists } from '../../helpers' 7import { areValidationErrors } from './utils'
7 8
8const videosBlacklistRemoveValidator = [ 9const videosBlacklistRemoveValidator = [
9 param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'), 10 param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'),
10 11
11 (req: express.Request, res: express.Response, next: express.NextFunction) => { 12 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
12 logger.debug('Checking blacklistRemove parameters.', { parameters: req.params }) 13 logger.debug('Checking blacklistRemove parameters.', { parameters: req.params })
13 14
14 checkErrors(req, res, () => { 15 if (areValidationErrors(req, res)) return
15 checkVideoExists(req.params.videoId, res, () => { 16 if (!await isVideoExist(req.params.videoId, res)) return
16 checkVideoIsBlacklisted(req, res, next) 17 if (!await checkVideoIsBlacklisted(res.locals.video, res)) return
17 }) 18
18 }) 19 return next()
19 } 20 }
20] 21]
21 22
22const videosBlacklistAddValidator = [ 23const videosBlacklistAddValidator = [
23 param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'), 24 param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'),
24 25
25 (req: express.Request, res: express.Response, next: express.NextFunction) => { 26 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
26 logger.debug('Checking videosBlacklist parameters', { parameters: req.params }) 27 logger.debug('Checking videosBlacklist parameters', { parameters: req.params })
27 28
28 checkErrors(req, res, () => { 29 if (areValidationErrors(req, res)) return
29 checkVideoExists(req.params.videoId, res, () => { 30 if (!await isVideoExist(req.params.videoId, res)) return
30 checkVideoIsBlacklistable(req, res, next) 31 if (!checkVideoIsBlacklistable(res.locals.video, res)) return
31 }) 32
32 }) 33 return next()
33 } 34 }
34] 35]
35 36
@@ -41,27 +42,27 @@ export {
41} 42}
42// --------------------------------------------------------------------------- 43// ---------------------------------------------------------------------------
43 44
44function checkVideoIsBlacklistable (req: express.Request, res: express.Response, callback: () => void) { 45function checkVideoIsBlacklistable (video: VideoInstance, res: express.Response) {
45 if (res.locals.video.isOwned() === true) { 46 if (video.isOwned() === true) {
46 return res.status(403) 47 res.status(403)
47 .json({ error: 'Cannot blacklist a local video' }) 48 .json({ error: 'Cannot blacklist a local video' })
48 .end() 49 .end()
50
51 return false
49 } 52 }
50 53
51 callback() 54 return true
52} 55}
53 56
54function checkVideoIsBlacklisted (req: express.Request, res: express.Response, callback: () => void) { 57async function checkVideoIsBlacklisted (video: VideoInstance, res: express.Response) {
55 db.BlacklistedVideo.loadByVideoId(res.locals.video.id) 58 const blacklistedVideo = await db.BlacklistedVideo.loadByVideoId(video.id)
56 .then(blacklistedVideo => { 59 if (!blacklistedVideo) {
57 if (!blacklistedVideo) return res.status(404).send('Blacklisted video not found') 60 res.status(404)
61 .send('Blacklisted video not found')
58 62
59 res.locals.blacklistedVideo = blacklistedVideo 63 return false
64 }
60 65
61 callback() 66 res.locals.blacklistedVideo = blacklistedVideo
62 }) 67 return true
63 .catch(err => {
64 logger.error('Error in blacklistRemove request validator', { error: err })
65 return res.sendStatus(500)
66 })
67} 68}
diff --git a/server/middlewares/validators/video-channels.ts b/server/middlewares/validators/video-channels.ts
index f30fbf0dc..4683c91e1 100644
--- a/server/middlewares/validators/video-channels.ts
+++ b/server/middlewares/validators/video-channels.ts
@@ -1,29 +1,30 @@
1import * as express from 'express' 1import * as express from 'express'
2import { body, param } from 'express-validator/check' 2import { body, param } from 'express-validator/check'
3import { UserRight } from '../../../shared' 3import { UserRight } from '../../../shared'
4import { checkAccountIdExists } from '../../helpers/custom-validators/accounts'
5import { isIdValid } from '../../helpers/custom-validators/misc' 4import { isIdValid } from '../../helpers/custom-validators/misc'
6import { 5import {
7 checkVideoChannelExists,
8 isVideoChannelDescriptionValid, 6 isVideoChannelDescriptionValid,
9 isVideoChannelExistsPromise, 7 isVideoChannelExist,
10 isVideoChannelNameValid 8 isVideoChannelNameValid
11} from '../../helpers/custom-validators/video-channels' 9} from '../../helpers/custom-validators/video-channels'
12import { isIdOrUUIDValid } from '../../helpers/index' 10import { isIdOrUUIDValid } from '../../helpers/index'
13import { logger } from '../../helpers/logger' 11import { logger } from '../../helpers/logger'
14import { database as db } from '../../initializers' 12import { database as db } from '../../initializers'
15import { UserInstance } from '../../models' 13import { UserInstance } from '../../models'
16import { areValidationErrors, checkErrors } from './utils' 14import { areValidationErrors } from './utils'
15import { isAccountIdExist } from '../../helpers/custom-validators/accounts'
16import { VideoChannelInstance } from '../../models/video/video-channel-interface'
17 17
18const listVideoAccountChannelsValidator = [ 18const listVideoAccountChannelsValidator = [
19 param('accountId').custom(isIdOrUUIDValid).withMessage('Should have a valid account id'), 19 param('accountId').custom(isIdOrUUIDValid).withMessage('Should have a valid account id'),
20 20
21 (req: express.Request, res: express.Response, next: express.NextFunction) => { 21 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
22 logger.debug('Checking listVideoAccountChannelsValidator parameters', { parameters: req.body }) 22 logger.debug('Checking listVideoAccountChannelsValidator parameters', { parameters: req.body })
23 23
24 checkErrors(req, res, () => { 24 if (areValidationErrors(req, res)) return
25 checkAccountIdExists(req.params.accountId, res, next) 25 if (!await isAccountIdExist(req.params.accountId, res)) return
26 }) 26
27 return next()
27 } 28 }
28] 29]
29 30
@@ -34,7 +35,9 @@ const videoChannelsAddValidator = [
34 (req: express.Request, res: express.Response, next: express.NextFunction) => { 35 (req: express.Request, res: express.Response, next: express.NextFunction) => {
35 logger.debug('Checking videoChannelsAdd parameters', { parameters: req.body }) 36 logger.debug('Checking videoChannelsAdd parameters', { parameters: req.body })
36 37
37 checkErrors(req, res, next) 38 if (areValidationErrors(req, res)) return
39
40 return next()
38 } 41 }
39] 42]
40 43
@@ -43,56 +46,56 @@ const videoChannelsUpdateValidator = [
43 body('name').optional().custom(isVideoChannelNameValid).withMessage('Should have a valid name'), 46 body('name').optional().custom(isVideoChannelNameValid).withMessage('Should have a valid name'),
44 body('description').optional().custom(isVideoChannelDescriptionValid).withMessage('Should have a valid description'), 47 body('description').optional().custom(isVideoChannelDescriptionValid).withMessage('Should have a valid description'),
45 48
46 (req: express.Request, res: express.Response, next: express.NextFunction) => { 49 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
47 logger.debug('Checking videoChannelsUpdate parameters', { parameters: req.body }) 50 logger.debug('Checking videoChannelsUpdate parameters', { parameters: req.body })
48 51
49 checkErrors(req, res, () => { 52 if (areValidationErrors(req, res)) return
50 checkVideoChannelExists(req.params.id, res, () => { 53 if (!await isVideoChannelExist(req.params.id, res)) return
51 // We need to make additional checks 54
52 if (res.locals.videoChannel.isOwned() === false) { 55 // We need to make additional checks
53 return res.status(403) 56 if (res.locals.videoChannel.isOwned() === false) {
54 .json({ error: 'Cannot update video channel of another server' }) 57 return res.status(403)
55 .end() 58 .json({ error: 'Cannot update video channel of another server' })
56 } 59 .end()
57 60 }
58 if (res.locals.videoChannel.Account.userId !== res.locals.oauth.token.User.id) { 61
59 return res.status(403) 62 if (res.locals.videoChannel.Account.userId !== res.locals.oauth.token.User.id) {
60 .json({ error: 'Cannot update video channel of another user' }) 63 return res.status(403)
61 .end() 64 .json({ error: 'Cannot update video channel of another user' })
62 } 65 .end()
63 66 }
64 next() 67
65 }) 68 return next()
66 })
67 } 69 }
68] 70]
69 71
70const videoChannelsRemoveValidator = [ 72const videoChannelsRemoveValidator = [
71 param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), 73 param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'),
72 74
73 (req: express.Request, res: express.Response, next: express.NextFunction) => { 75 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
74 logger.debug('Checking videoChannelsRemove parameters', { parameters: req.params }) 76 logger.debug('Checking videoChannelsRemove parameters', { parameters: req.params })
75 77
76 checkErrors(req, res, () => { 78 if (areValidationErrors(req, res)) return
77 checkVideoChannelExists(req.params.id, res, () => { 79 if (!await isVideoChannelExist(req.params.id, res)) return
78 // Check if the user who did the request is able to delete the video 80
79 checkUserCanDeleteVideoChannel(res, () => { 81 // Check if the user who did the request is able to delete the video
80 checkVideoChannelIsNotTheLastOne(res, next) 82 if (!checkUserCanDeleteVideoChannel(res.locals.user, res.locals.videoChannel, res)) return
81 }) 83 if (!await checkVideoChannelIsNotTheLastOne(res)) return
82 }) 84
83 }) 85 return next()
84 } 86 }
85] 87]
86 88
87const videoChannelsGetValidator = [ 89const videoChannelsGetValidator = [
88 param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), 90 param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'),
89 91
90 (req: express.Request, res: express.Response, next: express.NextFunction) => { 92 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
91 logger.debug('Checking videoChannelsGet parameters', { parameters: req.params }) 93 logger.debug('Checking videoChannelsGet parameters', { parameters: req.params })
92 94
93 checkErrors(req, res, () => { 95 if (areValidationErrors(req, res)) return
94 checkVideoChannelExists(req.params.id, res, next) 96 if (!await isVideoChannelExist(req.params.id, res)) return
95 }) 97
98 return next()
96 } 99 }
97] 100]
98 101
@@ -104,7 +107,7 @@ const videoChannelsShareValidator = [
104 logger.debug('Checking videoChannelShare parameters', { parameters: req.params }) 107 logger.debug('Checking videoChannelShare parameters', { parameters: req.params })
105 108
106 if (areValidationErrors(req, res)) return 109 if (areValidationErrors(req, res)) return
107 if (!await isVideoChannelExistsPromise(req.params.id, res)) return 110 if (!await isVideoChannelExist(req.params.id, res)) return
108 111
109 const share = await db.VideoChannelShare.load(res.locals.video.id, req.params.accountId) 112 const share = await db.VideoChannelShare.load(res.locals.video.id, req.params.accountId)
110 if (!share) { 113 if (!share) {
@@ -131,38 +134,40 @@ export {
131 134
132// --------------------------------------------------------------------------- 135// ---------------------------------------------------------------------------
133 136
134function checkUserCanDeleteVideoChannel (res: express.Response, callback: () => void) { 137function checkUserCanDeleteVideoChannel (user: UserInstance, videoChannel: VideoChannelInstance, res: express.Response) {
135 const user: UserInstance = res.locals.oauth.token.User
136
137 // Retrieve the user who did the request 138 // Retrieve the user who did the request
138 if (res.locals.videoChannel.isOwned() === false) { 139 if (videoChannel.isOwned() === false) {
139 return res.status(403) 140 res.status(403)
140 .json({ error: 'Cannot remove video channel of another server.' }) 141 .json({ error: 'Cannot remove video channel of another server.' })
141 .end() 142 .end()
143
144 return false
142 } 145 }
143 146
144 // Check if the user can delete the video channel 147 // Check if the user can delete the video channel
145 // The user can delete it if s/he is an admin 148 // The user can delete it if s/he is an admin
146 // Or if s/he is the video channel's account 149 // Or if s/he is the video channel's account
147 if (user.hasRight(UserRight.REMOVE_ANY_VIDEO_CHANNEL) === false && res.locals.videoChannel.Account.userId !== user.id) { 150 if (user.hasRight(UserRight.REMOVE_ANY_VIDEO_CHANNEL) === false && videoChannel.Account.userId !== user.id) {
148 return res.status(403) 151 res.status(403)
149 .json({ error: 'Cannot remove video channel of another user' }) 152 .json({ error: 'Cannot remove video channel of another user' })
150 .end() 153 .end()
154
155 return false
151 } 156 }
152 157
153 // If we reach this comment, we can delete the video 158 return true
154 callback()
155} 159}
156 160
157function checkVideoChannelIsNotTheLastOne (res: express.Response, callback: () => void) { 161async function checkVideoChannelIsNotTheLastOne (res: express.Response) {
158 db.VideoChannel.countByAccount(res.locals.oauth.token.User.Account.id) 162 const count = await db.VideoChannel.countByAccount(res.locals.oauth.token.User.Account.id)
159 .then(count => { 163
160 if (count <= 1) { 164 if (count <= 1) {
161 return res.status(409) 165 res.status(409)
162 .json({ error: 'Cannot remove the last channel of this user' }) 166 .json({ error: 'Cannot remove the last channel of this user' })
163 .end() 167 .end()
164 } 168
165 169 return false
166 callback() 170 }
167 }) 171
172 return true
168} 173}
diff --git a/server/middlewares/validators/videos.ts b/server/middlewares/validators/videos.ts
index 5ffc85210..3cbf98312 100644
--- a/server/middlewares/validators/videos.ts
+++ b/server/middlewares/validators/videos.ts
@@ -3,11 +3,11 @@ import { body, param, query } from 'express-validator/check'
3import { UserRight, VideoPrivacy } from '../../../shared' 3import { UserRight, VideoPrivacy } from '../../../shared'
4import { isIdOrUUIDValid, isIdValid } from '../../helpers/custom-validators/misc' 4import { isIdOrUUIDValid, isIdValid } from '../../helpers/custom-validators/misc'
5import { 5import {
6 checkVideoExists,
7 isVideoAbuseReasonValid, 6 isVideoAbuseReasonValid,
8 isVideoCategoryValid, 7 isVideoCategoryValid,
9 isVideoDescriptionValid, 8 isVideoDescriptionValid,
10 isVideoDurationValid, 9 isVideoDurationValid,
10 isVideoExist,
11 isVideoFile, 11 isVideoFile,
12 isVideoLanguageValid, 12 isVideoLanguageValid,
13 isVideoLicenceValid, 13 isVideoLicenceValid,
@@ -20,12 +20,11 @@ import {
20import { getDurationFromVideoFile } from '../../helpers/ffmpeg-utils' 20import { getDurationFromVideoFile } from '../../helpers/ffmpeg-utils'
21import { logger } from '../../helpers/logger' 21import { logger } from '../../helpers/logger'
22import { CONSTRAINTS_FIELDS, SEARCHABLE_COLUMNS } from '../../initializers' 22import { CONSTRAINTS_FIELDS, SEARCHABLE_COLUMNS } from '../../initializers'
23
24import { database as db } from '../../initializers/database' 23import { database as db } from '../../initializers/database'
25import { UserInstance } from '../../models/account/user-interface' 24import { UserInstance } from '../../models/account/user-interface'
25import { VideoInstance } from '../../models/video/video-interface'
26import { authenticate } from '../oauth' 26import { authenticate } from '../oauth'
27import { areValidationErrors, checkErrors } from './utils' 27import { areValidationErrors } from './utils'
28import { isVideoExistsPromise } from '../../helpers/index'
29 28
30const videosAddValidator = [ 29const videosAddValidator = [
31 body('videofile').custom((value, { req }) => isVideoFile(req.files)).withMessage( 30 body('videofile').custom((value, { req }) => isVideoFile(req.files)).withMessage(
@@ -42,68 +41,58 @@ const videosAddValidator = [
42 body('privacy').custom(isVideoPrivacyValid).withMessage('Should have correct video privacy'), 41 body('privacy').custom(isVideoPrivacyValid).withMessage('Should have correct video privacy'),
43 body('tags').optional().custom(isVideoTagsValid).withMessage('Should have correct tags'), 42 body('tags').optional().custom(isVideoTagsValid).withMessage('Should have correct tags'),
44 43
45 (req: express.Request, res: express.Response, next: express.NextFunction) => { 44 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
46 logger.debug('Checking videosAdd parameters', { parameters: req.body, files: req.files }) 45 logger.debug('Checking videosAdd parameters', { parameters: req.body, files: req.files })
47 46
48 checkErrors(req, res, () => { 47 if (areValidationErrors(req, res)) return
49 const videoFile: Express.Multer.File = req.files['videofile'][0] 48
50 const user = res.locals.oauth.token.User 49 const videoFile: Express.Multer.File = req.files['videofile'][0]
50 const user = res.locals.oauth.token.User
51 51
52 return db.VideoChannel.loadByIdAndAccount(req.body.channelId, user.Account.id) 52 const videoChannel = await db.VideoChannel.loadByIdAndAccount(req.body.channelId, user.Account.id)
53 .then(videoChannel => { 53 if (!videoChannel) {
54 if (!videoChannel) { 54 res.status(400)
55 res.status(400) 55 .json({ error: 'Unknown video video channel for this account.' })
56 .json({ error: 'Unknown video video channel for this account.' }) 56 .end()
57 .end()
58 57
59 return undefined 58 return
60 } 59 }
61 60
62 res.locals.videoChannel = videoChannel 61 res.locals.videoChannel = videoChannel
63 62
64 return user.isAbleToUploadVideo(videoFile) 63 const isAble = await user.isAbleToUploadVideo(videoFile)
65 }) 64 if (isAble === false) {
66 .then(isAble => { 65 res.status(403)
67 if (isAble === false) { 66 .json({ error: 'The user video quota is exceeded with this video.' })
68 res.status(403) 67 .end()
69 .json({ error: 'The user video quota is exceeded with this video.' }) 68
70 .end() 69 return
71 70 }
72 return undefined 71
73 } 72 let duration: number
74 73
75 return getDurationFromVideoFile(videoFile.path) 74 try {
76 .catch(err => { 75 duration = await getDurationFromVideoFile(videoFile.path)
77 logger.error('Invalid input file in videosAddValidator.', err) 76 } catch (err) {
78 res.status(400) 77 logger.error('Invalid input file in videosAddValidator.', err)
79 .json({ error: 'Invalid input file.' }) 78 res.status(400)
80 .end() 79 .json({ error: 'Invalid input file.' })
81 80 .end()
82 return undefined 81
83 }) 82 return
84 }) 83 }
85 .then(duration => { 84
86 // Previous test failed, abort 85 if (!isVideoDurationValid('' + duration)) {
87 if (duration === undefined) return undefined 86 return res.status(400)
88 87 .json({
89 if (!isVideoDurationValid('' + duration)) { 88 error: 'Duration of the video file is too big (max: ' + CONSTRAINTS_FIELDS.VIDEOS.DURATION.max + 's).'
90 return res.status(400) 89 })
91 .json({ 90 .end()
92 error: 'Duration of the video file is too big (max: ' + CONSTRAINTS_FIELDS.VIDEOS.DURATION.max + 's).' 91 }
93 }) 92
94 .end() 93 videoFile['duration'] = duration
95 } 94
96 95 return next()
97 videoFile['duration'] = duration
98 next()
99 })
100 .catch(err => {
101 logger.error('Error in video add validator', err)
102 res.sendStatus(500)
103
104 return undefined
105 })
106 })
107 } 96 }
108] 97]
109 98
@@ -118,61 +107,59 @@ const videosUpdateValidator = [
118 body('description').optional().custom(isVideoDescriptionValid).withMessage('Should have a valid description'), 107 body('description').optional().custom(isVideoDescriptionValid).withMessage('Should have a valid description'),
119 body('tags').optional().custom(isVideoTagsValid).withMessage('Should have correct tags'), 108 body('tags').optional().custom(isVideoTagsValid).withMessage('Should have correct tags'),
120 109
121 (req: express.Request, res: express.Response, next: express.NextFunction) => { 110 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
122 logger.debug('Checking videosUpdate parameters', { parameters: req.body }) 111 logger.debug('Checking videosUpdate parameters', { parameters: req.body })
123 112
124 checkErrors(req, res, () => { 113 if (areValidationErrors(req, res)) return
125 checkVideoExists(req.params.id, res, () => { 114 if (!await isVideoExist(req.params.id, res)) return
126 const video = res.locals.video 115
127 116 const video = res.locals.video
128 // We need to make additional checks 117
129 if (video.isOwned() === false) { 118 // We need to make additional checks
130 return res.status(403) 119 if (video.isOwned() === false) {
131 .json({ error: 'Cannot update video of another server' }) 120 return res.status(403)
132 .end() 121 .json({ error: 'Cannot update video of another server' })
133 } 122 .end()
134 123 }
135 if (video.VideoChannel.Account.userId !== res.locals.oauth.token.User.id) { 124
136 return res.status(403) 125 if (video.VideoChannel.Account.userId !== res.locals.oauth.token.User.id) {
137 .json({ error: 'Cannot update video of another user' }) 126 return res.status(403)
138 .end() 127 .json({ error: 'Cannot update video of another user' })
139 } 128 .end()
140 129 }
141 if (video.privacy !== VideoPrivacy.PRIVATE && req.body.privacy === VideoPrivacy.PRIVATE) { 130
142 return res.status(409) 131 if (video.privacy !== VideoPrivacy.PRIVATE && req.body.privacy === VideoPrivacy.PRIVATE) {
143 .json({ error: 'Cannot set "private" a video that was not private anymore.' }) 132 return res.status(409)
144 .end() 133 .json({ error: 'Cannot set "private" a video that was not private anymore.' })
145 } 134 .end()
146 135 }
147 next() 136
148 }) 137 return next()
149 })
150 } 138 }
151] 139]
152 140
153const videosGetValidator = [ 141const videosGetValidator = [
154 param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), 142 param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'),
155 143
156 (req: express.Request, res: express.Response, next: express.NextFunction) => { 144 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
157 logger.debug('Checking videosGet parameters', { parameters: req.params }) 145 logger.debug('Checking videosGet parameters', { parameters: req.params })
158 146
159 checkErrors(req, res, () => { 147 if (areValidationErrors(req, res)) return
160 checkVideoExists(req.params.id, res, () => { 148 if (!await isVideoExist(req.params.id, res)) return
161 const video = res.locals.video
162 149
163 // Video is not private, anyone can access it 150 const video = res.locals.video
164 if (video.privacy !== VideoPrivacy.PRIVATE) return next()
165 151
166 authenticate(req, res, () => { 152 // Video is not private, anyone can access it
167 if (video.VideoChannel.Account.userId !== res.locals.oauth.token.User.id) { 153 if (video.privacy !== VideoPrivacy.PRIVATE) return next()
168 return res.status(403)
169 .json({ error: 'Cannot get this private video of another user' })
170 .end()
171 }
172 154
173 next() 155 authenticate(req, res, () => {
174 }) 156 if (video.VideoChannel.Account.userId !== res.locals.oauth.token.User.id) {
175 }) 157 return res.status(403)
158 .json({ error: 'Cannot get this private video of another user' })
159 .end()
160 }
161
162 return next()
176 }) 163 })
177 } 164 }
178] 165]
@@ -180,17 +167,16 @@ const videosGetValidator = [
180const videosRemoveValidator = [ 167const videosRemoveValidator = [
181 param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), 168 param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'),
182 169
183 (req: express.Request, res: express.Response, next: express.NextFunction) => { 170 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
184 logger.debug('Checking videosRemove parameters', { parameters: req.params }) 171 logger.debug('Checking videosRemove parameters', { parameters: req.params })
185 172
186 checkErrors(req, res, () => { 173 if (areValidationErrors(req, res)) return
187 checkVideoExists(req.params.id, res, () => { 174 if (!await isVideoExist(req.params.id, res)) return
188 // Check if the user who did the request is able to delete the video 175
189 checkUserCanDeleteVideo(res.locals.oauth.token.User, res, () => { 176 // Check if the user who did the request is able to delete the video
190 next() 177 if (!checkUserCanDeleteVideo(res.locals.oauth.token.User, res.locals.video, res)) return
191 }) 178
192 }) 179 return next()
193 })
194 } 180 }
195] 181]
196 182
@@ -201,7 +187,9 @@ const videosSearchValidator = [
201 (req: express.Request, res: express.Response, next: express.NextFunction) => { 187 (req: express.Request, res: express.Response, next: express.NextFunction) => {
202 logger.debug('Checking videosSearch parameters', { parameters: req.params }) 188 logger.debug('Checking videosSearch parameters', { parameters: req.params })
203 189
204 checkErrors(req, res, next) 190 if (areValidationErrors(req, res)) return
191
192 return next()
205 } 193 }
206] 194]
207 195
@@ -209,12 +197,13 @@ const videoAbuseReportValidator = [
209 param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), 197 param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'),
210 body('reason').custom(isVideoAbuseReasonValid).withMessage('Should have a valid reason'), 198 body('reason').custom(isVideoAbuseReasonValid).withMessage('Should have a valid reason'),
211 199
212 (req: express.Request, res: express.Response, next: express.NextFunction) => { 200 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
213 logger.debug('Checking videoAbuseReport parameters', { parameters: req.body }) 201 logger.debug('Checking videoAbuseReport parameters', { parameters: req.body })
214 202
215 checkErrors(req, res, () => { 203 if (areValidationErrors(req, res)) return
216 checkVideoExists(req.params.id, res, next) 204 if (!await isVideoExist(req.params.id, res)) return
217 }) 205
206 return next()
218 } 207 }
219] 208]
220 209
@@ -222,12 +211,13 @@ const videoRateValidator = [
222 param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), 211 param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'),
223 body('rating').custom(isVideoRatingTypeValid).withMessage('Should have a valid rate type'), 212 body('rating').custom(isVideoRatingTypeValid).withMessage('Should have a valid rate type'),
224 213
225 (req: express.Request, res: express.Response, next: express.NextFunction) => { 214 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
226 logger.debug('Checking videoRate parameters', { parameters: req.body }) 215 logger.debug('Checking videoRate parameters', { parameters: req.body })
227 216
228 checkErrors(req, res, () => { 217 if (areValidationErrors(req, res)) return
229 checkVideoExists(req.params.id, res, next) 218 if (!await isVideoExist(req.params.id, res)) return
230 }) 219
220 return next()
231 } 221 }
232] 222]
233 223
@@ -239,7 +229,7 @@ const videosShareValidator = [
239 logger.debug('Checking videoShare parameters', { parameters: req.params }) 229 logger.debug('Checking videoShare parameters', { parameters: req.params })
240 230
241 if (areValidationErrors(req, res)) return 231 if (areValidationErrors(req, res)) return
242 if (!await isVideoExistsPromise(req.params.id, res)) return 232 if (!await isVideoExist(req.params.id, res)) return
243 233
244 const share = await db.VideoShare.load(req.params.accountId, res.locals.video.id) 234 const share = await db.VideoShare.load(req.params.accountId, res.locals.video.id)
245 if (!share) { 235 if (!share) {
@@ -248,7 +238,6 @@ const videosShareValidator = [
248 } 238 }
249 239
250 res.locals.videoShare = share 240 res.locals.videoShare = share
251
252 return next() 241 return next()
253 } 242 }
254] 243]
@@ -270,24 +259,25 @@ export {
270 259
271// --------------------------------------------------------------------------- 260// ---------------------------------------------------------------------------
272 261
273function checkUserCanDeleteVideo (user: UserInstance, res: express.Response, callback: () => void) { 262function checkUserCanDeleteVideo (user: UserInstance, video: VideoInstance, res: express.Response) {
274 // Retrieve the user who did the request 263 // Retrieve the user who did the request
275 if (res.locals.video.isOwned() === false) { 264 if (video.isOwned() === false) {
276 return res.status(403) 265 res.status(403)
277 .json({ error: 'Cannot remove video of another server, blacklist it' }) 266 .json({ error: 'Cannot remove video of another server, blacklist it' })
278 .end() 267 .end()
268 return false
279 } 269 }
280 270
281 // Check if the user can delete the video 271 // Check if the user can delete the video
282 // The user can delete it if s/he is an admin 272 // The user can delete it if s/he is an admin
283 // Or if s/he is the video's account 273 // Or if s/he is the video's account
284 const account = res.locals.video.VideoChannel.Account 274 const account = video.VideoChannel.Account
285 if (user.hasRight(UserRight.REMOVE_ANY_VIDEO) === false && account.userId !== user.id) { 275 if (user.hasRight(UserRight.REMOVE_ANY_VIDEO) === false && account.userId !== user.id) {
286 return res.status(403) 276 res.status(403)
287 .json({ error: 'Cannot remove video of another user' }) 277 .json({ error: 'Cannot remove video of another user' })
288 .end() 278 .end()
279 return false
289 } 280 }
290 281
291 // If we reach this comment, we can delete the video 282 return true
292 callback()
293} 283}
diff --git a/server/middlewares/validators/webfinger.ts b/server/middlewares/validators/webfinger.ts
index 7852c1c2b..34e62c66d 100644
--- a/server/middlewares/validators/webfinger.ts
+++ b/server/middlewares/validators/webfinger.ts
@@ -1,37 +1,31 @@
1import * as express from 'express' 1import * as express from 'express'
2import { query } from 'express-validator/check' 2import { query } from 'express-validator/check'
3import { isWebfingerResourceValid } from '../../helpers/custom-validators/webfinger' 3import { isWebfingerResourceValid } from '../../helpers/custom-validators/webfinger'
4import { database as db } from '../../initializers'
5import { checkErrors } from './utils'
6import { logger } from '../../helpers/logger' 4import { logger } from '../../helpers/logger'
5import { database as db } from '../../initializers'
6import { areValidationErrors } from './utils'
7 7
8const webfingerValidator = [ 8const webfingerValidator = [
9 query('resource').custom(isWebfingerResourceValid).withMessage('Should have a valid webfinger resource'), 9 query('resource').custom(isWebfingerResourceValid).withMessage('Should have a valid webfinger resource'),
10 10
11 (req: express.Request, res: express.Response, next: express.NextFunction) => { 11 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
12 logger.debug('Checking webfinger parameters', { parameters: req.query }) 12 logger.debug('Checking webfinger parameters', { parameters: req.query })
13 13
14 checkErrors(req, res, () => { 14 if (areValidationErrors(req, res)) return
15 // Remove 'acct:' from the beginning of the string 15
16 const nameWithHost = req.query.resource.substr(5) 16 // Remove 'acct:' from the beginning of the string
17 const [ name ] = nameWithHost.split('@') 17 const nameWithHost = req.query.resource.substr(5)
18 18 const [ name ] = nameWithHost.split('@')
19 db.Account.loadLocalByName(name) 19
20 .then(account => { 20 const account = await db.Account.loadLocalByName(name)
21 if (!account) { 21 if (!account) {
22 return res.status(404) 22 return res.status(404)
23 .send({ error: 'Account not found' }) 23 .send({ error: 'Account not found' })
24 .end() 24 .end()
25 } 25 }
26 26
27 res.locals.account = account 27 res.locals.account = account
28 return next() 28 return next()
29 })
30 .catch(err => {
31 logger.error('Error in webfinger validator.', err)
32 return res.sendStatus(500)
33 })
34 })
35 } 29 }
36] 30]
37 31
diff --git a/server/models/account/account.ts b/server/models/account/account.ts
index fff3ce087..c721656cb 100644
--- a/server/models/account/account.ts
+++ b/server/models/account/account.ts
@@ -2,17 +2,12 @@ import * as Sequelize from 'sequelize'
2import { 2import {
3 activityPubContextify, 3 activityPubContextify,
4 isAccountFollowersCountValid, 4 isAccountFollowersCountValid,
5 isAccountFollowersValid,
6 isAccountFollowingCountValid, 5 isAccountFollowingCountValid,
7 isAccountFollowingValid,
8 isAccountInboxValid,
9 isAccountOutboxValid,
10 isAccountPrivateKeyValid, 6 isAccountPrivateKeyValid,
11 isAccountPublicKeyValid, 7 isAccountPublicKeyValid,
12 isAccountSharedInboxValid,
13 isAccountUrlValid,
14 isUserUsernameValid 8 isUserUsernameValid
15} from '../../helpers' 9} from '../../helpers'
10import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
16import { CONFIG, CONSTRAINTS_FIELDS } from '../../initializers/constants' 11import { CONFIG, CONSTRAINTS_FIELDS } from '../../initializers/constants'
17import { sendDeleteAccount } from '../../lib/activitypub/send/send-delete' 12import { sendDeleteAccount } from '../../lib/activitypub/send/send-delete'
18 13
@@ -61,7 +56,7 @@ export default function defineAccount (sequelize: Sequelize.Sequelize, DataTypes
61 allowNull: false, 56 allowNull: false,
62 validate: { 57 validate: {
63 urlValid: value => { 58 urlValid: value => {
64 const res = isAccountUrlValid(value) 59 const res = isActivityPubUrlValid(value)
65 if (res === false) throw new Error('URL is not valid.') 60 if (res === false) throw new Error('URL is not valid.')
66 } 61 }
67 } 62 }
@@ -111,7 +106,7 @@ export default function defineAccount (sequelize: Sequelize.Sequelize, DataTypes
111 allowNull: false, 106 allowNull: false,
112 validate: { 107 validate: {
113 inboxUrlValid: value => { 108 inboxUrlValid: value => {
114 const res = isAccountInboxValid(value) 109 const res = isActivityPubUrlValid(value)
115 if (res === false) throw new Error('Inbox URL is not valid.') 110 if (res === false) throw new Error('Inbox URL is not valid.')
116 } 111 }
117 } 112 }
@@ -121,7 +116,7 @@ export default function defineAccount (sequelize: Sequelize.Sequelize, DataTypes
121 allowNull: false, 116 allowNull: false,
122 validate: { 117 validate: {
123 outboxUrlValid: value => { 118 outboxUrlValid: value => {
124 const res = isAccountOutboxValid(value) 119 const res = isActivityPubUrlValid(value)
125 if (res === false) throw new Error('Outbox URL is not valid.') 120 if (res === false) throw new Error('Outbox URL is not valid.')
126 } 121 }
127 } 122 }
@@ -131,7 +126,7 @@ export default function defineAccount (sequelize: Sequelize.Sequelize, DataTypes
131 allowNull: false, 126 allowNull: false,
132 validate: { 127 validate: {
133 sharedInboxUrlValid: value => { 128 sharedInboxUrlValid: value => {
134 const res = isAccountSharedInboxValid(value) 129 const res = isActivityPubUrlValid(value)
135 if (res === false) throw new Error('Shared inbox URL is not valid.') 130 if (res === false) throw new Error('Shared inbox URL is not valid.')
136 } 131 }
137 } 132 }
@@ -141,7 +136,7 @@ export default function defineAccount (sequelize: Sequelize.Sequelize, DataTypes
141 allowNull: false, 136 allowNull: false,
142 validate: { 137 validate: {
143 followersUrlValid: value => { 138 followersUrlValid: value => {
144 const res = isAccountFollowersValid(value) 139 const res = isActivityPubUrlValid(value)
145 if (res === false) throw new Error('Followers URL is not valid.') 140 if (res === false) throw new Error('Followers URL is not valid.')
146 } 141 }
147 } 142 }
@@ -151,7 +146,7 @@ export default function defineAccount (sequelize: Sequelize.Sequelize, DataTypes
151 allowNull: false, 146 allowNull: false,
152 validate: { 147 validate: {
153 followingUrlValid: value => { 148 followingUrlValid: value => {
154 const res = isAccountFollowingValid(value) 149 const res = isActivityPubUrlValid(value)
155 if (res === false) throw new Error('Following URL is not valid.') 150 if (res === false) throw new Error('Following URL is not valid.')
156 } 151 }
157 } 152 }
diff --git a/server/models/video/video-channel.ts b/server/models/video/video-channel.ts
index e11268b2c..54f12dce3 100644
--- a/server/models/video/video-channel.ts
+++ b/server/models/video/video-channel.ts
@@ -1,6 +1,5 @@
1import * as Sequelize from 'sequelize' 1import * as Sequelize from 'sequelize'
2import { isVideoChannelDescriptionValid, isVideoChannelNameValid } from '../../helpers' 2import { isVideoChannelDescriptionValid, isVideoChannelNameValid } from '../../helpers'
3import { isVideoChannelUrlValid } from '../../helpers/custom-validators/video-channels'
4import { CONSTRAINTS_FIELDS } from '../../initializers/constants' 3import { CONSTRAINTS_FIELDS } from '../../initializers/constants'
5import { sendDeleteVideoChannel } from '../../lib/activitypub/send/send-delete' 4import { sendDeleteVideoChannel } from '../../lib/activitypub/send/send-delete'
6 5
@@ -8,6 +7,7 @@ import { addMethodsToModel, getSort } from '../utils'
8import { VideoChannelAttributes, VideoChannelInstance, VideoChannelMethods } from './video-channel-interface' 7import { VideoChannelAttributes, VideoChannelInstance, VideoChannelMethods } from './video-channel-interface'
9import { getAnnounceActivityPubUrl } from '../../lib/activitypub/url' 8import { getAnnounceActivityPubUrl } from '../../lib/activitypub/url'
10import { activityPubCollection } from '../../helpers/activitypub' 9import { activityPubCollection } from '../../helpers/activitypub'
10import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
11 11
12let VideoChannel: Sequelize.Model<VideoChannelInstance, VideoChannelAttributes> 12let VideoChannel: Sequelize.Model<VideoChannelInstance, VideoChannelAttributes>
13let toFormattedJSON: VideoChannelMethods.ToFormattedJSON 13let toFormattedJSON: VideoChannelMethods.ToFormattedJSON
@@ -66,7 +66,7 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
66 allowNull: false, 66 allowNull: false,
67 validate: { 67 validate: {
68 urlValid: value => { 68 urlValid: value => {
69 const res = isVideoChannelUrlValid(value) 69 const res = isActivityPubUrlValid(value)
70 if (res === false) throw new Error('Video channel URL is not valid.') 70 if (res === false) throw new Error('Video channel URL is not valid.')
71 } 71 }
72 } 72 }
diff --git a/server/models/video/video.ts b/server/models/video/video.ts
index 4956b57ee..3f416d04c 100644
--- a/server/models/video/video.ts
+++ b/server/models/video/video.ts
@@ -7,7 +7,18 @@ import * as Sequelize from 'sequelize'
7import { VideoPrivacy, VideoResolution } from '../../../shared' 7import { VideoPrivacy, VideoResolution } from '../../../shared'
8import { VideoTorrentObject } from '../../../shared/models/activitypub/objects/video-torrent-object' 8import { VideoTorrentObject } from '../../../shared/models/activitypub/objects/video-torrent-object'
9import { activityPubCollection } from '../../helpers/activitypub' 9import { activityPubCollection } from '../../helpers/activitypub'
10import { isVideoCategoryValid, isVideoLanguageValid, isVideoPrivacyValid, isVideoUrlValid } from '../../helpers/custom-validators/videos' 10import { createTorrentPromise, renamePromise, statPromise, unlinkPromise, writeFilePromise } from '../../helpers/core-utils'
11import { isVideoCategoryValid, isVideoLanguageValid, isVideoPrivacyValid } from '../../helpers/custom-validators/videos'
12import { generateImageFromVideoFile, getVideoFileHeight, transcode } from '../../helpers/ffmpeg-utils'
13import {
14 isActivityPubUrlValid,
15 isVideoDescriptionValid,
16 isVideoDurationValid,
17 isVideoLicenceValid,
18 isVideoNameValid,
19 isVideoNSFWValid
20} from '../../helpers/index'
21import { logger } from '../../helpers/logger'
11import { 22import {
12 API_VERSION, 23 API_VERSION,
13 CONFIG, 24 CONFIG,
@@ -21,18 +32,12 @@ import {
21 VIDEO_LICENCES, 32 VIDEO_LICENCES,
22 VIDEO_PRIVACIES 33 VIDEO_PRIVACIES
23} from '../../initializers/constants' 34} from '../../initializers/constants'
35import { getAnnounceActivityPubUrl } from '../../lib/activitypub/url'
24import { sendDeleteVideo } from '../../lib/index' 36import { sendDeleteVideo } from '../../lib/index'
25
26import { addMethodsToModel, getSort } from '../utils' 37import { addMethodsToModel, getSort } from '../utils'
27
28import { TagInstance } from './tag-interface' 38import { TagInstance } from './tag-interface'
29import { VideoFileInstance, VideoFileModel } from './video-file-interface' 39import { VideoFileInstance, VideoFileModel } from './video-file-interface'
30import { VideoAttributes, VideoInstance, VideoMethods } from './video-interface' 40import { VideoAttributes, VideoInstance, VideoMethods } from './video-interface'
31import { isVideoNameValid, isVideoLicenceValid, isVideoNSFWValid, isVideoDescriptionValid, isVideoDurationValid } from '../../helpers/index'
32import { logger } from '../../helpers/logger'
33import { generateImageFromVideoFile, transcode, getVideoFileHeight } from '../../helpers/ffmpeg-utils'
34import { createTorrentPromise, writeFilePromise, unlinkPromise, renamePromise, statPromise } from '../../helpers/core-utils'
35import { getAnnounceActivityPubUrl } from '../../lib/activitypub/url'
36 41
37let Video: Sequelize.Model<VideoInstance, VideoAttributes> 42let Video: Sequelize.Model<VideoInstance, VideoAttributes>
38let getOriginalFile: VideoMethods.GetOriginalFile 43let getOriginalFile: VideoMethods.GetOriginalFile
@@ -205,7 +210,7 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
205 allowNull: false, 210 allowNull: false,
206 validate: { 211 validate: {
207 urlValid: value => { 212 urlValid: value => {
208 const res = isVideoUrlValid(value) 213 const res = isActivityPubUrlValid(value)
209 if (res === false) throw new Error('Video URL is not valid.') 214 if (res === false) throw new Error('Video URL is not valid.')
210 } 215 }
211 } 216 }
diff --git a/support/doc/server/code.md b/support/doc/server/code.md
index f0ed62acc..7233eaf5b 100644
--- a/support/doc/server/code.md
+++ b/support/doc/server/code.md
@@ -55,10 +55,10 @@ If you want to test the decentralization feature, you can easily run 3 instances
55 55
56The server is composed by: 56The server is composed by:
57 57
58 * a REST API (throught Express framework) 58 * a REST API (Express framework)
59 * a WebTorrent Tracker 59 * a WebTorrent Tracker
60 60
61A video is seeded by the server throught the [WebSeed](http://www.bittorrent.org/beps/bep_0019.html) protocol (HTTP). 61A video is seeded by the server with the [WebSeed](http://www.bittorrent.org/beps/bep_0019.html) protocol (HTTP).
62 62
63![Architecture scheme](https://github.com/Chocobozzz/PeerTube/blob/master/support/doc/server/upload-video.png) 63![Architecture scheme](https://github.com/Chocobozzz/PeerTube/blob/master/support/doc/server/upload-video.png)
64 64