diff options
Diffstat (limited to 'server/middlewares/validators/users.ts')
-rw-r--r-- | server/middlewares/validators/users.ts | 118 |
1 files changed, 61 insertions, 57 deletions
diff --git a/server/middlewares/validators/users.ts b/server/middlewares/validators/users.ts index 2de5265fb..055af3b64 100644 --- a/server/middlewares/validators/users.ts +++ b/server/middlewares/validators/users.ts | |||
@@ -1,9 +1,8 @@ | |||
1 | import express from 'express' | 1 | import express from 'express' |
2 | import { body, param, query } from 'express-validator' | 2 | import { body, param, query } from 'express-validator' |
3 | import { Hooks } from '@server/lib/plugins/hooks' | 3 | import { Hooks } from '@server/lib/plugins/hooks' |
4 | import { MUserDefault } from '@server/types/models' | ||
5 | import { HttpStatusCode, UserRegister, UserRight, UserRole } from '@shared/models' | 4 | import { HttpStatusCode, UserRegister, UserRight, UserRole } from '@shared/models' |
6 | import { isBooleanValid, isIdValid, toBooleanOrNull, toIntOrNull } from '../../helpers/custom-validators/misc' | 5 | import { exists, isBooleanValid, isIdValid, toBooleanOrNull, toIntOrNull } from '../../helpers/custom-validators/misc' |
7 | import { isThemeNameValid } from '../../helpers/custom-validators/plugins' | 6 | import { isThemeNameValid } from '../../helpers/custom-validators/plugins' |
8 | import { | 7 | import { |
9 | isUserAdminFlagsValid, | 8 | isUserAdminFlagsValid, |
@@ -30,8 +29,15 @@ import { isThemeRegistered } from '../../lib/plugins/theme-utils' | |||
30 | import { Redis } from '../../lib/redis' | 29 | import { Redis } from '../../lib/redis' |
31 | import { isSignupAllowed, isSignupAllowedForCurrentIP } from '../../lib/signup' | 30 | import { isSignupAllowed, isSignupAllowedForCurrentIP } from '../../lib/signup' |
32 | import { ActorModel } from '../../models/actor/actor' | 31 | import { ActorModel } from '../../models/actor/actor' |
33 | import { UserModel } from '../../models/user/user' | 32 | import { |
34 | import { areValidationErrors, doesVideoChannelIdExist, doesVideoExist, isValidVideoIdParam } from './shared' | 33 | areValidationErrors, |
34 | checkUserEmailExist, | ||
35 | checkUserIdExist, | ||
36 | checkUserNameOrEmailDoesNotAlreadyExist, | ||
37 | doesVideoChannelIdExist, | ||
38 | doesVideoExist, | ||
39 | isValidVideoIdParam | ||
40 | } from './shared' | ||
35 | 41 | ||
36 | const usersListValidator = [ | 42 | const usersListValidator = [ |
37 | query('blocked') | 43 | query('blocked') |
@@ -411,6 +417,13 @@ const usersAskResetPasswordValidator = [ | |||
411 | return res.status(HttpStatusCode.NO_CONTENT_204).end() | 417 | return res.status(HttpStatusCode.NO_CONTENT_204).end() |
412 | } | 418 | } |
413 | 419 | ||
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 | |||
414 | return next() | 427 | return next() |
415 | } | 428 | } |
416 | ] | 429 | ] |
@@ -428,7 +441,7 @@ const usersResetPasswordValidator = [ | |||
428 | if (!await checkUserIdExist(req.params.id, res)) return | 441 | if (!await checkUserIdExist(req.params.id, res)) return |
429 | 442 | ||
430 | const user = res.locals.user | 443 | const user = res.locals.user |
431 | const redisVerificationString = await Redis.Instance.getResetPasswordLink(user.id) | 444 | const redisVerificationString = await Redis.Instance.getResetPasswordVerificationString(user.id) |
432 | 445 | ||
433 | if (redisVerificationString !== req.body.verificationString) { | 446 | if (redisVerificationString !== req.body.verificationString) { |
434 | return res.fail({ | 447 | return res.fail({ |
@@ -454,6 +467,13 @@ const usersAskSendVerifyEmailValidator = [ | |||
454 | return res.status(HttpStatusCode.NO_CONTENT_204).end() | 467 | return res.status(HttpStatusCode.NO_CONTENT_204).end() |
455 | } | 468 | } |
456 | 469 | ||
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 | |||
457 | return next() | 477 | return next() |
458 | } | 478 | } |
459 | ] | 479 | ] |
@@ -486,6 +506,41 @@ const usersVerifyEmailValidator = [ | |||
486 | } | 506 | } |
487 | ] | 507 | ] |
488 | 508 | ||
509 | const usersCheckCurrentPasswordFactory = (targetUserIdGetter: (req: express.Request) => number | string) => { | ||
510 | return [ | ||
511 | body('currentPassword').optional().custom(exists), | ||
512 | |||
513 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
514 | if (areValidationErrors(req, res)) return | ||
515 | |||
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) + '') | ||
519 | |||
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 | } | ||
543 | |||
489 | const userAutocompleteValidator = [ | 544 | const userAutocompleteValidator = [ |
490 | param('search') | 545 | param('search') |
491 | .isString() | 546 | .isString() |
@@ -553,6 +608,7 @@ export { | |||
553 | usersUpdateValidator, | 608 | usersUpdateValidator, |
554 | usersUpdateMeValidator, | 609 | usersUpdateMeValidator, |
555 | usersVideoRatingValidator, | 610 | usersVideoRatingValidator, |
611 | usersCheckCurrentPasswordFactory, | ||
556 | ensureUserRegistrationAllowed, | 612 | ensureUserRegistrationAllowed, |
557 | ensureUserRegistrationAllowedForIP, | 613 | ensureUserRegistrationAllowedForIP, |
558 | usersGetValidator, | 614 | usersGetValidator, |
@@ -566,55 +622,3 @@ export { | |||
566 | ensureCanModerateUser, | 622 | ensureCanModerateUser, |
567 | ensureCanManageChannelOrAccount | 623 | ensureCanManageChannelOrAccount |
568 | } | 624 | } |
569 | |||
570 | // --------------------------------------------------------------------------- | ||
571 | |||
572 | function checkUserIdExist (idArg: number | string, res: express.Response, withStats = false) { | ||
573 | const id = parseInt(idArg + '', 10) | ||
574 | return checkUserExist(() => UserModel.loadByIdWithChannels(id, withStats), res) | ||
575 | } | ||
576 | |||
577 | function checkUserEmailExist (email: string, res: express.Response, abortResponse = true) { | ||
578 | return checkUserExist(() => UserModel.loadByEmail(email), res, abortResponse) | ||
579 | } | ||
580 | |||
581 | async function checkUserNameOrEmailDoesNotAlreadyExist (username: string, email: string, res: express.Response) { | ||
582 | const user = await UserModel.loadByUsernameOrEmail(username, email) | ||
583 | |||
584 | if (user) { | ||
585 | res.fail({ | ||
586 | status: HttpStatusCode.CONFLICT_409, | ||
587 | message: 'User with this username or email already exists.' | ||
588 | }) | ||
589 | return false | ||
590 | } | ||
591 | |||
592 | const actor = await ActorModel.loadLocalByName(username) | ||
593 | if (actor) { | ||
594 | res.fail({ | ||
595 | status: HttpStatusCode.CONFLICT_409, | ||
596 | message: 'Another actor (account/channel) with this name on this instance already exists or has already existed.' | ||
597 | }) | ||
598 | return false | ||
599 | } | ||
600 | |||
601 | return true | ||
602 | } | ||
603 | |||
604 | async function checkUserExist (finder: () => Promise<MUserDefault>, res: express.Response, abortResponse = true) { | ||
605 | const user = await finder() | ||
606 | |||
607 | if (!user) { | ||
608 | if (abortResponse === true) { | ||
609 | res.fail({ | ||
610 | status: HttpStatusCode.NOT_FOUND_404, | ||
611 | message: 'User not found' | ||
612 | }) | ||
613 | } | ||
614 | |||
615 | return false | ||
616 | } | ||
617 | |||
618 | res.locals.user = user | ||
619 | return true | ||
620 | } | ||