overleaf/overleafserver/UserRegistrationHandler.js

137 lines
4 KiB
JavaScript
Raw Normal View History

2024-07-21 16:39:08 +02:00
const { User } = require('../../models/User')
const UserCreator = require('./UserCreator')
const UserGetter = require('./UserGetter')
const AuthenticationManager = require('../Authentication/AuthenticationManager')
const NewsletterManager = require('../Newsletter/NewsletterManager')
const logger = require('@overleaf/logger')
const crypto = require('crypto')
const EmailHandler = require('../Email/EmailHandler')
const OneTimeTokenHandler = require('../Security/OneTimeTokenHandler')
const settings = require('@overleaf/settings')
const EmailHelper = require('../Helpers/EmailHelper')
const {
callbackify,
callbackifyMultiResult,
} = require('@overleaf/promise-utils')
const OError = require('@overleaf/o-error')
const UserRegistrationHandler = {
_registrationRequestIsValid(body) {
const invalidEmail = AuthenticationManager.validateEmail(body.email || '')
const invalidPassword = AuthenticationManager.validatePassword(
body.password || '',
body.email
)
return !(invalidEmail || invalidPassword)
},
async _createNewUserIfRequired(user, userDetails) {
if (!user) {
userDetails.holdingAccount = false
return await UserCreator.promises.createNewUser(
{
holdingAccount: false,
email: userDetails.email,
first_name: userDetails.first_name,
last_name: userDetails.last_name,
analyticsId: userDetails.analyticsId,
},
{}
)
}
return user
},
async registerNewUser(userDetails) {
const requestIsValid =
UserRegistrationHandler._registrationRequestIsValid(userDetails)
if (!requestIsValid) {
throw new Error('request is not valid')
}
userDetails.email = EmailHelper.parseEmail(userDetails.email)
let user = await UserGetter.promises.getUserByAnyEmail(userDetails.email)
if (user && user.holdingAccount === false) {
// We add userId to the error object so that the calling function can access
// the id of the already existing user account.
throw new OError('EmailAlreadyRegistered', { userId: user._id })
}
user = await UserRegistrationHandler._createNewUserIfRequired(
user,
userDetails
)
await User.updateOne(
{ _id: user._id },
{ $set: { holdingAccount: false } }
).exec()
await AuthenticationManager.promises.setUserPassword(
user,
userDetails.password
)
if (userDetails.subscribeToNewsletter === 'true') {
try {
NewsletterManager.subscribe(user)
} catch (error) {
logger.warn(
{ err: error, user },
'Failed to subscribe user to newsletter'
)
throw error
}
}
return user
},
async registerNewUserAndSendActivationEmail(email) {
let user
try {
user = await UserRegistrationHandler.registerNewUser({
email,
password: crypto.randomBytes(32).toString('hex'),
})
} catch (error) {
if (error.message === 'EmailAlreadyRegistered') {
logger.debug({ email }, 'user already exists, resending welcome email')
user = await UserGetter.promises.getUserByAnyEmail(email)
} else {
throw error
}
}
const ONE_WEEK = 7 * 24 * 60 * 60 // seconds
const token = await OneTimeTokenHandler.promises.getNewToken(
'password',
{ user_id: user._id.toString(), email: user.email },
{ expiresIn: ONE_WEEK }
)
const setNewPasswordUrl = `${settings.siteUrl}/user/activate?token=${token}&user_id=${user._id}`
await EmailHandler.promises
.sendEmail('registered', {
to: user.email,
setNewPasswordUrl,
})
.catch(error => {
logger.warn({ err: error }, 'failed to send activation email')
})
return { user, setNewPasswordUrl }
},
}
module.exports = {
registerNewUser: callbackify(UserRegistrationHandler.registerNewUser),
registerNewUserAndSendActivationEmail: callbackifyMultiResult(
UserRegistrationHandler.registerNewUserAndSendActivationEmail,
['user', 'setNewPasswordUrl']
),
promises: UserRegistrationHandler,
}