@aarconada/urserver
Version:
Basic Server definitions to develope REST API with a node + express Server
631 lines (612 loc) • 33.6 kB
JavaScript
/**
* 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
});
}
}