UNPKG

we-core

Version:

We.js is a node.js framework for build real time applications, sites or blogs!

657 lines (551 loc) 19.7 kB
/** * Authentication controller */ var _ = require('lodash'); var async = require('async'); module.exports = { _config: { acl: false }, // getter for current logged in user current: function current(req, res) { if (!req.isAuthenticated() ) return res.send({}); return res.ok(req.user); }, /** * Signup action for POST and GET methods */ signup: function Register(req, res) { var we = req.we; // check allowRegister flag how block signup if (!we.config.auth.allowRegister) return res.forbidden(); // anti spam honeypot field if (req.body.mel) { we.log.info('Bot get mel:', req.ip, req.body.email); return res.forbidden(); } if (req.method !== 'POST' || req.isAuthenticated()) { return res.ok(); } var newUser, requireAccountActivation; // -- set req.body for handle db errors res.locals.data = req.body; async.series([ function checkIfIsSpam(cb) { we.antiSpam.recaptcha.verify(req, res, function (err, isSpam) { if (err) return cb(err); if (isSpam) { we.log.warn('auth.signup: spambot found in recaptcha verify: ', req.ip, req.body.email); res.addMessage('warning', { text: 'auth.register.spam', vars: { email: req.body.email } }); return res.queryError(); } requireAccountActivation = we.config.auth.requireAccountActivation; // if dont need a account activation email then create a active user if (!requireAccountActivation) req.body.active = true; cb(); }); }, function checkUSerAcceptTermsField(cb) { if (!req.body.acceptTerms || req.body.acceptTerms == 'false') { cb('auth.register.acceptTerms.required'); } else { cb(); } }, // save the user and password with transaction function saveUserAndPassword(cb) { we.db.defaultConnection.transaction(function (t) { // create the user return we.db.models.user.create( req.body, { transaction: t } ).then(function (u) { newUser = u; // save password return we.db.models.password.create({ userId: u.id, password: req.body.password, confirmPassword: req.body.confirmPassword }, { transaction: t }); }); }).then(function () { we.log.info( 'Auth plugin:New user:', req.body.email , 'username:' , req.body.username , 'ID:' , newUser.id ); cb(); }).catch(function (err) { cb(err); }); } ], function afterCreateUserAndPassword(err) { if(err) return res.queryError(err); if (requireAccountActivation) { return we.db.models.authtoken.create({ userId: newUser.id, redirectUrl: res.locals.redirectTo }).then(function (token) { var templateVariables = { user: newUser, site: { name: we.config.appName }, confirmUrl: we.config.hostname + '/user/'+ newUser.id +'/activate/' + token.token }; var options = { subject: req.__('we.email.AccontActivationEmail.subject', templateVariables), to: newUser.email }; // send email in async we.email.sendEmail('AccontActivationEmail', options, templateVariables, function (err) { if (err) { we.log.error('Action:Login sendAccontActivationEmail:', err); } }); res.addMessage('warning', { text: 'auth.register.require.email.activation', vars: { email: newUser.email } }, { requireActivation: true, email: newUser.email }); res.locals.authToken = token; res.locals.newUserCreated = true; res.locals.skipRedirect = true; return res.created(); }); } we.auth.logIn(req, res, newUser, function (err) { if (err) { we.log.error('logIn error: ', err); return res.serverError(err); } if (req.accepts('html')) { return res.goTo( (res.locals.redirectTo || '/') ); } res.locals.newUserCreated = true; res.locals.model = 'user'; res.locals.data = newUser; res.created(); }); }); }, /** * Log out current user * Beware! this dont run socket.io disconect */ logout: function logout(req, res) { var we = req.getWe(); we.auth.logOut(req, res, function (err) { if (err) we.log.error('Error on logout user', req.id, req.cookie); res.goTo('/'); }) }, /** * Login API with session and passport-local strategy * * This action receives the static and JSON request */ login: function login(req, res, next) { var we = req.we; if (!we.config.passport || !we.config.passport.strategies || !we.config.passport.strategies.local) { return res.notFound(); } var email = req.body.email; if (req.method !== 'POST' || req.isAuthenticated()) { // else show login page return res.ok(); } // -- set req.body for error page res.locals.data = req.body; return we.passport.authenticate('local', function(err, user, info) { if (err) { we.log.error('AuthController:login:Error on get user ', err, email); return res.serverError(err); } if (!user) { we.log.debug('AuthController:login:User not found', email); res.addMessage('warning', { text: info.message, vars: { email: email } }); return res.badRequest(); } if (!user.active) { we.log.debug('AuthController:login:User not active', email); res.addMessage('warning', { text: 'auth.login.user.not.active', vars: { email: email } }); return res.badRequest(); } we.auth.logIn(req, res, user, function (err) { if (err) return res.serverError(err); we.log.info('AuthController:login: user autheticated:', user.id, user.username); if (err) { we.log.error('logIn error: ', err); return res.serverError(err); } res.locals.newUserCreated = true; // redirect if are a html response if (req.accepts('html')) return res.goTo( (res.locals.redirectTo || '/') ); res.send({ user: user}); }); })(req, res, next); }, /** * Activate a user account with activation code */ activate: function activate(req, res) { var we = req.getWe(); var user = {}; user.id = req.params.id; var token = req.params.token; var responseForbiden = function responseForbiden() { res.addMessage('warning', 'auth.access.invalid.token'); return res.goTo('/login'); }; we.db.models.authtoken.validAuthToken(user.id, token, function (err, result, authToken) { if (err) { we.log.error('auth:activate: Error on validate token: ', err, token, user.id); return responseForbiden(); } // token is invalid if (!result) { we.log.info('auth:activate: invalid token: ', token, user.id); return responseForbiden(); } // token is valid then get user form db we.db.models.user.findById(user.id).then(function (usr) { // user found if (!usr) { we.log.error('auth:activate: user not found: ', user.id); // user not found return res.badRequest(); } // activate user and login usr.active = true; usr.save().then(function () { var rediredtUrl = ( authToken.redirectUrl || '/' ); // destroy auth token after use authToken.destroy().catch(function (err) { if (err) we.log.error('Error on delete token', err); }); // login and redirect the user we.auth.logIn(req, res, usr, function(err) { if (err) { we.log.error('logIn error:', err); return res.serverError(err); } return res.goTo(rediredtUrl); }); }).catch(res.queryError); }).catch(res.queryError); }); }, /** * Forgot password API endpoint * Generate one reset token and send to user email */ forgotPassword: function forgotPassword(req, res) { var we = req.getWe(); var email = req.body.email; res.locals.emailSend = false; res.locals.messages = []; res.locals.user = req.body.user; if (req.method !== 'POST') return res.ok(); if (!res.locals.user) res.locals.user = {}; res.locals.formAction = '/auth/forgot-password'; if (!email) { return res.badRequest('auth.forgot-password.field.email.required'); } we.db.models.user.find({ where: {email: email }}) .then(function (user) { if (!user) return res.badRequest('auth.forgot-password.user.not-found'); we.db.models.authtoken.create({ userId: user.id, tokenType: 'resetPassword' }).then(function (token) { var options = { email: user.email, subject: we.config.appName + ' - ' + req.__('auth.forgot-password.reset-password'), from: we.config.email.siteEmail }; user = user.toJSON(); if (!user.displayName) { user.displayName = user.username; } var templateVariables = { user: { name: user.username, displayName: user.displayName }, site: { name: we.config.appName, url: we.config.hostname }, resetPasswordUrl: token.getResetUrl() }; we.email.sendEmail('AuthResetPasswordEmail', options, templateVariables, function(err , emailResp){ if (err) { we.log.error('Error on send email AuthResetPasswordEmail', err, emailResp); return res.serverError(); } we.log.verbose('AuthResetPasswordEmail: Email resp:', emailResp); }); res.addMessage('success', 'auth.forgot-password.email.send'); res.locals.emailSend = true; if (req.accepts('json')) return res.ok(); return res.ok(); }).catch(res.queryError); }).catch(res.queryError); }, /** * Generate and return one auth token * Only allow admin users in permissions */ authToken: function authToken(req, res) { if (!req.isAuthenticated()) return res.forbiden(); var we = req.getWe(); var email = req.params.email; if (!email) { return res.badRequest('Email is required to request a password reset token.'); } we.db.models.user.find({ where: {email: email}}) .then(function (user) { if (!user) return res.badRequest('unknow error trying to find a user'); we.db.models.authtoken.create({ 'userId': user.id, tokenType: 'resetPassword' }).then(function (token) { if (!token) { return res.serverError('unknow error on create auth token'); } return res.json(token.getResetUrl()); }); }); }, /** * Api endpoint to check if current user can change the password without old password */ checkIfCanResetPassword: function (req, res){ if(!req.isAuthenticated()) return res.forbidden(); if (req.session && req.session.resetPassword) { res.addMessage('success', 'auth.reset-password.success.can'); return res.ok(); } res.addMessage('error', 'auth.reset-password.error.forbidden'); return res.forbidden(); }, consumeForgotPasswordToken: function (req, res, next){ var we = req.getWe(); var uid = req.params.id; var token = req.params.token; if (!uid || !token){ we.log.info('consumeForgotPasswordToken: Uid of token not found', uid, token); return next(); } loadUserAndAuthToken(we, uid, token, function(error, user, authToken){ if (error) { we.log.error('AuthController:consumeForgotPasswordToken: Error on loadUserAndAuthToken', error); return res.serverError(); } if (!user || !authToken) { we.log.warn('consumeForgotPasswordToken: invalid token: ', token, ' for uid: ', uid); req.flash('messages',[{ status: 'warning', type: 'updated', message: req.__('auth.consumeForgotPasswordToken.token.invalid') }]); return res.goTo('/auth/forgot-password'); } if (user.active) { return respondToUser(); } else { // If user dont are active, change and salve the active status user.active = true; user.save().then(function () { respondToUser(); }); } function respondToUser() { we.auth.logIn(req, res, user, function (err) { if (err) { we.log.error('AuthController:consumeForgotPasswordToken:logIn error', err); return res.serverError(err); } // consumes the token authToken.isValid = false; authToken.destroy().then(function () { // set session variable req.session.resetPassword to indicate that there is a new password to be defined req.session.resetPassword = true; if (req.accepts('json')) { return res.status(200).send(); } res.goTo( '/auth/' + user.id + '/new-password/'); }).catch(function(err) { if (err) we.log.error('auth:consumeForgotPasswordToken: Error on dstroy token:', err); }); }); } }); }, /** * newPassword page * Page to set new user password after click in new password link */ newPassword: function newPasswordAction(req, res) { // not authenticated if (!req.isAuthenticated()) return res.goTo('/auth/forgot-password'); // check access if ( req.session.resetPassword && (req.params.id != req.user.id) ) { req.we.log.warn('auth.newPassword cant change other user password: '+req.params.id+ ' auid: '+req.user.id); return res.goTo('/auth/'+req.user.id+'/new-password'); } else if (req.we.acl.canStatic('manage_users', req.userRoleNames)) { // can manage users then can change others users password } else if (!req.session.resetPassword){ req.we.log.warn('auth.newPassword req.session.resetPassword is false, uid: '+req.params.id+' auid: '+req.user.id); return res.goTo('/auth/forgot-password'); } var we = req.we; if (req.method !== 'POST') return res.ok(); var newPassword = req.body.newPassword; var rNewPassword = req.body.rNewPassword; var userId = req.params.id; if ( _.isEmpty(newPassword) || _.isEmpty(rNewPassword) ) return res.badRequest('auth.confirmPassword.and.password.required'); if (newPassword !== rNewPassword) return res.badRequest('auth.newPassword.and.password.diferent'); we.db.models.user.findById(userId) .then(function (user) { if (!user) { we.log.info('newPassword: User not found', user); return res.serverError(); } user.updatePassword(newPassword, function (err) { if (err) return res.serverError(err); // Reset req.session.resetPassword to indicate that the operation has been completed delete req.session.resetPassword; if (req.accepts('json')) { return res.status(200).send({messages: res.locals.messages}); } res.addMessage('success', 'auth.new-password.set.successfully'); res.locals.successfully = true; return res.ok(); }); }).catch(res.queryError); }, /** * Change authenticated user password */ changePassword: function (req, res) { if(!req.isAuthenticated()) return res.goTo('/'); var we = req.getWe(); if (req.method !== 'POST') return res.ok(); var oldPassword = req.body.password; var newPassword = req.body.newPassword; var rNewPassword = req.body.rNewPassword; var userId = req.user.id; if(!req.isAuthenticated()) return res.badRequest('auth.change-password.forbiden'); // skip old password if have resetPassword flag in session if (!req.session.resetPassword) { if (!oldPassword) return res.badRequest('field.password.required'); } if ( _.isEmpty(newPassword) || _.isEmpty(rNewPassword) ) { return res.badRequest('field.confirm-password.password.required'); } if (newPassword !== rNewPassword) { return res.badRequest('field.password.confirm-password.diferent'); } we.db.models.user.findById(userId) .then(function (user) { if (!user) { we.log.info('resetPassword: User not found', user); return res.badRequest(); } // skip password check if have resetPassord flag active if (req.session.resetPassword) { return changePassword(); } else { user.verifyPassword(oldPassword, function(err, passwordOk) { if (!passwordOk) { res.addMessage('error', 'field.password.invalid'); return res.badRequest(); } return changePassword(); }); } function changePassword() { // set newPassword and save it for generate the password hash on update user.updatePassword(newPassword, function (err) { if(err) { we.log.error('Error on save user to update password: ', err); return res.serverError(err); } // Reset req.session.resetPassword to indicate that the operation has been completed delete req.session.resetPassword; var appName = we.config.appName; var options = { email: user.email, subject: appName + ' - ' + req.__('auth.change-password.reset-password'), from: we.config.email.siteEmail }; user = user.toJSON(); if (!user.displayName) { user.displayName = user.username; } var templateVariables = { user: { name: user.username, displayName: user.displayName }, site: { name: appName, slogan: we.config.slogan, url: we.config.hostname } }; we.email.sendEmail('AuthChangePasswordEmail', options, templateVariables, function (err , emailResp) { if (err) { we.log.error('Error on send email AuthChangePasswordEmail', err, emailResp); } res.addMessage('success', 'auth.change-password.success'); return res.ok(); }); }) } }).catch(res.queryError); } }; /** * Load user and auth token * @param {string} uid user id * @param {string} token user token * @param {Function} callback callback(error, user, authToken) */ function loadUserAndAuthToken(we, uid, token, callback){ we.db.models.user.findById(uid) .then(function (user) { if (!user) { // user not found return callback(null, null, null); } we.db.models.authtoken.find({ where: { userId: user.id, token: token, isValid: true }}).then(function(authToken){ if (authToken) { return callback(null, user, authToken); }else{ return callback(null, user, null); } }); }); }