index.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. import * as express from 'express'
  2. import * as RateLimit from 'express-rate-limit'
  3. import { UserCreate, UserRight, UserRole, UserUpdate } from '../../../../shared'
  4. import { logger } from '../../../helpers/logger'
  5. import { getFormattedObjects } from '../../../helpers/utils'
  6. import { RATES_LIMIT, WEBSERVER } from '../../../initializers/constants'
  7. import { Emailer } from '../../../lib/emailer'
  8. import { Redis } from '../../../lib/redis'
  9. import { createUserAccountAndChannelAndPlaylist, sendVerifyUserEmail } from '../../../lib/user'
  10. import {
  11. asyncMiddleware,
  12. asyncRetryTransactionMiddleware,
  13. authenticate,
  14. ensureUserHasRight,
  15. ensureUserRegistrationAllowed,
  16. ensureUserRegistrationAllowedForIP,
  17. paginationValidator,
  18. setDefaultPagination,
  19. setDefaultSort,
  20. token,
  21. userAutocompleteValidator,
  22. usersAddValidator,
  23. usersGetValidator,
  24. usersRegisterValidator,
  25. usersRemoveValidator,
  26. usersSortValidator,
  27. usersUpdateValidator
  28. } from '../../../middlewares'
  29. import {
  30. usersAskResetPasswordValidator,
  31. usersAskSendVerifyEmailValidator,
  32. usersBlockingValidator,
  33. usersResetPasswordValidator,
  34. usersVerifyEmailValidator
  35. } from '../../../middlewares/validators'
  36. import { UserModel } from '../../../models/account/user'
  37. import { auditLoggerFactory, getAuditIdFromRes, UserAuditView } from '../../../helpers/audit-logger'
  38. import { meRouter } from './me'
  39. import { deleteUserToken } from '../../../lib/oauth-model'
  40. import { myBlocklistRouter } from './my-blocklist'
  41. import { myVideoPlaylistsRouter } from './my-video-playlists'
  42. import { myVideosHistoryRouter } from './my-history'
  43. import { myNotificationsRouter } from './my-notifications'
  44. import { Notifier } from '../../../lib/notifier'
  45. import { mySubscriptionsRouter } from './my-subscriptions'
  46. import { CONFIG } from '../../../initializers/config'
  47. import { sequelizeTypescript } from '../../../initializers/database'
  48. import { UserAdminFlag } from '../../../../shared/models/users/user-flag.model'
  49. import { UserRegister } from '../../../../shared/models/users/user-register.model'
  50. const auditLogger = auditLoggerFactory('users')
  51. // FIXME: https://github.com/nfriedly/express-rate-limit/issues/138
  52. // @ts-ignore
  53. const loginRateLimiter = RateLimit({
  54. windowMs: RATES_LIMIT.LOGIN.WINDOW_MS,
  55. max: RATES_LIMIT.LOGIN.MAX
  56. })
  57. // @ts-ignore
  58. const askSendEmailLimiter = new RateLimit({
  59. windowMs: RATES_LIMIT.ASK_SEND_EMAIL.WINDOW_MS,
  60. max: RATES_LIMIT.ASK_SEND_EMAIL.MAX
  61. })
  62. const usersRouter = express.Router()
  63. usersRouter.use('/', myNotificationsRouter)
  64. usersRouter.use('/', mySubscriptionsRouter)
  65. usersRouter.use('/', myBlocklistRouter)
  66. usersRouter.use('/', myVideosHistoryRouter)
  67. usersRouter.use('/', myVideoPlaylistsRouter)
  68. usersRouter.use('/', meRouter)
  69. usersRouter.get('/autocomplete',
  70. userAutocompleteValidator,
  71. asyncMiddleware(autocompleteUsers)
  72. )
  73. usersRouter.get('/',
  74. authenticate,
  75. ensureUserHasRight(UserRight.MANAGE_USERS),
  76. paginationValidator,
  77. usersSortValidator,
  78. setDefaultSort,
  79. setDefaultPagination,
  80. asyncMiddleware(listUsers)
  81. )
  82. usersRouter.post('/:id/block',
  83. authenticate,
  84. ensureUserHasRight(UserRight.MANAGE_USERS),
  85. asyncMiddleware(usersBlockingValidator),
  86. asyncMiddleware(blockUser)
  87. )
  88. usersRouter.post('/:id/unblock',
  89. authenticate,
  90. ensureUserHasRight(UserRight.MANAGE_USERS),
  91. asyncMiddleware(usersBlockingValidator),
  92. asyncMiddleware(unblockUser)
  93. )
  94. usersRouter.get('/:id',
  95. authenticate,
  96. ensureUserHasRight(UserRight.MANAGE_USERS),
  97. asyncMiddleware(usersGetValidator),
  98. getUser
  99. )
  100. usersRouter.post('/',
  101. authenticate,
  102. ensureUserHasRight(UserRight.MANAGE_USERS),
  103. asyncMiddleware(usersAddValidator),
  104. asyncRetryTransactionMiddleware(createUser)
  105. )
  106. usersRouter.post('/register',
  107. asyncMiddleware(ensureUserRegistrationAllowed),
  108. ensureUserRegistrationAllowedForIP,
  109. asyncMiddleware(usersRegisterValidator),
  110. asyncRetryTransactionMiddleware(registerUser)
  111. )
  112. usersRouter.put('/:id',
  113. authenticate,
  114. ensureUserHasRight(UserRight.MANAGE_USERS),
  115. asyncMiddleware(usersUpdateValidator),
  116. asyncMiddleware(updateUser)
  117. )
  118. usersRouter.delete('/:id',
  119. authenticate,
  120. ensureUserHasRight(UserRight.MANAGE_USERS),
  121. asyncMiddleware(usersRemoveValidator),
  122. asyncMiddleware(removeUser)
  123. )
  124. usersRouter.post('/ask-reset-password',
  125. asyncMiddleware(usersAskResetPasswordValidator),
  126. asyncMiddleware(askResetUserPassword)
  127. )
  128. usersRouter.post('/:id/reset-password',
  129. asyncMiddleware(usersResetPasswordValidator),
  130. asyncMiddleware(resetUserPassword)
  131. )
  132. usersRouter.post('/ask-send-verify-email',
  133. askSendEmailLimiter,
  134. asyncMiddleware(usersAskSendVerifyEmailValidator),
  135. asyncMiddleware(reSendVerifyUserEmail)
  136. )
  137. usersRouter.post('/:id/verify-email',
  138. asyncMiddleware(usersVerifyEmailValidator),
  139. asyncMiddleware(verifyUserEmail)
  140. )
  141. usersRouter.post('/token',
  142. loginRateLimiter,
  143. token,
  144. success
  145. )
  146. // TODO: Once https://github.com/oauthjs/node-oauth2-server/pull/289 is merged, implement revoke token route
  147. // ---------------------------------------------------------------------------
  148. export {
  149. usersRouter
  150. }
  151. // ---------------------------------------------------------------------------
  152. async function createUser (req: express.Request, res: express.Response) {
  153. const body: UserCreate = req.body
  154. const userToCreate = new UserModel({
  155. username: body.username,
  156. password: body.password,
  157. email: body.email,
  158. nsfwPolicy: CONFIG.INSTANCE.DEFAULT_NSFW_POLICY,
  159. autoPlayVideo: true,
  160. role: body.role,
  161. videoQuota: body.videoQuota,
  162. videoQuotaDaily: body.videoQuotaDaily,
  163. adminFlags: body.adminFlags || UserAdminFlag.NONE
  164. })
  165. const { user, account } = await createUserAccountAndChannelAndPlaylist({ userToCreate: userToCreate })
  166. auditLogger.create(getAuditIdFromRes(res), new UserAuditView(user.toFormattedJSON()))
  167. logger.info('User %s with its channel and account created.', body.username)
  168. return res.json({
  169. user: {
  170. id: user.id,
  171. account: {
  172. id: account.id
  173. }
  174. }
  175. }).end()
  176. }
  177. async function registerUser (req: express.Request, res: express.Response) {
  178. const body: UserRegister = req.body
  179. const userToCreate = new UserModel({
  180. username: body.username,
  181. password: body.password,
  182. email: body.email,
  183. nsfwPolicy: CONFIG.INSTANCE.DEFAULT_NSFW_POLICY,
  184. autoPlayVideo: true,
  185. role: UserRole.USER,
  186. videoQuota: CONFIG.USER.VIDEO_QUOTA,
  187. videoQuotaDaily: CONFIG.USER.VIDEO_QUOTA_DAILY,
  188. emailVerified: CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION ? false : null
  189. })
  190. const { user } = await createUserAccountAndChannelAndPlaylist({
  191. userToCreate: userToCreate,
  192. userDisplayName: body.displayName || undefined,
  193. channelNames: body.channel
  194. })
  195. auditLogger.create(body.username, new UserAuditView(user.toFormattedJSON()))
  196. logger.info('User %s with its channel and account registered.', body.username)
  197. if (CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION) {
  198. await sendVerifyUserEmail(user)
  199. }
  200. Notifier.Instance.notifyOnNewUserRegistration(user)
  201. return res.type('json').status(204).end()
  202. }
  203. async function unblockUser (req: express.Request, res: express.Response) {
  204. const user = res.locals.user
  205. await changeUserBlock(res, user, false)
  206. return res.status(204).end()
  207. }
  208. async function blockUser (req: express.Request, res: express.Response) {
  209. const user = res.locals.user
  210. const reason = req.body.reason
  211. await changeUserBlock(res, user, true, reason)
  212. return res.status(204).end()
  213. }
  214. function getUser (req: express.Request, res: express.Response) {
  215. return res.json(res.locals.user.toFormattedJSON({ withAdminFlags: true }))
  216. }
  217. async function autocompleteUsers (req: express.Request, res: express.Response) {
  218. const resultList = await UserModel.autoComplete(req.query.search as string)
  219. return res.json(resultList)
  220. }
  221. async function listUsers (req: express.Request, res: express.Response) {
  222. const resultList = await UserModel.listForApi(req.query.start, req.query.count, req.query.sort, req.query.search)
  223. return res.json(getFormattedObjects(resultList.data, resultList.total, { withAdminFlags: true }))
  224. }
  225. async function removeUser (req: express.Request, res: express.Response) {
  226. const user = res.locals.user
  227. await user.destroy()
  228. auditLogger.delete(getAuditIdFromRes(res), new UserAuditView(user.toFormattedJSON()))
  229. return res.sendStatus(204)
  230. }
  231. async function updateUser (req: express.Request, res: express.Response) {
  232. const body: UserUpdate = req.body
  233. const userToUpdate = res.locals.user
  234. const oldUserAuditView = new UserAuditView(userToUpdate.toFormattedJSON())
  235. const roleChanged = body.role !== undefined && body.role !== userToUpdate.role
  236. if (body.password !== undefined) userToUpdate.password = body.password
  237. if (body.email !== undefined) userToUpdate.email = body.email
  238. if (body.emailVerified !== undefined) userToUpdate.emailVerified = body.emailVerified
  239. if (body.videoQuota !== undefined) userToUpdate.videoQuota = body.videoQuota
  240. if (body.videoQuotaDaily !== undefined) userToUpdate.videoQuotaDaily = body.videoQuotaDaily
  241. if (body.role !== undefined) userToUpdate.role = body.role
  242. if (body.adminFlags !== undefined) userToUpdate.adminFlags = body.adminFlags
  243. const user = await userToUpdate.save()
  244. // Destroy user token to refresh rights
  245. if (roleChanged || body.password !== undefined) await deleteUserToken(userToUpdate.id)
  246. auditLogger.update(getAuditIdFromRes(res), new UserAuditView(user.toFormattedJSON()), oldUserAuditView)
  247. // Don't need to send this update to followers, these attributes are not federated
  248. return res.sendStatus(204)
  249. }
  250. async function askResetUserPassword (req: express.Request, res: express.Response) {
  251. const user = res.locals.user
  252. const verificationString = await Redis.Instance.setResetPasswordVerificationString(user.id)
  253. const url = WEBSERVER.URL + '/reset-password?userId=' + user.id + '&verificationString=' + verificationString
  254. await Emailer.Instance.addPasswordResetEmailJob(user.email, url)
  255. return res.status(204).end()
  256. }
  257. async function resetUserPassword (req: express.Request, res: express.Response) {
  258. const user = res.locals.user
  259. user.password = req.body.password
  260. await user.save()
  261. return res.status(204).end()
  262. }
  263. async function reSendVerifyUserEmail (req: express.Request, res: express.Response) {
  264. const user = res.locals.user
  265. await sendVerifyUserEmail(user)
  266. return res.status(204).end()
  267. }
  268. async function verifyUserEmail (req: express.Request, res: express.Response) {
  269. const user = res.locals.user
  270. user.emailVerified = true
  271. if (req.body.isPendingEmail === true) {
  272. user.email = user.pendingEmail
  273. user.pendingEmail = null
  274. }
  275. await user.save()
  276. return res.status(204).end()
  277. }
  278. function success (req: express.Request, res: express.Response) {
  279. res.end()
  280. }
  281. async function changeUserBlock (res: express.Response, user: UserModel, block: boolean, reason?: string) {
  282. const oldUserAuditView = new UserAuditView(user.toFormattedJSON())
  283. user.blocked = block
  284. user.blockedReason = reason || null
  285. await sequelizeTypescript.transaction(async t => {
  286. await deleteUserToken(user.id, t)
  287. await user.save({ transaction: t })
  288. })
  289. await Emailer.Instance.addUserBlockJob(user, block, reason)
  290. auditLogger.update(getAuditIdFromRes(res), new UserAuditView(user.toFormattedJSON()), oldUserAuditView)
  291. }