]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/controllers/api/users/registrations.ts
Implement signup approval in server
[github/Chocobozzz/PeerTube.git] / server / controllers / api / users / registrations.ts
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 }