diff --git a/.env b/.env new file mode 100644 index 0000000..e69de29 diff --git a/config/environments/development.js b/config/environments/development.js index f9426e1..f9cf7a9 100644 --- a/config/environments/development.js +++ b/config/environments/development.js @@ -9,7 +9,7 @@ module.exports = { session: { 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: { diff --git a/config/environments/production.js b/config/environments/production.js index ba7c13c..58a665c 100644 --- a/config/environments/production.js +++ b/config/environments/production.js @@ -9,7 +9,7 @@ module.exports = { session: { 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: { diff --git a/core/express.js b/core/express.js index a4ef98e..612e84f 100644 --- a/core/express.js +++ b/core/express.js @@ -12,7 +12,7 @@ const passport = require('passport'); const router = require('./router'); const error = require('../middlewares/error'); -const access = require('../middlewares/access'); + /** * Express instance @@ -42,12 +42,14 @@ app.use(methodOverride()); // secure apps by setting various HTTP headers app.use(helmet()); - // enable CORS - Cross Origin Resource Sharing app.use(cors({ exposeHeaders: [ "WWW-Authenticate", - "Server-Authorization" + "Server-Authorization", + "Content-Disposition", + "Content-Type", + "Content-Length" ], maxAge: 31536000, credentials: true, @@ -71,7 +73,7 @@ app.use(cors({ // Access validator app.use(passport.initialize()); -passport.use('jwt', access.jwt); +require('./passport'); // Set routes diff --git a/core/models.js b/core/models.js index fa5a24e..144df7f 100644 --- a/core/models.js +++ b/core/models.js @@ -5,9 +5,7 @@ const Sequelize = require('sequelize'); const config = require('../config'); const log = require('./logger'); - const modulesDir = path.resolve(__dirname + '/../modules/') -const basename = path.basename(__dirname); const globOptions = { cwd: modulesDir, nocase: true, diff --git a/core/passport.js b/core/passport.js new file mode 100644 index 0000000..38496a9 --- /dev/null +++ b/core/passport.js @@ -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); + } +})); + diff --git a/helpers/controller.helper.js b/helpers/controller.helper.js new file mode 100644 index 0000000..b345f45 --- /dev/null +++ b/helpers/controller.helper.js @@ -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 +}; \ No newline at end of file diff --git a/helpers/message.helper.js b/helpers/message.helper.js new file mode 100644 index 0000000..d8a718a --- /dev/null +++ b/helpers/message.helper.js @@ -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 +} \ No newline at end of file diff --git a/helpers/security.helper.js b/helpers/security.helper.js new file mode 100644 index 0000000..37da9ea --- /dev/null +++ b/helpers/security.helper.js @@ -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); + } +} diff --git a/middlewares/access.js b/middlewares/access.js deleted file mode 100644 index ffd1410..0000000 --- a/middlewares/access.js +++ /dev/null @@ -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); \ No newline at end of file diff --git a/middlewares/accessValidator.js b/middlewares/accessValidator.js new file mode 100644 index 0000000..4542fb6 --- /dev/null +++ b/middlewares/accessValidator.js @@ -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}`); + } +}; \ No newline at end of file diff --git a/middlewares/error.js b/middlewares/error.js index 41b4f06..e6dd008 100644 --- a/middlewares/error.js +++ b/middlewares/error.js @@ -1,9 +1,9 @@ 'use strict'; const httpStatus = require('http-status'); -const expressValidation = require('express-validation'); const APIError = require('./APIError'); + /** * Error handler. Send stacktrace only during development * @public @@ -28,14 +28,7 @@ exports.handler = handler; exports.converter = (err, req, res, next) => { let convertedError = err; - if (err instanceof expressValidation.ValidationError) { - convertedError = new APIError({ - message: 'Error de validación', - errors: err.errors, - status: err.status, - stack: err.stack, - }); - } else if (!(err instanceof APIError)) { + if (!(err instanceof APIError)) { convertedError = new APIError({ message: err.message, status: err.status, diff --git a/middlewares/schemaValidator.js b/middlewares/schemaValidator.js new file mode 100644 index 0000000..d5d2753 --- /dev/null +++ b/middlewares/schemaValidator.js @@ -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(); + }; +}; \ No newline at end of file diff --git a/modules/auth/auth.controller.js b/modules/auth/auth.controller.js new file mode 100644 index 0000000..f01b278 --- /dev/null +++ b/modules/auth/auth.controller.js @@ -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 +} \ No newline at end of file diff --git a/modules/auth/auth.routes.js b/modules/auth/auth.routes.js new file mode 100644 index 0000000..a8a740e --- /dev/null +++ b/modules/auth/auth.routes.js @@ -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; \ No newline at end of file diff --git a/modules/auth/auth.service.js b/modules/auth/auth.service.js new file mode 100644 index 0000000..ee178aa --- /dev/null +++ b/modules/auth/auth.service.js @@ -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) => { + + + } +} \ No newline at end of file diff --git a/modules/auth/auth.validations.js b/modules/auth/auth.validations.js new file mode 100644 index 0000000..8b2adfa --- /dev/null +++ b/modules/auth/auth.validations.js @@ -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, +}; diff --git a/modules/auth/user.model.js b/modules/auth/user.model.js new file mode 100644 index 0000000..0cf3fc5 --- /dev/null +++ b/modules/auth/user.model.js @@ -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; +}; \ No newline at end of file diff --git a/modules/blog/blog.routes.js b/modules/blog/blog.routes.js index 91af2ee..e8be15e 100644 --- a/modules/blog/blog.routes.js +++ b/modules/blog/blog.routes.js @@ -1,16 +1,35 @@ const routes = require('express').Router(); const models = require('../../core/models'); -const postService = require('./post.service')(models.Post); -const postController = require('./post.controller')(postService); +//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.use((req, res, next) => { // here we can access the req.params object and make auth checks next(); }); -routes.get('/posts', function (req, res) { - data = postController.find(req) - res.status(200).json(data); +routes.get('/posts', postHandler.query()); + +/*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; \ No newline at end of file diff --git a/modules/blog/category.model.js b/modules/blog/category.model.js index bb01518..4d05880 100755 --- a/modules/blog/category.model.js +++ b/modules/blog/category.model.js @@ -9,8 +9,6 @@ module.exports = function (sequelize, DataTypes) { allowNull: false }, 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, defaultValue: 0, allowNull: false, diff --git a/modules/blog/post.model.js b/modules/blog/post.model.js index 0999271..de22709 100644 --- a/modules/blog/post.model.js +++ b/modules/blog/post.model.js @@ -7,13 +7,6 @@ module.exports = function (sequelize, DataTypes) { defaultValue: DataTypes.UUIDV4, primaryKey: true, }, - /*userId: { - type: DataTypes.UUID, - allowNull: true, - primaryKey: false, - unique: false, - foreignKey: true - },*/ date: { type: DataTypes.DATE, allowNull: false, @@ -35,7 +28,6 @@ module.exports = function (sequelize, DataTypes) { tableName: 'post', freezeTableName: true, timestamps: true, - updatedAt: false, }); Post.associate = function (models) { diff --git a/modules/blog/post.service.js b/modules/blog/post.service.js index b9f30d2..a81b141 100644 --- a/modules/blog/post.service.js +++ b/modules/blog/post.service.js @@ -19,7 +19,7 @@ module.exports = function (Post) { * @return {Promise} */ - fetchAll: (params) => { + fetchAll: async (params) => { // Convert `params` object to filters compatible with Bookshelf. const filters = []; //strapi.utils.models.convertParams('post', params); // Select field to populate. diff --git a/package.json b/package.json index e87778f..7449b67 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "dependencies": { "args-list": "^0.3.3", "async": "^2.6.2", + "bcrypt": "^3.0.6", "body-parser": "^1.18.3", "buffer": "^5.2.1", "cheerio": "^1.0.0-rc.3", @@ -35,6 +36,7 @@ "helmet": "^3.16.0", "http": "^0.0.0", "http-status": "^1.3.2", + "joi": "^14.3.1", "lodash": "^4.17.11", "method-override": "^3.0.0", "moment": "^2.24.0", @@ -42,10 +44,12 @@ "os": "^0.1.1", "passport": "^0.4.0", "passport-jwt": "^4.0.0", + "passport-local": "^1.0.0", "path": "^0.12.7", "pino": "^4.7.1", "response-time": "^2.3.2", "sequelize": "^5.3.5", + "sequelize-handlers": "^1.0.7", "vm": "^0.1.0", "winston": "^3.2.1" } diff --git a/private.key b/private.key new file mode 100644 index 0000000..8c987d9 --- /dev/null +++ b/private.key @@ -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----- \ No newline at end of file diff --git a/public.key b/public.key new file mode 100644 index 0000000..ac83978 --- /dev/null +++ b/public.key @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANHIOJ5cQCp97Je11BqiyOrPauRnc26m +HFMEua3a35vdNxpkJnLi2/3JxhBRDGj0buvmQDO3Pc2BbfoV1lJRwF8CAwEAAQ== +-----END PUBLIC KEY----- \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 1858206..62ea133 100644 --- a/yarn.lock +++ b/yarn.lock @@ -203,6 +203,14 @@ base@^0.11.1: mixin-deep "^1.2.0" 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: version "1.13.1" 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" 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: version "2.0.0" 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" 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: version "2.0.0" 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" 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: version "3.0.2" 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" integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= -nan@^2.9.2: +nan@2.13.2, nan@^2.9.2: version "2.13.2" resolved "https://registry.yarnpkg.com/nan/-/nan-2.13.2.tgz#f51dc7ae66ba7d5d55e1e6d4d8092e802c9aefe7" 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" 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: version "0.10.3" 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" 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: version "1.0.0" 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" 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: version "6.5.2" 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" 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: version "1.0.2" 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" 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: version "1.0.1" resolved "https://registry.yarnpkg.com/toposort-class/-/toposort-class-1.0.1.tgz#7ffd1f78c8be28c3ba45cd4e1a3f5ee193bd9988"