]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/middlewares/validators/users.ts
Don't get recommended videos twice
[github/Chocobozzz/PeerTube.git] / server / middlewares / validators / users.ts
CommitLineData
ecb4e35f 1import * as Bluebird from 'bluebird'
69818c93 2import * as express from 'express'
a2431b7d
C
3import 'express-validator'
4import { body, param } from 'express-validator/check'
ecb4e35f 5import { omit } from 'lodash'
3fd3ab2d 6import { isIdOrUUIDValid } from '../../helpers/custom-validators/misc'
b60e5f38 7import {
eacb25c4 8 isUserAutoPlayVideoValid, isUserBlockedReasonValid,
4bbfc6c6
C
9 isUserDescriptionValid,
10 isUserDisplayNameValid,
0883b324 11 isUserNSFWPolicyValid,
ecb4e35f
C
12 isUserPasswordValid,
13 isUserRoleValid,
14 isUserUsernameValid,
bee0abff
FA
15 isUserVideoQuotaValid,
16 isUserVideoQuotaDailyValid
3fd3ab2d 17} from '../../helpers/custom-validators/users'
47564bbe 18import { isVideoExist } from '../../helpers/custom-validators/videos'
da854ddd 19import { logger } from '../../helpers/logger'
06215f15 20import { isSignupAllowed, isSignupAllowedForCurrentIP } from '../../helpers/signup'
ecb4e35f 21import { Redis } from '../../lib/redis'
3fd3ab2d 22import { UserModel } from '../../models/account/user'
a2431b7d 23import { areValidationErrors } from './utils'
2ef6a063 24import { ActorModel } from '../../models/activitypub/actor'
9bd26629 25
b60e5f38 26const usersAddValidator = [
563d032e 27 body('username').custom(isUserUsernameValid).withMessage('Should have a valid username (lowercase alphanumeric characters)'),
b60e5f38
C
28 body('password').custom(isUserPasswordValid).withMessage('Should have a valid password'),
29 body('email').isEmail().withMessage('Should have a valid email'),
30 body('videoQuota').custom(isUserVideoQuotaValid).withMessage('Should have a valid user quota'),
bee0abff 31 body('videoQuotaDaily').custom(isUserVideoQuotaDailyValid).withMessage('Should have a valid daily user quota'),
954605a8 32 body('role').custom(isUserRoleValid).withMessage('Should have a valid role'),
9bd26629 33
a2431b7d 34 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
ce97fe36 35 logger.debug('Checking usersAdd parameters', { parameters: omit(req.body, 'password') })
9bd26629 36
a2431b7d
C
37 if (areValidationErrors(req, res)) return
38 if (!await checkUserNameOrEmailDoesNotAlreadyExist(req.body.username, req.body.email, res)) return
39
40 return next()
b60e5f38
C
41 }
42]
6fcd19ba 43
b60e5f38
C
44const usersRegisterValidator = [
45 body('username').custom(isUserUsernameValid).withMessage('Should have a valid username'),
46 body('password').custom(isUserPasswordValid).withMessage('Should have a valid password'),
47 body('email').isEmail().withMessage('Should have a valid email'),
77a5501f 48
a2431b7d 49 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
ce97fe36 50 logger.debug('Checking usersRegister parameters', { parameters: omit(req.body, 'password') })
77a5501f 51
a2431b7d
C
52 if (areValidationErrors(req, res)) return
53 if (!await checkUserNameOrEmailDoesNotAlreadyExist(req.body.username, req.body.email, res)) return
54
55 return next()
b60e5f38
C
56 }
57]
9bd26629 58
b60e5f38
C
59const usersRemoveValidator = [
60 param('id').isInt().not().isEmpty().withMessage('Should have a valid id'),
9bd26629 61
a2431b7d 62 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
b60e5f38 63 logger.debug('Checking usersRemove parameters', { parameters: req.params })
9bd26629 64
a2431b7d
C
65 if (areValidationErrors(req, res)) return
66 if (!await checkUserIdExist(req.params.id, res)) return
67
68 const user = res.locals.user
69 if (user.username === 'root') {
70 return res.status(400)
71 .send({ error: 'Cannot remove the root user' })
72 .end()
73 }
74
75 return next()
b60e5f38
C
76 }
77]
8094a898 78
e6921918
C
79const usersBlockingValidator = [
80 param('id').isInt().not().isEmpty().withMessage('Should have a valid id'),
eacb25c4 81 body('reason').optional().custom(isUserBlockedReasonValid).withMessage('Should have a valid blocking reason'),
e6921918
C
82
83 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
eacb25c4 84 logger.debug('Checking usersBlocking parameters', { parameters: req.params })
e6921918
C
85
86 if (areValidationErrors(req, res)) return
87 if (!await checkUserIdExist(req.params.id, res)) return
88
89 const user = res.locals.user
90 if (user.username === 'root') {
91 return res.status(400)
92 .send({ error: 'Cannot block the root user' })
93 .end()
94 }
95
96 return next()
97 }
98]
99
92b9d60c
C
100const deleteMeValidator = [
101 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
102 const user: UserModel = res.locals.oauth.token.User
103 if (user.username === 'root') {
104 return res.status(400)
105 .send({ error: 'You cannot delete your root account.' })
106 .end()
107 }
108
109 return next()
110 }
111]
112
b60e5f38
C
113const usersUpdateValidator = [
114 param('id').isInt().not().isEmpty().withMessage('Should have a valid id'),
115 body('email').optional().isEmail().withMessage('Should have a valid email attribute'),
116 body('videoQuota').optional().custom(isUserVideoQuotaValid).withMessage('Should have a valid user quota'),
bee0abff 117 body('videoQuotaDaily').optional().custom(isUserVideoQuotaDailyValid).withMessage('Should have a valid daily user quota'),
954605a8 118 body('role').optional().custom(isUserRoleValid).withMessage('Should have a valid role'),
8094a898 119
a2431b7d 120 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
b60e5f38 121 logger.debug('Checking usersUpdate parameters', { parameters: req.body })
9bd26629 122
a2431b7d
C
123 if (areValidationErrors(req, res)) return
124 if (!await checkUserIdExist(req.params.id, res)) return
125
f8b8c36b
C
126 const user = res.locals.user
127 if (user.username === 'root' && req.body.role !== undefined && user.role !== req.body.role) {
128 return res.status(400)
129 .send({ error: 'Cannot change root role.' })
130 .end()
131 }
132
a2431b7d 133 return next()
b60e5f38
C
134 }
135]
9bd26629 136
b60e5f38 137const usersUpdateMeValidator = [
ed56ad11 138 body('displayName').optional().custom(isUserDisplayNameValid).withMessage('Should have a valid display name'),
2422c46b 139 body('description').optional().custom(isUserDescriptionValid).withMessage('Should have a valid description'),
b60e5f38
C
140 body('password').optional().custom(isUserPasswordValid).withMessage('Should have a valid password'),
141 body('email').optional().isEmail().withMessage('Should have a valid email attribute'),
0883b324 142 body('nsfwPolicy').optional().custom(isUserNSFWPolicyValid).withMessage('Should have a valid display Not Safe For Work policy'),
7efe153b 143 body('autoPlayVideo').optional().custom(isUserAutoPlayVideoValid).withMessage('Should have a valid automatically plays video attribute'),
9bd26629 144
b60e5f38
C
145 (req: express.Request, res: express.Response, next: express.NextFunction) => {
146 // TODO: Add old password verification
ce97fe36 147 logger.debug('Checking usersUpdateMe parameters', { parameters: omit(req.body, 'password') })
8094a898 148
a2431b7d
C
149 if (areValidationErrors(req, res)) return
150
151 return next()
b60e5f38
C
152 }
153]
8094a898 154
b60e5f38
C
155const usersGetValidator = [
156 param('id').isInt().not().isEmpty().withMessage('Should have a valid id'),
d38b8281 157
a2431b7d 158 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
ce97fe36 159 logger.debug('Checking usersGet parameters', { parameters: req.params })
a2431b7d
C
160
161 if (areValidationErrors(req, res)) return
162 if (!await checkUserIdExist(req.params.id, res)) return
163
164 return next()
b60e5f38
C
165 }
166]
d38b8281 167
b60e5f38 168const usersVideoRatingValidator = [
72c7248b 169 param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid video id'),
0a6658fd 170
a2431b7d 171 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
b60e5f38 172 logger.debug('Checking usersVideoRating parameters', { parameters: req.params })
0a6658fd 173
a2431b7d
C
174 if (areValidationErrors(req, res)) return
175 if (!await isVideoExist(req.params.videoId, res)) return
176
177 return next()
b60e5f38
C
178 }
179]
180
181const ensureUserRegistrationAllowed = [
a2431b7d
C
182 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
183 const allowed = await isSignupAllowed()
184 if (allowed === false) {
185 return res.status(403)
186 .send({ error: 'User registration is not enabled or user limit is reached.' })
187 .end()
188 }
189
190 return next()
b60e5f38
C
191 }
192]
291e8d3e 193
ff2c1fe8
RK
194const ensureUserRegistrationAllowedForIP = [
195 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
196 const allowed = isSignupAllowedForCurrentIP(req.ip)
197
198 if (allowed === false) {
199 return res.status(403)
200 .send({ error: 'You are not on a network authorized for registration.' })
201 .end()
202 }
203
204 return next()
205 }
206]
207
ecb4e35f
C
208const usersAskResetPasswordValidator = [
209 body('email').isEmail().not().isEmpty().withMessage('Should have a valid email'),
210
211 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
212 logger.debug('Checking usersAskResetPassword parameters', { parameters: req.body })
213
214 if (areValidationErrors(req, res)) return
215 const exists = await checkUserEmailExist(req.body.email, res, false)
216 if (!exists) {
217 logger.debug('User with email %s does not exist (asking reset password).', req.body.email)
218 // Do not leak our emails
219 return res.status(204).end()
220 }
221
222 return next()
223 }
224]
225
226const usersResetPasswordValidator = [
227 param('id').isInt().not().isEmpty().withMessage('Should have a valid id'),
228 body('verificationString').not().isEmpty().withMessage('Should have a valid verification string'),
229 body('password').custom(isUserPasswordValid).withMessage('Should have a valid password'),
230
231 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
232 logger.debug('Checking usersResetPassword parameters', { parameters: req.params })
233
234 if (areValidationErrors(req, res)) return
235 if (!await checkUserIdExist(req.params.id, res)) return
236
237 const user = res.locals.user as UserModel
238 const redisVerificationString = await Redis.Instance.getResetPasswordLink(user.id)
239
240 if (redisVerificationString !== req.body.verificationString) {
241 return res
242 .status(403)
243 .send({ error: 'Invalid verification string.' })
f076daa7 244 .end()
ecb4e35f
C
245 }
246
247 return next()
248 }
249]
250
d9eaee39
JM
251const usersAskSendVerifyEmailValidator = [
252 body('email').isEmail().not().isEmpty().withMessage('Should have a valid email'),
253
254 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
255 logger.debug('Checking askUsersSendVerifyEmail parameters', { parameters: req.body })
256
257 if (areValidationErrors(req, res)) return
258 const exists = await checkUserEmailExist(req.body.email, res, false)
259 if (!exists) {
260 logger.debug('User with email %s does not exist (asking verify email).', req.body.email)
261 // Do not leak our emails
262 return res.status(204).end()
263 }
264
265 return next()
266 }
267]
268
269const usersVerifyEmailValidator = [
270 param('id').isInt().not().isEmpty().withMessage('Should have a valid id'),
271 body('verificationString').not().isEmpty().withMessage('Should have a valid verification string'),
272
273 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
274 logger.debug('Checking usersVerifyEmail parameters', { parameters: req.params })
275
276 if (areValidationErrors(req, res)) return
277 if (!await checkUserIdExist(req.params.id, res)) return
278
279 const user = res.locals.user as UserModel
280 const redisVerificationString = await Redis.Instance.getVerifyEmailLink(user.id)
281
282 if (redisVerificationString !== req.body.verificationString) {
283 return res
284 .status(403)
285 .send({ error: 'Invalid verification string.' })
286 .end()
287 }
288
289 return next()
290 }
291]
292
74d63469
GR
293const userAutocompleteValidator = [
294 param('search').isString().not().isEmpty().withMessage('Should have a search parameter')
295]
296
9bd26629
C
297// ---------------------------------------------------------------------------
298
65fcc311
C
299export {
300 usersAddValidator,
92b9d60c 301 deleteMeValidator,
77a5501f 302 usersRegisterValidator,
e6921918 303 usersBlockingValidator,
65fcc311
C
304 usersRemoveValidator,
305 usersUpdateValidator,
8094a898 306 usersUpdateMeValidator,
291e8d3e 307 usersVideoRatingValidator,
8094a898 308 ensureUserRegistrationAllowed,
ff2c1fe8 309 ensureUserRegistrationAllowedForIP,
c5911fd3 310 usersGetValidator,
ecb4e35f 311 usersAskResetPasswordValidator,
d9eaee39
JM
312 usersResetPasswordValidator,
313 usersAskSendVerifyEmailValidator,
74d63469
GR
314 usersVerifyEmailValidator,
315 userAutocompleteValidator
8094a898
C
316}
317
318// ---------------------------------------------------------------------------
319
ecb4e35f
C
320function checkUserIdExist (id: number, res: express.Response) {
321 return checkUserExist(() => UserModel.loadById(id), res)
322}
a2431b7d 323
ecb4e35f
C
324function checkUserEmailExist (email: string, res: express.Response, abortResponse = true) {
325 return checkUserExist(() => UserModel.loadByEmail(email), res, abortResponse)
65fcc311 326}
77a5501f 327
a2431b7d 328async function checkUserNameOrEmailDoesNotAlreadyExist (username: string, email: string, res: express.Response) {
3fd3ab2d 329 const user = await UserModel.loadByUsernameOrEmail(username, email)
a2431b7d
C
330
331 if (user) {
332 res.status(409)
edf7f40a 333 .send({ error: 'User with this username or email already exists.' })
a2431b7d
C
334 .end()
335 return false
336 }
337
2ef6a063
C
338 const actor = await ActorModel.loadLocalByName(username)
339 if (actor) {
340 res.status(409)
c907c2fa 341 .send({ error: 'Another actor (account/channel) with this name on this instance already exists or has already existed.' })
2ef6a063
C
342 .end()
343 return false
344 }
345
a2431b7d 346 return true
77a5501f 347}
ecb4e35f
C
348
349async function checkUserExist (finder: () => Bluebird<UserModel>, res: express.Response, abortResponse = true) {
350 const user = await finder()
351
352 if (!user) {
353 if (abortResponse === true) {
354 res.status(404)
355 .send({ error: 'User not found' })
356 .end()
357 }
358
359 return false
360 }
361
362 res.locals.user = user
363
364 return true
365}