UserPagesController.js 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. const UserGetter = require('./UserGetter')
  2. const OError = require('@overleaf/o-error')
  3. const UserSessionsManager = require('./UserSessionsManager')
  4. const logger = require('@overleaf/logger')
  5. const Settings = require('@overleaf/settings')
  6. const AuthenticationController = require('../Authentication/AuthenticationController')
  7. const SessionManager = require('../Authentication/SessionManager')
  8. const NewsletterManager = require('../Newsletter/NewsletterManager')
  9. const _ = require('lodash')
  10. const { expressify } = require('../../util/promises')
  11. const SplitTestHandler = require('../SplitTests/SplitTestHandler')
  12. async function settingsPage(req, res) {
  13. const userId = SessionManager.getLoggedInUserId(req.session)
  14. const reconfirmationRemoveEmail = req.query.remove
  15. // SSO
  16. const ssoError = req.session.ssoError
  17. if (ssoError) {
  18. delete req.session.ssoError
  19. }
  20. const ssoErrorMessage = req.session.ssoErrorMessage
  21. if (ssoErrorMessage) {
  22. delete req.session.ssoErrorMessage
  23. }
  24. // Institution SSO
  25. let institutionLinked = _.get(req.session, ['saml', 'linked'])
  26. if (institutionLinked) {
  27. // copy object if exists because _.get does not
  28. institutionLinked = Object.assign(
  29. {
  30. hasEntitlement: _.get(req.session, ['saml', 'hasEntitlement']),
  31. },
  32. institutionLinked
  33. )
  34. }
  35. const samlError = _.get(req.session, ['saml', 'error'])
  36. const institutionEmailNonCanonical = _.get(req.session, [
  37. 'saml',
  38. 'emailNonCanonical',
  39. ])
  40. const institutionRequestedEmail = _.get(req.session, [
  41. 'saml',
  42. 'requestedEmail',
  43. ])
  44. const reconfirmedViaSAML = _.get(req.session, ['saml', 'reconfirmed'])
  45. delete req.session.saml
  46. let shouldAllowEditingDetails = true
  47. if (Settings.ldap && Settings.ldap.updateUserDetailsOnLogin) {
  48. shouldAllowEditingDetails = false
  49. }
  50. if (Settings.saml && Settings.saml.updateUserDetailsOnLogin) {
  51. shouldAllowEditingDetails = false
  52. }
  53. const oauthProviders = Settings.oauthProviders || {}
  54. const user = await UserGetter.promises.getUser(userId)
  55. if (!user) {
  56. // The user has just deleted their account.
  57. return res.redirect('/logout')
  58. }
  59. const assignment = await SplitTestHandler.promises.getAssignment(
  60. req,
  61. res,
  62. 'settings-page'
  63. )
  64. if (assignment.variant === 'react') {
  65. res.render('user/settings-react', {
  66. title: 'account_settings',
  67. user: {
  68. id: user.id,
  69. isAdmin: user.isAdmin,
  70. email: user.email,
  71. allowedFreeTrial: user.allowedFreeTrial,
  72. first_name: user.first_name,
  73. last_name: user.last_name,
  74. features: {
  75. dropbox: user.features.dropbox,
  76. github: user.features.github,
  77. mendeley: user.features.mendeley,
  78. zotero: user.features.zotero,
  79. references: user.features.references,
  80. },
  81. refProviders: {
  82. mendeley: user.refProviders?.mendeley,
  83. zotero: user.refProviders?.zotero,
  84. },
  85. },
  86. hasPassword: !!user.hashedPassword,
  87. shouldAllowEditingDetails,
  88. oauthProviders: UserPagesController._translateProviderDescriptions(
  89. oauthProviders,
  90. req
  91. ),
  92. institutionLinked,
  93. samlError,
  94. institutionEmailNonCanonical:
  95. institutionEmailNonCanonical && institutionRequestedEmail
  96. ? institutionEmailNonCanonical
  97. : undefined,
  98. reconfirmedViaSAML,
  99. reconfirmationRemoveEmail,
  100. samlBeta: req.session.samlBeta,
  101. ssoErrorMessage,
  102. thirdPartyIds: UserPagesController._restructureThirdPartyIds(user),
  103. })
  104. } else {
  105. res.render('user/settings', {
  106. title: 'account_settings',
  107. user,
  108. hasPassword: !!user.hashedPassword,
  109. shouldAllowEditingDetails,
  110. languages: Settings.languages,
  111. accountSettingsTabActive: true,
  112. oauthProviders: UserPagesController._translateProviderDescriptions(
  113. oauthProviders,
  114. req
  115. ),
  116. oauthUseV2: Settings.oauthUseV2 || false,
  117. institutionLinked,
  118. samlError,
  119. institutionEmailNonCanonical:
  120. institutionEmailNonCanonical && institutionRequestedEmail
  121. ? institutionEmailNonCanonical
  122. : undefined,
  123. reconfirmedViaSAML,
  124. reconfirmationRemoveEmail,
  125. samlBeta: req.session.samlBeta,
  126. ssoError: ssoError,
  127. thirdPartyIds: UserPagesController._restructureThirdPartyIds(user),
  128. })
  129. }
  130. }
  131. const UserPagesController = {
  132. registerPage(req, res) {
  133. const sharedProjectData = {
  134. project_name: req.query.project_name,
  135. user_first_name: req.query.user_first_name,
  136. }
  137. const newTemplateData = {}
  138. if (req.session.templateData != null) {
  139. newTemplateData.templateName = req.session.templateData.templateName
  140. }
  141. res.render('user/register', {
  142. title: 'register',
  143. sharedProjectData,
  144. newTemplateData,
  145. samlBeta: req.session.samlBeta,
  146. })
  147. },
  148. loginPage(req, res) {
  149. // if user is being sent to /login with explicit redirect (redir=/foo),
  150. // such as being sent from the editor to /login, then set the redirect explicitly
  151. if (
  152. req.query.redir != null &&
  153. AuthenticationController._getRedirectFromSession(req) == null
  154. ) {
  155. AuthenticationController.setRedirectInSession(req, req.query.redir)
  156. }
  157. res.render('user/login', {
  158. title: 'login',
  159. })
  160. },
  161. /**
  162. * Landing page for users who may have received one-time login
  163. * tokens from the read-only maintenance site.
  164. *
  165. * We tell them that Overleaf is back up and that they can login normally.
  166. */
  167. oneTimeLoginPage(req, res, next) {
  168. res.render('user/one_time_login')
  169. },
  170. logoutPage(req, res) {
  171. res.render('user/logout')
  172. },
  173. renderReconfirmAccountPage(req, res) {
  174. const pageData = {
  175. reconfirm_email: req.session.reconfirm_email,
  176. }
  177. // when a user must reconfirm their account
  178. res.render('user/reconfirm', pageData)
  179. },
  180. settingsPage: expressify(settingsPage),
  181. sessionsPage(req, res, next) {
  182. const user = SessionManager.getSessionUser(req.session)
  183. logger.log({ userId: user._id }, 'loading sessions page')
  184. const currentSession = {
  185. ip_address: user.ip_address,
  186. session_created: user.session_created,
  187. }
  188. UserSessionsManager.getAllUserSessions(
  189. user,
  190. [req.sessionID],
  191. (err, sessions) => {
  192. if (err != null) {
  193. OError.tag(err, 'error getting all user sessions', {
  194. userId: user._id,
  195. })
  196. return next(err)
  197. }
  198. res.render('user/sessions', {
  199. title: 'sessions',
  200. currentSession,
  201. sessions,
  202. })
  203. }
  204. )
  205. },
  206. emailPreferencesPage(req, res, next) {
  207. const userId = SessionManager.getLoggedInUserId(req.session)
  208. UserGetter.getUser(userId, (err, user) => {
  209. if (err != null) {
  210. return next(err)
  211. }
  212. NewsletterManager.subscribed(user, (err, subscribed) => {
  213. if (err != null) {
  214. OError.tag(err, 'error getting newsletter subscription status')
  215. return next(err)
  216. }
  217. res.render('user/email-preferences', {
  218. title: 'newsletter_info_title',
  219. subscribed,
  220. })
  221. })
  222. })
  223. },
  224. _restructureThirdPartyIds(user) {
  225. // 3rd party identifiers are an array of objects
  226. // this turn them into a single object, which
  227. // makes data easier to use in template
  228. if (
  229. !user.thirdPartyIdentifiers ||
  230. user.thirdPartyIdentifiers.length === 0
  231. ) {
  232. return null
  233. }
  234. return user.thirdPartyIdentifiers.reduce((obj, identifier) => {
  235. obj[identifier.providerId] = identifier.externalUserId
  236. return obj
  237. }, {})
  238. },
  239. _translateProviderDescriptions(providers, req) {
  240. const result = {}
  241. if (providers) {
  242. for (const provider in providers) {
  243. const data = providers[provider]
  244. data.description = req.i18n.translate(
  245. data.descriptionKey,
  246. Object.assign({}, data.descriptionOptions)
  247. )
  248. result[provider] = data
  249. }
  250. }
  251. return result
  252. },
  253. }
  254. module.exports = UserPagesController