From b0a2b61be4ea00aba8a29911d343c42cc4f7cabe Mon Sep 17 00:00:00 2001 From: Jim Laurie Date: Thu, 16 Nov 2017 14:12:03 +0100 Subject: [PATCH] Register new user with hashed password --- packages/strapi-mongoose/lib/index.js | 2 +- .../config/queries/mongoose.js | 6 ++- .../config/routes.json | 9 ++++ .../controllers/Auth.js | 50 +++++++++++++++++++ .../controllers/UsersPermissions.js | 3 +- .../models/User.js | 9 ++-- .../package.json | 5 +- .../services/Jwt.js | 19 +++++++ .../services/User.js | 25 +++++++++- packages/strapi/lib/Strapi.js | 2 +- 10 files changed, 118 insertions(+), 12 deletions(-) create mode 100644 packages/strapi-plugin-users-permissions/services/Jwt.js diff --git a/packages/strapi-mongoose/lib/index.js b/packages/strapi-mongoose/lib/index.js index 9e6eac1d13..a0d5f37e07 100755 --- a/packages/strapi-mongoose/lib/index.js +++ b/packages/strapi-mongoose/lib/index.js @@ -169,7 +169,7 @@ module.exports = function (strapi) { // Parse every registered model. _.forEach(models, (definition, model) => { if (plugin) { - definition.globalId = _.upperFirst(_.camelCase(`${plugin}-${model}`)); + definition.globalId = _.upperFirst(_.camelCase(_.get(strapi.config.hook.settings.mongoose.collections, mongooseUtils.toCollectionName(model)) ? `${plugin}-${model}` : model)); } definition.globalName = _.upperFirst(_.camelCase(definition.globalId)); diff --git a/packages/strapi-plugin-users-permissions/config/queries/mongoose.js b/packages/strapi-plugin-users-permissions/config/queries/mongoose.js index 27877b12d1..c12423039d 100644 --- a/packages/strapi-plugin-users-permissions/config/queries/mongoose.js +++ b/packages/strapi-plugin-users-permissions/config/queries/mongoose.js @@ -24,19 +24,21 @@ module.exports = { create: async function (params) { const entry = await this.create(Object.keys(params.values).reduce((acc, current) => { - if (this._attributes[current].type) { + if (_.get(this._attributes, [current, 'type'])) { acc[current] = params.values[current]; } return acc; }, {})); - return module.exports.update.call(this, { + await module.exports.update.call(this, { [this.primaryKey]: entry[this.primaryKey], values: _.merge({ id: entry[this.primaryKey] }, params.values) }); + + return entry; }, update: async function (params) { diff --git a/packages/strapi-plugin-users-permissions/config/routes.json b/packages/strapi-plugin-users-permissions/config/routes.json index 005ecd8e0a..b2e058b190 100644 --- a/packages/strapi-plugin-users-permissions/config/routes.json +++ b/packages/strapi-plugin-users-permissions/config/routes.json @@ -42,6 +42,15 @@ "prefix": "" } }, + { + "method": "POST", + "path": "/auth/local/register", + "handler": "Auth.register", + "config": { + "policies": [], + "prefix": "" + } + }, { "method": "GET", diff --git a/packages/strapi-plugin-users-permissions/controllers/Auth.js b/packages/strapi-plugin-users-permissions/controllers/Auth.js index 8ca9840a9d..e912ea8df3 100644 --- a/packages/strapi-plugin-users-permissions/controllers/Auth.js +++ b/packages/strapi-plugin-users-permissions/controllers/Auth.js @@ -6,6 +6,8 @@ * @description: A set of functions called "actions" for managing `Auth`. */ +const _ = require('lodash'); + module.exports = { callback: async (ctx) => { const provider = ctx.params.provider || 'local'; @@ -101,5 +103,53 @@ module.exports = { }; } } + }, + + register: async (ctx) => { + const params = _.assign(ctx.request.body, { + provider: 'local' + }); + + // Password is required. + if (!params.password) { + ctx.status = 400; + return ctx.body = { + message: 'Invalid password field.' + }; + } + + // Throw an error if the password selected by the user + // contains more than two times the symbol '$'. + if (strapi.plugins['users-permissions'].services.user.isHashed(params.password)) { + ctx.status = 400; + return ctx.body = { + message: 'Your password can not contain more than three times the symbol `$`.' + }; + } + + // First, check if the user is the first one to register. + try { + const usersCount = await strapi.query('user', 'users-permissions').count(); + + // Check if the user is the first to register + if (usersCount === 0) { + params.admin = true; + } + + const user = await strapi.query('user', 'users-permissions').create({ + values: params + }); + + ctx.status = 200; + ctx.body = { + jwt: strapi.plugins['users-permissions'].services.jwt.issue(user), + user: user + }; + } catch (err) { + ctx.status = 500; + return ctx.body = { + message: err.message + }; + } } }; diff --git a/packages/strapi-plugin-users-permissions/controllers/UsersPermissions.js b/packages/strapi-plugin-users-permissions/controllers/UsersPermissions.js index ccc5352897..c10fc83c71 100644 --- a/packages/strapi-plugin-users-permissions/controllers/UsersPermissions.js +++ b/packages/strapi-plugin-users-permissions/controllers/UsersPermissions.js @@ -1,13 +1,12 @@ 'use strict'; -const fakeData = require('../config/fakeData.json'); -const _ = require('lodash'); /** * UsersPermissions.js controller * * @description: A set of functions called "actions" of the `users-permissions` plugin. */ +const fakeData = require('../config/fakeData.json'); const _ = require('lodash'); module.exports = { diff --git a/packages/strapi-plugin-users-permissions/models/User.js b/packages/strapi-plugin-users-permissions/models/User.js index e308fd2fc2..6bab16ed6f 100644 --- a/packages/strapi-plugin-users-permissions/models/User.js +++ b/packages/strapi-plugin-users-permissions/models/User.js @@ -46,10 +46,11 @@ module.exports = { // Before creating a value. // Fired before `insert` query. - // beforeCreate: function (next) { - // // Use `this` to get your current object - // next(); - // }, + beforeCreate: async function (next) { + // Use `this` to get your current object + this.password = await strapi.plugins['users-permissions'].services.user.hashPassword(this); + next(); + }, // After creating a value. // Fired after `insert` query. diff --git a/packages/strapi-plugin-users-permissions/package.json b/packages/strapi-plugin-users-permissions/package.json index 47bcc8fb10..d90d9724bc 100644 --- a/packages/strapi-plugin-users-permissions/package.json +++ b/packages/strapi-plugin-users-permissions/package.json @@ -23,7 +23,10 @@ "prepublish": "npm run build", "postinstall": "node node_modules/strapi-helper-plugin/lib/internals/scripts/postinstall.js" }, - "dependencies": {}, + "dependencies": { + "bcryptjs": "^2.4.3", + "jsonwebtoken": "^8.1.0" + }, "devDependencies": { "cross-env": "^5.1.1", "eslint": "^4.11.0", diff --git a/packages/strapi-plugin-users-permissions/services/Jwt.js b/packages/strapi-plugin-users-permissions/services/Jwt.js new file mode 100644 index 0000000000..65274c56c5 --- /dev/null +++ b/packages/strapi-plugin-users-permissions/services/Jwt.js @@ -0,0 +1,19 @@ +'use strict'; + +/** + * Jwt.js service + * + * @description: A set of functions similar to controller's actions to avoid code duplication. + */ + +const _ = require('lodash'); +const jwt = require('jsonwebtoken'); + +module.exports = { + issue: (payload) => { + return jwt.sign( + _.clone(payload.toJSON()), + process.env.JWT_SECRET || _.get(strapi, 'api.user.config.jwtSecret') || 'oursecret' + ); + } +}; diff --git a/packages/strapi-plugin-users-permissions/services/User.js b/packages/strapi-plugin-users-permissions/services/User.js index 4585542af7..528a0dea1a 100644 --- a/packages/strapi-plugin-users-permissions/services/User.js +++ b/packages/strapi-plugin-users-permissions/services/User.js @@ -8,6 +8,7 @@ // Public dependencies. const _ = require('lodash'); +const bcrypt = require('bcryptjs'); module.exports = { @@ -90,5 +91,27 @@ module.exports = { }); return data; - } + }, + + hashPassword: function (user) { + return new Promise((resolve) => { + user = user.toJSON(); + + if (!user.hasOwnProperty('password') || !user.password || this.isHashed(user.password)) { + resolve(null); + } else { + bcrypt.hash(user.password, 10, (err, hash) => { + resolve(hash) + }); + } + }); + }, + + isHashed: (password) => { + if (typeof password !== 'string' || !password) { + return false; + } + + return password.split('$').length === 4; + }, }; diff --git a/packages/strapi/lib/Strapi.js b/packages/strapi/lib/Strapi.js index 77a0a0bdd7..49f26bb2f3 100755 --- a/packages/strapi/lib/Strapi.js +++ b/packages/strapi/lib/Strapi.js @@ -255,7 +255,7 @@ class Strapi extends EventEmitter { const model = entity.toLowerCase(); - const Model = get(strapi, ['models', model]) || get(strapi.plugins, [plugin, 'models', model]) || undefined; + const Model = get(strapi.plugins, [plugin, 'models', model]) || get(strapi, ['models', model]) || undefined; if (!Model) { return this.log.error(`The model ${model} can't be found.`);