aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/middlewares/validators/user-registrations.ts
diff options
context:
space:
mode:
Diffstat (limited to 'server/middlewares/validators/user-registrations.ts')
-rw-r--r--server/middlewares/validators/user-registrations.ts203
1 files changed, 203 insertions, 0 deletions
diff --git a/server/middlewares/validators/user-registrations.ts b/server/middlewares/validators/user-registrations.ts
new file mode 100644
index 000000000..e263c27c5
--- /dev/null
+++ b/server/middlewares/validators/user-registrations.ts
@@ -0,0 +1,203 @@
1import express from 'express'
2import { body, param, query, ValidationChain } from 'express-validator'
3import { exists, isIdValid } from '@server/helpers/custom-validators/misc'
4import { isRegistrationModerationResponseValid, isRegistrationReasonValid } from '@server/helpers/custom-validators/user-registration'
5import { CONFIG } from '@server/initializers/config'
6import { Hooks } from '@server/lib/plugins/hooks'
7import { HttpStatusCode, UserRegister, UserRegistrationRequest, UserRegistrationState } from '@shared/models'
8import { isUserDisplayNameValid, isUserPasswordValid, isUserUsernameValid } from '../../helpers/custom-validators/users'
9import { isVideoChannelDisplayNameValid, isVideoChannelUsernameValid } from '../../helpers/custom-validators/video-channels'
10import { isSignupAllowed, isSignupAllowedForCurrentIP, SignupMode } from '../../lib/signup'
11import { ActorModel } from '../../models/actor/actor'
12import { areValidationErrors, checkUserNameOrEmailDoNotAlreadyExist } from './shared'
13import { checkRegistrationHandlesDoNotAlreadyExist, checkRegistrationIdExist } from './shared/user-registrations'
14
15const usersDirectRegistrationValidator = usersCommonRegistrationValidatorFactory()
16
17const 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
42function 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 enabled, user limit is reached or registration requires approval.'
63 })
64 }
65
66 return next()
67 }
68}
69
70const 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
87const acceptOrRejectRegistrationValidator = [
88 param('registrationId')
89 .custom(isIdValid),
90
91 body('moderationResponse')
92 .custom(isRegistrationModerationResponseValid),
93
94 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
95 if (areValidationErrors(req, res)) return
96 if (!await checkRegistrationIdExist(req.params.registrationId, res)) return
97
98 if (res.locals.userRegistration.state !== UserRegistrationState.PENDING) {
99 return res.fail({
100 status: HttpStatusCode.CONFLICT_409,
101 message: 'This registration is already accepted or rejected.'
102 })
103 }
104
105 return next()
106 }
107]
108
109// ---------------------------------------------------------------------------
110
111const getRegistrationValidator = [
112 param('registrationId')
113 .custom(isIdValid),
114
115 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
116 if (areValidationErrors(req, res)) return
117 if (!await checkRegistrationIdExist(req.params.registrationId, res)) return
118
119 return next()
120 }
121]
122
123// ---------------------------------------------------------------------------
124
125const listRegistrationsValidator = [
126 query('search')
127 .optional()
128 .custom(exists),
129
130 (req: express.Request, res: express.Response, next: express.NextFunction) => {
131 if (areValidationErrors(req, res)) return
132
133 return next()
134 }
135]
136
137// ---------------------------------------------------------------------------
138
139export {
140 usersDirectRegistrationValidator,
141 usersRequestRegistrationValidator,
142
143 ensureUserRegistrationAllowedFactory,
144 ensureUserRegistrationAllowedForIP,
145
146 getRegistrationValidator,
147 listRegistrationsValidator,
148
149 acceptOrRejectRegistrationValidator
150}
151
152// ---------------------------------------------------------------------------
153
154function usersCommonRegistrationValidatorFactory (additionalValidationChain: ValidationChain[] = []) {
155 return [
156 body('username')
157 .custom(isUserUsernameValid),
158 body('password')
159 .custom(isUserPasswordValid),
160 body('email')
161 .isEmail(),
162 body('displayName')
163 .optional()
164 .custom(isUserDisplayNameValid),
165
166 body('channel.name')
167 .optional()
168 .custom(isVideoChannelUsernameValid),
169 body('channel.displayName')
170 .optional()
171 .custom(isVideoChannelDisplayNameValid),
172
173 ...additionalValidationChain,
174
175 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
176 if (areValidationErrors(req, res, { omitBodyLog: true })) return
177
178 const body: UserRegister | UserRegistrationRequest = req.body
179
180 if (!await checkUserNameOrEmailDoNotAlreadyExist(body.username, body.email, res)) return
181
182 if (body.channel) {
183 if (!body.channel.name || !body.channel.displayName) {
184 return res.fail({ message: 'Channel is optional but if you specify it, channel.name and channel.displayName are required.' })
185 }
186
187 if (body.channel.name === body.username) {
188 return res.fail({ message: 'Channel name cannot be the same as user username.' })
189 }
190
191 const existing = await ActorModel.loadLocalByName(body.channel.name)
192 if (existing) {
193 return res.fail({
194 status: HttpStatusCode.CONFLICT_409,
195 message: `Channel with name ${body.channel.name} already exists.`
196 })
197 }
198 }
199
200 return next()
201 }
202 ]
203}