index.ts 11 KB

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