137 lines
4 KiB
JavaScript
137 lines
4 KiB
JavaScript
|
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,
|
||
|
}
|