This commit is contained in:
David Arranz 2019-04-24 23:01:54 +02:00
parent 1f6d27f220
commit a28814a420
26 changed files with 691 additions and 60 deletions

0
.env Normal file
View File

View File

@ -9,7 +9,7 @@ module.exports = {
session: { session: {
secret_token: process.env.SECRET_TOKEN || "B57J=7B`NQ$y98|~5;hc715bo09^5oz8NR+]n9r~215B91Nd9P%25_N6r!GHcOKp|18y5-73Dr5^@9k7n]5l<-41D1o", secret_token: process.env.SECRET_TOKEN || "B57J=7B`NQ$y98|~5;hc715bo09^5oz8NR+]n9r~215B91Nd9P%25_N6r!GHcOKp|18y5-73Dr5^@9k7n]5l<-41D1o",
token_expires_in: 86400000 token_expires_in: '99d'
}, },
server: { server: {

View File

@ -9,7 +9,7 @@ module.exports = {
session: { session: {
secret_token: process.env.SECRET_TOKEN || "B57J=7B`NQ$y98|~5;hc715bo09^5oz8NR+]n9r~215B91Nd9P%25_N6r!GHcOKp|18y5-73Dr5^@9k7n]5l<-41D1o", secret_token: process.env.SECRET_TOKEN || "B57J=7B`NQ$y98|~5;hc715bo09^5oz8NR+]n9r~215B91Nd9P%25_N6r!GHcOKp|18y5-73Dr5^@9k7n]5l<-41D1o",
token_expires_in: 86400000 token_expires_in: '24h'
}, },
server: { server: {

View File

@ -12,7 +12,7 @@ const passport = require('passport');
const router = require('./router'); const router = require('./router');
const error = require('../middlewares/error'); const error = require('../middlewares/error');
const access = require('../middlewares/access');
/** /**
* Express instance * Express instance
@ -42,12 +42,14 @@ app.use(methodOverride());
// secure apps by setting various HTTP headers // secure apps by setting various HTTP headers
app.use(helmet()); app.use(helmet());
// enable CORS - Cross Origin Resource Sharing // enable CORS - Cross Origin Resource Sharing
app.use(cors({ app.use(cors({
exposeHeaders: [ exposeHeaders: [
"WWW-Authenticate", "WWW-Authenticate",
"Server-Authorization" "Server-Authorization",
"Content-Disposition",
"Content-Type",
"Content-Length"
], ],
maxAge: 31536000, maxAge: 31536000,
credentials: true, credentials: true,
@ -71,7 +73,7 @@ app.use(cors({
// Access validator // Access validator
app.use(passport.initialize()); app.use(passport.initialize());
passport.use('jwt', access.jwt); require('./passport');
// Set routes // Set routes

View File

@ -5,9 +5,7 @@ const Sequelize = require('sequelize');
const config = require('../config'); const config = require('../config');
const log = require('./logger'); const log = require('./logger');
const modulesDir = path.resolve(__dirname + '/../modules/') const modulesDir = path.resolve(__dirname + '/../modules/')
const basename = path.basename(__dirname);
const globOptions = { const globOptions = {
cwd: modulesDir, cwd: modulesDir,
nocase: true, nocase: true,

63
core/passport.js Normal file
View File

@ -0,0 +1,63 @@
const _ = require('lodash');
const passport = require('passport');
const { Strategy: LocalStrategy } = require('passport-local');
const { Strategy: JWTStrategy} = require('passport-jwt');
const config = require('../config');
const models = require('./models');
const securityHelper = require('../helpers/security.helper');
passport.serializeUser((user, done) => {
done(null, user.id);
});
passport.deserializeUser((id, done) => {
models.User.findById(id, (err, user) => {
done(err, user);
});
});
/**
* Sign in using Email and Password.
*/
const localOptions = {
usernameField: 'email',
passwordField: 'password'
}
passport.use('local', new LocalStrategy(localOptions, async (email, password, done) => {
try {
const user = await models.User.findOne({ where: { email } });
if (_.isNull(user)) {
return done(null, false, { message: 'User not found' })
} else {
const isPasswordValid = await user.comparePassword(password);
if (!isPasswordValid) {
return done(null, false, { message: 'Wrong Password' })
} else {
return done(null, user, { message: 'Logged in Successfully' });
}
}
} catch (error) {
return done(error);
}
}));
// JWT
passport.use('jwt', new JWTStrategy(securityHelper.jwtOptions, async (jwtPayload, done) => {
try {
const user = await models.User.findOne({ where: { id: jwtPayload.id } });
if (_.isNull(user)) {
return done(null, false, { message: 'User not found' })
} else {
return done(null, user, { message: 'User found' });
}
} catch (error) {
return done(error);
}
}));

View File

@ -0,0 +1,55 @@
'use strict';
const _ = require('lodash');
const httpStatus = require('http-status');
////////////////////////////////////////////////////////////////////////////////
// PRIVATE FUNCTIONS
////////////////////////////////////////////////////////////////////////////////
function buildErrorLog(err) {
let errorLog;
if (_.isUndefined(err)) {
errorLog = 'Error not defined';
} else if (!_.isUndefined(err.message)) {
errorLog = err.message;
} else if (!_.isUndefined(err.stack)) {
errorLog = err.stack;
} else {
errorLog = JSON.stringify(err);
}
return errorLog;
}
function buildErrorResponse(nameController, nameMethod, error) {
const errorDescription = buildErrorLog(error);
const jsonResultFailed = {
code: httpStatus.INTERNAL_SERVER_ERROR,
message: 'Internal Server Error',
description: `Internal Application Error in ${nameController}:${nameMethod}. ${errorDescription}`
}
return jsonResultFailed;
}
////////////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS
////////////////////////////////////////////////////////////////////////////////
function handleErrorResponse(controllerName, methodName, error, res) {
const jsonResultFailed = buildErrorResponse(controllerName, methodName, error);
res.status(httpStatus.INTERNAL_SERVER_ERROR).send(jsonResultFailed);
}
////////////////////////////////////////////////////////////////////////////////
// MODULE EXPORTS
////////////////////////////////////////////////////////////////////////////////
module.exports = {
handleErrorResponse,
// for testing
buildErrorLog,
buildErrorResponse
};

37
helpers/message.helper.js Normal file
View File

@ -0,0 +1,37 @@
'use strict';
////////////////////////////////////////////////////////////////////////////////
// CONSTANTS
////////////////////////////////////////////////////////////////////////////////
const TITLE_ERROR = 'error';
const TITLE_MESSAGE = 'message';
////////////////////////////////////////////////////////////////////////////////
// PRIVATE FUNCTIONS
////////////////////////////////////////////////////////////////////////////////
function buildGenericMessage(nameMessage, textMessage) {
const jsonMessageResult = {};
jsonMessageResult[nameMessage] = textMessage;
return jsonMessageResult;
}
////////////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS
////////////////////////////////////////////////////////////////////////////////
function buildErrorMessage(text) {
return buildGenericMessage(TITLE_ERROR, text)
}
function buildMessage(text) {
return buildGenericMessage(TITLE_MESSAGE, text)
}
module.exports = {
buildErrorMessage,
buildMessage,
//For testing
buildGenericMessage
}

111
helpers/security.helper.js Normal file
View File

@ -0,0 +1,111 @@
const fs = require('fs');
const path = require('path');
const jwt = require('jsonwebtoken');
const bCrypt = require('bcrypt');
const config = require('../config');
const privateKEY = fs.readFileSync(path.join(__dirname, '..', 'private.key'), 'utf8');
const publicKEY = fs.readFileSync(path.join(__dirname, '..', 'public.key'), 'utf8');
const signOptions = {
issuer: 'Fundación LQDVI',
subject: 'info@loquedeverdadimporta.org',
audience: 'htts://www.loquedeverdadimporta.org',
};
const _genSalt = (rounds = 10) => {
return new Promise((resolve, reject) => {
bCrypt.genSalt(rounds, function (err, salt) {
if (err) return reject(err);
return resolve(salt);
});
});
}
const _hashPassword = (password, salt) => {
return new Promise((resolve, reject) => {
bCrypt.hash(password, salt, function (err, hash) {
if (err) return reject(err);
return resolve(hash);
});
});
}
// https://medium.com/@siddharthac6/json-web-token-jwt-the-right-way-of-implementing-with-node-js-65b8915d550e
const _sign = (payload, options) => {
/*
options = {
issuer: "Authorizaxtion/Resource/This server",
subject: "iam@user.me",
audience: "Client_Identity" // this should be provided by client
}
*/
// Token signing options
const signOptions = {
issuer: options.issuer,
subject: options.subject,
audience: options.audience,
expiresIn: config.session.token_expires_in,
algorithm: "RS256"
};
return jwt.sign(payload, privateKEY, signOptions);
}
const _verify = (token, options) => {
/*
options = {
issuer: "Authorization/Resource/This server",
subject: "iam@user.me",
audience: "Client_Identity" // this should be provided by client
}
*/
const verifyOptions = {
issuer: options.issuer,
subject: options.subject,
audience: options.audience,
expiresIn: config.session.token_expires_in,
algorithm: ["RS256"]
};
try {
return jwt.verify(token, publicKEY, verifyOptions);
} catch (err) {
return false;
}
}
const _decode = (token) => {
//returns null if token is invalid
return jwt.decode(token, { complete: true });
}
module.exports = {
jwtOptions: {
jwtFromRequest: (req) => ((req && req.headers && req.headers['x-access-token']) ? req.headers['x-access-token'] : null),
secretOrKey: publicKEY,
...signOptions,
},
generateHashPassword: async (password) => {
const salt = await _genSalt();
return _hashPassword(password, salt)
},
isValidPassword: async (password, candidate) => {
return await bCrypt.compareSync(candidate, password);
},
generateToken: (payload) => {
return _sign(payload, signOptions);
},
verify: (token) => {
return _verify(token, signOptions);
}
}

View File

@ -1,26 +0,0 @@
'use strict';
const config = require('../config');
const { logger } = require('../core');
const JwtStrategy = require('passport-jwt').Strategy;
const { ExtractJwt } = require('passport-jwt');
//const User = this.models.User;
const jwtOptions = {
secretOrKey: config.session.secret_token,
jwtFromRequest: ExtractJwt.fromAuthHeaderWithScheme('Bearer'),
};
const jwt = async (payload, done) => {
logger.info(payload);
try {
//const user = await User.findById(payload.sub);
//if (user) return done(null, user);
return done(null, false);
} catch (error) {
return done(error, false);
}
};
module.exports.jwt = new JwtStrategy(jwtOptions, jwt);

View File

@ -0,0 +1,29 @@
'use strict';
const passportJWT = require("passport-jwt");
const JWTStrategy = passportJWT.Strategy;
const ExtractJWT = passportJWT.ExtractJwt;
const config = require('../config');
/**
* Login Required middleware.
*/
exports.isAuthenticated = (req, res, next) => {
if (req.isAuthenticated()) {
return next();
}
res.redirect('/login');
};
/**
* Authorization Required middleware.
*/
exports.isAuthorized = (req, res, next) => {
const provider = req.path.split('/').slice(-1)[0];
const token = req.user.tokens.find(token => token.kind === provider);
if (token) {
next();
} else {
res.redirect(`/auth/${provider}`);
}
};

View File

@ -1,9 +1,9 @@
'use strict'; 'use strict';
const httpStatus = require('http-status'); const httpStatus = require('http-status');
const expressValidation = require('express-validation');
const APIError = require('./APIError'); const APIError = require('./APIError');
/** /**
* Error handler. Send stacktrace only during development * Error handler. Send stacktrace only during development
* @public * @public
@ -28,14 +28,7 @@ exports.handler = handler;
exports.converter = (err, req, res, next) => { exports.converter = (err, req, res, next) => {
let convertedError = err; let convertedError = err;
if (err instanceof expressValidation.ValidationError) { if (!(err instanceof APIError)) {
convertedError = new APIError({
message: 'Error de validación',
errors: err.errors,
status: err.status,
stack: err.stack,
});
} else if (!(err instanceof APIError)) {
convertedError = new APIError({ convertedError = new APIError({
message: err.message, message: err.message,
status: err.status, status: err.status,

View File

@ -0,0 +1,73 @@
const _ = require('lodash');
const Joi = require('joi');
module.exports = (schema, useJoiError = false) => {
// useJoiError determines if we should respond with the base Joi error
// boolean: defaults to false
const _useJoiError = _.isBoolean(useJoiError) && useJoiError;
// enabled HTTP methods for request data validation
const _supportedMethods = ['post', 'put'];
// Joi validation options
const _validationOptions = {
abortEarly: false, // abort after the last validation error
allowUnknown: true, // allow unknown keys that will be ignored
stripUnknown: true // remove unknown keys from the validated data
};
// return the validation middleware
return (req, res, next) => {
const route = req.route.path;
const method = req.method.toLowerCase();
if (_.includes(_supportedMethods, method)) {
// get schema for the current route
const _schema = schema
if (_schema) {
console.log(req.body);
// Validate req.body using the schema and validation options
return Joi.validate(req.body, _schema, _validationOptions, (err, data) => {
if (err) {
// Joi Error
const JoiError = {
status: 'failed',
error: {
original: err._object,
// fetch only message and type from each error
details: _.map(err.details, ({ message, type }) => ({
message: message.replace(/['"]/g, ''),
type
}))
}
};
// Custom Error
const CustomError = {
status: 'failed',
error: 'Invalid request data. Please review request and try again.'
};
// Send back the JSON error response
res.status(422).json(_useJoiError ? JoiError : CustomError);
} else {
// Replace req.body with the data after Joi validation
req.body = data;
next();
}
});
}
}
next();
};
};

View File

@ -0,0 +1,72 @@
'use strict';
const _ = require('lodash');
const httpStatus = require('http-status');
const passport = require('passport');
const controllerHelper = require('../../helpers/controller.helper');
const messageHelper = require('../../helpers/message.helper');
const securityHelper = require('../../helpers/security.helper');
const authService = require('./auth.service');
////////////////////////////////////////////////////////////////////////////////
// CONSTANTS
////////////////////////////////////////////////////////////////////////////////
// Module Name
const MODULE_NAME = '[auth.controller]';
// Error Messages
const NOT_FOUND = 'Videogame not found';
// Success Messages
const VG_CT_VIDEOGAME_DELETED_SUCCESSFULLY = 'Videogame deleted successfully';
////////////////////////////////////////////////////////////////////////////////
// PUBLIC METHODS
////////////////////////////////////////////////////////////////////////////////
async function login(req, res, next) {
try {
passport.authenticate('local', { session: false }, async (error, user, info) => {
try {
if (!user) {
return res.status(httpStatus.NOT_FOUND).json(messageHelper.buildMessage(NOT_FOUND));
}
req.login(user, { session: false }, async (error) => {
if (error) {
return controllerHelper.handleErrorResponse(MODULE_NAME, login.name, error, res);
}
//We don't want to store the sensitive information such as the
//user password in the token so we pick only the email and id
const data = {
id: user.id,
email: user.email
};
//Send back the token to the user
return res.json({
token: securityHelper.generateToken({ user: data }),
user: {
id: data.id,
email: data.email
},
});
});
} catch (error) {
return next(error);
}
})(req, res, next);
} catch (error) {
controllerHelper.handleErrorResponse(MODULE_NAME, login.name, error, res)
}
}
module.exports = {
login,
MODULE_NAME
}

View File

@ -0,0 +1,35 @@
const routes = require('express').Router();
const passport = require('passport');
const authController = require('./auth.controller');
const authValidation = require('./auth.validations');
const securityHelper = require('../../helpers/security.helper');
const SchemaValidator = require('../../middlewares/schemaValidator');
//const postService = require('./post.service')(models.Post);
//const postController = require('./post.controller')(postService);
//const { ModelHandler } = require('sequelize-handlers');
//const postHandler = new ModelHandler(models.Post);
routes.post('/auth',
SchemaValidator(authValidation.login, true),
passport.authenticate('local'),
function (req, res, next) {
const data = {
id: req.user.id,
email: req.user.email
};
return res.json({
token: securityHelper.generateToken(data),
user: data,
});
}
//authController.login
);
routes.get('/pepepe', passport.authenticate('jwt', { session: false }), function (req, res, next) {
res.send(req.user.email);
});
module.exports = routes;

View File

@ -0,0 +1,17 @@
'use strict';
//const JwtHelper = require('../../helpers/jwt.helper');
const models = require('../../core/models');
function findOne(params) {
return models.User.findOne({
where: params
})
}
module.exports = {
findOne,
login: async (params) => {
}
}

View File

@ -0,0 +1,15 @@
const Joi = require('joi');
const LoginInputType = Joi.object().keys({
email: Joi.string().email().required(),
password: Joi.string().required()
});
const LoginOutputType = Joi.object().keys({
token: Joi.string().required()
});
module.exports = {
login: LoginInputType,
};

View File

@ -0,0 +1,60 @@
'use strict';
const { isValidPassword, generateHashPassword } = require('../../helpers/security.helper');
module.exports = function (sequelize, DataTypes) {
const User = sequelize.define('User', {
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true,
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
validate: { isEmail: true }
},
password: {
type: DataTypes.STRING,
allowNull: false,
},
}, {
tableName: 'user',
freezeTableName: true,
timestamps: true,
});
User.associate = function (models) {
/*User.Categories = User.belongsToMany(models.Category, {
through: models.UserCategory,
foreignKey: 'UserId'
});*/
//User.Comments = User.hasMany(models.UserComment, { foreignKey: 'UserId' });
//User.Reactions = User.hasMany(models.UserReaction, { foreignKey: 'UserId' });
//User.User = User.belongsTo(models.User, { foreignKey: 'userId' });
};
User.beforeCreate(async function (model, options) {
const encrypted = await generateHashPassword(model.password)
model.password = encrypted;
return model;
});
// Instance Methods
// InventoryLevel.prototype.someMethod = function () {...}
User.prototype.comparePassword = async function (candidatePassword) {
const user = this;
if (user.password) {
return await isValidPassword(user.password, candidatePassword)
} else {
return false;
}
}
return User;
};

View File

@ -1,16 +1,35 @@
const routes = require('express').Router(); const routes = require('express').Router();
const models = require('../../core/models'); const models = require('../../core/models');
const postService = require('./post.service')(models.Post); //const postService = require('./post.service')(models.Post);
const postController = require('./post.controller')(postService); //const postController = require('./post.controller')(postService);
const { ModelHandler } = require('sequelize-handlers');
const postHandler = new ModelHandler(models.Post);
routes.use((req, res, next) => { routes.use((req, res, next) => {
// here we can access the req.params object and make auth checks // here we can access the req.params object and make auth checks
next(); next();
}); });
routes.get('/posts', function (req, res) { routes.get('/posts', postHandler.query());
data = postController.find(req)
res.status(200).json(data); /*routes.get('/posts', function (req, res) {
postController.find(req).then(data => {
console.log(data);
res.status(200).json(data);
})
});*/
routes.get('/posts/count', function (req, res) {
//res.status(200).json(postController.count(req));
}); });
routes.get('/posts/:id', function (req, res) {
//res.status(200).json(postController.findOne(req));
});
module.exports = routes; module.exports = routes;

View File

@ -9,8 +9,6 @@ module.exports = function (sequelize, DataTypes) {
allowNull: false allowNull: false
}, },
sort: { sort: {
// Se cambia el nombre del campo de 'order' a 'sort' porque 'order' es una palabra reservada SQL y da problemas al generar las consultas SQL.
field: 'order', // <- Chapuza!! El nombre del campo en MySQL es una palabra reservada en SQL.
type: DataTypes.INTEGER, type: DataTypes.INTEGER,
defaultValue: 0, defaultValue: 0,
allowNull: false, allowNull: false,

View File

@ -7,13 +7,6 @@ module.exports = function (sequelize, DataTypes) {
defaultValue: DataTypes.UUIDV4, defaultValue: DataTypes.UUIDV4,
primaryKey: true, primaryKey: true,
}, },
/*userId: {
type: DataTypes.UUID,
allowNull: true,
primaryKey: false,
unique: false,
foreignKey: true
},*/
date: { date: {
type: DataTypes.DATE, type: DataTypes.DATE,
allowNull: false, allowNull: false,
@ -35,7 +28,6 @@ module.exports = function (sequelize, DataTypes) {
tableName: 'post', tableName: 'post',
freezeTableName: true, freezeTableName: true,
timestamps: true, timestamps: true,
updatedAt: false,
}); });
Post.associate = function (models) { Post.associate = function (models) {

View File

@ -19,7 +19,7 @@ module.exports = function (Post) {
* @return {Promise} * @return {Promise}
*/ */
fetchAll: (params) => { fetchAll: async (params) => {
// Convert `params` object to filters compatible with Bookshelf. // Convert `params` object to filters compatible with Bookshelf.
const filters = []; //strapi.utils.models.convertParams('post', params); const filters = []; //strapi.utils.models.convertParams('post', params);
// Select field to populate. // Select field to populate.

View File

@ -22,6 +22,7 @@
"dependencies": { "dependencies": {
"args-list": "^0.3.3", "args-list": "^0.3.3",
"async": "^2.6.2", "async": "^2.6.2",
"bcrypt": "^3.0.6",
"body-parser": "^1.18.3", "body-parser": "^1.18.3",
"buffer": "^5.2.1", "buffer": "^5.2.1",
"cheerio": "^1.0.0-rc.3", "cheerio": "^1.0.0-rc.3",
@ -35,6 +36,7 @@
"helmet": "^3.16.0", "helmet": "^3.16.0",
"http": "^0.0.0", "http": "^0.0.0",
"http-status": "^1.3.2", "http-status": "^1.3.2",
"joi": "^14.3.1",
"lodash": "^4.17.11", "lodash": "^4.17.11",
"method-override": "^3.0.0", "method-override": "^3.0.0",
"moment": "^2.24.0", "moment": "^2.24.0",
@ -42,10 +44,12 @@
"os": "^0.1.1", "os": "^0.1.1",
"passport": "^0.4.0", "passport": "^0.4.0",
"passport-jwt": "^4.0.0", "passport-jwt": "^4.0.0",
"passport-local": "^1.0.0",
"path": "^0.12.7", "path": "^0.12.7",
"pino": "^4.7.1", "pino": "^4.7.1",
"response-time": "^2.3.2", "response-time": "^2.3.2",
"sequelize": "^5.3.5", "sequelize": "^5.3.5",
"sequelize-handlers": "^1.0.7",
"vm": "^0.1.0", "vm": "^0.1.0",
"winston": "^3.2.1" "winston": "^3.2.1"
} }

9
private.key Normal file
View File

@ -0,0 +1,9 @@
-----BEGIN RSA PRIVATE KEY-----
MIIBOQIBAAJBANHIOJ5cQCp97Je11BqiyOrPauRnc26mHFMEua3a35vdNxpkJnLi
2/3JxhBRDGj0buvmQDO3Pc2BbfoV1lJRwF8CAwEAAQJAU7skziBoBA3K1yreA9uh
cgdj1NJtwmZOu9wQ+JF52s2Ryrz+CS7R1NNNa39ujJgVdNv/DbS6RIVmaM1KN69j
gQIhAPARxNtaVhB1d7EVVd9DZqxcjbGoDfnQXULDy9TO4Go/AiEA37PwsP48KZYC
ybGDlSjG8+8utbb6q+Q/SdKk4AON4eECIC42vXachUTV2By2xrkb+H/lsUEp4Mbe
XZWkq3BkAr9xAiA6iv2/ZBlcjLyYChO0cmJ2ri6cMZGycF47tJufYG6+IQIgPEx/
Un8VU2psMTSmRDFp5f3l9Rx39BAghBf7CTqyWSk=
-----END RSA PRIVATE KEY-----

4
public.key Normal file
View File

@ -0,0 +1,4 @@
-----BEGIN PUBLIC KEY-----
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANHIOJ5cQCp97Je11BqiyOrPauRnc26m
HFMEua3a35vdNxpkJnLi2/3JxhBRDGj0buvmQDO3Pc2BbfoV1lJRwF8CAwEAAQ==
-----END PUBLIC KEY-----

View File

@ -203,6 +203,14 @@ base@^0.11.1:
mixin-deep "^1.2.0" mixin-deep "^1.2.0"
pascalcase "^0.1.1" pascalcase "^0.1.1"
bcrypt@^3.0.6:
version "3.0.6"
resolved "https://registry.yarnpkg.com/bcrypt/-/bcrypt-3.0.6.tgz#f607846df62d27e60d5e795612c4f67d70206eb2"
integrity sha512-taA5bCTfXe7FUjKroKky9EXpdhkVvhE5owfxfLYodbrAR1Ul3juLmIQmIQBK4L9a5BuUcE6cqmwT+Da20lF9tg==
dependencies:
nan "2.13.2"
node-pre-gyp "0.12.0"
binary-extensions@^1.0.0: binary-extensions@^1.0.0:
version "1.13.1" version "1.13.1"
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65"
@ -1414,6 +1422,11 @@ hide-powered-by@1.0.0:
resolved "https://registry.yarnpkg.com/hide-powered-by/-/hide-powered-by-1.0.0.tgz#4a85ad65881f62857fc70af7174a1184dccce32b" resolved "https://registry.yarnpkg.com/hide-powered-by/-/hide-powered-by-1.0.0.tgz#4a85ad65881f62857fc70af7174a1184dccce32b"
integrity sha1-SoWtZYgfYoV/xwr3F0oRhNzM4ys= integrity sha1-SoWtZYgfYoV/xwr3F0oRhNzM4ys=
hoek@6.x.x:
version "6.1.3"
resolved "https://registry.yarnpkg.com/hoek/-/hoek-6.1.3.tgz#73b7d33952e01fe27a38b0457294b79dd8da242c"
integrity sha512-YXXAAhmF9zpQbC7LEcREFtXfGq5K1fmd+4PHkBq8NUqmzW3G+Dq10bI/i0KucLRwss3YYFQ0fSfoxBZYiGUqtQ==
hpkp@2.0.0: hpkp@2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/hpkp/-/hpkp-2.0.0.tgz#10e142264e76215a5d30c44ec43de64dee6d1672" resolved "https://registry.yarnpkg.com/hpkp/-/hpkp-2.0.0.tgz#10e142264e76215a5d30c44ec43de64dee6d1672"
@ -1749,6 +1762,13 @@ isarray@1.0.0, isarray@~1.0.0:
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
isemail@3.x.x:
version "3.2.0"
resolved "https://registry.yarnpkg.com/isemail/-/isemail-3.2.0.tgz#59310a021931a9fb06bbb51e155ce0b3f236832c"
integrity sha512-zKqkK+O+dGqevc93KNsbZ/TqTUFd46MwWjYOoMrjIMZ51eU7DtQG3Wmd9SQQT7i7RVnuTPEiYEWHU3MSbxC1Tg==
dependencies:
punycode "2.x.x"
isexe@^2.0.0: isexe@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
@ -1766,6 +1786,15 @@ isobject@^3.0.0, isobject@^3.0.1:
resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8=
joi@^14.3.1:
version "14.3.1"
resolved "https://registry.yarnpkg.com/joi/-/joi-14.3.1.tgz#164a262ec0b855466e0c35eea2a885ae8b6c703c"
integrity sha512-LQDdM+pkOrpAn4Lp+neNIFV3axv1Vna3j38bisbQhETPMANYRbFJFUyOZcOClYvM/hppMhGWuKSFEK9vjrB+bQ==
dependencies:
hoek "6.x.x"
isemail "3.x.x"
topo "3.x.x"
js-tokens@^3.0.2: js-tokens@^3.0.2:
version "3.0.2" version "3.0.2"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
@ -2103,7 +2132,7 @@ mute-stream@0.0.7:
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=
nan@^2.9.2: nan@2.13.2, nan@^2.9.2:
version "2.13.2" version "2.13.2"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.13.2.tgz#f51dc7ae66ba7d5d55e1e6d4d8092e802c9aefe7" resolved "https://registry.yarnpkg.com/nan/-/nan-2.13.2.tgz#f51dc7ae66ba7d5d55e1e6d4d8092e802c9aefe7"
integrity sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw== integrity sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw==
@ -2154,6 +2183,22 @@ node-fetch@^2.3.0:
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.3.0.tgz#1a1d940bbfb916a1d3e0219f037e89e71f8c5fa5" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.3.0.tgz#1a1d940bbfb916a1d3e0219f037e89e71f8c5fa5"
integrity sha512-MOd8pV3fxENbryESLgVIeaGKrdl+uaYhCSSVkjeOb/31/njTpcis5aWfdqgNlHIrKOLRbMnfPINPOML2CIFeXA== integrity sha512-MOd8pV3fxENbryESLgVIeaGKrdl+uaYhCSSVkjeOb/31/njTpcis5aWfdqgNlHIrKOLRbMnfPINPOML2CIFeXA==
node-pre-gyp@0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.12.0.tgz#39ba4bb1439da030295f899e3b520b7785766149"
integrity sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A==
dependencies:
detect-libc "^1.0.2"
mkdirp "^0.5.1"
needle "^2.2.1"
nopt "^4.0.1"
npm-packlist "^1.1.6"
npmlog "^4.0.2"
rc "^1.2.7"
rimraf "^2.6.1"
semver "^5.3.0"
tar "^4"
node-pre-gyp@^0.10.0: node-pre-gyp@^0.10.0:
version "0.10.3" version "0.10.3"
resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.10.3.tgz#3070040716afdc778747b61b6887bf78880b80fc" resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.10.3.tgz#3070040716afdc778747b61b6887bf78880b80fc"
@ -2389,6 +2434,13 @@ passport-jwt@^4.0.0:
jsonwebtoken "^8.2.0" jsonwebtoken "^8.2.0"
passport-strategy "^1.0.0" passport-strategy "^1.0.0"
passport-local@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/passport-local/-/passport-local-1.0.0.tgz#1fe63268c92e75606626437e3b906662c15ba6ee"
integrity sha1-H+YyaMkudWBmJkN+O5BmYsFbpu4=
dependencies:
passport-strategy "1.x.x"
passport-strategy@1.x.x, passport-strategy@^1.0.0: passport-strategy@1.x.x, passport-strategy@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/passport-strategy/-/passport-strategy-1.0.0.tgz#b5539aa8fc225a3d1ad179476ddf236b440f52e4" resolved "https://registry.yarnpkg.com/passport-strategy/-/passport-strategy-1.0.0.tgz#b5539aa8fc225a3d1ad179476ddf236b440f52e4"
@ -2530,6 +2582,11 @@ pump@^3.0.0:
end-of-stream "^1.1.0" end-of-stream "^1.1.0"
once "^1.3.1" once "^1.3.1"
punycode@2.x.x:
version "2.1.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
qs@6.5.2: qs@6.5.2:
version "6.5.2" version "6.5.2"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
@ -2771,6 +2828,13 @@ send@0.16.2:
range-parser "~1.2.0" range-parser "~1.2.0"
statuses "~1.4.0" statuses "~1.4.0"
sequelize-handlers@^1.0.7:
version "1.0.7"
resolved "https://registry.yarnpkg.com/sequelize-handlers/-/sequelize-handlers-1.0.7.tgz#1ef1d81447bdcd608ae42f5855d7fc2d978bf57a"
integrity sha512-fyNPYLSF03mRwy/gyJJOxH7fN3R6os5Ptsj18Gezfpt/T6mLFwOvxHp+dWd6O1KDNBr0JZi4kIj8FuDq55upbw==
dependencies:
lodash "^4.17.11"
sequelize-pool@^1.0.2: sequelize-pool@^1.0.2:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/sequelize-pool/-/sequelize-pool-1.0.2.tgz#89c767882bbdb8a41dac66922ed9820939a5401e" resolved "https://registry.yarnpkg.com/sequelize-pool/-/sequelize-pool-1.0.2.tgz#89c767882bbdb8a41dac66922ed9820939a5401e"
@ -3127,6 +3191,13 @@ to-regex@^3.0.1, to-regex@^3.0.2:
regex-not "^1.0.2" regex-not "^1.0.2"
safe-regex "^1.1.0" safe-regex "^1.1.0"
topo@3.x.x:
version "3.0.3"
resolved "https://registry.yarnpkg.com/topo/-/topo-3.0.3.tgz#d5a67fb2e69307ebeeb08402ec2a2a6f5f7ad95c"
integrity sha512-IgpPtvD4kjrJ7CRA3ov2FhWQADwv+Tdqbsf1ZnPUSAtCJ9e1Z44MmoSGDXGk4IppoZA7jd/QRkNddlLJWlUZsQ==
dependencies:
hoek "6.x.x"
toposort-class@^1.0.1: toposort-class@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/toposort-class/-/toposort-class-1.0.1.tgz#7ffd1f78c8be28c3ba45cd4e1a3f5ee193bd9988" resolved "https://registry.yarnpkg.com/toposort-class/-/toposort-class-1.0.1.tgz#7ffd1f78c8be28c3ba45cd4e1a3f5ee193bd9988"