lolo-backend
Version:
Lolo Backend is a REST Framework and provides an automatic asynchronous CRUD backend server generator based on your models' definitions and validation rules for Node.js and MongoDB.
159 lines (136 loc) • 6.42 kB
JavaScript
/*!
* lolo-backend
* Copyright(c) 2021 Camilo Pifano Herrera
* MIT Licensed
*/
//--------------------------------------------------------------------------------------------------------------------//
// CRUD MIDDLEWARE:
//--------------------------------------------------------------------------------------------------------------------//
//Import external modules:
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
//Import services module:
const services = require('./services');
//Import file settings (YAML):
const settings = services.getFileSettings();
//Set language:
const currentLang = require('./supported_languages')(settings.language);
//--------------------------------------------------------------------------------------------------------------------//
// ALLOWED VALIDATE:
//--------------------------------------------------------------------------------------------------------------------//
// Allowed Validate checks the schema of the current model which elements can be set and which cannot.
// The allowed elements are returned in a set object to be validated.
// The NOT allowed elements are returned to an object called blocked.
//--------------------------------------------------------------------------------------------------------------------//
const allowedValidate = (allowedSchemaKeys) => {
return (req, res, next) => {
if(req.body){
//Initialize attrAllowed and attrNotAllowed objects:
let attrAllowed = {};
let attrNotAllowed = {};
//Initialize validation result object (Inside the Request):
req.validatedResult = {};
//Loop through elements of the POST request:
Object.entries(req.body).forEach(([key, value]) => {
//Check if the element you want to modify exists or not in the current model:
if(allowedSchemaKeys.includes(key)){
//Set allowed parameters:
attrAllowed[key] = value;
} else {
//Save parameters NOT allowed:
attrNotAllowed[key] = value;
}
});
//Check if there is data to modify:
if(Object.keys(attrAllowed).length === 0){
req.validatedResult['set'] = false; //No data to modify.
} else {
req.validatedResult['set'] = attrAllowed; //Assign validated allowed attributes.
}
//Check if there are NOT allowed attributes:
if(Object.keys(attrNotAllowed).length === 0){
req.validatedResult['blocked'] = false; //No blocked attributes.
} else {
req.validatedResult['blocked'] = attrNotAllowed; //Return attributes NOT allowed.
}
}
next();
}
};
//--------------------------------------------------------------------------------------------------------------------//
//--------------------------------------------------------------------------------------------------------------------//
// IS PASSWORD:
// Encrypts the fields designated as passwords in the schema before saving them to the database.
//--------------------------------------------------------------------------------------------------------------------//
const isPassword = (Schema, fieldName = 'password') => {
//Set amount of repetition of the SALT algorithm:
const saltRounds = 12;
//PRE SAVE INSERT:
Schema.pre('save', function(next){
//Check if the password field exists according to the name indicated:
if(!this[fieldName]) return next(); //If password is not updated:
//Hash the password:
const data = this;
bcrypt.hash(data[fieldName], saltRounds, (error, hashedPassword) => {
if(error){
next(error);
} else {
//Save hashed password:
data[fieldName] = hashedPassword;
next();
}
});
});
//PRE SAVE UPDATE:
Schema.pre('findOneAndUpdate', async function(next){
//Check if the password field exists according to the name indicated:
if(!this._update.$set[fieldName]) return next(); //If password is not updated:
//Hash the password:
const hashedPassword = await bcrypt.hash(this._update.$set[fieldName], saltRounds);
//Create set object to preserve fieldName as key:
let set = {};
set[fieldName] = hashedPassword;
//Set hashed password:
this.set(set);
});
//Return MongoDB Schema:
return Schema;
}
//--------------------------------------------------------------------------------------------------------------------//
//--------------------------------------------------------------------------------------------------------------------//
// CHECK JWT:
// Checks for the existence of a JWT and validates it to protect itself from unwanted requests.
//--------------------------------------------------------------------------------------------------------------------//
const checkJWT = (req, res, next) => {
//Get bearer token from headers:
let token = req.headers['x-access-token'] || req.headers['authorization'];
//Validate that token exists:
if(!token){
res.status(401).send({ success: false, message: currentLang.jwt.check_empty_token });
return;
}
//Sanitize header token:
if(token.startsWith('Bearer ')){
token = token.slice(7, token.length);
}
//Validate header token:
if(token){
jwt.verify(token, settings.secret_token, (err, decoded) => {
if(err){
return res.status(401).send({ success: false, message: currentLang.jwt.check_invalid_token, error: err.message });
} else {
req.decoded = decoded;
next();
}
});
}
};
//--------------------------------------------------------------------------------------------------------------------//
//--------------------------------------------------------------------------------------------------------------------//
//Export CRUD middleware:
module.exports = {
allowedValidate,
isPassword,
checkJWT
};
//--------------------------------------------------------------------------------------------------------------------//