mirror of
https://github.com/strapi/strapi.git
synced 2025-07-14 04:21:56 +00:00
384 lines
12 KiB
JavaScript
384 lines
12 KiB
JavaScript
'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();
|
||
});
|
||
});
|
||
}
|
||
};
|