user-registrations.ts 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. import express from 'express'
  2. import { body, param, query, ValidationChain } from 'express-validator'
  3. import { exists, isIdValid } 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. async (req: express.Request, res: express.Response, next: express.NextFunction) => {
  76. if (areValidationErrors(req, res)) return
  77. if (!await checkRegistrationIdExist(req.params.registrationId, res)) return
  78. if (res.locals.userRegistration.state !== UserRegistrationState.PENDING) {
  79. return res.fail({
  80. status: HttpStatusCode.CONFLICT_409,
  81. message: 'This registration is already accepted or rejected.'
  82. })
  83. }
  84. return next()
  85. }
  86. ]
  87. // ---------------------------------------------------------------------------
  88. const getRegistrationValidator = [
  89. param('registrationId')
  90. .custom(isIdValid),
  91. async (req: express.Request, res: express.Response, next: express.NextFunction) => {
  92. if (areValidationErrors(req, res)) return
  93. if (!await checkRegistrationIdExist(req.params.registrationId, res)) return
  94. return next()
  95. }
  96. ]
  97. // ---------------------------------------------------------------------------
  98. const listRegistrationsValidator = [
  99. query('search')
  100. .optional()
  101. .custom(exists),
  102. (req: express.Request, res: express.Response, next: express.NextFunction) => {
  103. if (areValidationErrors(req, res)) return
  104. return next()
  105. }
  106. ]
  107. // ---------------------------------------------------------------------------
  108. export {
  109. usersDirectRegistrationValidator,
  110. usersRequestRegistrationValidator,
  111. ensureUserRegistrationAllowedFactory,
  112. ensureUserRegistrationAllowedForIP,
  113. getRegistrationValidator,
  114. listRegistrationsValidator,
  115. acceptOrRejectRegistrationValidator
  116. }
  117. // ---------------------------------------------------------------------------
  118. function usersCommonRegistrationValidatorFactory (additionalValidationChain: ValidationChain[] = []) {
  119. return [
  120. body('username')
  121. .custom(isUserUsernameValid),
  122. body('password')
  123. .custom(isUserPasswordValid),
  124. body('email')
  125. .isEmail(),
  126. body('displayName')
  127. .optional()
  128. .custom(isUserDisplayNameValid),
  129. body('channel.name')
  130. .optional()
  131. .custom(isVideoChannelUsernameValid),
  132. body('channel.displayName')
  133. .optional()
  134. .custom(isVideoChannelDisplayNameValid),
  135. ...additionalValidationChain,
  136. async (req: express.Request, res: express.Response, next: express.NextFunction) => {
  137. if (areValidationErrors(req, res, { omitBodyLog: true })) return
  138. const body: UserRegister | UserRegistrationRequest = req.body
  139. if (!await checkUserNameOrEmailDoNotAlreadyExist(body.username, body.email, res)) return
  140. if (body.channel) {
  141. if (!body.channel.name || !body.channel.displayName) {
  142. return res.fail({ message: 'Channel is optional but if you specify it, channel.name and channel.displayName are required.' })
  143. }
  144. if (body.channel.name === body.username) {
  145. return res.fail({ message: 'Channel name cannot be the same as user username.' })
  146. }
  147. const existing = await ActorModel.loadLocalByName(body.channel.name)
  148. if (existing) {
  149. return res.fail({
  150. status: HttpStatusCode.CONFLICT_409,
  151. message: `Channel with name ${body.channel.name} already exists.`
  152. })
  153. }
  154. }
  155. return next()
  156. }
  157. ]
  158. }