]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/middlewares/validators/user-registrations.ts
More specific message when signup is not allowed
[github/Chocobozzz/PeerTube.git] / server / middlewares / validators / user-registrations.ts
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'
14
15 const usersDirectRegistrationValidator = usersCommonRegistrationValidatorFactory()
16
17 const usersRequestRegistrationValidator = [
18 ...usersCommonRegistrationValidatorFactory([
19 body('registrationReason')
20 .custom(isRegistrationReasonValid)
21 ]),
22
23 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
24 const body: UserRegistrationRequest = req.body
25
26 if (CONFIG.SIGNUP.REQUIRES_APPROVAL !== true) {
27 return res.fail({
28 status: HttpStatusCode.BAD_REQUEST_400,
29 message: 'Signup approval is not enabled on this instance'
30 })
31 }
32
33 const options = { username: body.username, email: body.email, channelHandle: body.channel?.name, res }
34 if (!await checkRegistrationHandlesDoNotAlreadyExist(options)) return
35
36 return next()
37 }
38 ]
39
40 // ---------------------------------------------------------------------------
41
42 function ensureUserRegistrationAllowedFactory (signupMode: SignupMode) {
43 return async (req: express.Request, res: express.Response, next: express.NextFunction) => {
44 const allowedParams = {
45 body: req.body,
46 ip: req.ip,
47 signupMode
48 }
49
50 const allowedResult = await Hooks.wrapPromiseFun(
51 isSignupAllowed,
52 allowedParams,
53
54 signupMode === 'direct-registration'
55 ? 'filter:api.user.signup.allowed.result'
56 : 'filter:api.user.request-signup.allowed.result'
57 )
58
59 if (allowedResult.allowed === false) {
60 return res.fail({
61 status: HttpStatusCode.FORBIDDEN_403,
62 message: allowedResult.errorMessage || 'User registration is not allowed'
63 })
64 }
65
66 return next()
67 }
68 }
69
70 const ensureUserRegistrationAllowedForIP = [
71 (req: express.Request, res: express.Response, next: express.NextFunction) => {
72 const allowed = isSignupAllowedForCurrentIP(req.ip)
73
74 if (allowed === false) {
75 return res.fail({
76 status: HttpStatusCode.FORBIDDEN_403,
77 message: 'You are not on a network authorized for registration.'
78 })
79 }
80
81 return next()
82 }
83 ]
84
85 // ---------------------------------------------------------------------------
86
87 const acceptOrRejectRegistrationValidator = [
88 param('registrationId')
89 .custom(isIdValid),
90
91 body('moderationResponse')
92 .custom(isRegistrationModerationResponseValid),
93
94 body('preventEmailDelivery')
95 .optional()
96 .customSanitizer(toBooleanOrNull)
97 .custom(isBooleanValid).withMessage('Should have preventEmailDelivery boolean'),
98
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
102
103 if (res.locals.userRegistration.state !== UserRegistrationState.PENDING) {
104 return res.fail({
105 status: HttpStatusCode.CONFLICT_409,
106 message: 'This registration is already accepted or rejected.'
107 })
108 }
109
110 return next()
111 }
112 ]
113
114 // ---------------------------------------------------------------------------
115
116 const getRegistrationValidator = [
117 param('registrationId')
118 .custom(isIdValid),
119
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
123
124 return next()
125 }
126 ]
127
128 // ---------------------------------------------------------------------------
129
130 const listRegistrationsValidator = [
131 query('search')
132 .optional()
133 .custom(exists),
134
135 (req: express.Request, res: express.Response, next: express.NextFunction) => {
136 if (areValidationErrors(req, res)) return
137
138 return next()
139 }
140 ]
141
142 // ---------------------------------------------------------------------------
143
144 export {
145 usersDirectRegistrationValidator,
146 usersRequestRegistrationValidator,
147
148 ensureUserRegistrationAllowedFactory,
149 ensureUserRegistrationAllowedForIP,
150
151 getRegistrationValidator,
152 listRegistrationsValidator,
153
154 acceptOrRejectRegistrationValidator
155 }
156
157 // ---------------------------------------------------------------------------
158
159 function usersCommonRegistrationValidatorFactory (additionalValidationChain: ValidationChain[] = []) {
160 return [
161 body('username')
162 .custom(isUserUsernameValid),
163 body('password')
164 .custom(isUserPasswordValid),
165 body('email')
166 .isEmail(),
167 body('displayName')
168 .optional()
169 .custom(isUserDisplayNameValid),
170
171 body('channel.name')
172 .optional()
173 .custom(isVideoChannelUsernameValid),
174 body('channel.displayName')
175 .optional()
176 .custom(isVideoChannelDisplayNameValid),
177
178 ...additionalValidationChain,
179
180 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
181 if (areValidationErrors(req, res, { omitBodyLog: true })) return
182
183 const body: UserRegister | UserRegistrationRequest = req.body
184
185 if (!await checkUserNameOrEmailDoNotAlreadyExist(body.username, body.email, res)) return
186
187 if (body.channel) {
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.' })
190 }
191
192 if (body.channel.name === body.username) {
193 return res.fail({ message: 'Channel name cannot be the same as user username.' })
194 }
195
196 const existing = await ActorModel.loadLocalByName(body.channel.name)
197 if (existing) {
198 return res.fail({
199 status: HttpStatusCode.CONFLICT_409,
200 message: `Channel with name ${body.channel.name} already exists.`
201 })
202 }
203 }
204
205 return next()
206 }
207 ]
208 }