Move job queue to redis
[oweals/peertube.git] / server / middlewares / validators / users.ts
1 import * as express from 'express'
2 import 'express-validator'
3 import { body, param } from 'express-validator/check'
4 import { isIdOrUUIDValid } from '../../helpers/custom-validators/misc'
5 import {
6   isAvatarFile, isUserAutoPlayVideoValid, isUserDisplayNSFWValid, isUserPasswordValid, isUserRoleValid, isUserUsernameValid,
7   isUserVideoQuotaValid
8 } from '../../helpers/custom-validators/users'
9 import { isVideoExist } from '../../helpers/custom-validators/videos'
10 import { logger } from '../../helpers/logger'
11 import { isSignupAllowed } from '../../helpers/utils'
12 import { CONSTRAINTS_FIELDS } from '../../initializers'
13 import { UserModel } from '../../models/account/user'
14 import { areValidationErrors } from './utils'
15
16 const usersAddValidator = [
17   body('username').custom(isUserUsernameValid).withMessage('Should have a valid username (lowercase alphanumeric characters)'),
18   body('password').custom(isUserPasswordValid).withMessage('Should have a valid password'),
19   body('email').isEmail().withMessage('Should have a valid email'),
20   body('videoQuota').custom(isUserVideoQuotaValid).withMessage('Should have a valid user quota'),
21   body('role').custom(isUserRoleValid).withMessage('Should have a valid role'),
22
23   async (req: express.Request, res: express.Response, next: express.NextFunction) => {
24     logger.debug('Checking usersAdd parameters', { parameters: req.body })
25
26     if (areValidationErrors(req, res)) return
27     if (!await checkUserNameOrEmailDoesNotAlreadyExist(req.body.username, req.body.email, res)) return
28
29     return next()
30   }
31 ]
32
33 const usersRegisterValidator = [
34   body('username').custom(isUserUsernameValid).withMessage('Should have a valid username'),
35   body('password').custom(isUserPasswordValid).withMessage('Should have a valid password'),
36   body('email').isEmail().withMessage('Should have a valid email'),
37
38   async (req: express.Request, res: express.Response, next: express.NextFunction) => {
39     logger.debug('Checking usersRegister parameters', { parameters: req.body })
40
41     if (areValidationErrors(req, res)) return
42     if (!await checkUserNameOrEmailDoesNotAlreadyExist(req.body.username, req.body.email, res)) return
43
44     return next()
45   }
46 ]
47
48 const usersRemoveValidator = [
49   param('id').isInt().not().isEmpty().withMessage('Should have a valid id'),
50
51   async (req: express.Request, res: express.Response, next: express.NextFunction) => {
52     logger.debug('Checking usersRemove parameters', { parameters: req.params })
53
54     if (areValidationErrors(req, res)) return
55     if (!await checkUserIdExist(req.params.id, res)) return
56
57     const user = res.locals.user
58     if (user.username === 'root') {
59       return res.status(400)
60                 .send({ error: 'Cannot remove the root user' })
61                 .end()
62     }
63
64     return next()
65   }
66 ]
67
68 const usersUpdateValidator = [
69   param('id').isInt().not().isEmpty().withMessage('Should have a valid id'),
70   body('email').optional().isEmail().withMessage('Should have a valid email attribute'),
71   body('videoQuota').optional().custom(isUserVideoQuotaValid).withMessage('Should have a valid user quota'),
72   body('role').optional().custom(isUserRoleValid).withMessage('Should have a valid role'),
73
74   async (req: express.Request, res: express.Response, next: express.NextFunction) => {
75     logger.debug('Checking usersUpdate parameters', { parameters: req.body })
76
77     if (areValidationErrors(req, res)) return
78     if (!await checkUserIdExist(req.params.id, res)) return
79
80     const user = res.locals.user
81     if (user.username === 'root' && req.body.role !== undefined && user.role !== req.body.role) {
82       return res.status(400)
83         .send({ error: 'Cannot change root role.' })
84         .end()
85     }
86
87     return next()
88   }
89 ]
90
91 const usersUpdateMeValidator = [
92   body('password').optional().custom(isUserPasswordValid).withMessage('Should have a valid password'),
93   body('email').optional().isEmail().withMessage('Should have a valid email attribute'),
94   body('displayNSFW').optional().custom(isUserDisplayNSFWValid).withMessage('Should have a valid display Not Safe For Work attribute'),
95   body('autoPlayVideo').optional().custom(isUserAutoPlayVideoValid).withMessage('Should have a valid automatically plays video attribute'),
96
97   (req: express.Request, res: express.Response, next: express.NextFunction) => {
98     // TODO: Add old password verification
99     logger.debug('Checking usersUpdateMe parameters', { parameters: req.body })
100
101     if (areValidationErrors(req, res)) return
102
103     return next()
104   }
105 ]
106
107 const usersUpdateMyAvatarValidator = [
108   body('avatarfile').custom((value, { req }) => isAvatarFile(req.files)).withMessage(
109     'This file is not supported. Please, make sure it is of the following type : '
110     + CONSTRAINTS_FIELDS.ACTORS.AVATAR.EXTNAME.join(', ')
111   ),
112
113   (req: express.Request, res: express.Response, next: express.NextFunction) => {
114     logger.debug('Checking usersUpdateMyAvatarValidator parameters', { files: req.files })
115
116     if (areValidationErrors(req, res)) return
117
118     const imageFile = req.files['avatarfile'][0] as Express.Multer.File
119     if (imageFile.size > CONSTRAINTS_FIELDS.ACTORS.AVATAR.FILE_SIZE.max) {
120       res.status(400)
121         .send({ error: `The size of the avatar is too big (>${CONSTRAINTS_FIELDS.ACTORS.AVATAR.FILE_SIZE.max}).` })
122         .end()
123       return
124     }
125
126     return next()
127   }
128 ]
129
130 const usersGetValidator = [
131   param('id').isInt().not().isEmpty().withMessage('Should have a valid id'),
132
133   async (req: express.Request, res: express.Response, next: express.NextFunction) => {
134     logger.debug('Checking usersGet parameters', { parameters: req.body })
135
136     if (areValidationErrors(req, res)) return
137     if (!await checkUserIdExist(req.params.id, res)) return
138
139     return next()
140   }
141 ]
142
143 const usersVideoRatingValidator = [
144   param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid video id'),
145
146   async (req: express.Request, res: express.Response, next: express.NextFunction) => {
147     logger.debug('Checking usersVideoRating parameters', { parameters: req.params })
148
149     if (areValidationErrors(req, res)) return
150     if (!await isVideoExist(req.params.videoId, res)) return
151
152     return next()
153   }
154 ]
155
156 const ensureUserRegistrationAllowed = [
157   async (req: express.Request, res: express.Response, next: express.NextFunction) => {
158     const allowed = await isSignupAllowed()
159     if (allowed === false) {
160       return res.status(403)
161                 .send({ error: 'User registration is not enabled or user limit is reached.' })
162                 .end()
163     }
164
165     return next()
166   }
167 ]
168
169 // ---------------------------------------------------------------------------
170
171 export {
172   usersAddValidator,
173   usersRegisterValidator,
174   usersRemoveValidator,
175   usersUpdateValidator,
176   usersUpdateMeValidator,
177   usersVideoRatingValidator,
178   ensureUserRegistrationAllowed,
179   usersGetValidator,
180   usersUpdateMyAvatarValidator
181 }
182
183 // ---------------------------------------------------------------------------
184
185 async function checkUserIdExist (id: number, res: express.Response) {
186   const user = await UserModel.loadById(id)
187
188   if (!user) {
189     res.status(404)
190               .send({ error: 'User not found' })
191               .end()
192
193     return false
194   }
195
196   res.locals.user = user
197   return true
198 }
199
200 async function checkUserNameOrEmailDoesNotAlreadyExist (username: string, email: string, res: express.Response) {
201   const user = await UserModel.loadByUsernameOrEmail(username, email)
202
203   if (user) {
204     res.status(409)
205               .send({ error: 'User with this username of email already exists.' })
206               .end()
207     return false
208   }
209
210   return true
211 }