|
@@ -0,0 +1,236 @@
|
|
|
+import express from 'express'
|
|
|
+import { Emailer } from '@server/lib/emailer'
|
|
|
+import { Hooks } from '@server/lib/plugins/hooks'
|
|
|
+import { UserRegistrationModel } from '@server/models/user/user-registration'
|
|
|
+import { pick } from '@shared/core-utils'
|
|
|
+import { HttpStatusCode, UserRegister, UserRegistrationRequest, UserRegistrationState, UserRight } from '@shared/models'
|
|
|
+import { auditLoggerFactory, UserAuditView } from '../../../helpers/audit-logger'
|
|
|
+import { logger } from '../../../helpers/logger'
|
|
|
+import { CONFIG } from '../../../initializers/config'
|
|
|
+import { Notifier } from '../../../lib/notifier'
|
|
|
+import { buildUser, createUserAccountAndChannelAndPlaylist, sendVerifyRegistrationEmail, sendVerifyUserEmail } from '../../../lib/user'
|
|
|
+import {
|
|
|
+ acceptOrRejectRegistrationValidator,
|
|
|
+ asyncMiddleware,
|
|
|
+ asyncRetryTransactionMiddleware,
|
|
|
+ authenticate,
|
|
|
+ buildRateLimiter,
|
|
|
+ ensureUserHasRight,
|
|
|
+ ensureUserRegistrationAllowedFactory,
|
|
|
+ ensureUserRegistrationAllowedForIP,
|
|
|
+ getRegistrationValidator,
|
|
|
+ listRegistrationsValidator,
|
|
|
+ paginationValidator,
|
|
|
+ setDefaultPagination,
|
|
|
+ setDefaultSort,
|
|
|
+ userRegistrationsSortValidator,
|
|
|
+ usersDirectRegistrationValidator,
|
|
|
+ usersRequestRegistrationValidator
|
|
|
+} from '../../../middlewares'
|
|
|
+
|
|
|
+const auditLogger = auditLoggerFactory('users')
|
|
|
+
|
|
|
+const registrationRateLimiter = buildRateLimiter({
|
|
|
+ windowMs: CONFIG.RATES_LIMIT.SIGNUP.WINDOW_MS,
|
|
|
+ max: CONFIG.RATES_LIMIT.SIGNUP.MAX,
|
|
|
+ skipFailedRequests: true
|
|
|
+})
|
|
|
+
|
|
|
+const registrationsRouter = express.Router()
|
|
|
+
|
|
|
+registrationsRouter.post('/registrations/request',
|
|
|
+ registrationRateLimiter,
|
|
|
+ asyncMiddleware(ensureUserRegistrationAllowedFactory('request-registration')),
|
|
|
+ ensureUserRegistrationAllowedForIP,
|
|
|
+ asyncMiddleware(usersRequestRegistrationValidator),
|
|
|
+ asyncRetryTransactionMiddleware(requestRegistration)
|
|
|
+)
|
|
|
+
|
|
|
+registrationsRouter.post('/registrations/:registrationId/accept',
|
|
|
+ authenticate,
|
|
|
+ ensureUserHasRight(UserRight.MANAGE_REGISTRATIONS),
|
|
|
+ asyncMiddleware(acceptOrRejectRegistrationValidator),
|
|
|
+ asyncRetryTransactionMiddleware(acceptRegistration)
|
|
|
+)
|
|
|
+registrationsRouter.post('/registrations/:registrationId/reject',
|
|
|
+ authenticate,
|
|
|
+ ensureUserHasRight(UserRight.MANAGE_REGISTRATIONS),
|
|
|
+ asyncMiddleware(acceptOrRejectRegistrationValidator),
|
|
|
+ asyncRetryTransactionMiddleware(rejectRegistration)
|
|
|
+)
|
|
|
+
|
|
|
+registrationsRouter.delete('/registrations/:registrationId',
|
|
|
+ authenticate,
|
|
|
+ ensureUserHasRight(UserRight.MANAGE_REGISTRATIONS),
|
|
|
+ asyncMiddleware(getRegistrationValidator),
|
|
|
+ asyncRetryTransactionMiddleware(deleteRegistration)
|
|
|
+)
|
|
|
+
|
|
|
+registrationsRouter.get('/registrations',
|
|
|
+ authenticate,
|
|
|
+ ensureUserHasRight(UserRight.MANAGE_REGISTRATIONS),
|
|
|
+ paginationValidator,
|
|
|
+ userRegistrationsSortValidator,
|
|
|
+ setDefaultSort,
|
|
|
+ setDefaultPagination,
|
|
|
+ listRegistrationsValidator,
|
|
|
+ asyncMiddleware(listRegistrations)
|
|
|
+)
|
|
|
+
|
|
|
+registrationsRouter.post('/register',
|
|
|
+ registrationRateLimiter,
|
|
|
+ asyncMiddleware(ensureUserRegistrationAllowedFactory('direct-registration')),
|
|
|
+ ensureUserRegistrationAllowedForIP,
|
|
|
+ asyncMiddleware(usersDirectRegistrationValidator),
|
|
|
+ asyncRetryTransactionMiddleware(registerUser)
|
|
|
+)
|
|
|
+
|
|
|
+// ---------------------------------------------------------------------------
|
|
|
+
|
|
|
+export {
|
|
|
+ registrationsRouter
|
|
|
+}
|
|
|
+
|
|
|
+// ---------------------------------------------------------------------------
|
|
|
+
|
|
|
+async function requestRegistration (req: express.Request, res: express.Response) {
|
|
|
+ const body: UserRegistrationRequest = req.body
|
|
|
+
|
|
|
+ const registration = new UserRegistrationModel({
|
|
|
+ ...pick(body, [ 'username', 'password', 'email', 'registrationReason' ]),
|
|
|
+
|
|
|
+ accountDisplayName: body.displayName,
|
|
|
+ channelDisplayName: body.channel?.displayName,
|
|
|
+ channelHandle: body.channel?.name,
|
|
|
+
|
|
|
+ state: UserRegistrationState.PENDING,
|
|
|
+
|
|
|
+ emailVerified: CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION ? false : null
|
|
|
+ })
|
|
|
+
|
|
|
+ await registration.save()
|
|
|
+
|
|
|
+ if (CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION) {
|
|
|
+ await sendVerifyRegistrationEmail(registration)
|
|
|
+ }
|
|
|
+
|
|
|
+ Notifier.Instance.notifyOnNewRegistrationRequest(registration)
|
|
|
+
|
|
|
+ Hooks.runAction('action:api.user.requested-registration', { body, registration, req, res })
|
|
|
+
|
|
|
+ return res.json(registration.toFormattedJSON())
|
|
|
+}
|
|
|
+
|
|
|
+// ---------------------------------------------------------------------------
|
|
|
+
|
|
|
+async function acceptRegistration (req: express.Request, res: express.Response) {
|
|
|
+ const registration = res.locals.userRegistration
|
|
|
+
|
|
|
+ const userToCreate = buildUser({
|
|
|
+ username: registration.username,
|
|
|
+ password: registration.password,
|
|
|
+ email: registration.email,
|
|
|
+ emailVerified: registration.emailVerified
|
|
|
+ })
|
|
|
+ // We already encrypted password in registration model
|
|
|
+ userToCreate.skipPasswordEncryption = true
|
|
|
+
|
|
|
+ // TODO: handle conflicts if someone else created a channel handle/user handle/user email between registration and approval
|
|
|
+
|
|
|
+ const { user } = await createUserAccountAndChannelAndPlaylist({
|
|
|
+ userToCreate,
|
|
|
+ userDisplayName: registration.accountDisplayName,
|
|
|
+ channelNames: registration.channelHandle && registration.channelDisplayName
|
|
|
+ ? {
|
|
|
+ name: registration.channelHandle,
|
|
|
+ displayName: registration.channelDisplayName
|
|
|
+ }
|
|
|
+ : undefined
|
|
|
+ })
|
|
|
+
|
|
|
+ registration.userId = user.id
|
|
|
+ registration.state = UserRegistrationState.ACCEPTED
|
|
|
+ registration.moderationResponse = req.body.moderationResponse
|
|
|
+
|
|
|
+ await registration.save()
|
|
|
+
|
|
|
+ logger.info('Registration of %s accepted', registration.username)
|
|
|
+
|
|
|
+ Emailer.Instance.addUserRegistrationRequestProcessedJob(registration)
|
|
|
+
|
|
|
+ return res.sendStatus(HttpStatusCode.NO_CONTENT_204)
|
|
|
+}
|
|
|
+
|
|
|
+async function rejectRegistration (req: express.Request, res: express.Response) {
|
|
|
+ const registration = res.locals.userRegistration
|
|
|
+
|
|
|
+ registration.state = UserRegistrationState.REJECTED
|
|
|
+ registration.moderationResponse = req.body.moderationResponse
|
|
|
+
|
|
|
+ await registration.save()
|
|
|
+
|
|
|
+ Emailer.Instance.addUserRegistrationRequestProcessedJob(registration)
|
|
|
+
|
|
|
+ logger.info('Registration of %s rejected', registration.username)
|
|
|
+
|
|
|
+ return res.sendStatus(HttpStatusCode.NO_CONTENT_204)
|
|
|
+}
|
|
|
+
|
|
|
+// ---------------------------------------------------------------------------
|
|
|
+
|
|
|
+async function deleteRegistration (req: express.Request, res: express.Response) {
|
|
|
+ const registration = res.locals.userRegistration
|
|
|
+
|
|
|
+ await registration.destroy()
|
|
|
+
|
|
|
+ logger.info('Registration of %s deleted', registration.username)
|
|
|
+
|
|
|
+ return res.sendStatus(HttpStatusCode.NO_CONTENT_204)
|
|
|
+}
|
|
|
+
|
|
|
+// ---------------------------------------------------------------------------
|
|
|
+
|
|
|
+async function listRegistrations (req: express.Request, res: express.Response) {
|
|
|
+ const resultList = await UserRegistrationModel.listForApi({
|
|
|
+ start: req.query.start,
|
|
|
+ count: req.query.count,
|
|
|
+ sort: req.query.sort,
|
|
|
+ search: req.query.search
|
|
|
+ })
|
|
|
+
|
|
|
+ return res.json({
|
|
|
+ total: resultList.total,
|
|
|
+ data: resultList.data.map(d => d.toFormattedJSON())
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// ---------------------------------------------------------------------------
|
|
|
+
|
|
|
+async function registerUser (req: express.Request, res: express.Response) {
|
|
|
+ const body: UserRegister = req.body
|
|
|
+
|
|
|
+ const userToCreate = buildUser({
|
|
|
+ ...pick(body, [ 'username', 'password', 'email' ]),
|
|
|
+
|
|
|
+ emailVerified: CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION ? false : null
|
|
|
+ })
|
|
|
+
|
|
|
+ const { user, account, videoChannel } = await createUserAccountAndChannelAndPlaylist({
|
|
|
+ userToCreate,
|
|
|
+ userDisplayName: body.displayName || undefined,
|
|
|
+ channelNames: body.channel
|
|
|
+ })
|
|
|
+
|
|
|
+ auditLogger.create(body.username, new UserAuditView(user.toFormattedJSON()))
|
|
|
+ logger.info('User %s with its channel and account registered.', body.username)
|
|
|
+
|
|
|
+ if (CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION) {
|
|
|
+ await sendVerifyUserEmail(user)
|
|
|
+ }
|
|
|
+
|
|
|
+ Notifier.Instance.notifyOnNewDirectRegistration(user)
|
|
|
+
|
|
|
+ Hooks.runAction('action:api.user.registered', { body, user, account, videoChannel, req, res })
|
|
|
+
|
|
|
+ return res.sendStatus(HttpStatusCode.NO_CONTENT_204)
|
|
|
+}
|