mirror of
https://github.com/wassname/talk.git
synced 2026-06-28 23:10:43 +08:00
193 lines
4.9 KiB
JavaScript
193 lines
4.9 KiB
JavaScript
const { ErrNotAuthorized, ErrNotFound, ErrEmailTaken } = require('errors');
|
|
const {
|
|
ErrNoLocalProfile,
|
|
ErrLocalProfile,
|
|
ErrIncorrectPassword,
|
|
ErrDuplicateLocalProfile,
|
|
} = require('./errors');
|
|
const { get } = require('lodash');
|
|
|
|
// hasLocalProfile checks a user's profiles to see if they already have a local
|
|
// profile associated with their account.
|
|
const hasLocalProfile = user =>
|
|
get(user, 'profiles', []).some(({ provider }) => provider === 'local');
|
|
|
|
// updateUserEmailAddress will verify that the user has sent the correct
|
|
// password followed by executing the email change and notifying the emails
|
|
// about that change.
|
|
async function updateUserEmailAddress(ctx, email, confirmPassword) {
|
|
const {
|
|
user,
|
|
loaders: { Settings },
|
|
connectors: {
|
|
models: { User },
|
|
services: {
|
|
Mailer,
|
|
I18n,
|
|
Users,
|
|
Utils: { getRedirectUri },
|
|
},
|
|
},
|
|
} = ctx;
|
|
|
|
// Ensure that the user has a local profile associated with their account.
|
|
if (!hasLocalProfile(user)) {
|
|
throw new ErrNoLocalProfile();
|
|
}
|
|
|
|
// Ensure that the password provided matches what we have on file.
|
|
if (!(await user.verifyPassword(confirmPassword))) {
|
|
throw new ErrIncorrectPassword();
|
|
}
|
|
|
|
// Cleanup the email address.
|
|
email = email.toLowerCase().trim();
|
|
|
|
// Update the Users email address.
|
|
try {
|
|
await User.update(
|
|
{
|
|
id: user.id,
|
|
profiles: { $elemMatch: { provider: 'local' } },
|
|
},
|
|
{
|
|
$set: { 'profiles.$.id': email },
|
|
$unset: { 'profiles.$.metadata.confirmed_at': 1 },
|
|
}
|
|
);
|
|
} catch (err) {
|
|
if (err.code === 11000) {
|
|
throw new ErrEmailTaken();
|
|
}
|
|
|
|
throw err;
|
|
}
|
|
|
|
// Get some context for the email to be sent.
|
|
const { organizationContactEmail } = await Settings.select(
|
|
'organizationContactEmail'
|
|
);
|
|
|
|
// Send off the email to the old email address that we have changed it.
|
|
await Mailer.send({
|
|
email: user.firstEmail,
|
|
template: 'plain',
|
|
locals: {
|
|
body: I18n.t(
|
|
'email.email_change_original.body',
|
|
user.firstEmail,
|
|
email,
|
|
organizationContactEmail
|
|
),
|
|
},
|
|
subject: I18n.t('email.email_change_original.subject'),
|
|
});
|
|
|
|
// Try to get the root parent, and their redirect uri.
|
|
const redirectUri = getRedirectUri(ctx.rootParent);
|
|
|
|
// Send off the email to the new email address that we need to verify the new
|
|
// address.
|
|
await Users.sendEmailConfirmation(user, email, redirectUri);
|
|
}
|
|
|
|
// attachUserLocalAuth will attach a new local profile to an existing user.
|
|
async function attachUserLocalAuth(ctx, email, password) {
|
|
const {
|
|
user,
|
|
connectors: {
|
|
models: { User },
|
|
services: {
|
|
Users,
|
|
Utils: { getRedirectUri },
|
|
},
|
|
},
|
|
} = ctx;
|
|
|
|
// Ensure that the current user doesn't already have a local account
|
|
// associated with them.
|
|
if (hasLocalProfile(user)) {
|
|
throw new ErrLocalProfile();
|
|
}
|
|
|
|
// Cleanup the email address.
|
|
email = email.toLowerCase().trim();
|
|
|
|
// Validate the password.
|
|
await Users.isValidPassword(password);
|
|
|
|
// See if this email address already has a local profile setup.
|
|
if (await Users.findLocalUser(email)) {
|
|
throw new ErrDuplicateLocalProfile();
|
|
}
|
|
|
|
// Hash the new password.
|
|
const hashedPassword = await Users.hashPassword(password);
|
|
|
|
try {
|
|
// Associate the account with the user.
|
|
const updatedUser = await User.findOneAndUpdate(
|
|
{
|
|
id: user.id,
|
|
'profiles.provider': { $ne: 'local' },
|
|
},
|
|
{
|
|
$push: {
|
|
profiles: {
|
|
provider: 'local',
|
|
id: email,
|
|
},
|
|
},
|
|
$set: {
|
|
password: hashedPassword,
|
|
},
|
|
},
|
|
{ new: true }
|
|
);
|
|
if (!updatedUser) {
|
|
const foundUser = await User.findOne({ id: user.id });
|
|
if (!foundUser) {
|
|
throw new ErrNotFound();
|
|
}
|
|
|
|
// Check to see if this was the result of a race.
|
|
if (hasLocalProfile(foundUser)) {
|
|
throw new ErrLocalProfile();
|
|
}
|
|
|
|
throw new Error('local auth attachment failed due to unexpected reason');
|
|
}
|
|
|
|
// Try to get the root parent, and their redirect uri.
|
|
const redirectUri = getRedirectUri(ctx.rootParent);
|
|
|
|
// Send off the email to the new email address that we need to verify the
|
|
// new address.
|
|
await Users.sendEmailConfirmation(updatedUser, email, redirectUri);
|
|
} catch (err) {
|
|
if (err.code === 11000) {
|
|
throw new ErrDuplicateLocalProfile();
|
|
}
|
|
throw err;
|
|
}
|
|
}
|
|
|
|
module.exports = ctx => {
|
|
const mutators = {
|
|
User: {
|
|
updateEmailAddress: () => Promise.reject(new ErrNotAuthorized()),
|
|
attachLocalAuth: () => Promise.reject(new ErrNotAuthorized()),
|
|
},
|
|
};
|
|
|
|
if (ctx.user) {
|
|
mutators.User.updateEmailAddress = ({ email, confirmPassword }) =>
|
|
updateUserEmailAddress(ctx, email, confirmPassword);
|
|
|
|
mutators.User.attachLocalAuth = ({ email, password }) =>
|
|
attachUserLocalAuth(ctx, email, password);
|
|
}
|
|
|
|
return mutators;
|
|
};
|