|
@@ -337,6 +337,7 @@ module.exports = {
|
|
username: user.username
|
|
username: user.username
|
|
};
|
|
};
|
|
if (user.services.password && user.services.password.password) data.password = true;
|
|
if (user.services.password && user.services.password.password) data.password = true;
|
|
|
|
+ if (user.services.github && user.services.github.id) data.github = true;
|
|
logger.success("FIND_BY_SESSION", `User found. '${user.username}'.`);
|
|
logger.success("FIND_BY_SESSION", `User found. '${user.username}'.`);
|
|
return cb({
|
|
return cb({
|
|
status: 'success',
|
|
status: 'success',
|
|
@@ -549,6 +550,211 @@ module.exports = {
|
|
});
|
|
});
|
|
}),
|
|
}),
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Requests a password for a session
|
|
|
|
+ *
|
|
|
|
+ * @param {Object} session - the session object automatically added by socket.io
|
|
|
|
+ * @param {String} email - the email of the user that requests a password reset
|
|
|
|
+ * @param {Function} cb - gets called with the result
|
|
|
|
+ * @param {String} userId - the userId automatically added by hooks
|
|
|
|
+ */
|
|
|
|
+ requestPassword: hooks.loginRequired((session, cb, userId) => {
|
|
|
|
+ let code = utils.generateRandomString(8);
|
|
|
|
+ async.waterfall([
|
|
|
|
+ (next) => {
|
|
|
|
+ db.models.user.findOne({_id: userId}, next);
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ (user, next) => {
|
|
|
|
+ if (!user) return next('User not found.');
|
|
|
|
+ if (user.services.password && user.services.password.password) return next('You already have a password set.');
|
|
|
|
+ next(null, user);
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ (user, next) => {
|
|
|
|
+ let expires = new Date();
|
|
|
|
+ expires.setDate(expires.getDate() + 1);
|
|
|
|
+ db.models.user.findOneAndUpdate({"email.address": user.email.address}, {$set: {"services.password": {set: {code: code, expires}}}}, next);
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ (user, next) => {
|
|
|
|
+ mail.schemas.passwordRequest(user.email.address, user.username, code, next);
|
|
|
|
+ }
|
|
|
|
+ ], (err) => {
|
|
|
|
+ if (err && err !== true) {
|
|
|
|
+ let error = 'An error occurred.';
|
|
|
|
+ if (typeof err === "string") error = err;
|
|
|
|
+ else if (err.message) error = err.message;
|
|
|
|
+ logger.error("REQUEST_PASSWORD", `UserId '${userId}' failed to request password. '${error}'`);
|
|
|
|
+ cb({status: 'failure', message: error});
|
|
|
|
+ } else {
|
|
|
|
+ logger.success("REQUEST_PASSWORD", `UserId '${userId}' successfully requested a password.`);
|
|
|
|
+ cb({
|
|
|
|
+ status: 'success',
|
|
|
|
+ message: 'Successfully requested password.'
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ }),
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Verifies a password code
|
|
|
|
+ *
|
|
|
|
+ * @param {Object} session - the session object automatically added by socket.io
|
|
|
|
+ * @param {String} code - the password code
|
|
|
|
+ * @param {Function} cb - gets called with the result
|
|
|
|
+ * @param {String} userId - the userId automatically added by hooks
|
|
|
|
+ */
|
|
|
|
+ verifyPasswordCode: hooks.loginRequired((session, code, cb, userId) => {
|
|
|
|
+ async.waterfall([
|
|
|
|
+ (next) => {
|
|
|
|
+ if (!code || typeof code !== 'string') return next('Invalid code1.');
|
|
|
|
+ db.models.user.findOne({"services.password.set.code": code, _id: userId}, next);
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ (user, next) => {
|
|
|
|
+ if (!user) return next('Invalid code2.');
|
|
|
|
+ if (user.services.password.set.expires < new Date()) return next('That code has expired.');
|
|
|
|
+ next(null);
|
|
|
|
+ }
|
|
|
|
+ ], (err) => {
|
|
|
|
+ if (err && err !== true) {
|
|
|
|
+ let error = 'An error occurred.';
|
|
|
|
+ if (typeof err === "string") error = err;
|
|
|
|
+ else if (err.message) error = err.message;
|
|
|
|
+ logger.error("VERIFY_PASSWORD_CODE", `Code '${code}' failed to verify. '${error}'`);
|
|
|
|
+ cb({status: 'failure', message: error});
|
|
|
|
+ } else {
|
|
|
|
+ logger.success("VERIFY_PASSWORD_CODE", `Code '${code}' successfully verified.`);
|
|
|
|
+ cb({
|
|
|
|
+ status: 'success',
|
|
|
|
+ message: 'Successfully verified password code.'
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ }),
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Adds a password to a user with a code
|
|
|
|
+ *
|
|
|
|
+ * @param {Object} session - the session object automatically added by socket.io
|
|
|
|
+ * @param {String} code - the password code
|
|
|
|
+ * @param {String} newPassword - the new password code
|
|
|
|
+ * @param {Function} cb - gets called with the result
|
|
|
|
+ * @param {String} userId - the userId automatically added by hooks
|
|
|
|
+ */
|
|
|
|
+ changePasswordWithCode: hooks.loginRequired((session, code, newPassword, cb) => {
|
|
|
|
+ async.waterfall([
|
|
|
|
+ (next) => {
|
|
|
|
+ if (!code || typeof code !== 'string') return next('Invalid code1.');
|
|
|
|
+ db.models.user.findOne({"services.password.set.code": code}, next);
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ (user, next) => {
|
|
|
|
+ if (!user) return next('Invalid code2.');
|
|
|
|
+ if (!user.services.password.set.expires > new Date()) return next('That code has expired.');
|
|
|
|
+ next();
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ (next) => {
|
|
|
|
+ bcrypt.genSalt(10, next);
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ // hash the password
|
|
|
|
+ (salt, next) => {
|
|
|
|
+ bcrypt.hash(sha256(newPassword), salt, next);
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ (hashedPassword, next) => {
|
|
|
|
+ db.models.user.update({"services.password.set.code": code}, {$set: {"services.password.password": hashedPassword}, $unset: {"services.password.set": ''}}, next);
|
|
|
|
+ }
|
|
|
|
+ ], (err) => {
|
|
|
|
+ if (err && err !== true) {
|
|
|
|
+ let error = 'An error occurred.';
|
|
|
|
+ if (typeof err === "string") error = err;
|
|
|
|
+ else if (err.message) error = err.message;
|
|
|
|
+ logger.error("ADD_PASSWORD_WITH_CODE", `Code '${code}' failed to add password. '${error}'`);
|
|
|
|
+ cb({status: 'failure', message: error});
|
|
|
|
+ } else {
|
|
|
|
+ logger.success("ADD_PASSWORD_WITH_CODE", `Code '${code}' successfully added password.`);
|
|
|
|
+ cb({
|
|
|
|
+ status: 'success',
|
|
|
|
+ message: 'Successfully added password.'
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ }),
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Unlinks password from user
|
|
|
|
+ *
|
|
|
|
+ * @param {Object} session - the session object automatically added by socket.io
|
|
|
|
+ * @param {Function} cb - gets called with the result
|
|
|
|
+ * @param {String} userId - the userId automatically added by hooks
|
|
|
|
+ */
|
|
|
|
+ unlinkPassword: hooks.loginRequired((session, cb, userId) => {
|
|
|
|
+ async.waterfall([
|
|
|
|
+ (next) => {
|
|
|
|
+ db.models.user.findOne({_id: userId}, next);
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ (user, next) => {
|
|
|
|
+ if (!user) return next('Not logged in.');
|
|
|
|
+ if (!user.services.github || !user.services.github.id) return next('You can\'t remove password login without having GitHub login.');
|
|
|
|
+ db.models.user.update({_id: userId}, {$unset: {"services.password": ''}}, next);
|
|
|
|
+ }
|
|
|
|
+ ], (err) => {
|
|
|
|
+ if (err && err !== true) {
|
|
|
|
+ let error = 'An error occurred.';
|
|
|
|
+ if (typeof err === "string") error = err;
|
|
|
|
+ else if (err.message) error = err.message;
|
|
|
|
+ logger.error("UNLINK_PASSWORD", `Unlinking password failed for userId '${userId}'. '${error}'`);
|
|
|
|
+ cb({status: 'failure', message: error});
|
|
|
|
+ } else {
|
|
|
|
+ logger.success("UNLINK_PASSWORD", `Unlinking password successful for userId '${userId}'.`);
|
|
|
|
+ cb({
|
|
|
|
+ status: 'success',
|
|
|
|
+ message: 'Successfully unlinked password.'
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ }),
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Unlinks GitHub from user
|
|
|
|
+ *
|
|
|
|
+ * @param {Object} session - the session object automatically added by socket.io
|
|
|
|
+ * @param {Function} cb - gets called with the result
|
|
|
|
+ * @param {String} userId - the userId automatically added by hooks
|
|
|
|
+ */
|
|
|
|
+ unlinkGitHub: hooks.loginRequired((session, cb, userId) => {
|
|
|
|
+ async.waterfall([
|
|
|
|
+ (next) => {
|
|
|
|
+ db.models.user.findOne({_id: userId}, next);
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ (user, next) => {
|
|
|
|
+ if (!user) return next('Not logged in.');
|
|
|
|
+ if (!user.services.password || !user.services.password.password) return next('You can\'t remove GitHub login without having password login.');
|
|
|
|
+ db.models.user.update({_id: userId}, {$unset: {"services.github": ''}}, next);
|
|
|
|
+ }
|
|
|
|
+ ], (err) => {
|
|
|
|
+ if (err && err !== true) {
|
|
|
|
+ let error = 'An error occurred.';
|
|
|
|
+ if (typeof err === "string") error = err;
|
|
|
|
+ else if (err.message) error = err.message;
|
|
|
|
+ logger.error("UNLINK_GITHUB", `Unlinking GitHub failed for userId '${userId}'. '${error}'`);
|
|
|
|
+ cb({status: 'failure', message: error});
|
|
|
|
+ } else {
|
|
|
|
+ logger.success("UNLINK_GITHUB", `Unlinking GitHub successful for userId '${userId}'.`);
|
|
|
|
+ cb({
|
|
|
|
+ status: 'success',
|
|
|
|
+ message: 'Successfully unlinked GitHub.'
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ }),
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* Requests a password reset for an email
|
|
* Requests a password reset for an email
|
|
*
|
|
*
|
|
@@ -560,7 +766,7 @@ module.exports = {
|
|
let code = utils.generateRandomString(8);
|
|
let code = utils.generateRandomString(8);
|
|
async.waterfall([
|
|
async.waterfall([
|
|
(next) => {
|
|
(next) => {
|
|
- if (!email || typeof email !== 'string') return next('Invalid code.');
|
|
|
|
|
|
+ if (!email || typeof email !== 'string') return next('Invalid email.');
|
|
email = email.toLowerCase();
|
|
email = email.toLowerCase();
|
|
db.models.user.findOne({"email.address": email}, next);
|
|
db.models.user.findOne({"email.address": email}, next);
|
|
},
|
|
},
|