Asymmetrik/mean2-starter

View on GitHub
src/server/app/admin/services/users.authentication.server.service.js

Summary

Maintainability
A
1 hr
Test Coverage
'use strict';

let _ = require('lodash'),
    passport = require('passport'),
    path = require('path'),
    q = require('q'),

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

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


/**
 * ==========================================================
 * Private methods
 * ==========================================================
 */



/**
 * ==========================================================
 * Public Methods
 * ==========================================================
 */

/**
 * Initialize a new user
 * This method applies any common business logic that happens
 * when a new user is created in the system.
 */
module.exports.initializeNewUser = function(user) {
    // Add the default roles
    if (null != config.auth.defaultRoles) {
        user.roles = user.roles || {};
        _.defaults(user.roles, config.auth.defaultRoles);
    }

    // Resolve the user (this might seem like overkill, but planning for the future)
    return q(user);
};

// Send email alert about new account request
module.exports.signupEmail = function(user, req) {
    let defer = q.defer();

    req.app.render('../../admin/templates/user-signup-alert-email', {
        name: user.name,
        username: user.username,
        appName: config.app.title,
        url: `${config.app.baseUrl}/admin/users`
    }, (error, html) => {
        if (error) {
            logger.error({err: error, req: req}, 'Failure rendering template.');
            defer.reject(error);
        }
        else {
            let to = config.newUserEmail.email;

            let mailOptions = {
                to: to,
                from: config.mailer.from,
                subject: 'New Account Request',
                html: html
            };

            emailService.sendMail(mailOptions)
                .then((result) => {
                    logger.debug(`Sent new user(${user.username}) email to: ${to}`);
                    defer.resolve(user);
                }, (error) => {
                    // Log the error but this shouldn't block
                    // the user from signing up
                    logger.error({err: error, req: req}, 'Failure sending email.');
                    defer.resolve(user);
                });
        }
    });

    return defer.promise;
};


/**
 * Login the user
 * Does the work to log the user into the system
 * Updates the last logged in time
 * Audits the action
 */
module.exports.login = function(user, req) {
    let defer = q.defer();

    // Remove sensitive data before login
    delete user.password;
    delete user.salt;

    // Calls the login function (which goes to passport)
    req.login(user, function(err) {
        if (err) {
            defer.reject({ status: 500, type: 'login-error', message: err });
        } else {

            // update the user's last login time
            User.findOneAndUpdate(
                { _id: user._id },
                { lastLogin: Date.now() },
                { new: true, upsert: false },
                function(err, user) {
                    if(null != err) {
                        defer.reject({ status: 500, type: 'login-error', message: err });
                    }
                    else {
                        defer.resolve(User.fullCopy(user));
                    }
                }
            );

            // Audit the login
            auditService.audit('User successfully logged in', 'user-authentication', 'authentication succeeded',
                { }, User.auditCopy(user, util.getHeaderField(req.headers, 'x-real-ip')), req.headers);
        }
    });

    return defer.promise;
};

/**
 * Authenticate and then login depending on the outcome
 */
module.exports.authenticateAndLogin = function(req) {
    let defer = q.defer();

    // Attempt to authenticate the user using passport
    passport.authenticate(config.auth.strategy, function(err, user, info, status) {

        // If there was an error
        if(null != err) {
            // Reject the promise with a 500 error
            defer.reject({ status: 500, type: 'authentication-error', message: err });
        }
        // If the authentication failed
        else if (!user) {
            // In the case of a auth failure, info should have the reason
            // Here is a hack for the local strategy...
            if(null == info.status && null != status) {
                info.status = status;
                if(info.message === 'Missing credentials') {
                    info.type = 'missing-credentials';
                }
            }

            defer.reject(info);

            // Try to grab the username from the request
            let username = (req.body && req.body.username)? req.body.username : 'none provided';

            // Audit the failed attempt
            auditService.audit(info.message, 'user-authentication', 'authentication failed',
                { }, { username: username }, req.headers);

        }
        // Else the authentication was successful
        else {
            // Set the user ip if available.
            user.ip = ( _.isUndefined(req.headers['x-real-ip']) ) ? null : req.headers['x-real-ip'];
            module.exports.login(user, req).then(defer.resolve, defer.reject);
        }

    })(req);

    return defer.promise;
};