2018-01-17 19:00:36 +01:00

384 lines
12 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use strict';
const fs = require('fs')
const path = require('path');
const _ = require('lodash');
const request = require('request');
/**
* UsersPermissions.js service
*
* @description: A set of functions similar to controller's actions to avoid code duplication.
*/
module.exports = {
createRole: (role) => {
const appRoles = strapi.plugins['users-permissions'].config.roles;
const highestId = Math.max(...Object.keys(appRoles).map(Number)) + 1;
const newRole = _.pick(role, ['name', 'description', 'permissions']);
_.set(appRoles, highestId.toString(), newRole);
_.forEach(role.users, (user) => {
module.exports.updateUserRole(user, highestId);
});
module.exports.writePermissions(appRoles);
},
deleteRole: async (roleId) => {
const appRoles = strapi.plugins['users-permissions'].config.roles
module.exports.writePermissions(_.omit(appRoles, [roleId]));
const users = await strapi.query('user', 'users-permissions').find(strapi.utils.models.convertParams('user', {
role: roleId
}));
_.forEach(users, (user) => {
module.exports.updateUserRole(user, '1');
});
},
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) => {
const generateActions = (data) => (
Object.keys(data).reduce((acc, key) => {
acc[key] = { enabled: false, policy: '' };
return acc;
}, {}));
const appControllers = Object.keys(strapi.api || {}).reduce((acc, key) => {
acc.controllers[key] = generateActions(strapi.api[key].controllers[key]);
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) || {};
}
acc[key] = Object.keys(strapi.plugins[key].controllers).reduce((obj, k) => {
obj.controllers[k] = generateActions(strapi.plugins[key].controllers[k]);
return obj;
}, initialState);
return acc;
}, {});
const permissions = {
application: {
controllers: appControllers.controllers,
},
};
return _.merge(permissions, pluginsPermissions);;
},
getRole: async (roleId, plugins) => {
const appRoles = strapi.plugins['users-permissions'].config.roles;
appRoles[roleId].users = await strapi.query('user', 'users-permissions').find(strapi.utils.models.convertParams('user', { role: roleId }));
Object.keys(appRoles[roleId].permissions)
.filter(name => name !== 'application')
.map(name => {
appRoles[roleId].permissions[name].information = plugins.find(plugin => plugin.id === name) || {};
});
return appRoles[roleId];
},
getRoles: async () => {
const roles = await strapi.query('role', 'users-permissions').find({ sort: 'name ASC' }, []);
for (let i = 0; i < roles.length; ++i) {
roles[i].nb_users = await strapi.query('user', 'users-permissions').count({ role: roles[i].id || roles[i]._id });
}
return roles;
},
getRoutes: async () => {
const routes = Object.keys(strapi.api || {}).reduce((acc, current) => {
return acc.concat(strapi.api[current].config.routes);
}, []);
const pluginsRoutes = Object.keys(strapi.plugins || {}).reduce((acc, current) => {
acc[current] = strapi.plugins[current].config.routes;
return acc;
}, []);
return _.merge({ application: routes }, pluginsRoutes);
},
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);
});
return acc;
}, []);
// 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);
});
return acc;
}, []);
// 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);
}
if (cb) {
cb();
}
},
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);
},
updateRole: async (roleId, body) => {
const appRoles = strapi.plugins['users-permissions'].config.roles
const updatedRole = _.pick(body, ['name', 'description', 'permissions']);
_.set(appRoles, [roleId], updatedRole);
// TODO:
// - Call request.
// Role.update()
module.exports.writePermissions(appRoles);
const currentUsers = await strapi.query('user', 'users-permissions').find(strapi.utils.models.convertParams('user', {
role: roleId
}));
const userToAdd = _.differenceBy(body.users, currentUsers.toJSON ? currentUsers.toJSON() : currentUsers, 'id');
const userToRemove = _.differenceBy(currentUsers.toJSON ? currentUsers.toJSON() : currentUsers, body.users, 'id');
_.forEach(userToAdd, (user) => {
module.exports.updateUserRole(user, roleId);
});
_.forEach(userToRemove, (user) => {
module.exports.updateUserRole(user, '1');
});
},
updateUserRole: async (user, role) => {
strapi.query('user', 'users-permissions').update({
_id: user._id || user.id,
role: role.toString()
});
},
writeActions: (data) => {
const actionsPath = path.join(strapi.config.appPath, 'plugins', 'users-permissions', 'config', 'actions.json');
try {
// Rewrite actions.json file.
fs.writeFileSync(actionsPath, JSON.stringify({ actions: data }), 'utf8');
// Set value to AST to avoid restart.
_.set(strapi.plugins['users-permissions'], 'config.roles', data);
} catch(err) {
strapi.log.error(err);
}
},
syncSchema: (cb) => {
const Model = strapi.plugins['users-permissions'].models.user;
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,
username text,
email text,
provider text,
role text,
${Model.client === 'pg' ? '"resetPasswordToken"' : 'resetPasswordToken'} text,
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
`);
strapi.stop();
}
resolve();
});
})
.then(() => {
const attributes = _.cloneDeep(Model.attributes);
attributes.id = {
type: Model.client === 'pg' ? 'integer' : 'int'
};
attributes.updated_at = attributes.created_at = {
type: Model.client === 'pg' ? 'timestamp with time zone' : 'timestamp'
};
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};`;
}
resolve();
});
});
};
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
`);
strapi.stop();
}
cb();
});
});
}
};