1 import express from 'express'
2 import { body, param, query, ValidationChain } from 'express-validator'
3 import { exists, isBooleanValid, isIdValid, toBooleanOrNull } from '@server/helpers/custom-validators/misc'
4 import { isRegistrationModerationResponseValid, isRegistrationReasonValid } from '@server/helpers/custom-validators/user-registration'
5 import { CONFIG } from '@server/initializers/config'
6 import { Hooks } from '@server/lib/plugins/hooks'
7 import { HttpStatusCode, UserRegister, UserRegistrationRequest, UserRegistrationState } from '@shared/models'
8 import { isUserDisplayNameValid, isUserPasswordValid, isUserUsernameValid } from '../../helpers/custom-validators/users'
9 import { isVideoChannelDisplayNameValid, isVideoChannelUsernameValid } from '../../helpers/custom-validators/video-channels'
10 import { isSignupAllowed, isSignupAllowedForCurrentIP, SignupMode } from '../../lib/signup'
11 import { ActorModel } from '../../models/actor/actor'
12 import { areValidationErrors, checkUserNameOrEmailDoNotAlreadyExist } from './shared'
13 import { checkRegistrationHandlesDoNotAlreadyExist, checkRegistrationIdExist } from './shared/user-registrations'
15 const usersDirectRegistrationValidator = usersCommonRegistrationValidatorFactory()
17 const usersRequestRegistrationValidator = [
18 ...usersCommonRegistrationValidatorFactory([
19 body('registrationReason')
20 .custom(isRegistrationReasonValid)
23 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
24 const body: UserRegistrationRequest = req.body
26 if (CONFIG.SIGNUP.REQUIRES_APPROVAL !== true) {
28 status: HttpStatusCode.BAD_REQUEST_400,
29 message: 'Signup approval is not enabled on this instance'
33 const options = { username: body.username, email: body.email, channelHandle: body.channel?.name, res }
34 if (!await checkRegistrationHandlesDoNotAlreadyExist(options)) return
40 // ---------------------------------------------------------------------------
42 function ensureUserRegistrationAllowedFactory (signupMode: SignupMode) {
43 return async (req: express.Request, res: express.Response, next: express.NextFunction) => {
44 const allowedParams = {
50 const allowedResult = await Hooks.wrapPromiseFun(
54 signupMode === 'direct-registration'
55 ? 'filter:api.user.signup.allowed.result'
56 : 'filter:api.user.request-signup.allowed.result'
59 if (allowedResult.allowed === false) {
61 status: HttpStatusCode.FORBIDDEN_403,
62 message: allowedResult.errorMessage || 'User registration is not enabled, user limit is reached or registration requires approval.'
70 const ensureUserRegistrationAllowedForIP = [
71 (req: express.Request, res: express.Response, next: express.NextFunction) => {
72 const allowed = isSignupAllowedForCurrentIP(req.ip)
74 if (allowed === false) {
76 status: HttpStatusCode.FORBIDDEN_403,
77 message: 'You are not on a network authorized for registration.'
85 // ---------------------------------------------------------------------------
87 const acceptOrRejectRegistrationValidator = [
88 param('registrationId')
91 body('moderationResponse')
92 .custom(isRegistrationModerationResponseValid),
94 body('preventEmailDelivery')
96 .customSanitizer(toBooleanOrNull)
97 .custom(isBooleanValid).withMessage('Should have preventEmailDelivery boolean'),
99 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
100 if (areValidationErrors(req, res)) return
101 if (!await checkRegistrationIdExist(req.params.registrationId, res)) return
103 if (res.locals.userRegistration.state !== UserRegistrationState.PENDING) {
105 status: HttpStatusCode.CONFLICT_409,
106 message: 'This registration is already accepted or rejected.'
114 // ---------------------------------------------------------------------------
116 const getRegistrationValidator = [
117 param('registrationId')
120 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
121 if (areValidationErrors(req, res)) return
122 if (!await checkRegistrationIdExist(req.params.registrationId, res)) return
128 // ---------------------------------------------------------------------------
130 const listRegistrationsValidator = [
135 (req: express.Request, res: express.Response, next: express.NextFunction) => {
136 if (areValidationErrors(req, res)) return
142 // ---------------------------------------------------------------------------
145 usersDirectRegistrationValidator,
146 usersRequestRegistrationValidator,
148 ensureUserRegistrationAllowedFactory,
149 ensureUserRegistrationAllowedForIP,
151 getRegistrationValidator,
152 listRegistrationsValidator,
154 acceptOrRejectRegistrationValidator
157 // ---------------------------------------------------------------------------
159 function usersCommonRegistrationValidatorFactory (additionalValidationChain: ValidationChain[] = []) {
162 .custom(isUserUsernameValid),
164 .custom(isUserPasswordValid),
169 .custom(isUserDisplayNameValid),
173 .custom(isVideoChannelUsernameValid),
174 body('channel.displayName')
176 .custom(isVideoChannelDisplayNameValid),
178 ...additionalValidationChain,
180 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
181 if (areValidationErrors(req, res, { omitBodyLog: true })) return
183 const body: UserRegister | UserRegistrationRequest = req.body
185 if (!await checkUserNameOrEmailDoNotAlreadyExist(body.username, body.email, res)) return
188 if (!body.channel.name || !body.channel.displayName) {
189 return res.fail({ message: 'Channel is optional but if you specify it, channel.name and channel.displayName are required.' })
192 if (body.channel.name === body.username) {
193 return res.fail({ message: 'Channel name cannot be the same as user username.' })
196 const existing = await ActorModel.loadLocalByName(body.channel.name)
199 status: HttpStatusCode.CONFLICT_409,
200 message: `Channel with name ${body.channel.name} already exists.`