Fix user update and boolean values in DB

This commit is contained in:
Alexandre Bodin 2019-07-16 16:26:53 +02:00
parent c4b7ae55e6
commit 3e53d7c8c2
4 changed files with 292 additions and 246 deletions

View File

@ -643,13 +643,32 @@ module.exports = ({ models, target, plugin = false }, ctx) => {
}, },
]; ];
const jsonFormatter = attributes => { const formatter = attributes => {
Object.keys(attributes).map(key => { Object.keys(attributes).map(key => {
const attr = definition.attributes[key] || {}; const attr = definition.attributes[key] || {};
if (attr.type === 'json') { if (attr.type === 'json') {
attributes[key] = JSON.parse(attributes[key]); 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) { if (event.name.indexOf('collection') !== -1) {
fn = instance => fn = instance =>
instance.models.map(entry => { instance.models.map(entry => {
jsonFormatter(entry.attributes); formatter(entry.attributes);
}); });
} else { } else {
fn = instance => jsonFormatter(instance.attributes); fn = instance => formatter(instance.attributes);
} }
this.on(event.name, instance => { this.on(event.name, instance => {

View File

@ -15,11 +15,13 @@ const _ = require('lodash');
// Array of supported clients. // Array of supported clients.
const CLIENTS = [ const CLIENTS = [
'pg', 'pg',
'mysql', 'mysql2', 'mysql',
'mysql2',
'sqlite3', 'sqlite3',
'mariasql', 'mariasql',
'oracle', 'strong-oracle', 'oracle',
'mssql' 'strong-oracle',
'mssql',
]; ];
/** /**
@ -28,7 +30,6 @@ const CLIENTS = [
module.exports = strapi => { module.exports = strapi => {
const hook = { const hook = {
/** /**
* Default options * Default options
*/ */
@ -36,8 +37,8 @@ module.exports = strapi => {
defaults: { defaults: {
connection: { connection: {
host: 'localhost', host: 'localhost',
charset: 'utf8' charset: 'utf8',
} },
}, },
/** /**
@ -46,148 +47,212 @@ module.exports = strapi => {
initialize: cb => { initialize: cb => {
// For each connection in the config register a new Knex connection. // 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. // Make sure the client is supported.
switch (connection.settings.client) { if (!_.includes(CLIENTS, connection.settings.client)) {
case 'postgre': strapi.log.error(
case 'postgres': 'The client `' +
case 'postgresql': connection.settings.client +
connection.settings.client = 'pg'; '` for the `' +
break; name +
case 'sqlite': '` connection is not supported.'
connection.settings.client = 'sqlite3'; );
break; strapi.stop();
case 'maria': }
case 'mariadb':
connection.settings.client = 'mariasql';
break;
case 'ms':
connection.settings.client = 'mssql';
break;
}
// Make sure the client is supported. // Make sure the client is installed in the application
if (!_.includes(CLIENTS, connection.settings.client)) { // `node_modules` directory.
strapi.log.error('The client `' + connection.settings.client + '` for the `' + name + '` connection is not supported.'); let client;
strapi.stop(); 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 const options = _.defaultsDeep(
// `node_modules` directory. {
let client; client: connection.settings.client,
try { connection: {
client = require(connection.settings.client); host: _.get(connection.settings, 'host'),
} catch (err) { user:
strapi.log.error('The client `' + connection.settings.client + '` is not installed.'); _.get(connection.settings, 'username') ||
strapi.log.error('You can install it with `$ npm install ' + connection.settings.client + ' --save`.'); _.get(connection.settings, 'user'),
strapi.stop(); 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({ if (connection.settings.client !== 'sqlite3') {
client: connection.settings.client, options.pool = {
connection: { min: _.get(connection.options, 'pool.min', 0),
host: _.get(connection.settings, 'host'), max: _.get(connection.options, 'pool.max', 10),
user: _.get(connection.settings, 'username') || _.get(connection.settings, 'user'), acquireTimeoutMillis: _.get(
password: _.get(connection.settings, 'password'), connection.options,
database: _.get(connection.settings, 'database'), 'pool.acquireTimeoutMillis',
charset: _.get(connection.settings, 'charset'), 2000
schema: _.get(connection.settings, 'schema', 'public'), ),
port: _.get(connection.settings, 'port'), createTimeoutMillis: _.get(
socket: _.get(connection.settings, 'socketPath'), connection.options,
ssl: _.get(connection.settings, 'ssl', false), 'pool.createTimeoutMillis',
timezone: _.get(connection.settings, 'timezone', 'utc'), 2000
filename: _.get(connection.settings, 'filename', '.tmp/data.db') ),
}, idleTimeoutMillis: _.get(
debug: _.get(connection.options, 'debug', false), connection.options,
acquireConnectionTimeout: _.get(connection.options, 'acquireConnectionTimeout'), 'pool.idleTimeoutMillis',
migrations: _.get(connection.options, 'migrations'), 30000
useNullAsDefault: _.get(connection.options, 'useNullAsDefault'), ),
}, strapi.config.hook.settings.knex); reapIntervalMillis: _.get(
connection.options,
if (connection.settings.client !== 'sqlite3') { 'pool.reapIntervalMillis',
options.pool = { 1000
min: _.get(connection.options, 'pool.min', 0), ),
max: _.get(connection.options, 'pool.max', 10), createRetryIntervalMillis: _.get(
acquireTimeoutMillis: _.get(connection.options, 'pool.acquireTimeoutMillis', 2000), connection.options,
createTimeoutMillis: _.get(connection.options, 'pool.createTimeoutMillis', 2000), 'pool.createRetryIntervalMillis',
idleTimeoutMillis: _.get(connection.options, 'pool.idleTimeoutMillis', 30000), 200
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();
}; };
break; }
case 'pg':
client.types.setTypeParser(1700, 'text', parseFloat);
if (_.isString(_.get(options.connection, 'schema'))) { // Resolve path to the directory containing the database file.
options.pool = { const fileDirectory = options.connection.filename
min: _.get(connection.options, 'pool.min') || 0, ? path.dirname(
max: _.get(connection.options, 'pool.max') || 10, path.resolve(strapi.config.appPath, options.connection.filename)
afterCreate: (conn, cb) => { )
conn.query(`SET SESSION SCHEMA '${options.connection.schema}';`, (err) => { : '';
cb(err, conn);
}); 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 { break;
delete options.connection.schema; case 'pg':
} client.types.setTypeParser(1700, 'text', parseFloat);
break;
case 'sqlite3':
// Create the directory if it does not exist.
try {
fs.statSync(fileDirectory);
} catch (err) {
fs.mkdirSync(fileDirectory);
}
// Force base directory. if (_.isString(_.get(options.connection, 'schema'))) {
// Note: it removes the warning logs when starting the administration in development mode. options.pool = {
options.connection.filename = path.resolve(strapi.config.appPath, options.connection.filename); 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 // Force base directory.
// .returning() is not supported by sqlite3 and will not have any effect. // Note: it removes the warning logs when starting the administration in development mode.
options.log = { options.connection.filename = path.resolve(
warn: () => {} 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(); cb();
} },
}; };
return hook; return hook;

View File

@ -59,9 +59,10 @@ module.exports = {
* @return {Object} * @return {Object}
*/ */
async findOne(ctx) { async findOne(ctx) {
let data = await strapi.plugins['users-permissions'].services.user.fetch( const { id } = ctx.params;
ctx.params let data = await strapi.plugins['users-permissions'].services.user.fetch({
); id,
});
if (data) { if (data) {
data = sanitizeUser(data); data = sanitizeUser(data);
@ -91,11 +92,11 @@ module.exports = {
if (!username) return ctx.badRequest('missing.username'); if (!username) return ctx.badRequest('missing.username');
if (!password) return ctx.badRequest('missing.password'); if (!password) return ctx.badRequest('missing.password');
const adminsWithSameUsername = await strapi const userWithSameUsername = await strapi
.query('user', 'users-permissions') .query('user', 'users-permissions')
.findOne({ username }); .findOne({ username });
if (adminsWithSameUsername) { if (userWithSameUsername) {
return ctx.badRequest( return ctx.badRequest(
null, null,
ctx.request.admin ctx.request.admin
@ -108,11 +109,11 @@ module.exports = {
} }
if (advanced.unique_email) { if (advanced.unique_email) {
const user = await strapi const userWithSameEmail = await strapi
.query('user', 'users-permissions') .query('user', 'users-permissions')
.findOne({ email }); .findOne({ email });
if (user) { if (userWithSameEmail) {
return ctx.badRequest( return ctx.badRequest(
null, null,
ctx.request.admin ctx.request.admin
@ -126,10 +127,7 @@ module.exports = {
} }
const user = { const user = {
email, ...ctx.request.body,
username,
password,
role,
provider: 'local', provider: 'local',
}; };
@ -160,94 +158,74 @@ module.exports = {
* @return {Object} * @return {Object}
*/ */
async update(ctx) { async update(ctx) {
try { const advancedConfigs = await strapi
const advancedConfigs = await strapi .store({
.store({ environment: '',
environment: '', type: 'plugin',
type: 'plugin', name: 'users-permissions',
name: 'users-permissions', key: 'advanced',
key: 'advanced', })
}) .get();
.get();
if (advancedConfigs.unique_email && ctx.request.body.email) { const { id } = ctx.params;
const users = await strapi.plugins[ const { email, username, password } = ctx.request.body;
'users-permissions'
].services.user.fetchAll({ email: ctx.request.body.email });
if ( if (!email) return ctx.badRequest('missing.email');
users && if (!username) return ctx.badRequest('missing.username');
_.find( if (!password) return ctx.badRequest('missing.password');
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.'
);
}
}
const user = await strapi.plugins[ const userWithSameUsername = await strapi
'users-permissions' .query('user', 'users-permissions')
].services.user.fetch(ctx.params); .findOne({ username });
if (_.get(ctx.request, 'body.password') === user.password) { if (userWithSameUsername && userWithSameUsername.id != id) {
delete ctx.request.body.password; return ctx.badRequest(
}
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(
null, null,
ctx.request.admin ctx.request.admin
? [{ messages: [{ id: error.message, field: error.field }] }] ? adminError({
: error.message 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);
}, },
/** /**

View File

@ -6,8 +6,6 @@
* @description: A set of functions similar to controller's actions to avoid code duplication. * @description: A set of functions similar to controller's actions to avoid code duplication.
*/ */
// Public dependencies.
const _ = require('lodash');
const bcrypt = require('bcryptjs'); const bcrypt = require('bcryptjs');
module.exports = { module.exports = {
@ -39,19 +37,7 @@ module.exports = {
].services.user.hashPassword(values); ].services.user.hashPassword(values);
} }
// Use Content Manager business logic to handle relation. return strapi.query('user', 'users-permissions').update(params, values);
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));
}, },
/** /**
@ -59,9 +45,7 @@ module.exports = {
* @return {Promise} * @return {Promise}
*/ */
fetch(params) { fetch(params) {
return strapi return strapi.query('user', 'users-permissions').findOne(params);
.query('user', 'users-permissions')
.findOne(_.pick(params, ['_id', 'id']));
}, },
/** /**