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