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