UserController.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. /* eslint-disable
  2. camelcase,
  3. handle-callback-err,
  4. max-len,
  5. no-unused-vars,
  6. */
  7. // TODO: This file was created by bulk-decaffeinate.
  8. // Fix any style issues and re-enable lint.
  9. /*
  10. * decaffeinate suggestions:
  11. * DS102: Remove unnecessary code created because of implicit returns
  12. * DS207: Consider shorter variations of null checks
  13. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  14. */
  15. let UserController
  16. const UserHandler = require('./UserHandler')
  17. const UserDeleter = require('./UserDeleter')
  18. const UserGetter = require('./UserGetter')
  19. const { User } = require('../../models/User')
  20. const newsLetterManager = require('../Newsletter/NewsletterManager')
  21. const UserRegistrationHandler = require('./UserRegistrationHandler')
  22. const logger = require('logger-sharelatex')
  23. const metrics = require('metrics-sharelatex')
  24. const Url = require('url')
  25. const AuthenticationManager = require('../Authentication/AuthenticationManager')
  26. const AuthenticationController = require('../Authentication/AuthenticationController')
  27. const UserSessionsManager = require('./UserSessionsManager')
  28. const UserUpdater = require('./UserUpdater')
  29. const SudoModeHandler = require('../SudoMode/SudoModeHandler')
  30. const settings = require('settings-sharelatex')
  31. const Errors = require('../Errors/Errors')
  32. module.exports = UserController = {
  33. tryDeleteUser(req, res, next) {
  34. return UserController._tryDeleteUser(UserDeleter.deleteUser, req, res, next)
  35. },
  36. trySoftDeleteUser(req, res, next) {
  37. return UserController._tryDeleteUser(
  38. UserDeleter.softDeleteUser,
  39. req,
  40. res,
  41. next
  42. )
  43. },
  44. _tryDeleteUser(deleteMethod, req, res, next) {
  45. const user_id = AuthenticationController.getLoggedInUserId(req)
  46. const { password } = req.body
  47. logger.log({ user_id }, 'trying to delete user account')
  48. if (password == null || password === '') {
  49. logger.err(
  50. { user_id },
  51. 'no password supplied for attempt to delete account'
  52. )
  53. return res.sendStatus(403)
  54. }
  55. return AuthenticationManager.authenticate(
  56. { _id: user_id },
  57. password,
  58. function(err, user) {
  59. if (err != null) {
  60. logger.warn(
  61. { user_id },
  62. 'error authenticating during attempt to delete account'
  63. )
  64. return next(err)
  65. }
  66. if (!user) {
  67. logger.err(
  68. { user_id },
  69. 'auth failed during attempt to delete account'
  70. )
  71. return res.sendStatus(403)
  72. }
  73. return deleteMethod(user_id, function(err) {
  74. if (err != null) {
  75. if (err instanceof Errors.SubscriptionAdminDeletionError) {
  76. return res.status(422).json({ error: err.name })
  77. } else {
  78. logger.warn({ user_id }, 'error while deleting user account')
  79. return next(err)
  80. }
  81. }
  82. const sessionId = req.sessionID
  83. if (typeof req.logout === 'function') {
  84. req.logout()
  85. }
  86. return req.session.destroy(function(err) {
  87. if (err != null) {
  88. logger.warn({ err }, 'error destorying session')
  89. return next(err)
  90. }
  91. UserSessionsManager.untrackSession(user, sessionId)
  92. return res.sendStatus(200)
  93. })
  94. })
  95. }
  96. )
  97. },
  98. unsubscribe(req, res) {
  99. const user_id = AuthenticationController.getLoggedInUserId(req)
  100. return UserGetter.getUser(user_id, (err, user) =>
  101. newsLetterManager.unsubscribe(user, () => res.send())
  102. )
  103. },
  104. updateUserSettings(req, res) {
  105. const user_id = AuthenticationController.getLoggedInUserId(req)
  106. logger.log({ user_id }, 'updating account settings')
  107. return User.findById(user_id, function(err, user) {
  108. if (err != null || user == null) {
  109. logger.err({ err, user_id }, 'problem updaing user settings')
  110. return res.sendStatus(500)
  111. }
  112. if (req.body.first_name != null) {
  113. user.first_name = req.body.first_name.trim()
  114. }
  115. if (req.body.last_name != null) {
  116. user.last_name = req.body.last_name.trim()
  117. }
  118. if (req.body.role != null) {
  119. user.role = req.body.role.trim()
  120. }
  121. if (req.body.institution != null) {
  122. user.institution = req.body.institution.trim()
  123. }
  124. if (req.body.mode != null) {
  125. user.ace.mode = req.body.mode
  126. }
  127. if (req.body.editorTheme != null) {
  128. user.ace.theme = req.body.editorTheme
  129. }
  130. if (req.body.overallTheme != null) {
  131. user.ace.overallTheme = req.body.overallTheme
  132. }
  133. if (req.body.fontSize != null) {
  134. user.ace.fontSize = req.body.fontSize
  135. }
  136. if (req.body.autoComplete != null) {
  137. user.ace.autoComplete = req.body.autoComplete
  138. }
  139. if (req.body.autoPairDelimiters != null) {
  140. user.ace.autoPairDelimiters = req.body.autoPairDelimiters
  141. }
  142. if (req.body.spellCheckLanguage != null) {
  143. user.ace.spellCheckLanguage = req.body.spellCheckLanguage
  144. }
  145. if (req.body.pdfViewer != null) {
  146. user.ace.pdfViewer = req.body.pdfViewer
  147. }
  148. if (req.body.syntaxValidation != null) {
  149. user.ace.syntaxValidation = req.body.syntaxValidation
  150. }
  151. if (req.body.fontFamily != null) {
  152. user.ace.fontFamily = req.body.fontFamily
  153. }
  154. if (req.body.lineHeight != null) {
  155. user.ace.lineHeight = req.body.lineHeight
  156. }
  157. return user.save(function(err) {
  158. const newEmail =
  159. req.body.email != null
  160. ? req.body.email.trim().toLowerCase()
  161. : undefined
  162. if (
  163. newEmail == null ||
  164. newEmail === user.email ||
  165. req.externalAuthenticationSystemUsed()
  166. ) {
  167. // end here, don't update email
  168. AuthenticationController.setInSessionUser(req, {
  169. first_name: user.first_name,
  170. last_name: user.last_name
  171. })
  172. return res.sendStatus(200)
  173. } else if (newEmail.indexOf('@') === -1) {
  174. // email invalid
  175. return res.sendStatus(400)
  176. } else {
  177. // update the user email
  178. return UserUpdater.changeEmailAddress(user_id, newEmail, function(
  179. err
  180. ) {
  181. if (err != null) {
  182. let message
  183. logger.err(
  184. { err, user_id, newEmail },
  185. 'problem updaing users email address'
  186. )
  187. if (err instanceof Errors.EmailExistsError) {
  188. message = req.i18n.translate('email_already_registered')
  189. } else {
  190. message = req.i18n.translate('problem_changing_email_address')
  191. }
  192. return res.send(500, { message })
  193. }
  194. return User.findById(user_id, function(err, user) {
  195. if (err != null) {
  196. logger.err(
  197. { err, user_id },
  198. 'error getting user for email update'
  199. )
  200. return res.send(500)
  201. }
  202. AuthenticationController.setInSessionUser(req, {
  203. email: user.email,
  204. first_name: user.first_name,
  205. last_name: user.last_name
  206. })
  207. return UserHandler.populateTeamInvites(user, function(err) {
  208. // need to refresh this in the background
  209. if (err != null) {
  210. logger.err({ err }, 'error populateTeamInvites')
  211. }
  212. return res.sendStatus(200)
  213. })
  214. })
  215. })
  216. }
  217. })
  218. })
  219. },
  220. _doLogout(req, cb) {
  221. if (cb == null) {
  222. cb = function(err) {}
  223. }
  224. metrics.inc('user.logout')
  225. const user = AuthenticationController.getSessionUser(req)
  226. logger.log({ user }, 'logging out')
  227. const sessionId = req.sessionID
  228. if (typeof req.logout === 'function') {
  229. req.logout()
  230. } // passport logout
  231. return req.session.destroy(function(err) {
  232. if (err) {
  233. logger.warn({ err }, 'error destorying session')
  234. cb(err)
  235. }
  236. if (user != null) {
  237. UserSessionsManager.untrackSession(user, sessionId)
  238. SudoModeHandler.clearSudoMode(user._id)
  239. }
  240. return cb()
  241. })
  242. },
  243. logout(req, res, next) {
  244. return UserController._doLogout(req, function(err) {
  245. if (err != null) {
  246. return next(err)
  247. }
  248. const redirect_url =
  249. settings.overleaf != null
  250. ? settings.overleaf.host + '/users/ensure_signed_out'
  251. : '/login'
  252. return res.redirect(redirect_url)
  253. })
  254. },
  255. register(req, res, next) {
  256. if (next == null) {
  257. next = function(error) {}
  258. }
  259. const { email } = req.body
  260. if (email == null || email === '') {
  261. res.sendStatus(422) // Unprocessable Entity
  262. return
  263. }
  264. return UserRegistrationHandler.registerNewUserAndSendActivationEmail(
  265. email,
  266. function(error, user, setNewPasswordUrl) {
  267. if (error != null) {
  268. return next(error)
  269. }
  270. return res.json({
  271. email: user.email,
  272. setNewPasswordUrl
  273. })
  274. }
  275. )
  276. },
  277. clearSessions(req, res, next) {
  278. if (next == null) {
  279. next = function(error) {}
  280. }
  281. metrics.inc('user.clear-sessions')
  282. const user = AuthenticationController.getSessionUser(req)
  283. logger.log({ user_id: user._id }, 'clearing sessions for user')
  284. return UserSessionsManager.revokeAllUserSessions(
  285. user,
  286. [req.sessionID],
  287. function(err) {
  288. if (err != null) {
  289. return next(err)
  290. }
  291. return res.sendStatus(201)
  292. }
  293. )
  294. },
  295. changePassword(req, res, next) {
  296. if (next == null) {
  297. next = function(error) {}
  298. }
  299. metrics.inc('user.password-change')
  300. const oldPass = req.body.currentPassword
  301. const user_id = AuthenticationController.getLoggedInUserId(req)
  302. return AuthenticationManager.authenticate(
  303. { _id: user_id },
  304. oldPass,
  305. function(err, user) {
  306. if (err != null) {
  307. return next(err)
  308. }
  309. if (user) {
  310. logger.log({ user: user._id }, 'changing password')
  311. const { newPassword1 } = req.body
  312. const { newPassword2 } = req.body
  313. const validationError = AuthenticationManager.validatePassword(
  314. newPassword1
  315. )
  316. if (newPassword1 !== newPassword2) {
  317. logger.log({ user }, 'passwords do not match')
  318. return res.send({
  319. message: {
  320. type: 'error',
  321. text: 'Your passwords do not match'
  322. }
  323. })
  324. } else if (validationError != null) {
  325. logger.log({ user }, validationError.message)
  326. return res.send({
  327. message: {
  328. type: 'error',
  329. text: validationError.message
  330. }
  331. })
  332. } else {
  333. logger.log({ user }, 'password changed')
  334. return AuthenticationManager.setUserPassword(
  335. user._id,
  336. newPassword1,
  337. function(error) {
  338. if (error != null) {
  339. return next(error)
  340. }
  341. return UserSessionsManager.revokeAllUserSessions(
  342. user,
  343. [req.sessionID],
  344. function(err) {
  345. if (err != null) {
  346. return next(err)
  347. }
  348. return res.send({
  349. message: {
  350. type: 'success',
  351. text: 'Your password has been changed'
  352. }
  353. })
  354. }
  355. )
  356. }
  357. )
  358. }
  359. } else {
  360. logger.log({ user_id }, 'current password wrong')
  361. return res.send({
  362. message: {
  363. type: 'error',
  364. text: 'Your old password is wrong'
  365. }
  366. })
  367. }
  368. }
  369. )
  370. }
  371. }