]>
Commit | Line | Data |
---|---|---|
e364e31e C |
1 | import express from 'express' |
2 | import { Emailer } from '@server/lib/emailer' | |
3 | import { Hooks } from '@server/lib/plugins/hooks' | |
4 | import { UserRegistrationModel } from '@server/models/user/user-registration' | |
5 | import { pick } from '@shared/core-utils' | |
4115f200 C |
6 | import { |
7 | HttpStatusCode, | |
8 | UserRegister, | |
9 | UserRegistrationRequest, | |
10 | UserRegistrationState, | |
11 | UserRegistrationUpdateState, | |
12 | UserRight | |
13 | } from '@shared/models' | |
e364e31e C |
14 | import { auditLoggerFactory, UserAuditView } from '../../../helpers/audit-logger' |
15 | import { logger } from '../../../helpers/logger' | |
16 | import { CONFIG } from '../../../initializers/config' | |
17 | import { Notifier } from '../../../lib/notifier' | |
18 | import { buildUser, createUserAccountAndChannelAndPlaylist, sendVerifyRegistrationEmail, sendVerifyUserEmail } from '../../../lib/user' | |
19 | import { | |
20 | acceptOrRejectRegistrationValidator, | |
21 | asyncMiddleware, | |
22 | asyncRetryTransactionMiddleware, | |
23 | authenticate, | |
24 | buildRateLimiter, | |
25 | ensureUserHasRight, | |
26 | ensureUserRegistrationAllowedFactory, | |
27 | ensureUserRegistrationAllowedForIP, | |
28 | getRegistrationValidator, | |
29 | listRegistrationsValidator, | |
30 | paginationValidator, | |
31 | setDefaultPagination, | |
32 | setDefaultSort, | |
33 | userRegistrationsSortValidator, | |
34 | usersDirectRegistrationValidator, | |
35 | usersRequestRegistrationValidator | |
36 | } from '../../../middlewares' | |
37 | ||
38 | const auditLogger = auditLoggerFactory('users') | |
39 | ||
40 | const registrationRateLimiter = buildRateLimiter({ | |
41 | windowMs: CONFIG.RATES_LIMIT.SIGNUP.WINDOW_MS, | |
42 | max: CONFIG.RATES_LIMIT.SIGNUP.MAX, | |
43 | skipFailedRequests: true | |
44 | }) | |
45 | ||
46 | const registrationsRouter = express.Router() | |
47 | ||
48 | registrationsRouter.post('/registrations/request', | |
49 | registrationRateLimiter, | |
50 | asyncMiddleware(ensureUserRegistrationAllowedFactory('request-registration')), | |
51 | ensureUserRegistrationAllowedForIP, | |
52 | asyncMiddleware(usersRequestRegistrationValidator), | |
53 | asyncRetryTransactionMiddleware(requestRegistration) | |
54 | ) | |
55 | ||
56 | registrationsRouter.post('/registrations/:registrationId/accept', | |
57 | authenticate, | |
58 | ensureUserHasRight(UserRight.MANAGE_REGISTRATIONS), | |
59 | asyncMiddleware(acceptOrRejectRegistrationValidator), | |
60 | asyncRetryTransactionMiddleware(acceptRegistration) | |
61 | ) | |
62 | registrationsRouter.post('/registrations/:registrationId/reject', | |
63 | authenticate, | |
64 | ensureUserHasRight(UserRight.MANAGE_REGISTRATIONS), | |
65 | asyncMiddleware(acceptOrRejectRegistrationValidator), | |
66 | asyncRetryTransactionMiddleware(rejectRegistration) | |
67 | ) | |
68 | ||
69 | registrationsRouter.delete('/registrations/:registrationId', | |
70 | authenticate, | |
71 | ensureUserHasRight(UserRight.MANAGE_REGISTRATIONS), | |
72 | asyncMiddleware(getRegistrationValidator), | |
73 | asyncRetryTransactionMiddleware(deleteRegistration) | |
74 | ) | |
75 | ||
76 | registrationsRouter.get('/registrations', | |
77 | authenticate, | |
78 | ensureUserHasRight(UserRight.MANAGE_REGISTRATIONS), | |
79 | paginationValidator, | |
80 | userRegistrationsSortValidator, | |
81 | setDefaultSort, | |
82 | setDefaultPagination, | |
83 | listRegistrationsValidator, | |
84 | asyncMiddleware(listRegistrations) | |
85 | ) | |
86 | ||
87 | registrationsRouter.post('/register', | |
88 | registrationRateLimiter, | |
89 | asyncMiddleware(ensureUserRegistrationAllowedFactory('direct-registration')), | |
90 | ensureUserRegistrationAllowedForIP, | |
91 | asyncMiddleware(usersDirectRegistrationValidator), | |
92 | asyncRetryTransactionMiddleware(registerUser) | |
93 | ) | |
94 | ||
95 | // --------------------------------------------------------------------------- | |
96 | ||
97 | export { | |
98 | registrationsRouter | |
99 | } | |
100 | ||
101 | // --------------------------------------------------------------------------- | |
102 | ||
103 | async function requestRegistration (req: express.Request, res: express.Response) { | |
104 | const body: UserRegistrationRequest = req.body | |
105 | ||
106 | const registration = new UserRegistrationModel({ | |
107 | ...pick(body, [ 'username', 'password', 'email', 'registrationReason' ]), | |
108 | ||
109 | accountDisplayName: body.displayName, | |
110 | channelDisplayName: body.channel?.displayName, | |
111 | channelHandle: body.channel?.name, | |
112 | ||
113 | state: UserRegistrationState.PENDING, | |
114 | ||
115 | emailVerified: CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION ? false : null | |
116 | }) | |
117 | ||
118 | await registration.save() | |
119 | ||
120 | if (CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION) { | |
121 | await sendVerifyRegistrationEmail(registration) | |
122 | } | |
123 | ||
124 | Notifier.Instance.notifyOnNewRegistrationRequest(registration) | |
125 | ||
126 | Hooks.runAction('action:api.user.requested-registration', { body, registration, req, res }) | |
127 | ||
128 | return res.json(registration.toFormattedJSON()) | |
129 | } | |
130 | ||
131 | // --------------------------------------------------------------------------- | |
132 | ||
133 | async function acceptRegistration (req: express.Request, res: express.Response) { | |
134 | const registration = res.locals.userRegistration | |
4115f200 | 135 | const body: UserRegistrationUpdateState = req.body |
e364e31e C |
136 | |
137 | const userToCreate = buildUser({ | |
138 | username: registration.username, | |
139 | password: registration.password, | |
140 | email: registration.email, | |
141 | emailVerified: registration.emailVerified | |
142 | }) | |
143 | // We already encrypted password in registration model | |
144 | userToCreate.skipPasswordEncryption = true | |
145 | ||
146 | // TODO: handle conflicts if someone else created a channel handle/user handle/user email between registration and approval | |
147 | ||
148 | const { user } = await createUserAccountAndChannelAndPlaylist({ | |
149 | userToCreate, | |
150 | userDisplayName: registration.accountDisplayName, | |
151 | channelNames: registration.channelHandle && registration.channelDisplayName | |
152 | ? { | |
153 | name: registration.channelHandle, | |
154 | displayName: registration.channelDisplayName | |
155 | } | |
156 | : undefined | |
157 | }) | |
158 | ||
159 | registration.userId = user.id | |
160 | registration.state = UserRegistrationState.ACCEPTED | |
4115f200 | 161 | registration.moderationResponse = body.moderationResponse |
e364e31e C |
162 | |
163 | await registration.save() | |
164 | ||
165 | logger.info('Registration of %s accepted', registration.username) | |
166 | ||
4115f200 C |
167 | if (body.preventEmailDelivery !== true) { |
168 | Emailer.Instance.addUserRegistrationRequestProcessedJob(registration) | |
169 | } | |
e364e31e C |
170 | |
171 | return res.sendStatus(HttpStatusCode.NO_CONTENT_204) | |
172 | } | |
173 | ||
174 | async function rejectRegistration (req: express.Request, res: express.Response) { | |
175 | const registration = res.locals.userRegistration | |
4115f200 | 176 | const body: UserRegistrationUpdateState = req.body |
e364e31e C |
177 | |
178 | registration.state = UserRegistrationState.REJECTED | |
4115f200 | 179 | registration.moderationResponse = body.moderationResponse |
e364e31e C |
180 | |
181 | await registration.save() | |
182 | ||
4115f200 C |
183 | if (body.preventEmailDelivery !== true) { |
184 | Emailer.Instance.addUserRegistrationRequestProcessedJob(registration) | |
185 | } | |
e364e31e C |
186 | |
187 | logger.info('Registration of %s rejected', registration.username) | |
188 | ||
189 | return res.sendStatus(HttpStatusCode.NO_CONTENT_204) | |
190 | } | |
191 | ||
192 | // --------------------------------------------------------------------------- | |
193 | ||
194 | async function deleteRegistration (req: express.Request, res: express.Response) { | |
195 | const registration = res.locals.userRegistration | |
196 | ||
197 | await registration.destroy() | |
198 | ||
199 | logger.info('Registration of %s deleted', registration.username) | |
200 | ||
201 | return res.sendStatus(HttpStatusCode.NO_CONTENT_204) | |
202 | } | |
203 | ||
204 | // --------------------------------------------------------------------------- | |
205 | ||
206 | async function listRegistrations (req: express.Request, res: express.Response) { | |
207 | const resultList = await UserRegistrationModel.listForApi({ | |
208 | start: req.query.start, | |
209 | count: req.query.count, | |
210 | sort: req.query.sort, | |
211 | search: req.query.search | |
212 | }) | |
213 | ||
214 | return res.json({ | |
215 | total: resultList.total, | |
216 | data: resultList.data.map(d => d.toFormattedJSON()) | |
217 | }) | |
218 | } | |
219 | ||
220 | // --------------------------------------------------------------------------- | |
221 | ||
222 | async function registerUser (req: express.Request, res: express.Response) { | |
223 | const body: UserRegister = req.body | |
224 | ||
225 | const userToCreate = buildUser({ | |
226 | ...pick(body, [ 'username', 'password', 'email' ]), | |
227 | ||
228 | emailVerified: CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION ? false : null | |
229 | }) | |
230 | ||
231 | const { user, account, videoChannel } = await createUserAccountAndChannelAndPlaylist({ | |
232 | userToCreate, | |
233 | userDisplayName: body.displayName || undefined, | |
234 | channelNames: body.channel | |
235 | }) | |
236 | ||
237 | auditLogger.create(body.username, new UserAuditView(user.toFormattedJSON())) | |
238 | logger.info('User %s with its channel and account registered.', body.username) | |
239 | ||
240 | if (CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION) { | |
241 | await sendVerifyUserEmail(user) | |
242 | } | |
243 | ||
244 | Notifier.Instance.notifyOnNewDirectRegistration(user) | |
245 | ||
246 | Hooks.runAction('action:api.user.registered', { body, user, account, videoChannel, req, res }) | |
247 | ||
248 | return res.sendStatus(HttpStatusCode.NO_CONTENT_204) | |
249 | } |