Asymmetrik/mean2-starter

View on GitHub
src/server/app/admin/controllers/users/users.password.server.controller.js

Summary

Maintainability
D
2 days
Test Coverage
'use strict';

let
    async = require('async'),
    crypto = require('crypto'),
    path = require('path'),

    deps = require(path.resolve('./src/server/dependencies.js')),
    dbs = deps.dbs,
    config = deps.config,
    errorHandler = deps.errorHandler,
    logger = deps.logger,
    emailService = deps.emailService,

    User = dbs.admin.model('User');

/**
 * Forgot for reset password (forgot POST)
 */
exports.forgot = (req, res, next) => {

    // Make sure there is a username
    if(null == req.body.username) {
        return res.status(400).json({ message: 'Username is missing.' });
    }

    logger.info('Password reset request for username: %s', req.body.username);

    async.waterfall([
        // Generate random token
        (done) => {
            crypto.randomBytes(20, (error, buffer) => {
                let token = buffer.toString('hex');
                logger.debug('Generated reset token.');
                done(error, token);
            });
        },

        // Lookup user by username
        (token, done) => {

            // Try to find the user
            User.findOne({
                username: req.body.username
            }, '-salt -password', (error, user) => {

                // If we failed to find the user by username
                if (null != error || null == user) {
                    return res.status(400).json({
                        message: 'No account with that username has been found.'
                    });
                }

                // Generate the token and the expire date/time
                user.resetPasswordToken = token;
                user.resetPasswordExpires = Date.now() + 3600000; // 1 hour

                logger.debug('Found the user.');

                // Save the user with the token
                user.save((error) => {
                    logger.debug('Saved the user with reset token.');
                    done(error, token, user);
                });

            });
        },

        // Generate the email message (from template)
        (token, user, done) => {
            res.render('templates/reset-password-email', {
                name: user.name,
                appName: config.app.title,
                url: `${config.app.baseUrl}/password/reset/${token}`
            }, (error, emailHTML) => {
                logger.debug('Rendered email.');
                done(error, emailHTML, user);
            });
        },

        // Send the email
        (emailHTML, user, done) => {
            let mailOptions = {
                to: user.email,
                from: config.mailer.from,
                subject: 'Password Reset',
                html: emailHTML
            };

            emailService.sendMail(mailOptions)
                .then((result) => {
                    logger.debug(`Sent email to: ${user.email}`);
                    res.json(`An email has been sent to ${user.email} with further instructions.`);
                    done(null);

                }, (error) => {
                    logger.error({err: error, req: req}, 'Failure sending email.');
                    return res.status(400).json({ message: 'Failure sending email.' });
                });
        }
    ], (error) => {
        if (error) return next(error);
    });
};


/**
 * Reset password GET from email token
 */
exports.validateResetToken = (req, res) => {
    User.findOne({
        resetPasswordToken: req.params.token,
        resetPasswordExpires: {
            $gt: Date.now()
        }
    }, (error, user) => {
        if (!user) {
            return res.status('400').json({ message: 'invalid-token' });
        }

        return res.json({ message: 'valid-token' });
    });
};


/**
 * Reset password POST from email token
 */
exports.reset = (req, res, next) => {

    // Init Variables
    let password = req.body.password;

    // Make sure there is a username
    if(null == password) {
        return res.status(400).json({ message: 'Password is missing.' });
    }


    async.waterfall([

        (done) => {
            User.findOne({
                resetPasswordToken: req.params.token,
                resetPasswordExpires: {
                    $gt: Date.now()
                }
            }, (error, user) => {

                if(null != error || null == user) {
                    return res.status(400).json({
                        message: 'Password reset token is invalid or has expired.'
                    });
                }
                user.password = password;
                user.resetPasswordToken = undefined;
                user.resetPasswordExpires = undefined;

                user.save((error) => {
                    if (error) {
                        return res.status(400).json({
                            message: errorHandler.getErrorMessage(error)
                        });
                    } else {
                        req.login(user, (error) => {
                            if (error) {
                                return res.status(400).json({
                                    message: errorHandler.getErrorMessage(error)
                                });
                            } else {
                                // Return authenticated user
                                res.json(User.fullCopy(user));
                                done(error, user);
                            }
                        });
                    }
                });
            });
        },

        (user, done) => {
            res.render('templates/reset-password-confirm-email', {
                name: user.name,
                appName: config.app.title
            }, (error, emailHTML) => {
                done(error, emailHTML, user);
            });
        },

        // If valid email, send reset email using service
        (emailHTML, user, done) => {
            let mailOptions = {
                to: user.email,
                from: config.mailer.from,
                subject: 'Your password has been changed',
                html: emailHTML
            };

            emailService.sendMail(mailOptions)
                .then((result) => {
                    logger.debug(`Sent email to: ${user.email}`);
                    res.json(`An email has been sent to ${user.email} letting them know their password was reset.`);
                    done(null);
                }, (error) => {
                    logger.error({err: error, req: req}, 'Failure sending email.');
                    return res.status(400).json({ message: 'Failure sending email.' });
                });
        }
    ], (error) => {
        if (error) return next(error);
    });
};