aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/middlewares
diff options
context:
space:
mode:
Diffstat (limited to 'server/middlewares')
-rw-r--r--server/middlewares/csp.ts44
-rw-r--r--server/middlewares/dnt.ts2
-rw-r--r--server/middlewares/index.ts2
-rw-r--r--server/middlewares/oauth.ts22
-rw-r--r--server/middlewares/validators/config.ts19
-rw-r--r--server/middlewares/validators/index.ts1
-rw-r--r--server/middlewares/validators/server.ts49
-rw-r--r--server/middlewares/validators/sort.ts5
-rw-r--r--server/middlewares/validators/user-history.ts26
-rw-r--r--server/middlewares/validators/user-notifications.ts63
-rw-r--r--server/middlewares/validators/users.ts11
-rw-r--r--server/middlewares/validators/videos/video-blacklist.ts15
-rw-r--r--server/middlewares/validators/videos/video-watch.ts7
13 files changed, 255 insertions, 11 deletions
diff --git a/server/middlewares/csp.ts b/server/middlewares/csp.ts
new file mode 100644
index 000000000..8b919af0d
--- /dev/null
+++ b/server/middlewares/csp.ts
@@ -0,0 +1,44 @@
1import * as helmet from 'helmet'
2import { CONFIG } from '../initializers/constants'
3
4const baseDirectives = Object.assign({},
5 {
6 defaultSrc: ["'none'"], // by default, not specifying default-src = '*'
7 connectSrc: ['*', 'data:'],
8 mediaSrc: ["'self'", 'https:', 'blob:'],
9 fontSrc: ["'self'", 'data:'],
10 imgSrc: ["'self'", 'data:'],
11 scriptSrc: ["'self' 'unsafe-inline' 'unsafe-eval'"],
12 styleSrc: ["'self' 'unsafe-inline'"],
13 objectSrc: ["'none'"], // only define to allow plugins, else let defaultSrc 'none' block it
14 formAction: ["'self'"],
15 frameAncestors: ["'none'"],
16 baseUri: ["'self'"],
17 manifestSrc: ["'self'"],
18 frameSrc: ["'self'"], // instead of deprecated child-src / self because of test-embed
19 workerSrc: ["'self'"] // instead of deprecated child-src
20 },
21 CONFIG.SERVICES['CSP-LOGGER'] ? { reportUri: CONFIG.SERVICES['CSP-LOGGER'] } : {},
22 CONFIG.WEBSERVER.SCHEME === 'https' ? { upgradeInsecureRequests: true } : {}
23)
24
25const baseCSP = helmet.contentSecurityPolicy({
26 directives: baseDirectives,
27 browserSniff: false,
28 reportOnly: true
29})
30
31const embedCSP = helmet.contentSecurityPolicy({
32 directives: Object.assign(baseDirectives, {
33 frameAncestors: ['*']
34 }),
35 browserSniff: false, // assumes a modern browser, but allows CDN in front
36 reportOnly: true
37})
38
39// ---------------------------------------------------------------------------
40
41export {
42 baseCSP,
43 embedCSP
44}
diff --git a/server/middlewares/dnt.ts b/server/middlewares/dnt.ts
index cabad39c6..607def855 100644
--- a/server/middlewares/dnt.ts
+++ b/server/middlewares/dnt.ts
@@ -10,4 +10,4 @@ const advertiseDoNotTrack = (_, res, next) => {
10 10
11export { 11export {
12 advertiseDoNotTrack 12 advertiseDoNotTrack
13 } 13}
diff --git a/server/middlewares/index.ts b/server/middlewares/index.ts
index 0cef26953..b758a8586 100644
--- a/server/middlewares/index.ts
+++ b/server/middlewares/index.ts
@@ -6,3 +6,5 @@ export * from './pagination'
6export * from './servers' 6export * from './servers'
7export * from './sort' 7export * from './sort'
8export * from './user-right' 8export * from './user-right'
9export * from './dnt'
10export * from './csp'
diff --git a/server/middlewares/oauth.ts b/server/middlewares/oauth.ts
index 8c1df2c3e..1d193d467 100644
--- a/server/middlewares/oauth.ts
+++ b/server/middlewares/oauth.ts
@@ -3,6 +3,8 @@ import * as OAuthServer from 'express-oauth-server'
3import 'express-validator' 3import 'express-validator'
4import { OAUTH_LIFETIME } from '../initializers' 4import { OAUTH_LIFETIME } from '../initializers'
5import { logger } from '../helpers/logger' 5import { logger } from '../helpers/logger'
6import { Socket } from 'socket.io'
7import { getAccessToken } from '../lib/oauth-model'
6 8
7const oAuthServer = new OAuthServer({ 9const oAuthServer = new OAuthServer({
8 useErrorHandler: true, 10 useErrorHandler: true,
@@ -28,6 +30,25 @@ function authenticate (req: express.Request, res: express.Response, next: expres
28 }) 30 })
29} 31}
30 32
33function authenticateSocket (socket: Socket, next: (err?: any) => void) {
34 const accessToken = socket.handshake.query.accessToken
35
36 logger.debug('Checking socket access token %s.', accessToken)
37
38 getAccessToken(accessToken)
39 .then(tokenDB => {
40 const now = new Date()
41
42 if (!tokenDB || tokenDB.accessTokenExpiresAt < now || tokenDB.refreshTokenExpiresAt < now) {
43 return next(new Error('Invalid access token.'))
44 }
45
46 socket.handshake.query.user = tokenDB.User
47
48 return next()
49 })
50}
51
31function authenticatePromiseIfNeeded (req: express.Request, res: express.Response) { 52function authenticatePromiseIfNeeded (req: express.Request, res: express.Response) {
32 return new Promise(resolve => { 53 return new Promise(resolve => {
33 // Already authenticated? (or tried to) 54 // Already authenticated? (or tried to)
@@ -68,6 +89,7 @@ function token (req: express.Request, res: express.Response, next: express.NextF
68 89
69export { 90export {
70 authenticate, 91 authenticate,
92 authenticateSocket,
71 authenticatePromiseIfNeeded, 93 authenticatePromiseIfNeeded,
72 optionalAuthenticate, 94 optionalAuthenticate,
73 token 95 token
diff --git a/server/middlewares/validators/config.ts b/server/middlewares/validators/config.ts
index f3f257d57..90108fa82 100644
--- a/server/middlewares/validators/config.ts
+++ b/server/middlewares/validators/config.ts
@@ -1,29 +1,44 @@
1import * as express from 'express' 1import * as express from 'express'
2import { body } from 'express-validator/check' 2import { body } from 'express-validator/check'
3import { isUserNSFWPolicyValid, isUserVideoQuotaValid } from '../../helpers/custom-validators/users' 3import { isUserNSFWPolicyValid, isUserVideoQuotaValid, isUserVideoQuotaDailyValid } from '../../helpers/custom-validators/users'
4import { logger } from '../../helpers/logger' 4import { logger } from '../../helpers/logger'
5import { areValidationErrors } from './utils' 5import { areValidationErrors } from './utils'
6 6
7const customConfigUpdateValidator = [ 7const customConfigUpdateValidator = [
8 body('instance.name').exists().withMessage('Should have a valid instance name'), 8 body('instance.name').exists().withMessage('Should have a valid instance name'),
9 body('instance.shortDescription').exists().withMessage('Should have a valid instance short description'),
9 body('instance.description').exists().withMessage('Should have a valid instance description'), 10 body('instance.description').exists().withMessage('Should have a valid instance description'),
10 body('instance.terms').exists().withMessage('Should have a valid instance terms'), 11 body('instance.terms').exists().withMessage('Should have a valid instance terms'),
11 body('instance.defaultClientRoute').exists().withMessage('Should have a valid instance default client route'), 12 body('instance.defaultClientRoute').exists().withMessage('Should have a valid instance default client route'),
12 body('instance.defaultNSFWPolicy').custom(isUserNSFWPolicyValid).withMessage('Should have a valid NSFW policy'), 13 body('instance.defaultNSFWPolicy').custom(isUserNSFWPolicyValid).withMessage('Should have a valid NSFW policy'),
13 body('instance.customizations.css').exists().withMessage('Should have a valid instance CSS customization'), 14 body('instance.customizations.css').exists().withMessage('Should have a valid instance CSS customization'),
14 body('instance.customizations.javascript').exists().withMessage('Should have a valid instance JavaScript customization'), 15 body('instance.customizations.javascript').exists().withMessage('Should have a valid instance JavaScript customization'),
15 body('cache.previews.size').isInt().withMessage('Should have a valid previews size'), 16
17 body('services.twitter.username').exists().withMessage('Should have a valid twitter username'),
18 body('services.twitter.whitelisted').isBoolean().withMessage('Should have a valid twitter whitelisted boolean'),
19
20 body('cache.previews.size').isInt().withMessage('Should have a valid previews cache size'),
21 body('cache.captions.size').isInt().withMessage('Should have a valid captions cache size'),
22
16 body('signup.enabled').isBoolean().withMessage('Should have a valid signup enabled boolean'), 23 body('signup.enabled').isBoolean().withMessage('Should have a valid signup enabled boolean'),
17 body('signup.limit').isInt().withMessage('Should have a valid signup limit'), 24 body('signup.limit').isInt().withMessage('Should have a valid signup limit'),
25 body('signup.requiresEmailVerification').isBoolean().withMessage('Should have a valid requiresEmailVerification boolean'),
26
18 body('admin.email').isEmail().withMessage('Should have a valid administrator email'), 27 body('admin.email').isEmail().withMessage('Should have a valid administrator email'),
28 body('contactForm.enabled').isBoolean().withMessage('Should have a valid contact form enabled boolean'),
29
19 body('user.videoQuota').custom(isUserVideoQuotaValid).withMessage('Should have a valid video quota'), 30 body('user.videoQuota').custom(isUserVideoQuotaValid).withMessage('Should have a valid video quota'),
31 body('user.videoQuotaDaily').custom(isUserVideoQuotaDailyValid).withMessage('Should have a valid daily video quota'),
32
20 body('transcoding.enabled').isBoolean().withMessage('Should have a valid transcoding enabled boolean'), 33 body('transcoding.enabled').isBoolean().withMessage('Should have a valid transcoding enabled boolean'),
34 body('transcoding.allowAdditionalExtensions').isBoolean().withMessage('Should have a valid additional extensions boolean'),
21 body('transcoding.threads').isInt().withMessage('Should have a valid transcoding threads number'), 35 body('transcoding.threads').isInt().withMessage('Should have a valid transcoding threads number'),
22 body('transcoding.resolutions.240p').isBoolean().withMessage('Should have a valid transcoding 240p resolution enabled boolean'), 36 body('transcoding.resolutions.240p').isBoolean().withMessage('Should have a valid transcoding 240p resolution enabled boolean'),
23 body('transcoding.resolutions.360p').isBoolean().withMessage('Should have a valid transcoding 360p resolution enabled boolean'), 37 body('transcoding.resolutions.360p').isBoolean().withMessage('Should have a valid transcoding 360p resolution enabled boolean'),
24 body('transcoding.resolutions.480p').isBoolean().withMessage('Should have a valid transcoding 480p resolution enabled boolean'), 38 body('transcoding.resolutions.480p').isBoolean().withMessage('Should have a valid transcoding 480p resolution enabled boolean'),
25 body('transcoding.resolutions.720p').isBoolean().withMessage('Should have a valid transcoding 720p resolution enabled boolean'), 39 body('transcoding.resolutions.720p').isBoolean().withMessage('Should have a valid transcoding 720p resolution enabled boolean'),
26 body('transcoding.resolutions.1080p').isBoolean().withMessage('Should have a valid transcoding 1080p resolution enabled boolean'), 40 body('transcoding.resolutions.1080p').isBoolean().withMessage('Should have a valid transcoding 1080p resolution enabled boolean'),
41
27 body('import.videos.http.enabled').isBoolean().withMessage('Should have a valid import video http enabled boolean'), 42 body('import.videos.http.enabled').isBoolean().withMessage('Should have a valid import video http enabled boolean'),
28 body('import.videos.torrent.enabled').isBoolean().withMessage('Should have a valid import video torrent enabled boolean'), 43 body('import.videos.torrent.enabled').isBoolean().withMessage('Should have a valid import video torrent enabled boolean'),
29 44
diff --git a/server/middlewares/validators/index.ts b/server/middlewares/validators/index.ts
index 46c7f0f3a..65dd00335 100644
--- a/server/middlewares/validators/index.ts
+++ b/server/middlewares/validators/index.ts
@@ -12,3 +12,4 @@ export * from './videos'
12export * from './webfinger' 12export * from './webfinger'
13export * from './search' 13export * from './search'
14export * from './server' 14export * from './server'
15export * from './user-history'
diff --git a/server/middlewares/validators/server.ts b/server/middlewares/validators/server.ts
index a491dfeb3..d85afc2ff 100644
--- a/server/middlewares/validators/server.ts
+++ b/server/middlewares/validators/server.ts
@@ -1,9 +1,13 @@
1import * as express from 'express' 1import * as express from 'express'
2import { logger } from '../../helpers/logger' 2import { logger } from '../../helpers/logger'
3import { areValidationErrors } from './utils' 3import { areValidationErrors } from './utils'
4import { isHostValid } from '../../helpers/custom-validators/servers' 4import { isHostValid, isValidContactBody } from '../../helpers/custom-validators/servers'
5import { ServerModel } from '../../models/server/server' 5import { ServerModel } from '../../models/server/server'
6import { body } from 'express-validator/check' 6import { body } from 'express-validator/check'
7import { isUserDisplayNameValid } from '../../helpers/custom-validators/users'
8import { Emailer } from '../../lib/emailer'
9import { Redis } from '../../lib/redis'
10import { CONFIG } from '../../initializers/constants'
7 11
8const serverGetValidator = [ 12const serverGetValidator = [
9 body('host').custom(isHostValid).withMessage('Should have a valid host'), 13 body('host').custom(isHostValid).withMessage('Should have a valid host'),
@@ -26,8 +30,49 @@ const serverGetValidator = [
26 } 30 }
27] 31]
28 32
33const contactAdministratorValidator = [
34 body('fromName')
35 .custom(isUserDisplayNameValid).withMessage('Should have a valid name'),
36 body('fromEmail')
37 .isEmail().withMessage('Should have a valid email'),
38 body('body')
39 .custom(isValidContactBody).withMessage('Should have a valid body'),
40
41 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
42 logger.debug('Checking contactAdministratorValidator parameters', { parameters: req.body })
43
44 if (areValidationErrors(req, res)) return
45
46 if (CONFIG.CONTACT_FORM.ENABLED === false) {
47 return res
48 .status(409)
49 .send({ error: 'Contact form is not enabled on this instance.' })
50 .end()
51 }
52
53 if (Emailer.isEnabled() === false) {
54 return res
55 .status(409)
56 .send({ error: 'Emailer is not enabled on this instance.' })
57 .end()
58 }
59
60 if (await Redis.Instance.isContactFormIpExists(req.ip)) {
61 logger.info('Refusing a contact form by %s: already sent one recently.', req.ip)
62
63 return res
64 .status(403)
65 .send({ error: 'You already sent a contact form recently.' })
66 .end()
67 }
68
69 return next()
70 }
71]
72
29// --------------------------------------------------------------------------- 73// ---------------------------------------------------------------------------
30 74
31export { 75export {
32 serverGetValidator 76 serverGetValidator,
77 contactAdministratorValidator
33} 78}
diff --git a/server/middlewares/validators/sort.ts b/server/middlewares/validators/sort.ts
index 4c0577d8f..5ceda845f 100644
--- a/server/middlewares/validators/sort.ts
+++ b/server/middlewares/validators/sort.ts
@@ -18,6 +18,7 @@ const SORTABLE_FOLLOWING_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.FOLLOW
18const SORTABLE_USER_SUBSCRIPTIONS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.USER_SUBSCRIPTIONS) 18const SORTABLE_USER_SUBSCRIPTIONS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.USER_SUBSCRIPTIONS)
19const SORTABLE_ACCOUNTS_BLOCKLIST_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.ACCOUNTS_BLOCKLIST) 19const SORTABLE_ACCOUNTS_BLOCKLIST_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.ACCOUNTS_BLOCKLIST)
20const SORTABLE_SERVERS_BLOCKLIST_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.SERVERS_BLOCKLIST) 20const SORTABLE_SERVERS_BLOCKLIST_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.SERVERS_BLOCKLIST)
21const SORTABLE_USER_NOTIFICATIONS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.USER_NOTIFICATIONS)
21 22
22const usersSortValidator = checkSort(SORTABLE_USERS_COLUMNS) 23const usersSortValidator = checkSort(SORTABLE_USERS_COLUMNS)
23const accountsSortValidator = checkSort(SORTABLE_ACCOUNTS_COLUMNS) 24const accountsSortValidator = checkSort(SORTABLE_ACCOUNTS_COLUMNS)
@@ -35,6 +36,7 @@ const followingSortValidator = checkSort(SORTABLE_FOLLOWING_COLUMNS)
35const userSubscriptionsSortValidator = checkSort(SORTABLE_USER_SUBSCRIPTIONS_COLUMNS) 36const userSubscriptionsSortValidator = checkSort(SORTABLE_USER_SUBSCRIPTIONS_COLUMNS)
36const accountsBlocklistSortValidator = checkSort(SORTABLE_ACCOUNTS_BLOCKLIST_COLUMNS) 37const accountsBlocklistSortValidator = checkSort(SORTABLE_ACCOUNTS_BLOCKLIST_COLUMNS)
37const serversBlocklistSortValidator = checkSort(SORTABLE_SERVERS_BLOCKLIST_COLUMNS) 38const serversBlocklistSortValidator = checkSort(SORTABLE_SERVERS_BLOCKLIST_COLUMNS)
39const userNotificationsSortValidator = checkSort(SORTABLE_USER_NOTIFICATIONS_COLUMNS)
38 40
39// --------------------------------------------------------------------------- 41// ---------------------------------------------------------------------------
40 42
@@ -54,5 +56,6 @@ export {
54 userSubscriptionsSortValidator, 56 userSubscriptionsSortValidator,
55 videoChannelsSearchSortValidator, 57 videoChannelsSearchSortValidator,
56 accountsBlocklistSortValidator, 58 accountsBlocklistSortValidator,
57 serversBlocklistSortValidator 59 serversBlocklistSortValidator,
60 userNotificationsSortValidator
58} 61}
diff --git a/server/middlewares/validators/user-history.ts b/server/middlewares/validators/user-history.ts
new file mode 100644
index 000000000..418313d09
--- /dev/null
+++ b/server/middlewares/validators/user-history.ts
@@ -0,0 +1,26 @@
1import * as express from 'express'
2import 'express-validator'
3import { body } from 'express-validator/check'
4import { logger } from '../../helpers/logger'
5import { areValidationErrors } from './utils'
6import { isDateValid } from '../../helpers/custom-validators/misc'
7
8const userHistoryRemoveValidator = [
9 body('beforeDate')
10 .optional()
11 .custom(isDateValid).withMessage('Should have a valid before date'),
12
13 (req: express.Request, res: express.Response, next: express.NextFunction) => {
14 logger.debug('Checking userHistoryRemoveValidator parameters', { parameters: req.body })
15
16 if (areValidationErrors(req, res)) return
17
18 return next()
19 }
20]
21
22// ---------------------------------------------------------------------------
23
24export {
25 userHistoryRemoveValidator
26}
diff --git a/server/middlewares/validators/user-notifications.ts b/server/middlewares/validators/user-notifications.ts
new file mode 100644
index 000000000..46486e081
--- /dev/null
+++ b/server/middlewares/validators/user-notifications.ts
@@ -0,0 +1,63 @@
1import * as express from 'express'
2import 'express-validator'
3import { body, query } from 'express-validator/check'
4import { logger } from '../../helpers/logger'
5import { areValidationErrors } from './utils'
6import { isUserNotificationSettingValid } from '../../helpers/custom-validators/user-notifications'
7import { isNotEmptyIntArray } from '../../helpers/custom-validators/misc'
8
9const listUserNotificationsValidator = [
10 query('unread')
11 .optional()
12 .toBoolean()
13 .isBoolean().withMessage('Should have a valid unread boolean'),
14
15 (req: express.Request, res: express.Response, next: express.NextFunction) => {
16 logger.debug('Checking listUserNotificationsValidator parameters', { parameters: req.query })
17
18 if (areValidationErrors(req, res)) return
19
20 return next()
21 }
22]
23
24const updateNotificationSettingsValidator = [
25 body('newVideoFromSubscription')
26 .custom(isUserNotificationSettingValid).withMessage('Should have a valid new video from subscription notification setting'),
27 body('newCommentOnMyVideo')
28 .custom(isUserNotificationSettingValid).withMessage('Should have a valid new comment on my video notification setting'),
29 body('videoAbuseAsModerator')
30 .custom(isUserNotificationSettingValid).withMessage('Should have a valid new video abuse as moderator notification setting'),
31 body('blacklistOnMyVideo')
32 .custom(isUserNotificationSettingValid).withMessage('Should have a valid new blacklist on my video notification setting'),
33
34 (req: express.Request, res: express.Response, next: express.NextFunction) => {
35 logger.debug('Checking updateNotificationSettingsValidator parameters', { parameters: req.body })
36
37 if (areValidationErrors(req, res)) return
38
39 return next()
40 }
41]
42
43const markAsReadUserNotificationsValidator = [
44 body('ids')
45 .optional()
46 .custom(isNotEmptyIntArray).withMessage('Should have a valid notification ids to mark as read'),
47
48 (req: express.Request, res: express.Response, next: express.NextFunction) => {
49 logger.debug('Checking markAsReadUserNotificationsValidator parameters', { parameters: req.body })
50
51 if (areValidationErrors(req, res)) return
52
53 return next()
54 }
55]
56
57// ---------------------------------------------------------------------------
58
59export {
60 listUserNotificationsValidator,
61 updateNotificationSettingsValidator,
62 markAsReadUserNotificationsValidator
63}
diff --git a/server/middlewares/validators/users.ts b/server/middlewares/validators/users.ts
index ccaf2eeb6..1bb0bfb1b 100644
--- a/server/middlewares/validators/users.ts
+++ b/server/middlewares/validators/users.ts
@@ -5,15 +5,16 @@ import { body, param } from 'express-validator/check'
5import { omit } from 'lodash' 5import { omit } from 'lodash'
6import { isIdOrUUIDValid } from '../../helpers/custom-validators/misc' 6import { isIdOrUUIDValid } from '../../helpers/custom-validators/misc'
7import { 7import {
8 isUserAutoPlayVideoValid, isUserBlockedReasonValid, 8 isUserAutoPlayVideoValid,
9 isUserBlockedReasonValid,
9 isUserDescriptionValid, 10 isUserDescriptionValid,
10 isUserDisplayNameValid, 11 isUserDisplayNameValid,
11 isUserNSFWPolicyValid, 12 isUserNSFWPolicyValid,
12 isUserPasswordValid, 13 isUserPasswordValid,
13 isUserRoleValid, 14 isUserRoleValid,
14 isUserUsernameValid, 15 isUserUsernameValid,
15 isUserVideoQuotaValid, 16 isUserVideoQuotaDailyValid,
16 isUserVideoQuotaDailyValid 17 isUserVideoQuotaValid, isUserVideosHistoryEnabledValid
17} from '../../helpers/custom-validators/users' 18} from '../../helpers/custom-validators/users'
18import { isVideoExist } from '../../helpers/custom-validators/videos' 19import { isVideoExist } from '../../helpers/custom-validators/videos'
19import { logger } from '../../helpers/logger' 20import { logger } from '../../helpers/logger'
@@ -22,7 +23,6 @@ import { Redis } from '../../lib/redis'
22import { UserModel } from '../../models/account/user' 23import { UserModel } from '../../models/account/user'
23import { areValidationErrors } from './utils' 24import { areValidationErrors } from './utils'
24import { ActorModel } from '../../models/activitypub/actor' 25import { ActorModel } from '../../models/activitypub/actor'
25import { comparePassword } from '../../helpers/peertube-crypto'
26 26
27const usersAddValidator = [ 27const usersAddValidator = [
28 body('username').custom(isUserUsernameValid).withMessage('Should have a valid username (lowercase alphanumeric characters)'), 28 body('username').custom(isUserUsernameValid).withMessage('Should have a valid username (lowercase alphanumeric characters)'),
@@ -144,6 +144,9 @@ const usersUpdateMeValidator = [
144 body('email').optional().isEmail().withMessage('Should have a valid email attribute'), 144 body('email').optional().isEmail().withMessage('Should have a valid email attribute'),
145 body('nsfwPolicy').optional().custom(isUserNSFWPolicyValid).withMessage('Should have a valid display Not Safe For Work policy'), 145 body('nsfwPolicy').optional().custom(isUserNSFWPolicyValid).withMessage('Should have a valid display Not Safe For Work policy'),
146 body('autoPlayVideo').optional().custom(isUserAutoPlayVideoValid).withMessage('Should have a valid automatically plays video attribute'), 146 body('autoPlayVideo').optional().custom(isUserAutoPlayVideoValid).withMessage('Should have a valid automatically plays video attribute'),
147 body('videosHistoryEnabled')
148 .optional()
149 .custom(isUserVideosHistoryEnabledValid).withMessage('Should have a valid videos history enabled attribute'),
147 150
148 async (req: express.Request, res: express.Response, next: express.NextFunction) => { 151 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
149 logger.debug('Checking usersUpdateMe parameters', { parameters: omit(req.body, 'password') }) 152 logger.debug('Checking usersUpdateMe parameters', { parameters: omit(req.body, 'password') })
diff --git a/server/middlewares/validators/videos/video-blacklist.ts b/server/middlewares/validators/videos/video-blacklist.ts
index 13da7acff..2688f63ae 100644
--- a/server/middlewares/validators/videos/video-blacklist.ts
+++ b/server/middlewares/validators/videos/video-blacklist.ts
@@ -1,10 +1,11 @@
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 { isIdOrUUIDValid } from '../../../helpers/custom-validators/misc' 3import { isBooleanValid, isIdOrUUIDValid } from '../../../helpers/custom-validators/misc'
4import { isVideoExist } from '../../../helpers/custom-validators/videos' 4import { isVideoExist } from '../../../helpers/custom-validators/videos'
5import { logger } from '../../../helpers/logger' 5import { logger } from '../../../helpers/logger'
6import { areValidationErrors } from '../utils' 6import { areValidationErrors } from '../utils'
7import { isVideoBlacklistExist, isVideoBlacklistReasonValid } from '../../../helpers/custom-validators/video-blacklist' 7import { isVideoBlacklistExist, isVideoBlacklistReasonValid } from '../../../helpers/custom-validators/video-blacklist'
8import { VideoModel } from '../../../models/video/video'
8 9
9const videosBlacklistRemoveValidator = [ 10const videosBlacklistRemoveValidator = [
10 param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'), 11 param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'),
@@ -22,6 +23,10 @@ const videosBlacklistRemoveValidator = [
22 23
23const videosBlacklistAddValidator = [ 24const videosBlacklistAddValidator = [
24 param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'), 25 param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'),
26 body('unfederate')
27 .optional()
28 .toBoolean()
29 .custom(isBooleanValid).withMessage('Should have a valid unfederate boolean'),
25 body('reason') 30 body('reason')
26 .optional() 31 .optional()
27 .custom(isVideoBlacklistReasonValid).withMessage('Should have a valid reason'), 32 .custom(isVideoBlacklistReasonValid).withMessage('Should have a valid reason'),
@@ -32,6 +37,14 @@ const videosBlacklistAddValidator = [
32 if (areValidationErrors(req, res)) return 37 if (areValidationErrors(req, res)) return
33 if (!await isVideoExist(req.params.videoId, res)) return 38 if (!await isVideoExist(req.params.videoId, res)) return
34 39
40 const video: VideoModel = res.locals.video
41 if (req.body.unfederate === true && video.remote === true) {
42 return res
43 .status(409)
44 .send({ error: 'You cannot unfederate a remote video.' })
45 .end()
46 }
47
35 return next() 48 return next()
36 } 49 }
37] 50]
diff --git a/server/middlewares/validators/videos/video-watch.ts b/server/middlewares/validators/videos/video-watch.ts
index bca64662f..c38ad8a10 100644
--- a/server/middlewares/validators/videos/video-watch.ts
+++ b/server/middlewares/validators/videos/video-watch.ts
@@ -4,6 +4,7 @@ import { isIdOrUUIDValid } from '../../../helpers/custom-validators/misc'
4import { isVideoExist } from '../../../helpers/custom-validators/videos' 4import { isVideoExist } from '../../../helpers/custom-validators/videos'
5import { areValidationErrors } from '../utils' 5import { areValidationErrors } from '../utils'
6import { logger } from '../../../helpers/logger' 6import { logger } from '../../../helpers/logger'
7import { UserModel } from '../../../models/account/user'
7 8
8const videoWatchingValidator = [ 9const videoWatchingValidator = [
9 param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), 10 param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'),
@@ -17,6 +18,12 @@ const videoWatchingValidator = [
17 if (areValidationErrors(req, res)) return 18 if (areValidationErrors(req, res)) return
18 if (!await isVideoExist(req.params.videoId, res, 'id')) return 19 if (!await isVideoExist(req.params.videoId, res, 'id')) return
19 20
21 const user = res.locals.oauth.token.User as UserModel
22 if (user.videosHistoryEnabled === false) {
23 logger.warn('Cannot set videos to watch by user %d: videos history is disabled.', user.id)
24 return res.status(409).end()
25 }
26
20 return next() 27 return next()
21 } 28 }
22] 29]