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