606 lines
17 KiB
JavaScript
Raw Normal View History

'use strict';
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 = {
createRole: async params => {
if (!strapi.plugins['content-manager']) {
return new Error(
'This feature requires to install the Content Manager plugin',
);
}
2017-11-27 16:49:56 +01:00
if (!params.type) {
params.type = _.snakeCase(_.deburr(_.toLower(params.name)));
}
2019-04-26 10:17:04 +02:00
const role = await strapi.plugins['users-permissions'].queries('role', 'users-permissions')
.create(_.omit(params, ['users', 'permissions']));
const arrayOfPromises = Object.keys(params.permissions).reduce(
(acc, type) => {
Object.keys(params.permissions[type].controllers).forEach(
controller => {
Object.keys(
params.permissions[type].controllers[controller],
).forEach(action => {
acc.push(
2019-04-26 10:17:04 +02:00
strapi.plugins['users-permissions'].queries('permission', 'users-permissions').addPermission({
role: role._id || role.id,
type,
controller,
action: action.toLowerCase(),
...params.permissions[type].controllers[controller][action],
}),
);
});
},
);
2017-11-27 16:49:56 +01:00
return acc;
},
[],
);
// Use Content Manager business logic to handle relation.
arrayOfPromises.push(
strapi.plugins['content-manager'].services['contentmanager'].edit(
{
id: role._id || role.id,
model: 'role',
},
{
users: params.users,
},
'users-permissions',
),
);
2017-12-01 16:06:16 +01:00
return await Promise.all(arrayOfPromises);
2017-11-27 17:02:45 +01:00
},
2018-03-12 16:37:20 +01:00
deleteRole: async (roleID, publicRoleID) => {
2019-04-26 10:17:04 +02:00
const role = await strapi.plugins['users-permissions'].queries('role', 'users-permissions')
.findOne({ id: roleID }, ['users', 'permissions']);
if (!role) {
throw new Error('Cannot found this role');
}
// Move users to guest role.
const arrayOfPromises = role.users.reduce((acc, user) => {
acc.push(
2019-04-26 10:17:04 +02:00
strapi.plugins['users-permissions'].queries('user', 'users-permissions').update(
{
id: user._id || user.id,
},
{
role: publicRoleID,
},
),
);
return acc;
}, []);
2017-11-27 17:50:51 +01:00
// Remove permissions related to this role.
role.permissions.forEach(permission => {
arrayOfPromises.push(
2019-04-26 10:17:04 +02:00
strapi.plugins['users-permissions'].queries('permission', 'users-permissions').delete({
id: permission._id || permission.id,
}),
);
});
2017-12-01 16:06:16 +01:00
// Delete the role.
arrayOfPromises.push(
2019-04-26 10:17:04 +02:00
strapi.plugins['users-permissions'].queries('role', 'users-permissions').delete({
id: roleID,
}),
);
2017-12-01 16:06:16 +01:00
return await Promise.all(arrayOfPromises);
2017-11-27 16:49:56 +01:00
},
getPlugins: (plugin, lang = 'en') => {
return new Promise(resolve => {
request(
{
uri: `https://marketplace.strapi.io/plugins?lang=${lang}`,
json: true,
headers: {
'cache-control': 'max-age=3600',
},
},
(err, response, body) => {
if (err) {
return resolve([]);
}
resolve(body);
},
);
});
},
getActions: (plugins = [], withInfo = true) => {
const generateActions = data =>
2017-11-17 14:22:59 +01:00
Object.keys(data).reduce((acc, key) => {
if (_.isFunction(data[key])) {
acc[key] = { enabled: false, policy: '' };
}
2017-11-17 14:22:59 +01:00
return acc;
}, {});
2017-11-17 14:22:59 +01:00
const appControllers = Object.keys(strapi.api || {})
.filter(key => !!strapi.api[key].controllers)
.reduce(
(acc, key) => {
Object.keys(strapi.api[key].controllers).forEach(controller => {
acc.controllers[controller] = generateActions(
strapi.api[key].controllers[controller],
);
});
return acc;
},
{ controllers: {} },
);
2017-11-16 17:59:41 +01:00
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;
},
{},
);
2017-11-16 17:59:41 +01:00
const permissions = {
application: {
controllers: appControllers.controllers,
},
2017-11-16 17:59:41 +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) => {
2019-04-26 10:17:04 +02:00
const role = await strapi.plugins['users-permissions'].queries('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
2018-01-22 18:19:44 +01:00
// Group by `type`.
role.permissions = role.permissions.reduce((acc, permission) => {
_.set(
acc,
`${permission.type}.controllers.${permission.controller}.${
permission.action
}`,
{
enabled: _.toNumber(permission.enabled) == true,
policy: permission.policy,
},
);
if (
permission.type !== 'application' &&
!acc[permission.type].information
) {
acc[permission.type].information =
plugins.find(plugin => plugin.id === permission.type) || {};
2018-01-22 18:19:44 +01:00
}
return acc;
}, {});
return role;
2017-11-27 17:50:51 +01:00
},
2017-11-30 12:27:04 +01:00
getRoles: async () => {
2019-04-26 10:17:04 +02:00
const roles = await strapi.plugins['users-permissions'].queries('role', 'users-permissions')
2019-04-09 16:01:01 +02:00
.find({ _sort: 'name' }, []);
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;
2019-04-26 10:17:04 +02:00
roles[i].nb_users = await strapi.plugins['users-permissions'].queries('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) => {
return acc.concat(_.get(strapi.api[current].config, 'routes', []));
}, []);
const clonedPlugins = _.cloneDeep(strapi.plugins);
const pluginsRoutes = Object.keys(clonedPlugins || {}).reduce(
(acc, current) => {
const routes = _.get(
clonedPlugins,
[current, 'config', 'routes'],
[],
).reduce((acc, curr) => {
const prefix = curr.config.prefix;
const path =
prefix !== undefined
? `${prefix}${curr.path}`
: `/${current}${curr.path}`;
_.set(curr, 'path', path);
return acc.concat(curr);
}, []);
acc[current] = routes;
2017-11-30 16:34:43 +01:00
return acc;
},
{},
);
2017-12-07 18:16:18 +01:00
return _.merge({ application: routes }, pluginsRoutes);
2017-11-30 16:34:43 +01:00
},
async updatePermissions() {
// fetch all the current permissions from the database, and format them into an array of actions.
2019-04-26 10:17:04 +02:00
const databasePermissions = await strapi.plugins['users-permissions'].queries('permission', 'users-permissions')
2019-04-09 16:01:01 +02:00
.find({ _limit: -1 });
const actions = databasePermissions.map(
permission =>
`${permission.type}.${permission.controller}.${permission.action}`,
);
// Aggregate first level actions.
const appActions = Object.keys(strapi.api || {}).reduce((acc, api) => {
Object.keys(_.get(strapi.api[api], 'controllers', {})).forEach(
controller => {
const actions = Object.keys(strapi.api[api].controllers[controller])
.filter(action =>
_.isFunction(strapi.api[api].controllers[controller][action]),
)
.map(action => `application.${controller}.${action.toLowerCase()}`);
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).forEach(controller => {
const actions = Object.keys(
strapi.plugins[plugin].controllers[controller],
)
.filter(action =>
_.isFunction(
strapi.plugins[plugin].controllers[controller][action],
),
)
.map(action => `${plugin}.${controller}.${action.toLowerCase()}`);
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 = databasePermissions.length;
// 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 isAdminCallback =
obj.action === 'admincallback' &&
obj.controller === 'auth' &&
obj.type === 'users-permissions' &&
role.type === 'public';
const isAdminRegister =
obj.action === 'adminregister' &&
obj.controller === 'auth' &&
obj.type === 'users-permissions' &&
role.type === 'public';
const isCallback =
obj.action === 'callback' &&
obj.controller === 'auth' &&
obj.type === 'users-permissions' &&
role.type === 'public';
const isConnect =
obj.action === 'connect' &&
obj.controller === 'auth' &&
obj.type === 'users-permissions';
const isPassword =
obj.action === 'forgotpassword' &&
obj.controller === 'auth' &&
obj.type === 'users-permissions' &&
role.type === 'public';
const isRegister =
obj.action === 'register' &&
obj.controller === 'auth' &&
obj.type === 'users-permissions' &&
role.type === 'public';
const isConfirmation =
obj.action === 'emailconfirmation' &&
obj.controller === 'auth' &&
obj.type === 'users-permissions' &&
role.type === 'public';
const isNewPassword =
obj.action === 'changepassword' &&
obj.controller === 'auth' &&
obj.type === 'users-permissions' &&
role.type === 'public';
const isInit =
obj.action === 'init' && obj.controller === 'userspermissions';
const isMe =
obj.action === 'me' &&
obj.controller === 'user' &&
obj.type === 'users-permissions';
const isReload = obj.action === 'autoreload';
const enabled =
isCallback ||
isRegister ||
isInit ||
isPassword ||
isNewPassword ||
isMe ||
isReload ||
isConnect ||
isConfirmation ||
isAdminCallback ||
isAdminRegister;
return Object.assign(obj, { enabled, policy: '' });
};
// Retrieve roles
2019-04-26 10:17:04 +02:00
const roles = await strapi.plugins['users-permissions'].queries('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 =>
2019-04-26 10:17:04 +02:00
strapi.plugins['users-permissions'].queries('permission', 'users-permissions')
.addPermission(
Object.assign(action, { role: role.id || role._id }),
),
),
),
)
.concat([
Promise.all(
toRemove.map(action =>
2019-04-26 10:17:04 +02:00
strapi.plugins['users-permissions'].queries('permission', 'users-permissions')
.removePermission(action),
),
),
]),
);
2017-11-17 14:22:59 +01:00
}
2017-11-17 16:36:57 +01:00
},
2017-11-17 14:22:59 +01:00
removeDuplicate: async function() {
2019-04-26 10:17:04 +02:00
const primaryKey = strapi.plugins['users-permissions'].queries('permission', 'users-permissions')
.primaryKey;
// Retrieve permissions by creation date (ID or ObjectID).
2019-04-26 10:17:04 +02:00
const permissions = await strapi.plugins['users-permissions'].queries('permission', 'users-permissions')
.find({
2019-04-09 16:01:01 +02:00
_sort: `${primaryKey}`,
_limit: -1,
});
2018-05-02 11:40:42 +02:00
const value = permissions.reduce(
(acc, permission) => {
const index = acc.toKeep.findIndex(
element =>
element ===
`${permission.type}.controllers.${permission.controller}.${
permission.action
}.${permission.role[primaryKey]}`,
);
if (index === -1) {
acc.toKeep.push(
`${permission.type}.controllers.${permission.controller}.${
permission.action
}.${permission.role[primaryKey]}`,
);
} else {
acc.toRemove.push(permission[primaryKey]);
}
2018-05-02 11:40:42 +02:00
return acc;
},
{
toKeep: [],
toRemove: [],
},
);
2018-05-02 11:40:42 +02:00
2019-04-26 10:17:04 +02:00
return strapi.plugins['users-permissions'].queries('permission', 'users-permissions').deleteMany({
[primaryKey]: value.toRemove,
2018-05-02 11:40:42 +02:00
});
2017-11-17 16:36:57 +01:00
},
2017-11-17 14:22:59 +01:00
async initialize(cb) {
2019-04-26 10:17:04 +02:00
const roleCount = await strapi.plugins['users-permissions'].queries('role', 'users-permissions').count();
2018-05-23 14:25:32 +02:00
// It has already been initialized.
if (roleCount > 0) {
try {
await this.updatePermissions();
2018-05-23 14:25:32 +02:00
await this.removeDuplicate();
return cb();
} catch (err) {
return cb(err);
}
}
// Create two first default roles.
await Promise.all([
2019-04-26 10:17:04 +02:00
strapi.plugins['users-permissions'].queries('role', 'users-permissions').create({
name: 'Authenticated',
description: 'Default role given to authenticated user.',
type: 'authenticated',
}),
2019-04-26 10:17:04 +02:00
strapi.plugins['users-permissions'].queries('role', 'users-permissions').create({
2018-03-12 16:37:20 +01:00
name: 'Public',
description: 'Default role given to unauthenticated user.',
type: 'public',
}),
]);
this.updatePermissions().then(() => cb(), err => cb(err));
},
updateRole: async function(roleID, body) {
const [role, authenticated] = await Promise.all([
2018-01-22 18:19:44 +01:00
this.getRole(roleID, []),
2019-04-26 10:17:04 +02:00
strapi.plugins['users-permissions'].queries('role', 'users-permissions')
.findOne({ type: 'authenticated' }, []),
2018-01-22 18:19:44 +01:00
]);
2017-11-27 17:50:51 +01:00
const arrayOfPromises = Object.keys(body.permissions).reduce(
(acc, type) => {
Object.keys(body.permissions[type].controllers).forEach(controller => {
Object.keys(body.permissions[type].controllers[controller]).forEach(
action => {
const bodyAction =
body.permissions[type].controllers[controller][action];
const currentAction = _.get(
role.permissions,
`${type}.controllers.${controller}.${action}`,
{},
);
if (_.differenceWith([bodyAction], [currentAction]).length > 0) {
acc.push(
2019-04-26 10:17:04 +02:00
strapi.plugins['users-permissions'].queries('permission', 'users-permissions').update(
{
role: roleID,
type,
controller,
action: action.toLowerCase(),
},
bodyAction,
),
);
}
},
);
2018-01-22 18:19:44 +01:00
});
return acc;
},
[],
);
arrayOfPromises.push(
2019-04-26 10:17:04 +02:00
strapi.plugins['users-permissions'].queries('role', 'users-permissions').update(
{
id: roleID,
},
_.pick(body, ['name', 'description']),
),
);
2018-02-24 17:42:33 +01:00
// stringify mongoDB _id for add/remove matching
if (role._id ? '_id' : 'id' === '_id') {
role.users.reduce((acc, user) => {
const key = role._id ? '_id' : 'id';
user[key] = user[key].toString();
acc.push(user);
return acc;
}, []);
}
2018-01-22 18:19:44 +01:00
// Add user to this role.
_.differenceBy(body.users, role.users, role._id ? '_id' : 'id').forEach(
user => {
arrayOfPromises.push(this.updateUserRole(user, roleID));
},
);
2017-12-05 16:44:54 +01:00
// Remove user to this role and link him to authenticated.
_.differenceBy(role.users, body.users, role._id ? '_id' : 'id').forEach(
user => {
arrayOfPromises.push(
this.updateUserRole(user, authenticated._id || authenticated.id),
);
},
);
2018-01-22 18:19:44 +01:00
return Promise.all(arrayOfPromises);
2017-11-27 17:50:51 +01:00
},
2017-12-07 10:16:36 +01:00
updateUserRole: async (user, role) => {
2019-04-26 10:17:04 +02:00
return strapi.plugins['users-permissions'].queries('user', 'users-permissions').update(
{
id: user._id || user.id,
},
{
role: role.toString(),
},
);
2017-12-07 10:16:36 +01:00
},
2018-01-25 08:38:46 +01:00
template: (layout, data) => {
const compiledObject = _.template(layout);
return compiledObject(data);
},
};