user-registrations.ts 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  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. const usersDirectRegistrationValidator = usersCommonRegistrationValidatorFactory()
  15. const usersRequestRegistrationValidator = [
  16. ...usersCommonRegistrationValidatorFactory([
  17. body('registrationReason')
  18. .custom(isRegistrationReasonValid)
  19. ]),
  20. async (req: express.Request, res: express.Response, next: express.NextFunction) => {
  21. const body: UserRegistrationRequest = req.body
  22. if (CONFIG.SIGNUP.REQUIRES_APPROVAL !== true) {
  23. return res.fail({
  24. status: HttpStatusCode.BAD_REQUEST_400,
  25. message: 'Signup approval is not enabled on this instance'
  26. })
  27. }
  28. const options = { username: body.username, email: body.email, channelHandle: body.channel?.name, res }
  29. if (!await checkRegistrationHandlesDoNotAlreadyExist(options)) return
  30. return next()
  31. }
  32. ]
  33. // ---------------------------------------------------------------------------
  34. function ensureUserRegistrationAllowedFactory (signupMode: SignupMode) {
  35. return async (req: express.Request, res: express.Response, next: express.NextFunction) => {
  36. const allowedParams = {
  37. body: req.body,
  38. ip: req.ip,
  39. signupMode
  40. }
  41. const allowedResult = await Hooks.wrapPromiseFun(
  42. isSignupAllowed,
  43. allowedParams,
  44. signupMode === 'direct-registration'
  45. ? 'filter:api.user.signup.allowed.result'
  46. : 'filter:api.user.request-signup.allowed.result'
  47. )
  48. if (allowedResult.allowed === false) {
  49. return res.fail({
  50. status: HttpStatusCode.FORBIDDEN_403,
  51. message: allowedResult.errorMessage || 'User registration is not enabled, user limit is reached or registration requires approval.'
  52. })
  53. }
  54. return next()
  55. }
  56. }
  57. const ensureUserRegistrationAllowedForIP = [
  58. (req: express.Request, res: express.Response, next: express.NextFunction) => {
  59. const allowed = isSignupAllowedForCurrentIP(req.ip)
  60. if (allowed === false) {
  61. return res.fail({
  62. status: HttpStatusCode.FORBIDDEN_403,
  63. message: 'You are not on a network authorized for registration.'
  64. })
  65. }
  66. return next()
  67. }
  68. ]
  69. // ---------------------------------------------------------------------------
  70. const acceptOrRejectRegistrationValidator = [
  71. param('registrationId')
  72. .custom(isIdValid),
  73. body('moderationResponse')
  74. .custom(isRegistrationModerationResponseValid),
  75. body('preventEmailDelivery')
  76. .optional()
  77. .customSanitizer(toBooleanOrNull)
  78. .custom(isBooleanValid).withMessage('Should have preventEmailDelivery boolean'),
  79. async (req: express.Request, res: express.Response, next: express.NextFunction) => {
  80. if (areValidationErrors(req, res)) return
  81. if (!await checkRegistrationIdExist(req.params.registrationId, res)) return
  82. if (res.locals.userRegistration.state !== UserRegistrationState.PENDING) {
  83. return res.fail({
  84. status: HttpStatusCode.CONFLICT_409,
  85. message: 'This registration is already accepted or rejected.'
  86. })
  87. }
  88. return next()
  89. }
  90. ]
  91. // ---------------------------------------------------------------------------
  92. const getRegistrationValidator = [
  93. param('registrationId')
  94. .custom(isIdValid),
  95. async (req: express.Request, res: express.Response, next: express.NextFunction) => {
  96. if (areValidationErrors(req, res)) return
  97. if (!await checkRegistrationIdExist(req.params.registrationId, res)) return
  98. return next()
  99. }
  100. ]
  101. // ---------------------------------------------------------------------------
  102. const listRegistrationsValidator = [
  103. query('search')
  104. .optional()
  105. .custom(exists),
  106. (req: express.Request, res: express.Response, next: express.NextFunction) => {
  107. if (areValidationErrors(req, res)) return
  108. return next()
  109. }
  110. ]
  111. // ---------------------------------------------------------------------------
  112. export {
  113. usersDirectRegistrationValidator,
  114. usersRequestRegistrationValidator,
  115. ensureUserRegistrationAllowedFactory,
  116. ensureUserRegistrationAllowedForIP,
  117. getRegistrationValidator,
  118. listRegistrationsValidator,
  119. acceptOrRejectRegistrationValidator
  120. }
  121. // ---------------------------------------------------------------------------
  122. function usersCommonRegistrationValidatorFactory (additionalValidationChain: ValidationChain[] = []) {
  123. return [
  124. body('username')
  125. .custom(isUserUsernameValid),
  126. body('password')
  127. .custom(isUserPasswordValid),
  128. body('email')
  129. .isEmail(),
  130. body('displayName')
  131. .optional()
  132. .custom(isUserDisplayNameValid),
  133. body('channel.name')
  134. .optional()
  135. .custom(isVideoChannelUsernameValid),
  136. body('channel.displayName')
  137. .optional()
  138. .custom(isVideoChannelDisplayNameValid),
  139. ...additionalValidationChain,
  140. async (req: express.Request, res: express.Response, next: express.NextFunction) => {
  141. if (areValidationErrors(req, res, { omitBodyLog: true })) return
  142. const body: UserRegister | UserRegistrationRequest = req.body
  143. if (!await checkUserNameOrEmailDoNotAlreadyExist(body.username, body.email, res)) return
  144. if (body.channel) {
  145. if (!body.channel.name || !body.channel.displayName) {
  146. return res.fail({ message: 'Channel is optional but if you specify it, channel.name and channel.displayName are required.' })
  147. }
  148. if (body.channel.name === body.username) {
  149. return res.fail({ message: 'Channel name cannot be the same as user username.' })
  150. }
  151. const existing = await ActorModel.loadLocalByName(body.channel.name)
  152. if (existing) {
  153. return res.fail({
  154. status: HttpStatusCode.CONFLICT_409,
  155. message: `Channel with name ${body.channel.name} already exists.`
  156. })
  157. }
  158. }
  159. return next()
  160. }
  161. ]
  162. }