feat: Admin roles (wip)

pull/25/head
Boaz Poolman 2021-10-15 14:54:58 +02:00
parent 52dd4b4f55
commit 74498e6d1c
9 changed files with 4629 additions and 148 deletions

View File

@ -16,8 +16,8 @@
"test:unit": "jest --verbose" "test:unit": "jest --verbose"
}, },
"dependencies": { "dependencies": {
"redux-thunk": "^2.3.0", "react-diff-viewer": "^3.1.1",
"react-diff-viewer": "^3.1.1" "redux-thunk": "^2.3.0"
}, },
"author": { "author": {
"name": "Boaz Poolman", "name": "Boaz Poolman",
@ -39,6 +39,7 @@
"strapi-server.js" "strapi-server.js"
], ],
"devDependencies": { "devDependencies": {
"@strapi/admin": "^4.0.0-beta.2",
"@strapi/helper-plugin": "4.0.0-beta.2", "@strapi/helper-plugin": "4.0.0-beta.2",
"@strapi/icons": "^0.0.1-alpha.42", "@strapi/icons": "^0.0.1-alpha.42",
"@strapi/parts": "^0.0.1-alpha.42", "@strapi/parts": "^0.0.1-alpha.42",

View File

@ -2,12 +2,13 @@
module.exports = { module.exports = {
default: { default: {
destination: "extensions/config-sync/files/", destination: "src/extensions/config-sync/files/",
minify: false, minify: false,
importOnBootstrap: false, importOnBootstrap: false,
include: [ include: [
"core-store", "core-store",
"role-permissions", "user-role",
"admin-role",
"i18n-locale", "i18n-locale",
], ],
exclude: [ exclude: [

View File

@ -94,8 +94,8 @@ module.exports = () => ({
const fileConfigs = {}; const fileConfigs = {};
await Promise.all(configFiles.map(async (file) => { await Promise.all(configFiles.map(async (file) => {
const type = file.split('.')[0].replace('##', '::'); // Grab the first part of the filename. const type = file.split('.')[0]; // Grab the first part of the filename.
const name = file.split(/\.(.+)/)[1].split('.').slice(0, -1).join('.').replace('##', '::'); // Grab the rest of the filename minus the file extension. const name = file.split(/\.(.+)/)[1].split('.').slice(0, -1).join('.'); // Grab the rest of the filename minus the file extension.
if ( if (
configType && configType !== type configType && configType !== type

View File

@ -1,15 +1,15 @@
const { logMessage } = require('../utils'); const { logMessage, sanitizeConfig } = require('../utils');
const difference = require('../utils/getObjectDiff'); const difference = require('../utils/getObjectDiff');
const ConfigType = class ConfigType { const ConfigType = class ConfigType {
constructor(queryString, configPrefix, uid, fieldsToStringify) { constructor(queryString, configPrefix, uid, jsonFields) {
if (!queryString) { if (!queryString) {
strapi.log.error(logMessage('Query string is missing for ConfigType')); strapi.log.error(logMessage('Query string is missing for ConfigType'));
} }
this.queryString = queryString; this.queryString = queryString;
this.configPrefix = configPrefix; this.configPrefix = configPrefix;
this.uid = uid; this.uid = uid;
this.fieldsToStringify = fieldsToStringify || []; this.jsonFields = jsonFields || [];
} }
/** /**
@ -48,7 +48,7 @@ const ConfigType = class ConfigType {
) { ) {
await strapi.plugin('config-sync').service('main').deleteConfigFile(configName); await strapi.plugin('config-sync').service('main').deleteConfigFile(configName);
} else { } else {
await strapi.plugin('config-sync').service('main').writeConfigFile(this.configPrefix, currentConfig[this.uid].replace('::', '##'), currentConfig); await strapi.plugin('config-sync').service('main').writeConfigFile(this.configPrefix, currentConfig[this.uid], currentConfig);
} }
})); }));
} }
@ -68,7 +68,7 @@ const ConfigType = class ConfigType {
const queryAPI = strapi.query(this.queryString); const queryAPI = strapi.query(this.queryString);
const configExists = await queryAPI const configExists = await queryAPI
.findOne({ [this.uid]: configName }); .findOne({ where: { [this.uid]: configName } });
if (configExists && configContent === null) { if (configExists && configContent === null) {
await queryAPI.delete({ where: { [this.uid]: configName } }); await queryAPI.delete({ where: { [this.uid]: configName } });
@ -78,11 +78,11 @@ const ConfigType = class ConfigType {
if (!configExists) { if (!configExists) {
const query = { ...configContent }; const query = { ...configContent };
this.fieldsToStringify.map((field) => query[field] = JSON.stringify(configContent[field])); this.jsonFields.map((field) => query[field] = JSON.stringify(configContent[field]));
await queryAPI.create(query); await queryAPI.create({ data: query });
} else { } else {
const query = { ...configContent }; const query = { ...configContent };
this.fieldsToStringify.map((field) => query[field] = JSON.stringify(configContent[field])); this.jsonFields.map((field) => query[field] = JSON.stringify(configContent[field]));
await queryAPI.update({ where: { [this.uid]: configName }, data: { ...query } }); await queryAPI.update({ where: { [this.uid]: configName }, data: { ...query } });
} }
} }
@ -101,11 +101,10 @@ const ConfigType = class ConfigType {
const shouldExclude = strapi.config.get('plugin.config-sync.exclude').includes(`${this.configPrefix}.${config[this.uid]}`); const shouldExclude = strapi.config.get('plugin.config-sync.exclude').includes(`${this.configPrefix}.${config[this.uid]}`);
if (shouldExclude) return; if (shouldExclude) return;
// Do not export the _id field, as it is immutable config = sanitizeConfig(config);
delete config._id;
const formattedObject = { ...config }; const formattedObject = { ...config };
this.fieldsToStringify.map((field) => formattedObject[field] = JSON.parse(config[field])); this.jsonFields.map((field) => formattedObject[field] = JSON.parse(config[field]));
configs[`${this.configPrefix}.${config[this.uid]}`] = formattedObject; configs[`${this.configPrefix}.${config[this.uid]}`] = formattedObject;
}); });

View File

@ -0,0 +1,78 @@
const { assignPermissions } = require('@strapi/admin/server/services/role');
const { sanitizeConfig } = require('../utils');
const ConfigType = require("../services/type");
const AdminRolePermissionsConfigType = class AdminRolePermissionsConfigType extends ConfigType {
/**
* Import a single role-permissions config file into the db.
*
* @param {string} configName - The name of the config file.
* @param {string} configContent - The JSON content of the config file.
* @returns {void}
*/
importSingle = async (configName, configContent) => {
// Check if the config should be excluded.
const shouldExclude = strapi.config.get('plugin.config-sync.exclude').includes(`${this.configPrefix}.${configName}`);
if (shouldExclude) return;
const queryAPI = strapi.query(this.queryString);
const existingConfig = await queryAPI
.findOne({ where: { [this.uid]: configName } });
if (existingConfig && configContent === null) {
await queryAPI.delete({ where: { [this.uid]: configName } });
return;
}
if (!existingConfig) {
const { permissions } = configContent;
delete configContent.permissions;
const query = { ...configContent };
this.jsonFields.map((field) => query[field] = JSON.stringify(configContent[field]));
const newConfig = await queryAPI.create({ data: query });
await assignPermissions(newConfig.id, permissions);
} else {
await assignPermissions(existingConfig.id, configContent.permissions);
delete configContent.permissions;
const query = { ...configContent };
this.jsonFields.map((field) => query[field] = JSON.stringify(configContent[field]));
await queryAPI.update({ where: { [this.uid]: configName }, data: query });
}
}
/**
* Get all role-permissions config from the db.
*
* @returns {object} Object with key value pairs of configs.
*/
getAllFromDatabase = async () => {
const AllConfig = await strapi.query(this.queryString).findMany({ limit: 0 });
const configs = {};
await Promise.all(Object.values(AllConfig).map(async (config) => {
// Check if the config should be excluded.
const shouldExclude = strapi.config.get('plugin.config-sync.exclude').includes(`${this.configPrefix}.${config[this.uid]}`);
if (shouldExclude) return;
config = sanitizeConfig(config);
const existingPermissions = await strapi.admin.services.permission.findMany({
where: { role: { code: config.code } },
});
existingPermissions.map((permission) => sanitizeConfig(permission));
const formattedObject = { ...config, permissions: existingPermissions };
this.jsonFields.map((field) => formattedObject[field] = JSON.parse(config[field]));
configs[`${this.configPrefix}.${config[this.uid]}`] = formattedObject;
}));
return configs;
}
};
module.exports = AdminRolePermissionsConfigType;

View File

@ -1,10 +1,12 @@
'use strict'; 'use strict';
const ConfigType = require("../services/type"); const ConfigType = require("../services/type");
const RolePermissionsConfigType = require("./role-permissions"); const UserRoleConfigType = require("./user-role");
const AdminRoleConfigType = require("./admin-role");
module.exports = { module.exports = {
'i18n-locale': new ConfigType('plugin::i18n.locale', 'i18n-locale', 'code'), 'i18n-locale': new ConfigType('plugin::i18n.locale', 'i18n-locale', 'code'),
'core-store': new ConfigType('strapi::core-store', 'core-store', 'key', ['value']), 'core-store': new ConfigType('strapi::core-store', 'core-store', 'key', ['value']),
'role-permissions': new RolePermissionsConfigType('plugin::users-permissions.role', 'role-permissions', 'type'), 'user-role': new UserRoleConfigType('plugin::users-permissions.role', 'user-role', 'type'),
'admin-role': new AdminRoleConfigType('admin::role', 'admin-role', 'code'),
}; };

View File

@ -1,6 +1,7 @@
const { sanitizeConfig } = require('../utils');
const ConfigType = require("../services/type"); const ConfigType = require("../services/type");
const RolePermissionsConfigType = class RolePermissionsConfigType extends ConfigType { const UserRolePermissionsConfigType = class UserRolePermissionsConfigType extends ConfigType {
/** /**
* Import a single role-permissions config file into the db. * Import a single role-permissions config file into the db.
* *
@ -10,7 +11,7 @@ const RolePermissionsConfigType = class RolePermissionsConfigType extends Config
*/ */
importSingle = async (configName, configContent) => { importSingle = async (configName, configContent) => {
// Check if the config should be excluded. // Check if the config should be excluded.
const shouldExclude = strapi.plugins['config-sync'].config.exclude.includes(`${this.configPrefix}.${configName}`); const shouldExclude = strapi.config.get('plugin.config-sync.exclude').includes(`${this.configPrefix}.${configName}`);
if (shouldExclude) return; if (shouldExclude) return;
const roleService = strapi.plugin('users-permissions').service('role'); const roleService = strapi.plugin('users-permissions').service('role');
@ -63,8 +64,7 @@ const RolePermissionsConfigType = class RolePermissionsConfigType extends Config
const shouldExclude = strapi.config.get('plugin.config-sync.exclude').includes(`${this.configPrefix}.${config.type}`); const shouldExclude = strapi.config.get('plugin.config-sync.exclude').includes(`${this.configPrefix}.${config.type}`);
if (shouldExclude) return; if (shouldExclude) return;
// Do not export the _id field, as it is immutable config = sanitizeConfig(config);
delete config._id;
configs[`${this.configPrefix}.${config.type}`] = config; configs[`${this.configPrefix}.${config.type}`] = config;
}); });
@ -73,4 +73,4 @@ const RolePermissionsConfigType = class RolePermissionsConfigType extends Config
} }
}; };
module.exports = RolePermissionsConfigType; module.exports = UserRolePermissionsConfigType;

View File

@ -10,8 +10,18 @@ const getService = (name) => {
const logMessage = (msg = '') => `[strapi-plugin-config-sync]: ${msg}`; const logMessage = (msg = '') => `[strapi-plugin-config-sync]: ${msg}`;
const sanitizeConfig = (config) => {
delete config._id;
delete config.id;
delete config.updatedAt;
delete config.createdAt;
return config;
};
module.exports = { module.exports = {
getService, getService,
getCoreStore, getCoreStore,
logMessage, logMessage,
sanitizeConfig,
}; };

4638
yarn.lock

File diff suppressed because it is too large Load Diff