UserPagesController.js 7.9 KB

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