index.ts 11 KB

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