388 lines
12 KiB
JavaScript
Raw Normal View History

'use strict';
2017-11-17 14:22:59 +01:00
const fs = require('fs')
const path = require('path');
2017-11-16 17:59:41 +01:00
const _ = require('lodash');
const request = require('request');
2017-12-07 18:16:18 +01:00
/**
* UsersPermissions.js service
*
* @description: A set of functions similar to controller's actions to avoid code duplication.
*/
module.exports = {
2017-11-27 16:49:56 +01:00
createRole: (role) => {
2017-12-07 18:16:18 +01:00
const appRoles = strapi.plugins['users-permissions'].config.roles;
2017-12-08 14:40:42 +01:00
const highestId = Math.max(...Object.keys(appRoles).map(Number)) + 1;
2017-11-27 16:49:56 +01:00
const newRole = _.pick(role, ['name', 'description', 'permissions']);
2017-11-27 17:02:45 +01:00
_.set(appRoles, highestId.toString(), newRole);
2017-11-27 16:49:56 +01:00
2017-12-01 16:06:16 +01:00
_.forEach(role.users, (user) => {
2017-12-08 10:42:45 +01:00
module.exports.updateUserRole(user, highestId);
2017-12-01 16:06:16 +01:00
});
2017-12-08 10:42:45 +01:00
module.exports.writePermissions(appRoles);
2017-11-27 17:02:45 +01:00
},
2017-12-01 16:06:16 +01:00
deleteRole: async (roleId) => {
2017-12-07 18:16:18 +01:00
const appRoles = strapi.plugins['users-permissions'].config.roles
2017-11-27 17:50:51 +01:00
2017-12-08 10:42:45 +01:00
module.exports.writePermissions(_.omit(appRoles, [roleId]));
2017-12-01 16:06:16 +01:00
const users = await strapi.query('user', 'users-permissions').find(strapi.utils.models.convertParams('user', {
role: roleId
}));
_.forEach(users, (user) => {
2017-12-08 10:42:45 +01:00
module.exports.updateUserRole(user, '1');
2017-12-01 16:06:16 +01:00
});
2017-11-27 16:49:56 +01:00
},
getPlugins: (plugin, lang = 'en') => {
return new Promise((resolve, reject) => {
request({
uri: `https://marketplace.strapi.io/plugins?lang=${lang}`,
json: true,
headers: {
'cache-control': 'max-age=3600'
}
}, (err, response, body) => {
if (err) {
return reject(err);
}
resolve(body);
});
});
},
getActions: (plugins = [], withInfo = true) => {
2017-11-17 14:22:59 +01:00
const generateActions = (data) => (
Object.keys(data).reduce((acc, key) => {
acc[key] = { enabled: false, policy: '' };
2017-11-17 14:22:59 +01:00
return acc;
}, {}));
const appControllers = Object.keys(strapi.api || {}).reduce((acc, key) => {
2017-11-17 14:22:59 +01:00
acc.controllers[key] = generateActions(strapi.api[key].controllers[key]);
2017-11-16 17:59:41 +01:00
return acc;
}, { controllers: {} });
const pluginsPermissions = Object.keys(strapi.plugins).reduce((acc, key) => {
const initialState = {
controllers: {}
};
if (withInfo) {
initialState.information = plugins.find(plugin => plugin.id === key) || {};
}
2017-11-20 14:35:24 +01:00
acc[key] = Object.keys(strapi.plugins[key].controllers).reduce((obj, k) => {
2017-11-17 14:22:59 +01:00
obj.controllers[k] = generateActions(strapi.plugins[key].controllers[k]);
return obj;
}, initialState);
return acc;
}, {});
2017-11-16 17:59:41 +01:00
const permissions = {
application: {
controllers: appControllers.controllers,
},
2017-11-16 17:59:41 +01:00
};
2017-12-11 11:14:07 +01:00
return _.merge(permissions, pluginsPermissions);;
2017-11-17 16:36:57 +01:00
},
2017-11-17 14:22:59 +01:00
getRole: async (roleId, plugins) => {
const role = await strapi.query('role', 'users-permissions').findOne({ id: roleId }, ['users', 'permissions']);
if (!role) {
throw new Error('Cannot found this role');
}
2017-11-27 17:50:51 +01:00
// Add `information` key.
role.permissions
.filter(permission => permission.type !== 'application')
.map((permission, index) => {
role.permissions[index].information = plugins.find(plugin => plugin.id === permission.type) || {};
});
return role;
2017-11-27 17:50:51 +01:00
},
2017-11-30 12:27:04 +01:00
getRoles: async () => {
const roles = await strapi.query('role', 'users-permissions').find({ sort: 'name ASC' }, []);
2017-11-27 16:04:57 +01:00
for (let i = 0; i < roles.length; ++i) {
roles[i].id = roles[i].id || roles[i]._id;
roles[i].nb_users = await strapi.query('user', 'users-permissions').count({ role: roles[i].id });
}
2017-11-27 16:04:57 +01:00
return roles;
2017-11-27 16:04:57 +01:00
},
2017-11-30 16:34:43 +01:00
getRoutes: async () => {
const routes = Object.keys(strapi.api || {}).reduce((acc, current) => {
2017-12-07 18:16:18 +01:00
return acc.concat(strapi.api[current].config.routes);
}, []);
2017-12-07 18:16:18 +01:00
const pluginsRoutes = Object.keys(strapi.plugins || {}).reduce((acc, current) => {
2017-11-30 16:34:43 +01:00
acc[current] = strapi.plugins[current].config.routes;
return acc;
2017-12-07 18:16:18 +01:00
}, []);
return _.merge({ application: routes }, pluginsRoutes);
2017-11-30 16:34:43 +01:00
},
updatePermissions: async function (cb) {
const actions = strapi.plugins['users-permissions'].config.actions || [];
// Aggregate first level actions.
const appActions = Object.keys(strapi.api || {}).reduce((acc, api) => {
Object.keys(strapi.api[api].controllers)
.map(controller => {
const actions = Object.keys(strapi.api[api].controllers[controller])
.map(action => `application.${controller}.${action}`);
acc = acc.concat(actions);
2017-11-17 16:36:57 +01:00
});
return acc;
}, []);
2017-11-17 16:36:57 +01:00
// Aggregate plugins' actions.
const pluginsActions = Object.keys(strapi.plugins).reduce((acc, plugin) => {
Object.keys(strapi.plugins[plugin].controllers)
.map(controller => {
const actions = Object.keys(strapi.plugins[plugin].controllers[controller])
.map(action => `${plugin}.${controller}.${action}`);
acc = acc.concat(actions);
});
2017-12-07 10:16:36 +01:00
return acc;
}, []);
2017-11-17 16:36:57 +01:00
// Merge array into one.
const currentActions = appActions.concat(pluginsActions);
// Count permissions available.
const permissions = await strapi.query('permission', 'users-permissions').count();
// Compare to know if actions have been added or removed from controllers.
if (!_.isEqual(actions, currentActions) || permissions < 1) {
const splitted = (str) => {
const [type, controller, action] = str.split('.');
return { type, controller, action };
};
const defaultPolicy = (obj, role) => {
const isCallback = obj.action === 'callback' && obj.controller === 'auth' && obj.type === 'users-permissions' && role.type === 'guest';
const isRegister = obj.action === 'register' && obj.controller === 'auth' && obj.type === 'users-permissions' && role.type === 'guest';
const isPassword = obj.action === 'forgotPassword' && obj.controller === 'auth' && obj.type === 'users-permissions' && role.type === 'guest';
const isNewPassword = obj.action === 'changePassword' && obj.controller === 'auth' && obj.type === 'users-permissions' && role.type === 'guest';
const isInit = obj.action === 'init' && obj.controller === 'userspermissions';
const isMe = obj.action === 'me' && obj.controller === 'user' && obj.type === 'users-permissions';
const enabled = isCallback || isRegister || role.type === 'root' || isInit || isPassword || isNewPassword || isMe;
return Object.assign(obj, { enabled, policy: '' });
};
// Retrieve roles
const roles = await strapi.query('role', 'users-permissions').find();
// We have to know the difference to add or remove
// the permissions entries in the database.
const toRemove = _.difference(actions, currentActions).map(splitted);
const toAdd = (permissions < 1 ? currentActions : _.difference(currentActions, actions))
.map(splitted);
// Execute request to update entries in database for each role.
await Promise.all(
roles.map(role =>
Promise.all(
toAdd
.map(action => defaultPolicy(action, role))
.map(action => strapi.query('permission', 'users-permissions')
.addPermission(Object.assign(action, { role: role.id || role._id }))
)
)
),
Promise.all(toRemove.map(action => strapi.query('permission', 'users-permissions').removePermission(action)))
);
this.writeActions(currentActions);
2017-11-17 14:22:59 +01:00
}
2017-11-20 14:35:24 +01:00
if (cb) {
cb();
}
2017-11-17 16:36:57 +01:00
},
2017-11-17 14:22:59 +01:00
initialize: async function (cb) {
const roles = await strapi.query('role', 'users-permissions').count();
// It's has been already initialized.
if (roles > 0) {
return await this.updatePermissions(cb);
}
// Create two first default roles.
await Promise.all([
strapi.query('role', 'users-permissions').createRole({
name: 'Administrator',
description: 'These users have all access in the project.',
type: 'root'
}),
strapi.query('role', 'users-permissions').createRole({
name: 'Guest',
description: 'Default role given to unauthenticated user.',
type: 'guest'
}),
]);
await this.updatePermissions(cb);
},
2017-12-01 16:06:16 +01:00
updateRole: async (roleId, body) => {
2017-12-07 18:16:18 +01:00
const appRoles = strapi.plugins['users-permissions'].config.roles
2017-11-27 17:50:51 +01:00
const updatedRole = _.pick(body, ['name', 'description', 'permissions']);
_.set(appRoles, [roleId], updatedRole);
// TODO:
// - Call request.
// Role.update()
2017-12-08 10:42:45 +01:00
module.exports.writePermissions(appRoles);
2017-12-01 16:06:16 +01:00
2017-12-05 16:44:54 +01:00
const currentUsers = await strapi.query('user', 'users-permissions').find(strapi.utils.models.convertParams('user', {
2017-12-01 16:06:16 +01:00
role: roleId
2017-12-05 16:44:54 +01:00
}));
2017-12-07 18:40:55 +01:00
const userToAdd = _.differenceBy(body.users, currentUsers.toJSON ? currentUsers.toJSON() : currentUsers, 'id');
const userToRemove = _.differenceBy(currentUsers.toJSON ? currentUsers.toJSON() : currentUsers, body.users, 'id');
2017-12-05 16:44:54 +01:00
_.forEach(userToAdd, (user) => {
2017-12-08 10:42:45 +01:00
module.exports.updateUserRole(user, roleId);
2017-12-05 16:44:54 +01:00
});
_.forEach(userToRemove, (user) => {
2017-12-08 10:42:45 +01:00
module.exports.updateUserRole(user, '1');
2017-12-01 16:06:16 +01:00
});
2017-11-27 17:50:51 +01:00
},
2017-12-07 10:16:36 +01:00
updateUserRole: async (user, role) => {
2017-12-07 18:16:15 +01:00
strapi.query('user', 'users-permissions').update({
2017-12-07 10:16:36 +01:00
_id: user._id || user.id,
role: role.toString()
});
},
writeActions: (data) => {
const actionsPath = path.join(strapi.config.appPath, 'plugins', 'users-permissions', 'config', 'actions.json');
2017-11-17 16:36:57 +01:00
try {
// Rewrite actions.json file.
fs.writeFileSync(actionsPath, JSON.stringify({ actions: data }), 'utf8');
// Set value to AST to avoid restart.
2017-12-12 15:13:27 +01:00
_.set(strapi.plugins['users-permissions'], 'config.roles', data);
2017-11-17 16:36:57 +01:00
} catch(err) {
strapi.log.error(err);
}
2017-12-01 16:06:16 +01:00
},
syncSchema: (cb) => {
const Model = strapi.plugins['users-permissions'].models.user;
2018-01-11 15:00:34 +01:00
if (Model.orm !== 'bookshelf') {
return cb();
}
const tableName = Model.collectionName;
new Promise((resolve, reject) => {
strapi.connections[Model.connection].schema.hasTable(tableName)
.then(exist => {
if (!exist) {
strapi.log.warn(`
TABLE \`${tableName}\` DOESN'T EXIST
1 EXECUTE THE FOLLOWING SQL QUERY
CREATE TABLE "${tableName}" (
id ${Model.client === 'pg' ? 'SERIAL' : 'INT AUTO_INCREMENT'} NOT NULL PRIMARY KEY,
2018-01-11 15:00:34 +01:00
username text,
email text,
provider text,
2018-01-11 15:00:34 +01:00
role text,
${Model.client === 'pg' ? '"resetPasswordToken"' : 'resetPasswordToken'} text,
2018-01-11 15:00:34 +01:00
password text,
updated_at ${Model.client === 'pg' ? 'timestamp with time zone' : 'timestamp'},
created_at ${Model.client === 'pg' ? 'timestamp with time zone' : 'timestamp'}
);
2 RESTART YOUR SERVER
2018-01-11 15:00:34 +01:00
`);
2018-01-11 15:00:34 +01:00
strapi.stop();
}
2018-01-11 15:00:34 +01:00
resolve();
});
2018-01-11 15:00:34 +01:00
})
.then(() => {
const attributes = _.cloneDeep(Model.attributes);
attributes.id = {
type: Model.client === 'pg' ? 'integer' : 'int'
2018-01-11 15:00:34 +01:00
};
attributes.updated_at = attributes.created_at = {
type: Model.client === 'pg' ? 'timestamp with time zone' : 'timestamp'
2018-01-11 15:00:34 +01:00
};
let commands = '';
const columnExist = (description, attribute) => {
return new Promise((resolve, reject) => {
strapi.connections[Model.connection].schema.hasColumn(tableName, attribute)
.then(exist => {
if (!exist) {
if (description.type === 'string') {
description.type = 'text';
}
commands += `\r\nALTER TABLE "${tableName}" ADD ${Model.client === 'pg' ? `"${attribute}"` : `${attribute}`} ${description.type};`;
2018-01-11 15:00:34 +01:00
}
2018-01-11 15:00:34 +01:00
resolve();
});
});
2018-01-11 15:00:34 +01:00
};
2018-01-11 15:00:34 +01:00
const testsColumns = Object.entries(attributes).map(([attribute, description]) => columnExist(description, attribute));
Promise.all(testsColumns)
.then(() => {
if (!_.isEmpty(commands)) {
strapi.log.warn(`
TABLE \`${tableName}\` HAS MISSING COLUMNS
1 EXECUTE THE FOLLOWING SQL QUERIES
${commands}
2 RESTART YOUR SERVER
2018-01-11 15:00:34 +01:00
`);
2018-01-11 15:00:34 +01:00
strapi.stop();
}
2018-01-11 15:00:34 +01:00
cb();
});
2018-01-11 15:00:34 +01:00
});
}
};