]>
Commit | Line | Data |
---|---|---|
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 enabled, user limit is reached or registration requires approval.' | |
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 | } |