diff --git a/packages/strapi-admin/config/routes.json b/packages/strapi-admin/config/routes.json index 7f3d800bc6..36e21beaa8 100644 --- a/packages/strapi-admin/config/routes.json +++ b/packages/strapi-admin/config/routes.json @@ -134,6 +134,14 @@ "config": { "policies": [] } + }, + { + "method": "POST", + "path": "/users", + "handler": "User.create", + "config": { + "policies": [] + } } ] } diff --git a/packages/strapi-admin/controllers/User.js b/packages/strapi-admin/controllers/User.js new file mode 100644 index 0000000000..334f4eb025 --- /dev/null +++ b/packages/strapi-admin/controllers/User.js @@ -0,0 +1,60 @@ +'use strict'; + +const _ = require('lodash'); + +const formatError = error => [ + { messages: [{ id: error.id, message: error.message, field: error.field }] }, +]; + +const findNextMissingField = (obj, requiredFields) => { + for (const field of requiredFields) { + if (!obj[field]) { + return field; + } + } +}; + +module.exports = { + async create(ctx) { + const requiredFields = ['firstname', 'lastname', 'email', 'roles']; + const { body } = ctx.request; + + const missingField = findNextMissingField(body, requiredFields); + + if (missingField !== undefined) { + return ctx.badRequest( + null, + formatError({ + id: `missing.${missingField}`, + message: `Missing ${missingField}`, + field: [missingField], + }) + ); + } + + const requiredAttributes = _.pick(body, requiredFields); + + const userAlreadyExists = await strapi.admin.services.user.exists({ + email: requiredAttributes.email, + }); + + if (userAlreadyExists) { + return ctx.badRequest( + null, + formatError({ + id: 'Auth.form.error.email.taken', + message: 'Email already taken', + field: ['email'], + }) + ); + } + + const createdUser = await strapi.admin.services.user.create({ + ...requiredAttributes, + registrationToken: strapi.admin.services.token.generate(), + }); + + // Send 201 created + ctx.created(strapi.admin.services.auth.sanitizeUser(createdUser)); + }, +}; diff --git a/packages/strapi-admin/models/User.settings.json b/packages/strapi-admin/models/User.settings.json index 44db31a58e..5a1dd5d6c9 100644 --- a/packages/strapi-admin/models/User.settings.json +++ b/packages/strapi-admin/models/User.settings.json @@ -36,7 +36,7 @@ "minLength": 6, "configurable": false, "private": true, - "required": true + "required": false }, "resetPasswordToken": { "type": "string", diff --git a/packages/strapi-admin/services/token.js b/packages/strapi-admin/services/token.js index 8361bc2dc8..f93d9da467 100644 --- a/packages/strapi-admin/services/token.js +++ b/packages/strapi-admin/services/token.js @@ -1,47 +1,9 @@ 'use strict'; -const _ = require('lodash'); -const jwt = require('jsonwebtoken'); - -const defaultOptions = { expiresIn: '30d' }; - -const getTokenOptions = () => { - const { options, secret } = strapi.config.get('server.admin.auth', {}); - - return { - secret, - options: _.merge(defaultOptions, options), - }; -}; - -/** - * Creates a JWT token for an administration user - * @param {object} admon - admin user - */ -const createToken = user => { - const { options, secret } = getTokenOptions(); - - return jwt.sign({ id: user.id }, secret, options); -}; - -/** - * Tries to decode a token an return its payload and if it is valid - * @param {string} token - a token to decode - * @return {Object} decodeInfo - the decoded info - */ -const decodeToken = token => { - const { secret } = getTokenOptions(); - - try { - const payload = jwt.verify(token, secret); - return { payload, isValid: true }; - } catch (err) { - return { payload: null, isValid: false }; - } -}; +const crypto = require('crypto'); module.exports = { - createToken, - getTokenOptions, - decodeToken, + generate() { + return crypto.randomBytes(64).toString('hex'); + }, }; diff --git a/packages/strapi-admin/services/user.js b/packages/strapi-admin/services/user.js index f3a99ef433..247ba0c505 100644 --- a/packages/strapi-admin/services/user.js +++ b/packages/strapi-admin/services/user.js @@ -1,15 +1,20 @@ 'use strict'; -const _ = require('lodash'); - -/** - * Remove private user fields - * @param {Object} user - user to sanitize - */ -const sanitizeUser = user => { - return _.omit(user, ['password', 'resetPasswordToken']); -}; - module.exports = { - sanitizeUser, + withDefaults(attributes) { + return { + roles: [], + isActive: false, + ...attributes, + }; + }, + + async create(attributes) { + const user = this.withDefaults(attributes); + return strapi.query('user', 'admin').create(user); + }, + + async exists(attributes) { + return !!(await strapi.query('user', 'admin').findOne(attributes)); + }, };