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