]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/middlewares/validators/users.ts
Merge branch 'release/4.3.0' into develop
[github/Chocobozzz/PeerTube.git] / server / middlewares / validators / users.ts
CommitLineData
41fb13c3 1import express from 'express'
76314386 2import { body, param, query } from 'express-validator'
b49f22d8 3import { Hooks } from '@server/lib/plugins/hooks'
a37e9e74 4import { HttpStatusCode, UserRegister, UserRight, UserRole } from '@shared/models'
56f47830 5import { exists, isBooleanValid, isIdValid, toBooleanOrNull, toIntOrNull } from '../../helpers/custom-validators/misc'
b49f22d8 6import { isThemeNameValid } from '../../helpers/custom-validators/plugins'
b60e5f38 7import {
1eddc9a7 8 isUserAdminFlagsValid,
c1e5bd23 9 isUserAutoPlayNextVideoValid,
1a12adcd
C
10 isUserAutoPlayVideoValid,
11 isUserBlockedReasonValid,
4bbfc6c6
C
12 isUserDescriptionValid,
13 isUserDisplayNameValid,
8f581725 14 isUserNoModal,
0883b324 15 isUserNSFWPolicyValid,
a9bfa85d 16 isUserP2PEnabledValid,
ecb4e35f 17 isUserPasswordValid,
45f1bd72 18 isUserPasswordValidOrEmpty,
ecb4e35f 19 isUserRoleValid,
3e753302
C
20 isUserUsernameValid,
21 isUserVideoLanguages,
1a12adcd 22 isUserVideoQuotaDailyValid,
dae86118
C
23 isUserVideoQuotaValid,
24 isUserVideosHistoryEnabledValid
3fd3ab2d 25} from '../../helpers/custom-validators/users'
27db7840 26import { isVideoChannelDisplayNameValid, isVideoChannelUsernameValid } from '../../helpers/custom-validators/video-channels'
da854ddd 27import { logger } from '../../helpers/logger'
b49f22d8 28import { isThemeRegistered } from '../../lib/plugins/theme-utils'
ecb4e35f 29import { Redis } from '../../lib/redis'
10363c74 30import { isSignupAllowed, isSignupAllowedForCurrentIP } from '../../lib/signup'
7d9ba5c0 31import { ActorModel } from '../../models/actor/actor'
56f47830
C
32import {
33 areValidationErrors,
34 checkUserEmailExist,
35 checkUserIdExist,
36 checkUserNameOrEmailDoesNotAlreadyExist,
37 doesVideoChannelIdExist,
38 doesVideoExist,
39 isValidVideoIdParam
40} from './shared'
9bd26629 41
8491293b
RK
42const usersListValidator = [
43 query('blocked')
44 .optional()
f1273314 45 .customSanitizer(toBooleanOrNull)
396f6f01 46 .isBoolean().withMessage('Should be a valid blocked boolena'),
8491293b 47
ea7337cf 48 (req: express.Request, res: express.Response, next: express.NextFunction) => {
8491293b
RK
49 if (areValidationErrors(req, res)) return
50
51 return next()
52 }
53]
54
b60e5f38 55const usersAddValidator = [
396f6f01
C
56 body('username')
57 .custom(isUserUsernameValid)
58 .withMessage('Should have a valid username (lowercase alphanumeric characters)'),
59 body('password')
60 .custom(isUserPasswordValidOrEmpty),
61 body('email')
62 .isEmail(),
27db7840 63
396f6f01
C
64 body('channelName')
65 .optional()
66 .custom(isVideoChannelUsernameValid),
27db7840 67
396f6f01
C
68 body('videoQuota')
69 .custom(isUserVideoQuotaValid),
70 body('videoQuotaDaily')
71 .custom(isUserVideoQuotaDailyValid),
27db7840 72
dea16773
C
73 body('role')
74 .customSanitizer(toIntOrNull)
396f6f01
C
75 .custom(isUserRoleValid),
76
77 body('adminFlags')
78 .optional()
79 .custom(isUserAdminFlagsValid),
9bd26629 80
a2431b7d 81 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
a85d5303 82 if (areValidationErrors(req, res, { omitBodyLog: true })) return
a2431b7d
C
83 if (!await checkUserNameOrEmailDoesNotAlreadyExist(req.body.username, req.body.email, res)) return
84
a95a4cc8
C
85 const authUser = res.locals.oauth.token.User
86 if (authUser.role !== UserRole.ADMINISTRATOR && req.body.role !== UserRole.USER) {
76148b27
RK
87 return res.fail({
88 status: HttpStatusCode.FORBIDDEN_403,
89 message: 'You can only create users (and not administrators or moderators)'
90 })
a95a4cc8
C
91 }
92
766d13b4 93 if (req.body.channelName) {
94 if (req.body.channelName === req.body.username) {
76148b27 95 return res.fail({ message: 'Channel name cannot be the same as user username.' })
766d13b4 96 }
4e68fc86 97
766d13b4 98 const existing = await ActorModel.loadLocalByName(req.body.channelName)
99 if (existing) {
76148b27
RK
100 return res.fail({
101 status: HttpStatusCode.CONFLICT_409,
102 message: `Channel with name ${req.body.channelName} already exists.`
103 })
766d13b4 104 }
4e68fc86 105 }
106
a2431b7d 107 return next()
b60e5f38
C
108 }
109]
6fcd19ba 110
b60e5f38 111const usersRegisterValidator = [
396f6f01
C
112 body('username')
113 .custom(isUserUsernameValid),
114 body('password')
115 .custom(isUserPasswordValid),
116 body('email')
117 .isEmail(),
1f20622f
C
118 body('displayName')
119 .optional()
396f6f01 120 .custom(isUserDisplayNameValid),
1f20622f
C
121
122 body('channel.name')
123 .optional()
396f6f01 124 .custom(isVideoChannelUsernameValid),
1f20622f
C
125 body('channel.displayName')
126 .optional()
396f6f01 127 .custom(isVideoChannelDisplayNameValid),
77a5501f 128
a2431b7d 129 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
a85d5303 130 if (areValidationErrors(req, res, { omitBodyLog: true })) return
a2431b7d
C
131 if (!await checkUserNameOrEmailDoesNotAlreadyExist(req.body.username, req.body.email, res)) return
132
e590b4a5
C
133 const body: UserRegister = req.body
134 if (body.channel) {
135 if (!body.channel.name || !body.channel.displayName) {
76148b27 136 return res.fail({ message: 'Channel is optional but if you specify it, channel.name and channel.displayName are required.' })
e590b4a5
C
137 }
138
1d5342ab 139 if (body.channel.name === body.username) {
76148b27 140 return res.fail({ message: 'Channel name cannot be the same as user username.' })
1d5342ab
C
141 }
142
e590b4a5
C
143 const existing = await ActorModel.loadLocalByName(body.channel.name)
144 if (existing) {
76148b27
RK
145 return res.fail({
146 status: HttpStatusCode.CONFLICT_409,
147 message: `Channel with name ${body.channel.name} already exists.`
148 })
e590b4a5
C
149 }
150 }
151
a2431b7d 152 return next()
b60e5f38
C
153 }
154]
9bd26629 155
b60e5f38 156const usersRemoveValidator = [
396f6f01
C
157 param('id')
158 .custom(isIdValid),
9bd26629 159
a2431b7d 160 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
a2431b7d
C
161 if (areValidationErrors(req, res)) return
162 if (!await checkUserIdExist(req.params.id, res)) return
163
164 const user = res.locals.user
165 if (user.username === 'root') {
76148b27 166 return res.fail({ message: 'Cannot remove the root user' })
a2431b7d
C
167 }
168
169 return next()
b60e5f38
C
170 }
171]
8094a898 172
e6921918 173const usersBlockingValidator = [
396f6f01
C
174 param('id')
175 .custom(isIdValid),
176 body('reason')
177 .optional()
178 .custom(isUserBlockedReasonValid),
e6921918
C
179
180 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
e6921918
C
181 if (areValidationErrors(req, res)) return
182 if (!await checkUserIdExist(req.params.id, res)) return
183
184 const user = res.locals.user
185 if (user.username === 'root') {
76148b27 186 return res.fail({ message: 'Cannot block the root user' })
e6921918
C
187 }
188
189 return next()
190 }
191]
192
92b9d60c 193const deleteMeValidator = [
a1587156 194 (req: express.Request, res: express.Response, next: express.NextFunction) => {
dae86118 195 const user = res.locals.oauth.token.User
92b9d60c 196 if (user.username === 'root') {
76148b27 197 return res.fail({ message: 'You cannot delete your root account.' })
92b9d60c
C
198 }
199
200 return next()
201 }
202]
203
b60e5f38 204const usersUpdateValidator = [
396f6f01
C
205 param('id').custom(isIdValid),
206
207 body('password')
208 .optional()
209 .custom(isUserPasswordValid),
210 body('email')
211 .optional()
212 .isEmail(),
213 body('emailVerified')
214 .optional()
215 .isBoolean(),
216 body('videoQuota')
217 .optional()
218 .custom(isUserVideoQuotaValid),
219 body('videoQuotaDaily')
220 .optional()
221 .custom(isUserVideoQuotaDailyValid),
222 body('pluginAuth')
223 .optional()
224 .exists(),
dea16773
C
225 body('role')
226 .optional()
227 .customSanitizer(toIntOrNull)
396f6f01
C
228 .custom(isUserRoleValid),
229 body('adminFlags')
230 .optional()
231 .custom(isUserAdminFlagsValid),
8094a898 232
a2431b7d 233 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
a85d5303 234 if (areValidationErrors(req, res, { omitBodyLog: true })) return
a2431b7d
C
235 if (!await checkUserIdExist(req.params.id, res)) return
236
f8b8c36b
C
237 const user = res.locals.user
238 if (user.username === 'root' && req.body.role !== undefined && user.role !== req.body.role) {
76148b27 239 return res.fail({ message: 'Cannot change root role.' })
f8b8c36b
C
240 }
241
a2431b7d 242 return next()
b60e5f38
C
243 }
244]
9bd26629 245
b60e5f38 246const usersUpdateMeValidator = [
d1ab89de
C
247 body('displayName')
248 .optional()
396f6f01 249 .custom(isUserDisplayNameValid),
d1ab89de
C
250 body('description')
251 .optional()
396f6f01 252 .custom(isUserDescriptionValid),
d1ab89de
C
253 body('currentPassword')
254 .optional()
396f6f01 255 .custom(isUserPasswordValid),
d1ab89de
C
256 body('password')
257 .optional()
396f6f01 258 .custom(isUserPasswordValid),
d1ab89de
C
259 body('email')
260 .optional()
396f6f01 261 .isEmail(),
d1ab89de
C
262 body('nsfwPolicy')
263 .optional()
396f6f01 264 .custom(isUserNSFWPolicyValid),
d1ab89de
C
265 body('autoPlayVideo')
266 .optional()
396f6f01 267 .custom(isUserAutoPlayVideoValid),
a9bfa85d
C
268 body('p2pEnabled')
269 .optional()
270 .custom(isUserP2PEnabledValid).withMessage('Should have a valid p2p enabled boolean'),
3caf77d3
C
271 body('videoLanguages')
272 .optional()
396f6f01 273 .custom(isUserVideoLanguages),
1a12adcd
C
274 body('videosHistoryEnabled')
275 .optional()
396f6f01 276 .custom(isUserVideosHistoryEnabledValid).withMessage('Should have a valid videos history enabled boolean'),
7cd4d2ba
C
277 body('theme')
278 .optional()
396f6f01 279 .custom(v => isThemeNameValid(v) && isThemeRegistered(v)),
8f581725 280
43d0ea7f
C
281 body('noInstanceConfigWarningModal')
282 .optional()
8f581725 283 .custom(v => isUserNoModal(v)).withMessage('Should have a valid noInstanceConfigWarningModal boolean'),
43d0ea7f
C
284 body('noWelcomeModal')
285 .optional()
8f581725
C
286 .custom(v => isUserNoModal(v)).withMessage('Should have a valid noWelcomeModal boolean'),
287 body('noAccountSetupWarningModal')
288 .optional()
289 .custom(v => isUserNoModal(v)).withMessage('Should have a valid noAccountSetupWarningModal boolean'),
290
c1e5bd23
C
291 body('autoPlayNextVideo')
292 .optional()
293 .custom(v => isUserAutoPlayNextVideoValid(v)).withMessage('Should have a valid autoPlayNextVideo boolean'),
9bd26629 294
a890d1e0 295 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
9a7fd960
C
296 const user = res.locals.oauth.token.User
297
0ba5f5ba 298 if (req.body.password || req.body.email) {
9a7fd960 299 if (user.pluginAuth !== null) {
76148b27 300 return res.fail({ message: 'You cannot update your email or password that is associated with an external auth system.' })
9a7fd960
C
301 }
302
a890d1e0 303 if (!req.body.currentPassword) {
76148b27 304 return res.fail({ message: 'currentPassword parameter is missing.' })
a890d1e0
C
305 }
306
a890d1e0 307 if (await user.isPasswordMatch(req.body.currentPassword) !== true) {
76148b27
RK
308 return res.fail({
309 status: HttpStatusCode.UNAUTHORIZED_401,
310 message: 'currentPassword is invalid.'
311 })
a890d1e0
C
312 }
313 }
314
a85d5303 315 if (areValidationErrors(req, res, { omitBodyLog: true })) return
a2431b7d
C
316
317 return next()
b60e5f38
C
318 }
319]
8094a898 320
b60e5f38 321const usersGetValidator = [
396f6f01
C
322 param('id')
323 .custom(isIdValid),
324 query('withStats')
325 .optional()
326 .isBoolean().withMessage('Should have a valid withStats boolean'),
d38b8281 327
a2431b7d 328 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
a2431b7d 329 if (areValidationErrors(req, res)) return
76314386 330 if (!await checkUserIdExist(req.params.id, res, req.query.withStats)) return
a2431b7d
C
331
332 return next()
b60e5f38
C
333 }
334]
d38b8281 335
b60e5f38 336const usersVideoRatingValidator = [
d4a8e7a6 337 isValidVideoIdParam('videoId'),
0a6658fd 338
a2431b7d 339 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
a2431b7d 340 if (areValidationErrors(req, res)) return
0f6acda1 341 if (!await doesVideoExist(req.params.videoId, res, 'id')) return
a2431b7d
C
342
343 return next()
b60e5f38
C
344 }
345]
346
978c87e7
C
347const usersVideosValidator = [
348 query('isLive')
349 .optional()
350 .customSanitizer(toBooleanOrNull)
396f6f01 351 .custom(isBooleanValid).withMessage('Should have a valid isLive boolean'),
978c87e7
C
352
353 query('channelId')
354 .optional()
355 .customSanitizer(toIntOrNull)
396f6f01 356 .custom(isIdValid),
978c87e7
C
357
358 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
978c87e7
C
359 if (areValidationErrors(req, res)) return
360
361 if (req.query.channelId && !await doesVideoChannelIdExist(req.query.channelId, res)) return
362
363 return next()
364 }
365]
366
b60e5f38 367const ensureUserRegistrationAllowed = [
a2431b7d 368 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
4ce7eb71 369 const allowedParams = {
ba7b7e57
RK
370 body: req.body,
371 ip: req.ip
4ce7eb71
C
372 }
373
374 const allowedResult = await Hooks.wrapPromiseFun(
375 isSignupAllowed,
376 allowedParams,
377 'filter:api.user.signup.allowed.result'
378 )
379
380 if (allowedResult.allowed === false) {
76148b27
RK
381 return res.fail({
382 status: HttpStatusCode.FORBIDDEN_403,
383 message: allowedResult.errorMessage || 'User registration is not enabled or user limit is reached.'
384 })
a2431b7d
C
385 }
386
387 return next()
b60e5f38
C
388 }
389]
291e8d3e 390
ff2c1fe8 391const ensureUserRegistrationAllowedForIP = [
a1587156 392 (req: express.Request, res: express.Response, next: express.NextFunction) => {
ff2c1fe8
RK
393 const allowed = isSignupAllowedForCurrentIP(req.ip)
394
395 if (allowed === false) {
76148b27
RK
396 return res.fail({
397 status: HttpStatusCode.FORBIDDEN_403,
398 message: 'You are not on a network authorized for registration.'
399 })
ff2c1fe8
RK
400 }
401
402 return next()
403 }
404]
405
ecb4e35f 406const usersAskResetPasswordValidator = [
396f6f01
C
407 body('email')
408 .isEmail(),
ecb4e35f
C
409
410 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
ecb4e35f 411 if (areValidationErrors(req, res)) return
b426edd4 412
ecb4e35f
C
413 const exists = await checkUserEmailExist(req.body.email, res, false)
414 if (!exists) {
415 logger.debug('User with email %s does not exist (asking reset password).', req.body.email)
416 // Do not leak our emails
2d53be02 417 return res.status(HttpStatusCode.NO_CONTENT_204).end()
ecb4e35f
C
418 }
419
c5f3ff39
C
420 if (res.locals.user.pluginAuth) {
421 return res.fail({
422 status: HttpStatusCode.CONFLICT_409,
423 message: 'Cannot recover password of a user that uses a plugin authentication.'
424 })
425 }
426
ecb4e35f
C
427 return next()
428 }
429]
430
431const usersResetPasswordValidator = [
396f6f01
C
432 param('id')
433 .custom(isIdValid),
434 body('verificationString')
435 .not().isEmpty(),
436 body('password')
437 .custom(isUserPasswordValid),
ecb4e35f
C
438
439 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
ecb4e35f
C
440 if (areValidationErrors(req, res)) return
441 if (!await checkUserIdExist(req.params.id, res)) return
442
dae86118 443 const user = res.locals.user
56f47830 444 const redisVerificationString = await Redis.Instance.getResetPasswordVerificationString(user.id)
ecb4e35f
C
445
446 if (redisVerificationString !== req.body.verificationString) {
76148b27
RK
447 return res.fail({
448 status: HttpStatusCode.FORBIDDEN_403,
449 message: 'Invalid verification string.'
450 })
ecb4e35f
C
451 }
452
453 return next()
454 }
455]
456
d9eaee39
JM
457const usersAskSendVerifyEmailValidator = [
458 body('email').isEmail().not().isEmpty().withMessage('Should have a valid email'),
459
460 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
d9eaee39 461 if (areValidationErrors(req, res)) return
a85d5303 462
d9eaee39
JM
463 const exists = await checkUserEmailExist(req.body.email, res, false)
464 if (!exists) {
465 logger.debug('User with email %s does not exist (asking verify email).', req.body.email)
466 // Do not leak our emails
2d53be02 467 return res.status(HttpStatusCode.NO_CONTENT_204).end()
d9eaee39
JM
468 }
469
c5f3ff39
C
470 if (res.locals.user.pluginAuth) {
471 return res.fail({
472 status: HttpStatusCode.CONFLICT_409,
473 message: 'Cannot ask verification email of a user that uses a plugin authentication.'
474 })
475 }
476
d9eaee39
JM
477 return next()
478 }
479]
480
481const usersVerifyEmailValidator = [
d1ab89de
C
482 param('id')
483 .isInt().not().isEmpty().withMessage('Should have a valid id'),
484
485 body('verificationString')
486 .not().isEmpty().withMessage('Should have a valid verification string'),
487 body('isPendingEmail')
488 .optional()
2b65c4e5 489 .customSanitizer(toBooleanOrNull),
d9eaee39
JM
490
491 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
d9eaee39
JM
492 if (areValidationErrors(req, res)) return
493 if (!await checkUserIdExist(req.params.id, res)) return
494
dae86118 495 const user = res.locals.user
d9eaee39
JM
496 const redisVerificationString = await Redis.Instance.getVerifyEmailLink(user.id)
497
498 if (redisVerificationString !== req.body.verificationString) {
76148b27
RK
499 return res.fail({
500 status: HttpStatusCode.FORBIDDEN_403,
501 message: 'Invalid verification string.'
502 })
d9eaee39
JM
503 }
504
505 return next()
506 }
507]
508
2166c058
C
509const usersCheckCurrentPasswordFactory = (targetUserIdGetter: (req: express.Request) => number | string) => {
510 return [
511 body('currentPassword').optional().custom(exists),
56f47830 512
2166c058
C
513 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
514 if (areValidationErrors(req, res)) return
56f47830 515
2166c058
C
516 const user = res.locals.oauth.token.User
517 const isAdminOrModerator = user.role === UserRole.ADMINISTRATOR || user.role === UserRole.MODERATOR
518 const targetUserId = parseInt(targetUserIdGetter(req) + '')
56f47830 519
2166c058
C
520 // Admin/moderator action on another user, skip the password check
521 if (isAdminOrModerator && targetUserId !== user.id) {
522 return next()
523 }
524
525 if (!req.body.currentPassword) {
526 return res.fail({
527 status: HttpStatusCode.BAD_REQUEST_400,
528 message: 'currentPassword is missing'
529 })
530 }
531
532 if (await user.isPasswordMatch(req.body.currentPassword) !== true) {
533 return res.fail({
534 status: HttpStatusCode.FORBIDDEN_403,
535 message: 'currentPassword is invalid.'
536 })
537 }
538
539 return next()
540 }
541 ]
542}
56f47830 543
74d63469 544const userAutocompleteValidator = [
a85d5303
C
545 param('search')
546 .isString()
547 .not().isEmpty()
74d63469
GR
548]
549
c100a614 550const ensureAuthUserOwnsAccountValidator = [
a1587156 551 (req: express.Request, res: express.Response, next: express.NextFunction) => {
c100a614
YB
552 const user = res.locals.oauth.token.User
553
554 if (res.locals.account.id !== user.Account.id) {
76148b27
RK
555 return res.fail({
556 status: HttpStatusCode.FORBIDDEN_403,
7a4fd56c 557 message: 'Only owner of this account can access this resource.'
4beda9e1
C
558 })
559 }
560
561 return next()
562 }
563]
564
d4d9bbc6 565const ensureCanManageChannelOrAccount = [
4beda9e1 566 (req: express.Request, res: express.Response, next: express.NextFunction) => {
a37e9e74 567 const user = res.locals.oauth.token.user
d4d9bbc6
C
568 const account = res.locals.videoChannel?.Account ?? res.locals.account
569 const isUserOwner = account.userId === user.id
a37e9e74 570
571 if (!isUserOwner && user.hasRight(UserRight.MANAGE_ANY_VIDEO_CHANNEL) === false) {
d4d9bbc6 572 const message = `User ${user.username} does not have right this channel or account.`
4beda9e1 573
4beda9e1
C
574 return res.fail({
575 status: HttpStatusCode.FORBIDDEN_403,
a37e9e74 576 message
76148b27 577 })
c100a614
YB
578 }
579
580 return next()
581 }
582]
583
d4d9bbc6 584const ensureCanModerateUser = [
a95a4cc8
C
585 (req: express.Request, res: express.Response, next: express.NextFunction) => {
586 const authUser = res.locals.oauth.token.User
587 const onUser = res.locals.user
588
589 if (authUser.role === UserRole.ADMINISTRATOR) return next()
590 if (authUser.role === UserRole.MODERATOR && onUser.role === UserRole.USER) return next()
591
76148b27
RK
592 return res.fail({
593 status: HttpStatusCode.FORBIDDEN_403,
d4d9bbc6 594 message: 'A moderator can only manage users.'
76148b27 595 })
a95a4cc8
C
596 }
597]
598
9bd26629
C
599// ---------------------------------------------------------------------------
600
65fcc311 601export {
8491293b 602 usersListValidator,
65fcc311 603 usersAddValidator,
92b9d60c 604 deleteMeValidator,
77a5501f 605 usersRegisterValidator,
e6921918 606 usersBlockingValidator,
65fcc311
C
607 usersRemoveValidator,
608 usersUpdateValidator,
8094a898 609 usersUpdateMeValidator,
291e8d3e 610 usersVideoRatingValidator,
2166c058 611 usersCheckCurrentPasswordFactory,
8094a898 612 ensureUserRegistrationAllowed,
ff2c1fe8 613 ensureUserRegistrationAllowedForIP,
c5911fd3 614 usersGetValidator,
978c87e7 615 usersVideosValidator,
ecb4e35f 616 usersAskResetPasswordValidator,
d9eaee39
JM
617 usersResetPasswordValidator,
618 usersAskSendVerifyEmailValidator,
74d63469 619 usersVerifyEmailValidator,
c100a614 620 userAutocompleteValidator,
a95a4cc8 621 ensureAuthUserOwnsAccountValidator,
d4d9bbc6
C
622 ensureCanModerateUser,
623 ensureCanManageChannelOrAccount
8094a898 624}