diff --git a/packages/strapi-hook-bookshelf/lib/mount-models.js b/packages/strapi-hook-bookshelf/lib/mount-models.js index d34e58e77a..2b67e7b332 100644 --- a/packages/strapi-hook-bookshelf/lib/mount-models.js +++ b/packages/strapi-hook-bookshelf/lib/mount-models.js @@ -643,13 +643,32 @@ module.exports = ({ models, target, plugin = false }, ctx) => { }, ]; - const jsonFormatter = attributes => { + const formatter = attributes => { Object.keys(attributes).map(key => { const attr = definition.attributes[key] || {}; if (attr.type === 'json') { attributes[key] = JSON.parse(attributes[key]); } + + if (attr.type === 'boolean') { + if (typeof attributes[key] === 'boolean') { + return; + } + + const strVal = + attributes[key] !== null + ? attributes[key].toString() + : attributes[key]; + + if (strVal === '1') { + attributes[key] = true; + } else if (strVal === '0') { + attributes[key] = false; + } else { + attributes[key] = null; + } + } }); }; @@ -659,10 +678,10 @@ module.exports = ({ models, target, plugin = false }, ctx) => { if (event.name.indexOf('collection') !== -1) { fn = instance => instance.models.map(entry => { - jsonFormatter(entry.attributes); + formatter(entry.attributes); }); } else { - fn = instance => jsonFormatter(instance.attributes); + fn = instance => formatter(instance.attributes); } this.on(event.name, instance => { diff --git a/packages/strapi-hook-knex/lib/index.js b/packages/strapi-hook-knex/lib/index.js index 1763b5794d..1d8bb0373d 100644 --- a/packages/strapi-hook-knex/lib/index.js +++ b/packages/strapi-hook-knex/lib/index.js @@ -15,11 +15,13 @@ const _ = require('lodash'); // Array of supported clients. const CLIENTS = [ 'pg', - 'mysql', 'mysql2', + 'mysql', + 'mysql2', 'sqlite3', 'mariasql', - 'oracle', 'strong-oracle', - 'mssql' + 'oracle', + 'strong-oracle', + 'mssql', ]; /** @@ -28,7 +30,6 @@ const CLIENTS = [ module.exports = strapi => { const hook = { - /** * Default options */ @@ -36,8 +37,8 @@ module.exports = strapi => { defaults: { connection: { host: 'localhost', - charset: 'utf8' - } + charset: 'utf8', + }, }, /** @@ -46,148 +47,212 @@ module.exports = strapi => { initialize: cb => { // For each connection in the config register a new Knex connection. - _.forEach(_.pickBy(strapi.config.connections, {connector: 'strapi-hook-bookshelf'}), (connection, name) => { + _.forEach( + _.pickBy(strapi.config.connections, { + connector: 'strapi-hook-bookshelf', + }), + (connection, name) => { + // Make sure we use the client even if the typo is not the exact one. + switch (connection.settings.client) { + case 'postgre': + case 'postgres': + case 'postgresql': + connection.settings.client = 'pg'; + break; + case 'sqlite': + connection.settings.client = 'sqlite3'; + break; + case 'maria': + case 'mariadb': + connection.settings.client = 'mariasql'; + break; + case 'ms': + connection.settings.client = 'mssql'; + break; + } - // Make sure we use the client even if the typo is not the exact one. - switch (connection.settings.client) { - case 'postgre': - case 'postgres': - case 'postgresql': - connection.settings.client = 'pg'; - break; - case 'sqlite': - connection.settings.client = 'sqlite3'; - break; - case 'maria': - case 'mariadb': - connection.settings.client = 'mariasql'; - break; - case 'ms': - connection.settings.client = 'mssql'; - break; - } + // Make sure the client is supported. + if (!_.includes(CLIENTS, connection.settings.client)) { + strapi.log.error( + 'The client `' + + connection.settings.client + + '` for the `' + + name + + '` connection is not supported.' + ); + strapi.stop(); + } - // Make sure the client is supported. - if (!_.includes(CLIENTS, connection.settings.client)) { - strapi.log.error('The client `' + connection.settings.client + '` for the `' + name + '` connection is not supported.'); - strapi.stop(); - } + // Make sure the client is installed in the application + // `node_modules` directory. + let client; + try { + client = require(connection.settings.client); + } catch (err) { + strapi.log.error( + 'The client `' + + connection.settings.client + + '` is not installed.' + ); + strapi.log.error( + 'You can install it with `$ npm install ' + + connection.settings.client + + ' --save`.' + ); + strapi.stop(); + } - // Make sure the client is installed in the application - // `node_modules` directory. - let client; - try { - client = require(connection.settings.client); - } catch (err) { - strapi.log.error('The client `' + connection.settings.client + '` is not installed.'); - strapi.log.error('You can install it with `$ npm install ' + connection.settings.client + ' --save`.'); - strapi.stop(); - } + const options = _.defaultsDeep( + { + client: connection.settings.client, + connection: { + host: _.get(connection.settings, 'host'), + user: + _.get(connection.settings, 'username') || + _.get(connection.settings, 'user'), + password: _.get(connection.settings, 'password'), + database: _.get(connection.settings, 'database'), + charset: _.get(connection.settings, 'charset'), + schema: _.get(connection.settings, 'schema', 'public'), + port: _.get(connection.settings, 'port'), + socket: _.get(connection.settings, 'socketPath'), + ssl: _.get(connection.settings, 'ssl', false), + timezone: _.get(connection.settings, 'timezone', 'utc'), + filename: _.get( + connection.settings, + 'filename', + '.tmp/data.db' + ), + }, + debug: _.get(connection.options, 'debug', false), + acquireConnectionTimeout: _.get( + connection.options, + 'acquireConnectionTimeout' + ), + migrations: _.get(connection.options, 'migrations'), + useNullAsDefault: _.get(connection.options, 'useNullAsDefault'), + }, + strapi.config.hook.settings.knex + ); - const options = _.defaultsDeep({ - client: connection.settings.client, - connection: { - host: _.get(connection.settings, 'host'), - user: _.get(connection.settings, 'username') || _.get(connection.settings, 'user'), - password: _.get(connection.settings, 'password'), - database: _.get(connection.settings, 'database'), - charset: _.get(connection.settings, 'charset'), - schema: _.get(connection.settings, 'schema', 'public'), - port: _.get(connection.settings, 'port'), - socket: _.get(connection.settings, 'socketPath'), - ssl: _.get(connection.settings, 'ssl', false), - timezone: _.get(connection.settings, 'timezone', 'utc'), - filename: _.get(connection.settings, 'filename', '.tmp/data.db') - }, - debug: _.get(connection.options, 'debug', false), - acquireConnectionTimeout: _.get(connection.options, 'acquireConnectionTimeout'), - migrations: _.get(connection.options, 'migrations'), - useNullAsDefault: _.get(connection.options, 'useNullAsDefault'), - }, strapi.config.hook.settings.knex); - - if (connection.settings.client !== 'sqlite3') { - options.pool = { - min: _.get(connection.options, 'pool.min', 0), - max: _.get(connection.options, 'pool.max', 10), - acquireTimeoutMillis: _.get(connection.options, 'pool.acquireTimeoutMillis', 2000), - createTimeoutMillis: _.get(connection.options, 'pool.createTimeoutMillis', 2000), - idleTimeoutMillis: _.get(connection.options, 'pool.idleTimeoutMillis', 30000), - reapIntervalMillis: _.get(connection.options, 'pool.reapIntervalMillis', 1000), - createRetryIntervalMillis: _.get(connection.options, 'pool.createRetryIntervalMillis', 200), - }; - } - - // Resolve path to the directory containing the database file. - const fileDirectory = options.connection.filename - ? path.dirname(path.resolve(strapi.config.appPath, options.connection.filename)) - : ''; - - switch(options.client) { - case 'mysql': - options.connection.typeCast = (field, next) => { - if (field.type === 'TINY' && field.length === 1) { - return (field.string() === '1'); - } - return next(); + if (connection.settings.client !== 'sqlite3') { + options.pool = { + min: _.get(connection.options, 'pool.min', 0), + max: _.get(connection.options, 'pool.max', 10), + acquireTimeoutMillis: _.get( + connection.options, + 'pool.acquireTimeoutMillis', + 2000 + ), + createTimeoutMillis: _.get( + connection.options, + 'pool.createTimeoutMillis', + 2000 + ), + idleTimeoutMillis: _.get( + connection.options, + 'pool.idleTimeoutMillis', + 30000 + ), + reapIntervalMillis: _.get( + connection.options, + 'pool.reapIntervalMillis', + 1000 + ), + createRetryIntervalMillis: _.get( + connection.options, + 'pool.createRetryIntervalMillis', + 200 + ), }; - break; - case 'pg': - client.types.setTypeParser(1700, 'text', parseFloat); + } - if (_.isString(_.get(options.connection, 'schema'))) { - options.pool = { - min: _.get(connection.options, 'pool.min') || 0, - max: _.get(connection.options, 'pool.max') || 10, - afterCreate: (conn, cb) => { - conn.query(`SET SESSION SCHEMA '${options.connection.schema}';`, (err) => { - cb(err, conn); - }); + // Resolve path to the directory containing the database file. + const fileDirectory = options.connection.filename + ? path.dirname( + path.resolve(strapi.config.appPath, options.connection.filename) + ) + : ''; + + switch (options.client) { + case 'mysql': + options.connection.typeCast = (field, next) => { + if (field.type == 'TINY' && field.length == 1) { + let value = field.string(); + return value ? value == '1' : null; } + return next(); }; - } else { - delete options.connection.schema; - } - break; - case 'sqlite3': - // Create the directory if it does not exist. - try { - fs.statSync(fileDirectory); - } catch (err) { - fs.mkdirSync(fileDirectory); - } + break; + case 'pg': + client.types.setTypeParser(1700, 'text', parseFloat); - // Force base directory. - // Note: it removes the warning logs when starting the administration in development mode. - options.connection.filename = path.resolve(strapi.config.appPath, options.connection.filename); + if (_.isString(_.get(options.connection, 'schema'))) { + options.pool = { + min: _.get(connection.options, 'pool.min') || 0, + max: _.get(connection.options, 'pool.max') || 10, + afterCreate: (conn, cb) => { + conn.query( + `SET SESSION SCHEMA '${options.connection.schema}';`, + err => { + cb(err, conn); + } + ); + }, + }; + } else { + delete options.connection.schema; + } + break; + case 'sqlite3': + // Create the directory if it does not exist. + try { + fs.statSync(fileDirectory); + } catch (err) { + fs.mkdirSync(fileDirectory); + } - // Disable warn log - // .returning() is not supported by sqlite3 and will not have any effect. - options.log = { - warn: () => {} - }; + // Force base directory. + // Note: it removes the warning logs when starting the administration in development mode. + options.connection.filename = path.resolve( + strapi.config.appPath, + options.connection.filename + ); - break; + // Disable warn log + // .returning() is not supported by sqlite3 and will not have any effect. + options.log = { + warn: () => {}, + }; + + break; + } + + // Finally, use the client via `knex`. + // If anyone has a solution to use different paths for `knex` and clients + // please drop us an email at support@strapi.io-- it would avoid the Strapi + // applications to have `knex` as a dependency. + try { + // Try to require from local dependency. + const connection = require('knex')(options); + _.set(strapi, `connections.${name}`, connection); + } catch (err) { + strapi.log.error( + 'Impossible to use the `' + name + '` connection...' + ); + strapi.log.warn( + 'Be sure that your client `' + + name + + '` are in the same node_modules directory' + ); + strapi.log.error(err); + strapi.stop(); + } } - - // Finally, use the client via `knex`. - // If anyone has a solution to use different paths for `knex` and clients - // please drop us an email at support@strapi.io-- it would avoid the Strapi - // applications to have `knex` as a dependency. - try { - // Try to require from local dependency. - const connection = require('knex')(options); - _.set(strapi, `connections.${name}`, connection); - - } catch (err) { - strapi.log.error('Impossible to use the `' + name + '` connection...'); - strapi.log.warn('Be sure that your client `' + name + '` are in the same node_modules directory'); - strapi.log.error(err); - strapi.stop(); - } - }); + ); cb(); - } + }, }; return hook; diff --git a/packages/strapi-plugin-users-permissions/controllers/User.js b/packages/strapi-plugin-users-permissions/controllers/User.js index d4d9625395..b002cb10e5 100644 --- a/packages/strapi-plugin-users-permissions/controllers/User.js +++ b/packages/strapi-plugin-users-permissions/controllers/User.js @@ -59,9 +59,10 @@ module.exports = { * @return {Object} */ async findOne(ctx) { - let data = await strapi.plugins['users-permissions'].services.user.fetch( - ctx.params - ); + const { id } = ctx.params; + let data = await strapi.plugins['users-permissions'].services.user.fetch({ + id, + }); if (data) { data = sanitizeUser(data); @@ -91,11 +92,11 @@ module.exports = { if (!username) return ctx.badRequest('missing.username'); if (!password) return ctx.badRequest('missing.password'); - const adminsWithSameUsername = await strapi + const userWithSameUsername = await strapi .query('user', 'users-permissions') .findOne({ username }); - if (adminsWithSameUsername) { + if (userWithSameUsername) { return ctx.badRequest( null, ctx.request.admin @@ -108,11 +109,11 @@ module.exports = { } if (advanced.unique_email) { - const user = await strapi + const userWithSameEmail = await strapi .query('user', 'users-permissions') .findOne({ email }); - if (user) { + if (userWithSameEmail) { return ctx.badRequest( null, ctx.request.admin @@ -126,10 +127,7 @@ module.exports = { } const user = { - email, - username, - password, - role, + ...ctx.request.body, provider: 'local', }; @@ -160,94 +158,74 @@ module.exports = { * @return {Object} */ async update(ctx) { - try { - const advancedConfigs = await strapi - .store({ - environment: '', - type: 'plugin', - name: 'users-permissions', - key: 'advanced', - }) - .get(); + const advancedConfigs = await strapi + .store({ + environment: '', + type: 'plugin', + name: 'users-permissions', + key: 'advanced', + }) + .get(); - if (advancedConfigs.unique_email && ctx.request.body.email) { - const users = await strapi.plugins[ - 'users-permissions' - ].services.user.fetchAll({ email: ctx.request.body.email }); + const { id } = ctx.params; + const { email, username, password } = ctx.request.body; - if ( - users && - _.find( - users, - user => - (user.id || user._id).toString() !== - (ctx.params.id || ctx.params._id) - ) - ) { - return ctx.badRequest( - null, - ctx.request.admin - ? adminError({ - message: 'Auth.form.error.email.taken', - field: ['email'], - }) - : 'Email is already taken.' - ); - } - } + if (!email) return ctx.badRequest('missing.email'); + if (!username) return ctx.badRequest('missing.username'); + if (!password) return ctx.badRequest('missing.password'); - const user = await strapi.plugins[ - 'users-permissions' - ].services.user.fetch(ctx.params); + const userWithSameUsername = await strapi + .query('user', 'users-permissions') + .findOne({ username }); - if (_.get(ctx.request, 'body.password') === user.password) { - delete ctx.request.body.password; - } - - if ( - _.get(ctx.request, 'body.role', '').toString() === '0' && - (!_.get(ctx.state, 'user.role') || - _.get(ctx.state, 'user.role', '').toString() !== '0') - ) { - delete ctx.request.body.role; - } - - if (ctx.request.body.email && advancedConfigs.unique_email) { - const user = await strapi.query('user', 'users-permissions').findOne({ - email: ctx.request.body.email, - }); - - if ( - user !== null && - (user.id || user._id).toString() !== (ctx.params.id || ctx.params._id) - ) { - return ctx.badRequest( - null, - ctx.request.admin - ? adminError({ - message: 'Auth.form.error.email.taken', - field: ['email'], - }) - : 'Email is already taken.' - ); - } - } - - const data = await strapi.plugins['users-permissions'].services.user.edit( - ctx.params, - ctx.request.body - ); - - // Send 200 `ok` - ctx.send(data); - } catch (error) { - ctx.badRequest( + if (userWithSameUsername && userWithSameUsername.id != id) { + return ctx.badRequest( null, ctx.request.admin - ? [{ messages: [{ id: error.message, field: error.field }] }] - : error.message + ? adminError({ + message: 'Auth.form.error.username.taken', + field: ['username'], + }) + : 'username.alreadyTaken.' ); } + + if (advancedConfigs.unique_email) { + const userWithSameEmail = await strapi + .query('user', 'users-permissions') + .findOne({ email }); + + if (userWithSameEmail && userWithSameEmail.id != id) { + return ctx.badRequest( + null, + ctx.request.admin + ? adminError({ + message: 'Auth.form.error.email.taken', + field: ['email'], + }) + : 'email.alreadyTaken' + ); + } + } + + const user = await strapi.plugins['users-permissions'].services.user.fetch({ + id, + }); + + let updateData = { + ...ctx.request.body, + }; + + if (password === user.password) { + delete ctx.request.body.password; + } + + const data = await strapi.plugins['users-permissions'].services.user.edit( + { id }, + updateData + ); + + ctx.send(data); }, /** diff --git a/packages/strapi-plugin-users-permissions/services/User.js b/packages/strapi-plugin-users-permissions/services/User.js index 3b875406a6..d2cf9806d4 100644 --- a/packages/strapi-plugin-users-permissions/services/User.js +++ b/packages/strapi-plugin-users-permissions/services/User.js @@ -6,8 +6,6 @@ * @description: A set of functions similar to controller's actions to avoid code duplication. */ -// Public dependencies. -const _ = require('lodash'); const bcrypt = require('bcryptjs'); module.exports = { @@ -39,19 +37,7 @@ module.exports = { ].services.user.hashPassword(values); } - // Use Content Manager business logic to handle relation. - if (strapi.plugins['content-manager']) { - params.model = 'user'; - params.id = params._id || params.id; - - return await strapi.plugins['content-manager'].services[ - 'contentmanager' - ].edit(params, values, 'users-permissions'); - } - - return strapi - .query('user', 'users-permissions') - .update(_.assign(params, values)); + return strapi.query('user', 'users-permissions').update(params, values); }, /** @@ -59,9 +45,7 @@ module.exports = { * @return {Promise} */ fetch(params) { - return strapi - .query('user', 'users-permissions') - .findOne(_.pick(params, ['_id', 'id'])); + return strapi.query('user', 'users-permissions').findOne(params); }, /**