- Add new static methods to user model - `findByEmailAndUsername` - renames `findByMailOrName` to `findByEmailOrUsername` - `findByUsername` - `findByEmail` - Reverts case insensitive behavior for username
This commit is contained in:
parent
15ad07d5ce
commit
6259f58233
7 changed files with 132 additions and 79 deletions
|
@ -24,7 +24,7 @@ passport.deserializeUser((id, done) => {
|
||||||
* Sign in using Email/Username and Password.
|
* Sign in using Email/Username and Password.
|
||||||
*/
|
*/
|
||||||
passport.use(new LocalStrategy({ usernameField: 'email' }, (email, password, done) => {
|
passport.use(new LocalStrategy({ usernameField: 'email' }, (email, password, done) => {
|
||||||
User.findByMailOrName(email)
|
User.findByEmailOrUsername(email)
|
||||||
.then((user) => { // eslint-disable-line consistent-return
|
.then((user) => { // eslint-disable-line consistent-return
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return done(null, false, { msg: `Email ${email} not found.` });
|
return done(null, false, { msg: `Email ${email} not found.` });
|
||||||
|
@ -43,7 +43,7 @@ passport.use(new LocalStrategy({ usernameField: 'email' }, (email, password, don
|
||||||
* Authentificate using Basic Auth (Username + Api Key)
|
* Authentificate using Basic Auth (Username + Api Key)
|
||||||
*/
|
*/
|
||||||
passport.use(new BasicStrategy((userid, key, done) => {
|
passport.use(new BasicStrategy((userid, key, done) => {
|
||||||
User.findOne({ username: userid }).collation({ locale: 'en', strength: 2 }).exec((err, user) => { // eslint-disable-line consistent-return
|
User.findByUsername(userid, (err, user) => { // eslint-disable-line consistent-return
|
||||||
if (err) { return done(err); }
|
if (err) { return done(err); }
|
||||||
if (!user) { return done(null, false); }
|
if (!user) { return done(null, false); }
|
||||||
user.findMatchingKey(key, (innerErr, isMatch, keyDocument) => {
|
user.findMatchingKey(key, (innerErr, isMatch, keyDocument) => {
|
||||||
|
@ -98,9 +98,7 @@ passport.use(new GitHubStrategy({
|
||||||
const emails = getVerifiedEmails(profile.emails);
|
const emails = getVerifiedEmails(profile.emails);
|
||||||
const primaryEmail = getPrimaryEmail(profile.emails);
|
const primaryEmail = getPrimaryEmail(profile.emails);
|
||||||
|
|
||||||
User.findOne({
|
User.findByEmail(emails, (findByEmailErr, existingEmailUser) => {
|
||||||
email: { $in: emails },
|
|
||||||
}).collation({ locale: 'en', strength: 2 }).exec((findByEmailErr, existingEmailUser) => {
|
|
||||||
if (existingEmailUser) {
|
if (existingEmailUser) {
|
||||||
existingEmailUser.email = existingEmailUser.email || primaryEmail;
|
existingEmailUser.email = existingEmailUser.email || primaryEmail;
|
||||||
existingEmailUser.github = profile.id;
|
existingEmailUser.github = profile.id;
|
||||||
|
@ -141,47 +139,44 @@ passport.use(new GoogleStrategy({
|
||||||
|
|
||||||
const primaryEmail = profile._json.emails[0].value;
|
const primaryEmail = profile._json.emails[0].value;
|
||||||
|
|
||||||
User.findOne({
|
User.findByEmail(primaryEmail, (findByEmailErr, existingEmailUser) => {
|
||||||
email: primaryEmail,
|
|
||||||
}).collation({ locale: 'en', strength: 2 }).exec((findByEmailErr, existingEmailUser) => {
|
|
||||||
let username = profile._json.emails[0].value.split('@')[0];
|
let username = profile._json.emails[0].value.split('@')[0];
|
||||||
User.findOne({ username }).collation({ locale: 'en', strength: 2 })
|
User.findByUsername(username, (findByUsernameErr, existingUsernameUser) => {
|
||||||
.exec((findByUsernameErr, existingUsernameUser) => {
|
if (existingUsernameUser) {
|
||||||
if (existingUsernameUser) {
|
const adj = friendlyWords.predicates[Math.floor(Math.random() * friendlyWords.predicates.length)];
|
||||||
const adj = friendlyWords.predicates[Math.floor(Math.random() * friendlyWords.predicates.length)];
|
username = slugify(`${username} ${adj}`);
|
||||||
username = slugify(`${username} ${adj}`);
|
}
|
||||||
}
|
// what if a username is already taken from the display name too?
|
||||||
// what if a username is already taken from the display name too?
|
// then, append a random friendly word?
|
||||||
// then, append a random friendly word?
|
if (existingEmailUser) {
|
||||||
if (existingEmailUser) {
|
existingEmailUser.email = existingEmailUser.email || primaryEmail;
|
||||||
existingEmailUser.email = existingEmailUser.email || primaryEmail;
|
existingEmailUser.google = profile._json.emails[0].value;
|
||||||
existingEmailUser.google = profile._json.emails[0].value;
|
existingEmailUser.username = existingEmailUser.username || username;
|
||||||
existingEmailUser.username = existingEmailUser.username || username;
|
existingEmailUser.tokens.push({ kind: 'google', accessToken });
|
||||||
existingEmailUser.tokens.push({ kind: 'google', accessToken });
|
existingEmailUser.name = existingEmailUser.name || profile._json.displayName;
|
||||||
existingEmailUser.name = existingEmailUser.name || profile._json.displayName;
|
existingEmailUser.verified = User.EmailConfirmation.Verified;
|
||||||
existingEmailUser.verified = User.EmailConfirmation.Verified;
|
existingEmailUser.save((saveErr) => {
|
||||||
existingEmailUser.save((saveErr) => {
|
if (saveErr) {
|
||||||
if (saveErr) {
|
console.log(saveErr);
|
||||||
console.log(saveErr);
|
}
|
||||||
}
|
done(null, existingEmailUser);
|
||||||
done(null, existingEmailUser);
|
});
|
||||||
});
|
} else {
|
||||||
} else {
|
const user = new User();
|
||||||
const user = new User();
|
user.email = primaryEmail;
|
||||||
user.email = primaryEmail;
|
user.google = profile._json.emails[0].value;
|
||||||
user.google = profile._json.emails[0].value;
|
user.username = username;
|
||||||
user.username = username;
|
user.tokens.push({ kind: 'google', accessToken });
|
||||||
user.tokens.push({ kind: 'google', accessToken });
|
user.name = profile._json.displayName;
|
||||||
user.name = profile._json.displayName;
|
user.verified = User.EmailConfirmation.Verified;
|
||||||
user.verified = User.EmailConfirmation.Verified;
|
user.save((saveErr) => {
|
||||||
user.save((saveErr) => {
|
if (saveErr) {
|
||||||
if (saveErr) {
|
console.log(saveErr);
|
||||||
console.log(saveErr);
|
}
|
||||||
}
|
done(null, user);
|
||||||
done(null, user);
|
});
|
||||||
});
|
}
|
||||||
}
|
});
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -11,7 +11,7 @@ export default function collectionForUserExists(username, collectionId, callback
|
||||||
}
|
}
|
||||||
|
|
||||||
function findUser() {
|
function findUser() {
|
||||||
return User.findOne({ username }).collation({ locale: 'en', strength: 2 }).exec();
|
return User.findByUsername(username);
|
||||||
}
|
}
|
||||||
|
|
||||||
function findCollection(owner) {
|
function findCollection(owner) {
|
||||||
|
|
|
@ -4,7 +4,7 @@ import User from '../../models/user';
|
||||||
async function getOwnerUserId(req) {
|
async function getOwnerUserId(req) {
|
||||||
if (req.params.username) {
|
if (req.params.username) {
|
||||||
const user =
|
const user =
|
||||||
await User.findOne({ username: req.params.username }).collation({ locale: 'en', strength: 2 }).exec();
|
await User.findByUsername(req.params.username);
|
||||||
if (user && user._id) {
|
if (user && user._id) {
|
||||||
return user._id;
|
return user._id;
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,7 +64,7 @@ export function updateProject(req, res) {
|
||||||
|
|
||||||
export function getProject(req, res) {
|
export function getProject(req, res) {
|
||||||
const { project_id: projectId, username } = req.params;
|
const { project_id: projectId, username } = req.params;
|
||||||
User.findOne({ username }).collation({ locale: "en", strength: 2 }).exec((err, user) => { // eslint-disable-line
|
User.findByUsername(username, (err, user) => { // eslint-disable-line
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return res.status(404).send({ message: 'Project with that username does not exist' });
|
return res.status(404).send({ message: 'Project with that username does not exist' });
|
||||||
}
|
}
|
||||||
|
@ -141,7 +141,7 @@ export function projectExists(projectId, callback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function projectForUserExists(username, projectId, callback) {
|
export function projectForUserExists(username, projectId, callback) {
|
||||||
User.findOne({ username }).collation({ locale: 'en', strength: 2 }).exec((err, user) => {
|
User.findByUsername(username, (err, user) => {
|
||||||
if (!user) {
|
if (!user) {
|
||||||
callback(false);
|
callback(false);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -7,7 +7,7 @@ const UserNotFoundError = createApplicationErrorClass('UserNotFoundError');
|
||||||
|
|
||||||
function getProjectsForUserName(username) {
|
function getProjectsForUserName(username) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
User.findOne({ username }).collation({ locale: 'en', strength: 2 }).exec((err, user) => {
|
User.findByUsername(username, (err, user) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -30,7 +30,7 @@ const random = (done) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export function findUserByUsername(username, cb) {
|
export function findUserByUsername(username, cb) {
|
||||||
User.findOne({ username }).collation({ locale: 'en', strength: 2 }).exec((err, user) => {
|
User.findByUsername(username, (err, user) => {
|
||||||
cb(user);
|
cb(user);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -50,12 +50,7 @@ export function createUser(req, res, next) {
|
||||||
verifiedTokenExpires: EMAIL_VERIFY_TOKEN_EXPIRY_TIME,
|
verifiedTokenExpires: EMAIL_VERIFY_TOKEN_EXPIRY_TIME,
|
||||||
});
|
});
|
||||||
|
|
||||||
User.findOne({
|
User.findByEmailAndUsername(email, username, (err, existingUser) => {
|
||||||
$or: [
|
|
||||||
{ email },
|
|
||||||
{ username }
|
|
||||||
]
|
|
||||||
}).collation({ locale: 'en', strength: 2 }).exec((err, existingUser) => {
|
|
||||||
if (err) {
|
if (err) {
|
||||||
res.status(404).send({ error: err });
|
res.status(404).send({ error: err });
|
||||||
return;
|
return;
|
||||||
|
@ -100,6 +95,9 @@ export function duplicateUserCheck(req, res) {
|
||||||
const value = req.query[checkType];
|
const value = req.query[checkType];
|
||||||
const query = {};
|
const query = {};
|
||||||
query[checkType] = value;
|
query[checkType] = value;
|
||||||
|
// Don't want to use findByEmailOrUsername here, because in this case we do
|
||||||
|
// want to use case-insensitive search for usernames to prevent username
|
||||||
|
// duplicates, which overrides the default behavior.
|
||||||
User.findOne(query).collation({ locale: 'en', strength: 2 }).exec((err, user) => {
|
User.findOne(query).collation({ locale: 'en', strength: 2 }).exec((err, user) => {
|
||||||
if (user) {
|
if (user) {
|
||||||
return res.json({
|
return res.json({
|
||||||
|
@ -144,19 +142,18 @@ export function resetPasswordInitiate(req, res) {
|
||||||
async.waterfall([
|
async.waterfall([
|
||||||
random,
|
random,
|
||||||
(token, done) => {
|
(token, done) => {
|
||||||
User.findOne({ email: req.body.email.toLowerCase() })
|
User.findByEmail(req.body.email, (err, user) => {
|
||||||
.collation({ locale: 'en', strength: 2 }).exec((err, user) => {
|
if (!user) {
|
||||||
if (!user) {
|
res.json({ success: true, message: 'If the email is registered with the editor, an email has been sent.' });
|
||||||
res.json({ success: true, message: 'If the email is registered with the editor, an email has been sent.' });
|
return;
|
||||||
return;
|
}
|
||||||
}
|
user.resetPasswordToken = token;
|
||||||
user.resetPasswordToken = token;
|
user.resetPasswordExpires = Date.now() + 3600000; // 1 hour
|
||||||
user.resetPasswordExpires = Date.now() + 3600000; // 1 hour
|
|
||||||
|
|
||||||
user.save((saveErr) => {
|
user.save((saveErr) => {
|
||||||
done(saveErr, token, user);
|
done(saveErr, token, user);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
},
|
},
|
||||||
(token, user, done) => {
|
(token, user, done) => {
|
||||||
const protocol = process.env.NODE_ENV === 'production' ? 'https' : 'http';
|
const protocol = process.env.NODE_ENV === 'production' ? 'https' : 'http';
|
||||||
|
@ -275,7 +272,7 @@ export function updatePassword(req, res) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function userExists(username, callback) {
|
export function userExists(username, callback) {
|
||||||
User.findOne(username).collation({ locale: 'en', strength: 2 }).exec((err, user) => (
|
User.findByUsername(username, (err, user) => (
|
||||||
user ? callback(true) : callback(false)
|
user ? callback(true) : callback(false)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
|
@ -141,20 +141,81 @@ userSchema.methods.findMatchingKey = function findMatchingKey(candidateKey, cb)
|
||||||
if (!foundOne) cb('Matching API key not found !', false, null);
|
if (!foundOne) cb('Matching API key not found !', false, null);
|
||||||
};
|
};
|
||||||
|
|
||||||
userSchema.statics.findByMailOrName = function findByMailOrName(email) {
|
/**
|
||||||
const isEmail = email.indexOf('@') > -1;
|
*
|
||||||
if (isEmail) {
|
* Queries User collection by email and returns one User document.
|
||||||
const query = {
|
*
|
||||||
email: email.toLowerCase()
|
* @param {string|string[]} email - Email string or array of email strings
|
||||||
|
* @callback [cb] - Optional error-first callback that passes User document
|
||||||
|
* @return {Promise<Object>} - Returns Promise fulfilled by User document
|
||||||
|
*/
|
||||||
|
userSchema.statics.findByEmail = function findByEmail(email, cb) {
|
||||||
|
let query;
|
||||||
|
if (Array.isArray(email)) {
|
||||||
|
query = {
|
||||||
|
email: { $in: email }
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
query = {
|
||||||
|
email
|
||||||
};
|
};
|
||||||
// once emails are all lowercase, won't need to do collation
|
|
||||||
// but maybe it's not even necessary to make all emails lowercase??
|
|
||||||
return this.findOne(query).collation({ locale: 'en', strength: 2 }).exec();
|
|
||||||
}
|
}
|
||||||
|
// Email addresses should be case-insensitive unique
|
||||||
|
// In MongoDB, you must use collation in order to do a case-insensitive query
|
||||||
|
return this.findOne(query).collation({ locale: 'en', strength: 2 }).exec(cb);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Queries User collection by username and returns one User document.
|
||||||
|
*
|
||||||
|
* @param {string} username - Username string
|
||||||
|
* @callback [cb] - Optional error-first callback that passes User document
|
||||||
|
* @return {Promise<Object>} - Returns Promise fulfilled by User document
|
||||||
|
*/
|
||||||
|
userSchema.statics.findByUsername = function findByUsername(username, cb) {
|
||||||
const query = {
|
const query = {
|
||||||
username: email
|
username
|
||||||
};
|
};
|
||||||
return this.findOne(query).collation({ locale: 'en', strength: 2 }).exec();
|
return this.findOne(query, cb);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Queries User collection using email or username with optional callback.
|
||||||
|
* This function will determine automatically whether the data passed is
|
||||||
|
* a username or email.
|
||||||
|
*
|
||||||
|
* @param {string} value - Email or username
|
||||||
|
* @callback [cb] - Optional error-first callback that passes User document
|
||||||
|
* @return {Promise<Object>} - Returns Promise fulfilled by User document
|
||||||
|
*/
|
||||||
|
userSchema.statics.findByEmailOrUsername = function findByEmailOrUsername(value, cb) {
|
||||||
|
const isEmail = value.indexOf('@') > -1;
|
||||||
|
if (isEmail) {
|
||||||
|
return this.findByEmail(value, cb);
|
||||||
|
}
|
||||||
|
return this.findByUsername(value, cb);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Queries User collection, performing a MongoDB logical or with the email
|
||||||
|
* and username (i.e. if either one matches, will return the first document).
|
||||||
|
*
|
||||||
|
* @param {string} email
|
||||||
|
* @param {string} username
|
||||||
|
* @callback [cb] - Optional error-first callback that passes User document
|
||||||
|
* @return {Promise<Object>} - Returns Promise fulfilled by User document
|
||||||
|
*/
|
||||||
|
userSchema.statics.findByEmailAndUsername = function findByEmailAndUsername(email, username, cb) {
|
||||||
|
const query = {
|
||||||
|
$or: [
|
||||||
|
{ email },
|
||||||
|
{ username }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
return this.findOne(query).collation({ locale: 'en', strength: 2 }).exec(cb);
|
||||||
};
|
};
|
||||||
|
|
||||||
userSchema.statics.EmailConfirmation = EmailConfirmationStates;
|
userSchema.statics.EmailConfirmation = EmailConfirmationStates;
|
||||||
|
|
Loading…
Reference in a new issue