diff --git a/src/controllers/api/account/emailChangeRequestController.js b/src/controllers/api/account/emailChangeRequestController.js index 1b82d74..4791ba2 100644 --- a/src/controllers/api/account/emailChangeRequestController.js +++ b/src/controllers/api/account/emailChangeRequestController.js @@ -43,7 +43,7 @@ module.exports.post = async function(req, res){ //Check to make sure the user is logged in if(req.session.user == null){ - errorHandler(res, "Invalid user!"); + return errorHandler(res, "Invalid user!"); } //Authenticate and find user model from DB @@ -51,11 +51,22 @@ module.exports.post = async function(req, res){ //If we have an invalid user if(userDB == null){ - errorHandler(res, "Invalid user!"); + return errorHandler(res, "Invalid user!"); } if(userDB.email == email){ - errorHandler(res, "Cannot set current email!"); + return errorHandler(res, "Cannot set current email!"); + } + + + //Look through DB and migration cache for existing email + const existingDB = await userModel.findOne({email: new RegExp(email, 'i')}); + const needsMigration = userModel.migrationCache.emails.includes(email.toLowerCase()); + + //If the email is in use + if(existingDB != null || needsMigration){ + //Complain + return errorHandler(res, "Email already in use!"); } //Generate the password reset link diff --git a/src/schemas/user/migrationSchema.js b/src/schemas/user/migrationSchema.js index b03b1dc..2856094 100644 --- a/src/schemas/user/migrationSchema.js +++ b/src/schemas/user/migrationSchema.js @@ -27,7 +27,6 @@ const permissionModel = require('../permissionSchema'); const tokeModel = require('../tokebot/tokeSchema'); const loggerUtils = require('../../utils/loggerUtils'); - /** * DB Schema for documents representing legacy fore.st migration data for a single user account */ @@ -208,7 +207,7 @@ migrationSchema.statics.ingestLegacyUser = async function(rawProfile){ //If we found the user in the database if(foundMigration != null || foundUser != null){ //Scream - //loggerUtils.consoleWarn(`Found legacy user ${profileArray[1]} in database, skipping migration!`); + loggerUtils.consoleWarn(`Found legacy user ${profileArray[1]} in database, skipping migration!`); //BAIL! return; } @@ -292,4 +291,31 @@ migrationSchema.statics.ingestTokeMaps = async function(rawTokeMaps){ } } +migrationSchema.statics.buildMigrationCache = async function(){ + //Pull all profiles from the Legacy Profile Migration DB collection + const legacyProfiles = await this.find(); + + //For each profile in the migration collection + for(const profile of legacyProfiles){ + //Push the username into the migration cache + userModel.migrationCache.users.push(profile.user.toLowerCase()); + //If the profile has an email address set + if(profile.email != null && profile.email != ''){ + //Add the email to the migration cache + userModel.migrationCache.emails.push(profile.email.toLowerCase()); + } + } +} + +//Methods +/** + * Consumes a migration profile and creates a new, modern canopy profile from the original. + * @param {String} oldPass - Original password to authenticate migration against + * @param {String} newPass - New password to re-hash with modern hashing algo + * @param {String} confirmPass - Confirmation for the new pass + */ +migrationSchema.methods.consume = async function(oldPass, newPass, confirmPass){ + +} + module.exports = mongoose.model("migration", migrationSchema); \ No newline at end of file diff --git a/src/schemas/user/userSchema.js b/src/schemas/user/userSchema.js index dd13b24..003f2e6 100644 --- a/src/schemas/user/userSchema.js +++ b/src/schemas/user/userSchema.js @@ -226,6 +226,18 @@ userSchema.post('deleteOne', {document: true}, async function (){ }); //statics +/** + * Holds cache of usernames of profiles stored in the Legacy Profile Migration collection + * + * We can't directly reference migrationSchema, as it would cause a circular reference + * To deal with this, migration schema caches it's regestered users into this array on startup. + * Bonus pts for improved performance on registration calls + */ +userSchema.statics.migrationCache = { + users: [], + emails: [] +}; + /** * Registers a new user account with given information * @param {Object} userObj - Object representing user to register, generated by the client @@ -237,11 +249,31 @@ userSchema.statics.register = async function(userObj, ip){ //Check password confirmation matches if(pass == passConfirm){ + //Setup user query + let userQuery = {user: new RegExp(user, 'i')}; + + //If we have an email + if(email != null && email != ""){ + userQuery = {$or: [ + userQuery, + {email: new RegExp(email, 'i')} + ]}; + } + //Look for a user (case insensitive) - var userDB = await this.findOne({user: new RegExp(user, 'i')}); + var userDB = await this.findOne(userQuery); + + //Look for a legacy profile + let needsMigration = this.migrationCache.users.includes(user.toLowerCase()); + + //If the email isn't null and we didnt hit a migration username + if(email != null && !needsMigration){ + //Check for migration email + needsMigration = this.migrationCache.emails.includes(email.toLowerCase()); + } //If the user is found or someones trying to impersonate tokeboi - if(userDB || user.toLowerCase() == "tokebot"){ + if(userDB || needsMigration || user.toLowerCase() == "tokebot"){ throw loggerUtils.exceptionSmith("User name/email already taken!", "validation"); }else{ //Increment the user count, pulling the id to tattoo to the user diff --git a/src/server.js b/src/server.js index c1b2e2f..333dfc3 100644 --- a/src/server.js +++ b/src/server.js @@ -195,6 +195,9 @@ async function asyncKickStart(){ //Run legacy migration await migrationModel.ingestLegacyDump(); + //Build migration cache + await migrationModel.buildMigrationCache(); + //Calculate Toke Map await tokeModel.calculateTokeMap();