2019-05-29 05:21:06 -04:00
|
|
|
const UserGetter = require('./UserGetter')
|
2020-08-19 05:11:32 -04:00
|
|
|
const OError = require('@overleaf/o-error')
|
2019-05-29 05:21:06 -04:00
|
|
|
const UserSessionsManager = require('./UserSessionsManager')
|
2021-11-10 08:40:18 -05:00
|
|
|
const logger = require('@overleaf/logger')
|
2021-07-07 05:38:56 -04:00
|
|
|
const Settings = require('@overleaf/settings')
|
2019-05-29 05:21:06 -04:00
|
|
|
const AuthenticationController = require('../Authentication/AuthenticationController')
|
2021-07-28 04:51:20 -04:00
|
|
|
const SessionManager = require('../Authentication/SessionManager')
|
2019-09-30 09:21:31 -04:00
|
|
|
const _ = require('lodash')
|
2022-04-22 09:49:26 -04:00
|
|
|
const { expressify } = require('../../util/promises')
|
|
|
|
const SplitTestHandler = require('../SplitTests/SplitTestHandler')
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
// 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 res.redirect('/logout')
|
|
|
|
}
|
|
|
|
const assignment = await SplitTestHandler.promises.getAssignment(
|
|
|
|
req,
|
|
|
|
res,
|
|
|
|
'settings-page'
|
|
|
|
)
|
|
|
|
if (assignment.variant === 'react') {
|
|
|
|
res.render('user/settings-react', {
|
|
|
|
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,
|
|
|
|
features: {
|
|
|
|
dropbox: user.features.dropbox,
|
|
|
|
github: user.features.github,
|
|
|
|
mendeley: user.features.mendeley,
|
|
|
|
zotero: user.features.zotero,
|
|
|
|
references: user.features.references,
|
|
|
|
},
|
|
|
|
refProviders: {
|
|
|
|
mendeley: user.refProviders?.mendeley,
|
|
|
|
zotero: user.refProviders?.zotero,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
hasPassword: !!user.hashedPassword,
|
|
|
|
shouldAllowEditingDetails,
|
|
|
|
oauthProviders: UserPagesController._translateProviderDescriptions(
|
|
|
|
oauthProviders,
|
|
|
|
req
|
|
|
|
),
|
|
|
|
institutionLinked,
|
|
|
|
samlError,
|
|
|
|
institutionEmailNonCanonical:
|
|
|
|
institutionEmailNonCanonical && institutionRequestedEmail
|
|
|
|
? institutionEmailNonCanonical
|
|
|
|
: undefined,
|
|
|
|
reconfirmedViaSAML,
|
|
|
|
reconfirmationRemoveEmail,
|
|
|
|
samlBeta: req.session.samlBeta,
|
|
|
|
ssoError: ssoError,
|
|
|
|
thirdPartyIds: UserPagesController._restructureThirdPartyIds(user),
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
res.render('user/settings', {
|
|
|
|
title: 'account_settings',
|
|
|
|
user,
|
|
|
|
hasPassword: !!user.hashedPassword,
|
|
|
|
shouldAllowEditingDetails,
|
|
|
|
languages: Settings.languages,
|
|
|
|
accountSettingsTabActive: true,
|
|
|
|
oauthProviders: UserPagesController._translateProviderDescriptions(
|
|
|
|
oauthProviders,
|
|
|
|
req
|
|
|
|
),
|
|
|
|
oauthUseV2: Settings.oauthUseV2 || false,
|
|
|
|
institutionLinked,
|
|
|
|
samlError,
|
|
|
|
institutionEmailNonCanonical:
|
|
|
|
institutionEmailNonCanonical && institutionRequestedEmail
|
|
|
|
? institutionEmailNonCanonical
|
|
|
|
: undefined,
|
|
|
|
reconfirmedViaSAML,
|
|
|
|
reconfirmationRemoveEmail,
|
|
|
|
samlBeta: req.session.samlBeta,
|
|
|
|
ssoError: ssoError,
|
|
|
|
thirdPartyIds: UserPagesController._restructureThirdPartyIds(user),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2019-05-29 05:21:06 -04:00
|
|
|
|
2019-07-18 10:19:10 -04:00
|
|
|
const UserPagesController = {
|
2019-05-29 05:21:06 -04:00
|
|
|
registerPage(req, res) {
|
|
|
|
const sharedProjectData = {
|
|
|
|
project_name: req.query.project_name,
|
2021-04-27 03:52:58 -04:00
|
|
|
user_first_name: req.query.user_first_name,
|
2019-05-29 05:21:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
const newTemplateData = {}
|
|
|
|
if (req.session.templateData != null) {
|
|
|
|
newTemplateData.templateName = req.session.templateData.templateName
|
|
|
|
}
|
|
|
|
|
2019-07-18 10:19:10 -04:00
|
|
|
res.render('user/register', {
|
2019-05-29 05:21:06 -04:00
|
|
|
title: 'register',
|
|
|
|
sharedProjectData,
|
|
|
|
newTemplateData,
|
2021-04-27 03:52:58 -04:00
|
|
|
samlBeta: req.session.samlBeta,
|
2019-05-29 05:21:06 -04:00
|
|
|
})
|
|
|
|
},
|
|
|
|
|
|
|
|
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)
|
|
|
|
}
|
2019-07-18 10:19:10 -04:00
|
|
|
res.render('user/login', {
|
2021-04-27 03:52:58 -04:00
|
|
|
title: 'login',
|
2019-05-29 05:21:06 -04:00
|
|
|
})
|
|
|
|
},
|
|
|
|
|
2019-07-18 10:19:10 -04:00
|
|
|
/**
|
|
|
|
* 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')
|
|
|
|
},
|
|
|
|
|
2019-05-29 05:21:06 -04:00
|
|
|
logoutPage(req, res) {
|
2019-07-18 10:19:10 -04:00
|
|
|
res.render('user/logout')
|
2019-05-29 05:21:06 -04:00
|
|
|
},
|
|
|
|
|
|
|
|
renderReconfirmAccountPage(req, res) {
|
2019-07-18 10:19:10 -04:00
|
|
|
const pageData = {
|
2021-04-27 03:52:58 -04:00
|
|
|
reconfirm_email: req.session.reconfirm_email,
|
2019-05-29 05:21:06 -04:00
|
|
|
}
|
|
|
|
// when a user must reconfirm their account
|
2019-07-18 10:19:10 -04:00
|
|
|
res.render('user/reconfirm', pageData)
|
2019-05-29 05:21:06 -04:00
|
|
|
},
|
|
|
|
|
2022-04-22 09:49:26 -04:00
|
|
|
settingsPage: expressify(settingsPage),
|
2019-05-29 05:21:06 -04:00
|
|
|
|
|
|
|
sessionsPage(req, res, next) {
|
2021-07-28 04:51:20 -04:00
|
|
|
const user = SessionManager.getSessionUser(req.session)
|
2019-07-18 10:19:10 -04:00
|
|
|
logger.log({ userId: user._id }, 'loading sessions page')
|
2021-10-28 09:01:09 -04:00
|
|
|
const currentSession = {
|
|
|
|
ip_address: user.ip_address,
|
|
|
|
session_created: user.session_created,
|
|
|
|
}
|
2021-10-20 15:03:18 -04:00
|
|
|
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',
|
2021-10-28 09:01:09 -04:00
|
|
|
currentSession,
|
2021-10-20 15:03:18 -04:00
|
|
|
sessions,
|
2019-05-29 05:21:06 -04:00
|
|
|
})
|
|
|
|
}
|
2021-10-20 15:03:18 -04:00
|
|
|
)
|
2019-05-29 05:21:06 -04:00
|
|
|
},
|
|
|
|
|
|
|
|
_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
|
|
|
|
}
|
2019-07-18 10:19:10 -04:00
|
|
|
return user.thirdPartyIdentifiers.reduce((obj, identifier) => {
|
2019-05-29 05:21:06 -04:00
|
|
|
obj[identifier.providerId] = identifier.externalUserId
|
|
|
|
return obj
|
|
|
|
}, {})
|
|
|
|
},
|
|
|
|
|
|
|
|
_translateProviderDescriptions(providers, req) {
|
|
|
|
const result = {}
|
|
|
|
if (providers) {
|
2021-05-05 09:05:04 -04:00
|
|
|
for (const provider in providers) {
|
2019-05-29 05:21:06 -04:00
|
|
|
const data = providers[provider]
|
|
|
|
data.description = req.i18n.translate(
|
|
|
|
data.descriptionKey,
|
2020-06-10 06:22:50 -04:00
|
|
|
Object.assign({}, data.descriptionOptions)
|
2019-05-29 05:21:06 -04:00
|
|
|
)
|
|
|
|
result[provider] = data
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result
|
2021-04-27 03:52:58 -04:00
|
|
|
},
|
2019-05-29 05:21:06 -04:00
|
|
|
}
|
2019-07-18 10:19:10 -04:00
|
|
|
|
|
|
|
module.exports = UserPagesController
|