UNPKG

@aarconada/urserver

Version:

Basic Server definitions to develope REST API with a node + express Server

631 lines (612 loc) 33.6 kB
/** * Created by ubuntu on 12/11/18. */ 'use strict'; const server = require('../server')(); const bcrypt = require('bcryptjs'); const _ = require('lodash'); const promise = require('bluebird'); module.exports.enabled = server.configuration.authentication.basic.enabled; if(server.configuration.authentication.basic.enabled) { var basicUserSchema = server.configuration.authentication.basic.validation.enabled ? { email: {type: server.sequelize.api.STRING, allowNull: false, validate: {isEmail: true}}, type: {type: server.sequelize.api.INTEGER, allowNull: true}, password: {type: server.sequelize.api.STRING, allowNull: false}, lastlogin: {type: server.sequelize.api.DATE, allowNull: null}, remainingtries: {type: server.sequelize.api.INTEGER, allowNull: false}, lockeduntil: {type: server.sequelize.api.DATE, allowNull: null}, validationtoken: {type: server.sequelize.api.STRING, allowNull: true}, validationdate: {type: server.sequelize.api.DATE, allowNull: true}, recoverpasswordtoken: {type: server.sequelize.api.STRING, allowNull: true} } : { email: {type: server.sequelize.api.STRING, allowNull: false, validate: {isEmail: true}}, type: {type: server.sequelize.api.INTEGER, allowNull: true}, password: {type: server.sequelize.api.STRING, allowNull: false}, lastlogin: {type: server.sequelize.api.DATE, allowNull: null}, remainingtries: {type: server.sequelize.api.INTEGER, allowNull: false}, lockeduntil: {type: server.sequelize.api.DATE, allowNull: null}, recoverpasswordtoken: {type: server.sequelize.api.STRING, allowNull: true} }; const basicUserExcludes = []; /*server.configuration.authentication.basic.validation.enabled ? ['type', 'password', 'remainingtries', 'validationtoken', 'recoverpasswordtoken'] : ['type', 'password', 'remainingtries', 'recoverpasswordtoken'];*/ const basicUserModel = server.sequelize.define( { modelname: 'BasicUser', schema: basicUserSchema, configuration: { defaultScope: { attributes: { exclude: basicUserExcludes } } }, POST: { single: { enabled: false }, all: { enabled: false } }, PUT: { single: { enabled: false } , all: { enabled: false } }, DELETE: { single: { enabled: false } , all: { enabled: false } }, GET: { single: { enabled: false } , all: { enabled: false } } } ); module.exports.signup = function (transaction, type, email, password) { return basicUserModel.findOne( { where: {email: email}, transaction: transaction }) .then(existingUser => { server.debug('User exists yet?', existingUser !== null); if (!_.isUndefined(existingUser) && !_.isNull(existingUser)) server.utils.throwError(server.defaultResponses.authentication_user_exists_yet); const salt = bcrypt.genSaltSync(10); const hash = bcrypt.hashSync(password, salt); return basicUserModel.create( { email: email, password: hash, type: type, remainingtries: server.configuration.authentication.basic.logintries }, { transaction: transaction }) .then(newBasicUser => { server.debug('New basic user created?', newBasicUser !== null); if (server.configuration.authentication.basic.validation.enabled) { const newUserValidationToken = server.token.generateValidationToken( { userid: newBasicUser.id, usertype: newBasicUser.type } ); const newUserRefreshToken = server.token.generateRefreshToken( { userid: newBasicUser.id, usertype: newBasicUser.type } ); return newBasicUser.update( {validationtoken: newUserValidationToken}, {transaction: transaction} ) .then(newUserWithValidation => { server.debug('New basic user validation added?', newUserWithValidation !== null); server.debug('Sending email to basic user...'); if (_.isUndefined(server.configuration.authentication.basic.validation.template) || _.isNull(server.configuration.authentication.basic.validation.template)) { server.email.sendHtmlEmail(newUserWithValidation.email, newUserWithValidation.validationtoken, newUserWithValidation.validationtoken); } else { var template = server.fileSystem.readFile(server.configuration.email.templatePath, server.configuration.authentication.basic.validation.template); const templateAttachments = server.fileSystem.getFilesAttachments(server.configuration.email.templatePath, server.configuration.authentication.basic.validation.template); template = template .split("#CONFIRM").join(server.configuration.apiUrl + '/basicuser/validate?activationtoken=' + newUserValidationToken) .split("#RESEND").join(server.configuration.apiUrl + '/basicuser/refreshvalidationtoken?activationtoken=' + newUserValidationToken); server.email.sendHtmlEmailWithAttachments(newUserWithValidation.email, 'Validate account', template, templateAttachments); } return newUserWithValidation; }) .catch(err => { server.debug('Error during basic user creation', err); server.utils.throwError(server.defaultResponses.authentication_unable_signup, err); }); } else { return newBasicUser; } }) .catch(err => { server.debug('Error during basic user creation', err); server.utils.throwError(server.defaultResponses.authentication_unable_signup, err); }); }) }; module.exports.login = function (transaction, email, password) { return basicUserModel.findOne( { subQuery : false, where : {email: email}, include : {all: true}, transaction : transaction }) .then(foundedUser => { server.debug('User founded?', foundedUser !== null); if (_.isUndefined(foundedUser) || _.isNull(foundedUser)) server.utils.throwError(server.defaultResponses.authentication_user_not_found); if (server.configuration.authentication.basic.validation.enabled && (_.isUndefined(foundedUser.validationdate) || _.isNull(foundedUser.validationdate))) server.utils.throwError(server.defaultResponses.authentication_user_not_validated); const currentDate = new Date(); if (foundedUser.remainingtries <= 0) { if (foundedUser.lockeduntil > currentDate) server.utils.throwError(server.defaultResponses.authentication_user_locked); foundedUser = server.configuration.authentication.basic.logintries; } return bcrypt.compare(password, foundedUser.password) .then(result => { server.debug('Password compare result', result); if (result) { return foundedUser.update( { remainingtries: server.configuration.authentication.basic.logintries, lastlogin: currentDate }, {transaction: transaction}) .then(loggedUser => { return foundedUser; }) .catch(err => { server.debug('Error on user updating', err); server.utils.throwError(server.defaultResponses.entity_unable_update_element, err); }); } else { var lockedUntil = null; if (foundedUser.remainingtries <= 1) { server.debug('I have to lock user account'); lockedUntil = new Date(currentDate.getTime() + (1 * 60 * 1000)); } return foundedUser.update( { remainingtries: --foundedUser.remainingtries, lockeduntil: lockedUntil }, {transaction: transaction}) .then(lockedUser => { server.debug('Locked user', lockedUser); return server.defaultResponses.authentication_invalid_credentials; }) .catch(err => { server.debug('Error on locking user account', err); server.utils.throwError(server.defaultResponses.authentication_login_error, err); }); } }) .catch(err => { server.debug('Error on password comparing', err); server.utils.throwError(server.defaultResponses.authentication_login_error, err); }); }) .catch(err => { server.debug('Error on basic user login', err); server.utils.throwError(server.defaultResponses.authentication_login_error, err); }); }; if(server.configuration.authentication.basic.validation.enabled) { server.endpointmanager.addEndpoint({ name: 'Validate basic user account', description: 'This endpoint allows validate a basic user account with a valid validation token', route: '/basicuser/validate', method: server.utils.method.GET, callback: function (req, res, next, allowedResponses, transaction) { return basicUserModel.findOne( { where: { validationtoken: req.query.activationtoken }, transaction: transaction } ).then(unactivatedUser => { server.debug('Founded user', unactivatedUser != null); if (!unactivatedUser) server.utils.throwError(allowedResponses.invalid_token); return unactivatedUser.update( { validationtoken: null, validationdate: new Date() }, { transaction: transaction } ).then(() => { server.debug('Succesfully user activated'); if (_.isUndefined(server.configuration.authentication.basic.validation.OKtemplate) || _.isNull(server.configuration.authentication.basic.validation.OKtemplate)) { return '<h1>Account successfully activated</h1>'; } else { var template = server.fileSystem.readFile(server.configuration.email.templatePath, server.configuration.authentication.basic.validation.OKtemplate); return template; } }).catch(err => { server.debug('Error during user updating', err); if (_.isUndefined(server.configuration.authentication.basic.validation.KOtemplate) || _.isNull(server.configuration.authentication.basic.validation.KOtemplate)) { server.utils.throwError('<h1>Unable activate account</h1>'); } else { var template = server.fileSystem.readFile(server.configuration.email.templatePath, server.configuration.authentication.basic.validation.KOtemplate); server.utils.throwError(template); } }); }) }, token: { required: false }, session: { required: false }, parameters: [ { name: 'activationtoken', dataType: server.utils.dataType.STRING, parameterType: server.utils.parameterType.QUERY, required: true, description: 'Valid activation token of the account' } ], responses: { invalid_token: server.defaultResponses.invalid_token, entity_unable_update_element: server.defaultResponses.entity_unable_update_element }, transactional: true, htmlResponse: true }); server.endpointmanager.addEndpoint({ name: 'Refresh basic user validation token', description: 'Refersh the validation token of a non validated basic user account', route: '/basicuser/refreshvalidationtoken', method: server.utils.method.GET, callback: function (req, res, next, allowedResponses, transaction) { server.debug('Validation token', req.query.activationtoken); return basicUserModel.findOne( { where: { validationtoken: req.query.activationtoken }, transaction: transaction } ).then(currentUser => { server.debug('Generating new validation Token...'); if (!currentUser) server.utils.throwError(allowedResponses.refresh_user_not_exists); var validationToken = server.token.generateValidationToken({ userid: currentUser.id, usertype: currentUser.type }); server.debug('Sending email to user...'); if (_.isUndefined(server.configuration.authentication.basic.refresh.template) || _.isNull(server.configuration.authentication.basic.refresh.template)) { var validationEmailSended = server.email.sendHtmlEmail(currentUser.email, validationToken, validationToken); server.debug('Send email result', validationEmailSended); } else { var template = server.fileSystem.readFile(server.configuration.email.templatePath, server.configuration.authentication.basic.refresh.template); const templateAttachments = server.fileSystem.getFilesAttachments(server.configuration.email.templatePath, server.configuration.authentication.basic.refresh.template); template = template .split("#CONFIRM").join(server.configuration.apiUrl + '/basicuser/validate?activationtoken=' + validationToken) .split("#RESEND").join(server.configuration.apiUrl + '/basicuser/refreshvalidationtoken?activationtoken=' + validationToken); const validationEmailSended = server.email.sendHtmlEmailWithAttachments(currentUser.email, 'Validate account', template, templateAttachments); server.debug('Send email result', validationEmailSended); } server.debug('Send result: ', validationEmailSended); return currentUser.update( { validationtoken: validationToken }, { transaction: transaction } ).then(() => { server.debug('Succesfully validation token refreshing'); if (_.isUndefined(server.configuration.authentication.basic.refresh.OKtemplate) || _.isNull(server.configuration.authentication.basic.refresh.OKtemplate)) { return '<h1>Validation token generated</h1>'; } else { var template = server.fileSystem.readFile(server.configuration.email.templatePath, server.configuration.authentication.basic.refresh.OKtemplate); return template; } }).catch(err => { server.debug('Error during Common User Email Updating', err); server.utils.throwError(allowedResponses.refresh_unable_refresh, err); }); }).catch(err => { server.debug('Error during Common User Email refreshing token', err); if (_.isUndefined(server.configuration.authentication.basic.refresh.KOtemplate) || _.isNull(server.configuration.authentication.basic.refresh.KOtemplate)) { server.utils.throwError('<h1>Unable refresh token</h1>'); } else { var template = server.fileSystem.readFile(server.configuration.email.templatePath, server.configuration.authentication.basic.refresh.KOtemplate); server.utils.throwError(template); } }); }, token: { required: false }, session: { required: false }, parameters: [ { name: 'activationtoken', dataType: server.utils.dataType.STRING, parameterType: server.utils.parameterType.QUERY, required: true, description: 'Valid activation token of the basic user account' } ], responses: { refresh_user_not_exists: server.defaultResponses.refresh_user_not_exists, refresh_unable_refresh: server.defaultResponses.refresh_unable_refresh, refresh_invalid_token: server.defaultResponses.refresh_invalid_token }, transactional: true, htmlResponse: true }); server.endpointmanager.addEndpoint({ name: 'Request recover password', description: 'Starts the process to recover the basic user password', route: '/basicuser/requestrecoverpassword', method: server.utils.method.POST, callback: function (req, res, next, allowedResponses, transaction) { return basicUserModel.findOne( { where: { email: req.body.email }, transaction: transaction } ).then(currentUser => { server.debug('Generating new validation Token...'); if (!currentUser) server.utils.throwError(allowedResponses.refresh_user_not_exists); if (!currentUser.validationdate) server.utils.throwError(allowedResponses.user_not_validated); if (currentUser.remainingtries <= 0) { if(currentUser.lockeduntil > currentDate) server.utils.throwError(allowedResponses.locked_user); else currentUser.remainingtries = process.env.USER_LOGIN_ATTEMPTS ? process.env.USER_LOGIN_ATTEMPTS : 3; } var recoverToken = server.token.generateRecoverPasswordToken({ userid: currentUser.id, usertype: currentUser.type }); server.debug('RECOVER PASSWORD TOKEN', recoverToken); return currentUser.update( { recoverpasswordtoken: recoverToken }, { transaction: transaction } ).then(() => { server.debug('Succesfully validation token refreshing'); server.debug('Sending email to user...'); if (_.isUndefined(server.configuration.authentication.basic.change.template) || _.isNull(server.configuration.authentication.basic.change.template)) { var validationEmailSended = server.email.sendHtmlEmail(currentUser.email, 'Change account password', recoverToken); server.debug('Send email result', validationEmailSended); } else { var template = server.fileSystem.readFile(server.configuration.email.templatePath, server.configuration.authentication.basic.refresh.template); const templateAttachments = server.fileSystem.getFilesAttachments(server.configuration.email.templatePath, server.configuration.authentication.basic.refresh.template); const validationEmailSended = server.email.sendHtmlEmailWithAttachments(currentUser.email, 'Change password', template, templateAttachments); server.debug('Send email result', validationEmailSended); } return {}; }).catch(err => { server.debug('Error during Common User Email Updating', err); server.utils.throwError(allowedResponses.refresh_unable_refresh, err); }); }).catch(err => { server.debug('Error during Common User Email refreshing token', err); server.utils.throwError(err); }); }, token: { required: false }, session: { required: false }, parameters: [ { name: 'email', dataType: server.utils.dataType.STRING, parameterType: server.utils.parameterType.BODY, required: true, description: 'User account email' } ], responses: { refresh_user_not_exists: server.defaultResponses.refresh_user_not_exists, refresh_unable_refresh: server.defaultResponses.refresh_unable_refresh, refresh_invalid_token: server.defaultResponses.refresh_invalid_token, user_not_validated: server.defaultResponses.authentication_user_not_validated, locked_user: server.defaultResponses.authentication_user_locked }, transactional: true, htmlResponse: false }); server.endpointmanager.addEndpoint({ name: 'Recover password', description: 'Finish the process to recover the basic user password', route: '/basicuser/recoverpassword', method: server.utils.method.POST, callback: function (req, res, next, allowedResponses, transaction) { try { var tokenData = server.token.verifyRecoverPasswordToken(req.body.recovertoken); return basicUserModel.findOne( { where: { [server.sequelize.api.Op.and]: [ {id : tokenData.userid}, {recoverpasswordtoken : req.body.recovertoken} ] } } ).then(currentUser => { server.debug ('Founded user', currentUser !== null); if(!currentUser) server.utils.throwError(allowedResponses.refresh_user_not_exists); var salt = bcrypt.genSaltSync(10); server.debug('Obtaining salt', salt); var hash = bcrypt.hashSync(req.body.newpassword, salt); server.debug('Hashed Password', hash); return currentUser.update( { recoverpasswordtoken : null, password : hash } ).then(() => { server.session.removeSessionByIds(currentUser.id, currentUser.type, server.authentication.types.Basic); server.debug('Succesfully account password changed'); return {}; }).catch(err => { server.debug('Error during User change password', err); server.utils.throwError(allowedResponses.refresh_unable_refresh, err); }); }).catch(err => { server.debug('Error during User change password', err); server.utils.throwError(allowedResponses.refresh_unable_refresh, err); }); } catch(err) { server.debug('Invalid validation token: ', err); server.utils.throwError(allowedResponses.refresh_invalid_token, err) } }, token: { required: false }, session: { required: false }, parameters: [ { name: 'recovertoken', dataType: server.utils.dataType.STRING, parameterType: server.utils.parameterType.BODY, required: true, description: 'Recover password token of the user account' }, { name: 'newpassword', dataType: server.utils.dataType.STRING, parameterType: server.utils.parameterType.BODY, required: true, description: 'New password of the user account' } ], responses: { refresh_user_not_exists: server.defaultResponses.refresh_user_not_exists, refresh_unable_refresh: server.defaultResponses.refresh_unable_refresh, refresh_invalid_token: server.defaultResponses.refresh_invalid_token, user_not_validated: server.defaultResponses.authentication_user_not_validated, locked_user: server.defaultResponses.authentication_user_locked }, transactional: true, htmlResponse: false }); server.endpointmanager.addEndpoint({ name: 'Change password', description: 'Changes the basic user password', route: '/basicuser/changepassword', method: server.utils.method.POST, callback: function (req, res, next, allowedResponses, transaction) { try { server.debug(req.currentSession); return basicUserModel.findOne( { where: {id : req.currentSession.id} } ).then(currentUser => { server.debug ('Founded user', currentUser !== null); if(!currentUser) server.utils.throwError(allowedResponses.refresh_user_not_exists); return bcrypt.compare(req.body.oldpassword, currentUser.password) .then(result => { server.debug('Password compare result', result); if (result) { var salt = bcrypt.genSaltSync(10); server.debug('Obtaining salt', salt); var hash = bcrypt.hashSync(req.body.newpassword, salt); server.debug('Hashed Password', hash); return currentUser.update( { password : hash } ).then(() => { server.debug('Succesfully account password changed'); return {}; }).catch(err => { server.debug('Error during User change password', err); server.utils.throwError(allowedResponses.refresh_unable_refresh, err); }); } else { server.utils.throwError(allowedResponses.login_error, err); } }) .catch(err => { server.debug('Error on password comparing', err); server.utils.throwError(allowedResponses.login_error, err); }); }).catch(err => { server.debug('Error during User change password', err); server.utils.throwError(allowedResponses.refresh_unable_refresh, err); }); } catch(err) { server.debug('Invalid validation token: ', err); server.utils.throwError(allowedResponses.refresh_invalid_token, err) } }, token: { required: true }, session: { required: true }, parameters: [ { name: 'oldpassword', dataType: server.utils.dataType.STRING, parameterType: server.utils.parameterType.BODY, required: true, description: 'Old token of the user account' }, { name: 'newpassword', dataType: server.utils.dataType.STRING, parameterType: server.utils.parameterType.BODY, required: true, description: 'New password of the user account' } ], responses: { refresh_user_not_exists: server.defaultResponses.refresh_user_not_exists, refresh_unable_refresh: server.defaultResponses.refresh_unable_refresh, refresh_invalid_token: server.defaultResponses.refresh_invalid_token, user_not_validated: server.defaultResponses.authentication_user_not_validated, locked_user: server.defaultResponses.authentication_user_locked, login_error: server.defaultResponses.authentication_login_error }, transactional: true, htmlResponse: false }); } }