]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/middlewares/validators/users.ts
Add logs endpoint
[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
c100a614
YB
320const ensureAuthUserOwnsAccountValidator = [
321 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
322 const user = res.locals.oauth.token.User
323
324 if (res.locals.account.id !== user.Account.id) {
325 return res.status(403)
326 .send({ error: 'Only owner can access ratings list.' })
327 .end()
328 }
329
330 return next()
331 }
332]
333
9bd26629
C
334// ---------------------------------------------------------------------------
335
65fcc311
C
336export {
337 usersAddValidator,
92b9d60c 338 deleteMeValidator,
77a5501f 339 usersRegisterValidator,
e6921918 340 usersBlockingValidator,
65fcc311
C
341 usersRemoveValidator,
342 usersUpdateValidator,
8094a898 343 usersUpdateMeValidator,
291e8d3e 344 usersVideoRatingValidator,
8094a898 345 ensureUserRegistrationAllowed,
ff2c1fe8 346 ensureUserRegistrationAllowedForIP,
c5911fd3 347 usersGetValidator,
ecb4e35f 348 usersAskResetPasswordValidator,
d9eaee39
JM
349 usersResetPasswordValidator,
350 usersAskSendVerifyEmailValidator,
74d63469 351 usersVerifyEmailValidator,
c100a614
YB
352 userAutocompleteValidator,
353 ensureAuthUserOwnsAccountValidator
8094a898
C
354}
355
356// ---------------------------------------------------------------------------
357
ecb4e35f
C
358function checkUserIdExist (id: number, res: express.Response) {
359 return checkUserExist(() => UserModel.loadById(id), res)
360}
a2431b7d 361
ecb4e35f
C
362function checkUserEmailExist (email: string, res: express.Response, abortResponse = true) {
363 return checkUserExist(() => UserModel.loadByEmail(email), res, abortResponse)
65fcc311 364}
77a5501f 365
a2431b7d 366async function checkUserNameOrEmailDoesNotAlreadyExist (username: string, email: string, res: express.Response) {
3fd3ab2d 367 const user = await UserModel.loadByUsernameOrEmail(username, email)
a2431b7d
C
368
369 if (user) {
370 res.status(409)
edf7f40a 371 .send({ error: 'User with this username or email already exists.' })
a2431b7d
C
372 .end()
373 return false
374 }
375
2ef6a063
C
376 const actor = await ActorModel.loadLocalByName(username)
377 if (actor) {
378 res.status(409)
c907c2fa 379 .send({ error: 'Another actor (account/channel) with this name on this instance already exists or has already existed.' })
2ef6a063
C
380 .end()
381 return false
382 }
383
a2431b7d 384 return true
77a5501f 385}
ecb4e35f
C
386
387async function checkUserExist (finder: () => Bluebird<UserModel>, res: express.Response, abortResponse = true) {
388 const user = await finder()
389
390 if (!user) {
391 if (abortResponse === true) {
392 res.status(404)
393 .send({ error: 'User not found' })
394 .end()
395 }
396
397 return false
398 }
399
400 res.locals.user = user
401
402 return true
403}