| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338 |
- import UserGetter from './UserGetter.mjs'
- import OError from '@overleaf/o-error'
- import UserSessionsManager from './UserSessionsManager.mjs'
- import logger from '@overleaf/logger'
- import Settings from '@overleaf/settings'
- import AuthenticationController from '../Authentication/AuthenticationController.mjs'
- import SessionManager from '../Authentication/SessionManager.mjs'
- import NewsletterManager from '../Newsletter/NewsletterManager.mjs'
- import SubscriptionLocator from '../Subscription/SubscriptionLocator.mjs'
- import _ from 'lodash'
- import { expressify } from '@overleaf/promise-utils'
- import Features from '../../infrastructure/Features.mjs'
- import Modules from '../../infrastructure/Modules.js'
- async function settingsPage(req, res) {
- const userId = SessionManager.getLoggedInUserId(req.session)
- const reconfirmationRemoveEmail = req.query.remove
- // SSO
- const ssoError = req.session.ssoError
- if (ssoError) {
- delete req.session.ssoError
- }
- const ssoErrorMessage = req.session.ssoErrorMessage
- if (ssoErrorMessage) {
- delete req.session.ssoErrorMessage
- }
- const projectSyncSuccessMessage = req.session.projectSyncSuccessMessage
- if (projectSyncSuccessMessage) {
- delete req.session.projectSyncSuccessMessage
- }
- // Institution SSO
- let institutionLinked = _.get(req.session, ['saml', 'linked'])
- if (institutionLinked) {
- // copy object if exists because _.get does not
- institutionLinked = Object.assign(
- {
- hasEntitlement: _.get(req.session, ['saml', 'hasEntitlement']),
- },
- institutionLinked
- )
- }
- const samlError = _.get(req.session, ['saml', 'error'])
- const institutionEmailNonCanonical = _.get(req.session, [
- 'saml',
- 'emailNonCanonical',
- ])
- const institutionRequestedEmail = _.get(req.session, [
- 'saml',
- 'requestedEmail',
- ])
- const reconfirmedViaSAML = _.get(req.session, ['saml', 'reconfirmed'])
- delete req.session.saml
- let shouldAllowEditingDetails = true
- if (Settings.ldap && Settings.ldap.updateUserDetailsOnLogin) {
- shouldAllowEditingDetails = false
- }
- if (Settings.saml && Settings.saml.updateUserDetailsOnLogin) {
- shouldAllowEditingDetails = false
- }
- const oauthProviders = Settings.oauthProviders || {}
- const user = await UserGetter.promises.getUser(userId)
- if (!user) {
- // The user has just deleted their account.
- return UserSessionsManager.removeSessionsFromRedis(
- { _id: userId },
- null,
- () => res.redirect('/')
- )
- }
- let personalAccessTokens
- try {
- const results = await Modules.promises.hooks.fire(
- 'listPersonalAccessTokens',
- user._id
- )
- personalAccessTokens = results?.[0] ?? []
- } catch (error) {
- const err = OError.tag(error, 'listPersonalAccessTokens hook failed')
- logger.error({ err, userId }, err.message)
- }
- let currentManagedUserAdminEmail
- try {
- currentManagedUserAdminEmail =
- await SubscriptionLocator.promises.getAdminEmail(req.managedBy)
- } catch (err) {
- logger.error({ err }, 'error getting subscription admin email')
- }
- let memberOfSSOEnabledGroups = []
- try {
- memberOfSSOEnabledGroups =
- (
- await Modules.promises.hooks.fire(
- 'getUserGroupsSSOEnrollmentStatus',
- user._id,
- { teamName: 1 },
- ['email']
- )
- )?.[0] || []
- memberOfSSOEnabledGroups = memberOfSSOEnabledGroups.map(group => {
- return {
- groupId: group._id.toString(),
- linked: group.linked,
- groupName: group.teamName,
- adminEmail: group.admin_id?.email,
- }
- })
- } catch (error) {
- logger.error(
- { err: error },
- 'error fetching groups with Group SSO enabled the user may be member of'
- )
- }
- res.render('user/settings', {
- title: 'account_settings',
- user: {
- id: user._id,
- isAdmin: user.isAdmin,
- email: user.email,
- allowedFreeTrial: user.allowedFreeTrial,
- first_name: user.first_name,
- last_name: user.last_name,
- alphaProgram: user.alphaProgram,
- betaProgram: user.betaProgram,
- labsProgram: user.labsProgram,
- features: {
- dropbox: user.features.dropbox,
- github: user.features.github,
- mendeley: user.features.mendeley,
- zotero: user.features.zotero,
- papers: user.features.papers,
- references: user.features.references,
- },
- refProviders: {
- mendeley: Boolean(user.refProviders?.mendeley),
- zotero: Boolean(user.refProviders?.zotero),
- papers: Boolean(user.refProviders?.papers),
- },
- writefull: {
- enabled: Boolean(user.writefull?.enabled),
- },
- aiErrorAssistant: {
- enabled: Boolean(user.aiErrorAssistant?.enabled),
- },
- },
- labsExperiments: user.labsExperiments ?? [],
- hasPassword: !!user.hashedPassword,
- shouldAllowEditingDetails,
- oauthProviders: UserPagesController._translateProviderDescriptions(
- oauthProviders,
- req
- ),
- institutionLinked,
- samlError,
- institutionEmailNonCanonical:
- institutionEmailNonCanonical && institutionRequestedEmail
- ? institutionEmailNonCanonical
- : undefined,
- reconfirmedViaSAML,
- reconfirmationRemoveEmail,
- samlBeta: req.session.samlBeta,
- ssoErrorMessage,
- thirdPartyIds: UserPagesController._restructureThirdPartyIds(user),
- projectSyncSuccessMessage,
- personalAccessTokens,
- emailAddressLimit: Settings.emailAddressLimit,
- isManagedAccount: !!req.managedBy,
- userRestrictions: Array.from(req.userRestrictions || []),
- currentManagedUserAdminEmail,
- gitBridgeEnabled: Settings.enableGitBridge,
- isSaas: Features.hasFeature('saas'),
- memberOfSSOEnabledGroups,
- capabilities: [...req.capabilitySet],
- })
- }
- async function accountSuspended(req, res) {
- if (SessionManager.isUserLoggedIn(req.session)) {
- return res.redirect('/project')
- }
- res.render('user/accountSuspended', {
- title: 'your_account_is_suspended',
- })
- }
- async function reconfirmAccountPage(req, res) {
- const pageData = {
- reconfirm_email: req.session.reconfirm_email,
- }
- res.render('user/reconfirm', pageData)
- }
- const UserPagesController = {
- accountSuspended: expressify(accountSuspended),
- registerPage(req, res) {
- const sharedProjectData = req.session.sharedProjectData || {}
- const newTemplateData = {}
- if (req.session.templateData != null) {
- newTemplateData.templateName = req.session.templateData.templateName
- }
- res.render('user/register', {
- title: 'register',
- sharedProjectData,
- newTemplateData,
- samlBeta: req.session.samlBeta,
- })
- },
- loginPage(req, res) {
- // if user is being sent to /login with explicit redirect (redir=/foo),
- // such as being sent from the editor to /login, then set the redirect explicitly
- if (
- req.query.redir != null &&
- AuthenticationController.getRedirectFromSession(req) == null
- ) {
- AuthenticationController.setRedirectInSession(req, req.query.redir)
- }
- const metadata = { robotsNoindexNofollow: false }
- if (Object.keys(req.query).length !== 0) {
- metadata.robotsNoindexNofollow = true
- }
- res.render('user/login', {
- title: Settings.nav?.login_support_title || 'login',
- login_support_title: Settings.nav?.login_support_title,
- login_support_text: Settings.nav?.login_support_text,
- metadata,
- })
- },
- /**
- * Landing page for users who may have received one-time login
- * tokens from the read-only maintenance site.
- *
- * We tell them that Overleaf is back up and that they can login normally.
- */
- oneTimeLoginPage(req, res, next) {
- res.render('user/one_time_login')
- },
- renderReconfirmAccountPage: expressify(reconfirmAccountPage),
- settingsPage: expressify(settingsPage),
- sessionsPage(req, res, next) {
- const user = SessionManager.getSessionUser(req.session)
- logger.debug({ userId: user._id }, 'loading sessions page')
- const currentSession = {
- ip_address: user.ip_address,
- session_created: user.session_created,
- }
- UserSessionsManager.getAllUserSessions(
- user,
- [req.sessionID],
- (err, sessions) => {
- if (err != null) {
- OError.tag(err, 'error getting all user sessions', {
- userId: user._id,
- })
- return next(err)
- }
- res.render('user/sessions', {
- title: 'sessions',
- currentSession,
- sessions,
- })
- }
- )
- },
- emailPreferencesPage(req, res, next) {
- const userId = SessionManager.getLoggedInUserId(req.session)
- UserGetter.getUser(
- userId,
- { _id: 1, email: 1, first_name: 1, last_name: 1 },
- (err, user) => {
- if (err != null) {
- return next(err)
- }
- NewsletterManager.subscribed(user, (err, subscribed) => {
- if (err != null) {
- OError.tag(err, 'error getting newsletter subscription status')
- return next(err)
- }
- res.render('user/email-preferences', {
- title: 'newsletter_info_title',
- subscribed,
- })
- })
- }
- )
- },
- async compromisedPasswordPage(req, res) {
- res.render('user/compromised_password')
- },
- _restructureThirdPartyIds(user) {
- // 3rd party identifiers are an array of objects
- // this turn them into a single object, which
- // makes data easier to use in template
- if (
- !user.thirdPartyIdentifiers ||
- user.thirdPartyIdentifiers.length === 0
- ) {
- return null
- }
- return user.thirdPartyIdentifiers.reduce((obj, identifier) => {
- obj[identifier.providerId] = identifier.externalUserId
- return obj
- }, {})
- },
- _translateProviderDescriptions(providers, req) {
- const result = {}
- if (providers) {
- for (const provider in providers) {
- const data = providers[provider]
- data.description = req.i18n.translate(
- data.descriptionKey,
- Object.assign({}, data.descriptionOptions)
- )
- result[provider] = data
- }
- }
- return result
- },
- }
- export default UserPagesController
|