mirror of
https://github.com/strapi/strapi.git
synced 2025-11-02 10:55:37 +00:00
Merge branch 'releases/v4' into v4/graphql-schema-generation-refactor
This commit is contained in:
commit
ee5e7a026f
@ -45,15 +45,6 @@ const SettingsPage = lazy(() =>
|
||||
import(/* webpackChunkName: "Admin_settingsPage" */ '../SettingsPage')
|
||||
);
|
||||
|
||||
const CTB = lazy(() =>
|
||||
import(
|
||||
/* webpackChunkName: "content-type-builder" */ '@strapi/plugin-content-type-builder/admin/src/pages/App'
|
||||
)
|
||||
);
|
||||
const Upload = lazy(() =>
|
||||
import(/* webpackChunkName: "upload" */ '@strapi/plugin-upload/admin/src/pages/App')
|
||||
);
|
||||
|
||||
// Simple hook easier for testing
|
||||
const useTrackUsage = () => {
|
||||
const { trackUsage } = useTracking();
|
||||
@ -100,8 +91,6 @@ const Admin = () => {
|
||||
<Route path="/me" component={ProfilePage} exact />
|
||||
|
||||
<Route path="/content-manager" component={CM} />
|
||||
<Route path="/plugins/content-type-builder" component={CTB} />
|
||||
<Route path="/plugins/upload" component={Upload} />
|
||||
{routes}
|
||||
<Route path="/settings/:settingId" component={SettingsPage} />
|
||||
<Route path="/settings" component={SettingsPage} exact />
|
||||
|
||||
@ -9,14 +9,14 @@ import emailPlugin from '../../../email/admin/src';
|
||||
import uploadPlugin from '../../../upload/admin/src';
|
||||
|
||||
const plugins = {
|
||||
'@strapi/plugin-content-type-builder': ctbPlugin,
|
||||
'@strapi/plugin-documentation': documentationPlugin,
|
||||
'@strapi/plugin-i18n': i18nPlugin,
|
||||
'@strapi/plugin-email': emailPlugin,
|
||||
'@strapi/plugin-upload': uploadPlugin,
|
||||
'@strapi/plugin-graphql': graphqlPlugin,
|
||||
'@strapi/plugin-sentry': sentryPlugin,
|
||||
'@strapi/plugin-users-permissions': usersPermissionsPlugin,
|
||||
'content-type-builder': ctbPlugin,
|
||||
documentation: documentationPlugin,
|
||||
i18n: i18nPlugin,
|
||||
email: emailPlugin,
|
||||
upload: uploadPlugin,
|
||||
graphql: graphqlPlugin,
|
||||
sentry: sentryPlugin,
|
||||
'users-permissions': usersPermissionsPlugin,
|
||||
};
|
||||
|
||||
export default plugins;
|
||||
|
||||
@ -6,16 +6,19 @@ module.exports = {
|
||||
method: 'GET',
|
||||
path: '/providers',
|
||||
handler: 'authentication.getProviders',
|
||||
config: { auth: false },
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/connect/:provider',
|
||||
handler: 'authentication.providerLogin',
|
||||
config: { auth: false },
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/connect/:provider',
|
||||
handler: 'authentication.providerLogin',
|
||||
config: { auth: false },
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
'use strict';
|
||||
/* eslint-disable no-useless-escape */
|
||||
|
||||
const path = require('path');
|
||||
const _ = require('lodash');
|
||||
const fs = require('fs-extra');
|
||||
@ -34,9 +34,9 @@ function getCustomWebpackConfig(dir, config) {
|
||||
return webpackConfig;
|
||||
}
|
||||
|
||||
async function build({ dir, env, options, optimize }) {
|
||||
async function build({ plugins, dir, env, options, optimize }) {
|
||||
// Create the cache dir containing the front-end files.
|
||||
await createCacheDir(dir);
|
||||
await createCacheDir({ dir, plugins });
|
||||
|
||||
const cacheDir = path.resolve(dir, '.cache');
|
||||
const entry = path.resolve(cacheDir, 'admin', 'src');
|
||||
@ -48,7 +48,18 @@ async function build({ dir, env, options, optimize }) {
|
||||
ceRoot: path.resolve(cacheDir, 'admin', 'src'),
|
||||
};
|
||||
|
||||
const config = getCustomWebpackConfig(dir, { entry, dest, env, options, optimize, roots });
|
||||
const pluginsPath = Object.keys(plugins).map(pluginName => plugins[pluginName].pathToPlugin);
|
||||
|
||||
const config = getCustomWebpackConfig(dir, {
|
||||
entry,
|
||||
pluginsPath,
|
||||
cacheDir,
|
||||
dest,
|
||||
env,
|
||||
options,
|
||||
optimize,
|
||||
roots,
|
||||
});
|
||||
|
||||
const compiler = webpack(config);
|
||||
|
||||
@ -78,34 +89,28 @@ async function build({ dir, env, options, optimize }) {
|
||||
});
|
||||
}
|
||||
|
||||
async function createPluginsJs(plugins, localPlugins, dest) {
|
||||
const createPluginsArray = plugins =>
|
||||
plugins.map(name => {
|
||||
const shortName = _.camelCase(name.replace(/^@strapi\/plugin-/i, ''));
|
||||
|
||||
return { name, shortName };
|
||||
});
|
||||
const appPluginsArray = createPluginsArray(plugins);
|
||||
const localPluginsArray = createPluginsArray(localPlugins);
|
||||
async function createPluginsJs(plugins, dest) {
|
||||
const pluginsArray = plugins.map(({ pathToPlugin, name }) => {
|
||||
const shortName = _.camelCase(name);
|
||||
return {
|
||||
name,
|
||||
pathToPlugin: path.relative(path.resolve(dest, 'admin', 'src'), pathToPlugin),
|
||||
shortName,
|
||||
};
|
||||
});
|
||||
|
||||
const content = `
|
||||
${appPluginsArray
|
||||
.map(({ name, shortName }) => {
|
||||
const req = `'../../plugins/${name}/admin/src'`;
|
||||
${pluginsArray
|
||||
.map(({ pathToPlugin, shortName }) => {
|
||||
const req = `'${pathToPlugin}/admin/src'`;
|
||||
|
||||
return `import ${shortName} from ${req};`;
|
||||
})
|
||||
.join('\n')}
|
||||
${localPluginsArray
|
||||
.map(({ name, shortName }) => {
|
||||
const req = `'../../../plugins/${name}/admin/src'`;
|
||||
|
||||
return `import ${shortName} from ${req};`;
|
||||
})
|
||||
.join('\n')}
|
||||
|
||||
const plugins = {
|
||||
${[...appPluginsArray, ...localPluginsArray]
|
||||
${[...pluginsArray]
|
||||
.map(({ name, shortName }) => {
|
||||
return ` '${name}': ${shortName},`;
|
||||
})
|
||||
@ -126,21 +131,6 @@ async function clean({ dir }) {
|
||||
fs.removeSync(cacheDir);
|
||||
}
|
||||
|
||||
async function copyPlugin(name, dest) {
|
||||
const pkgFilePath = getPkgPath(name);
|
||||
|
||||
const resolveDepPath = (...args) => path.resolve(pkgFilePath, ...args);
|
||||
const resolveDest = (...args) => path.resolve(dest, 'plugins', name, ...args);
|
||||
|
||||
const copy = (...args) => {
|
||||
return fs.copy(resolveDepPath(...args), resolveDest(...args));
|
||||
};
|
||||
|
||||
// Copy the entire admin folder
|
||||
await copy('admin');
|
||||
await copy('package.json');
|
||||
}
|
||||
|
||||
async function copyAdmin(dest) {
|
||||
const adminPath = getPkgPath('@strapi/admin');
|
||||
|
||||
@ -154,25 +144,16 @@ async function copyAdmin(dest) {
|
||||
await fs.copy(path.resolve(adminPath, 'package.json'), path.resolve(dest, 'package.json'));
|
||||
}
|
||||
|
||||
async function createCacheDir(dir) {
|
||||
async function createCacheDir({ dir, plugins }) {
|
||||
const cacheDir = path.resolve(dir, '.cache');
|
||||
|
||||
const pkgJSON = require(path.join(dir, 'package.json'));
|
||||
|
||||
const pluginsToCopy = Object.keys(pkgJSON.dependencies).filter(
|
||||
dep =>
|
||||
dep.startsWith('@strapi/plugin') &&
|
||||
fs.existsSync(path.resolve(getPkgPath(dep), 'admin', 'src', 'index.js'))
|
||||
);
|
||||
|
||||
let localPluginsToCopy = [];
|
||||
if (fs.existsSync(path.join(dir, 'plugins'))) {
|
||||
localPluginsToCopy = fs
|
||||
.readdirSync(path.join(dir, 'plugins'))
|
||||
.filter(plugin =>
|
||||
fs.existsSync(path.resolve(dir, 'plugins', plugin, 'admin', 'src', 'index.js'))
|
||||
);
|
||||
}
|
||||
const pluginsWithFront = Object.keys(plugins)
|
||||
.filter(pluginName => {
|
||||
const pluginInfo = plugins[pluginName];
|
||||
// TODO: use strapi-admin
|
||||
return fs.existsSync(path.resolve(pluginInfo.pathToPlugin, 'admin', 'src', 'index.js'));
|
||||
})
|
||||
.map(name => ({ name, ...plugins[name] }));
|
||||
|
||||
// create .cache dir
|
||||
await fs.emptyDir(cacheDir);
|
||||
@ -180,9 +161,6 @@ async function createCacheDir(dir) {
|
||||
// copy admin core code
|
||||
await copyAdmin(cacheDir);
|
||||
|
||||
// copy plugins code
|
||||
await Promise.all(pluginsToCopy.map(name => copyPlugin(name, cacheDir)));
|
||||
|
||||
// Copy app.js
|
||||
const customAdminConfigFilePath = path.join(dir, 'admin', 'app.js');
|
||||
|
||||
@ -198,27 +176,30 @@ async function createCacheDir(dir) {
|
||||
}
|
||||
|
||||
// create plugins.js with plugins requires
|
||||
await createPluginsJs(pluginsToCopy, localPluginsToCopy, cacheDir);
|
||||
await createPluginsJs(pluginsWithFront, cacheDir);
|
||||
}
|
||||
|
||||
async function watchAdmin({ dir, host, port, browser, options }) {
|
||||
async function watchAdmin({ plugins, dir, host, port, browser, options }) {
|
||||
// Create the cache dir containing the front-end files.
|
||||
await createCacheDir(dir);
|
||||
const cacheDir = path.join(dir, '.cache');
|
||||
await createCacheDir({ dir, plugins });
|
||||
|
||||
const entry = path.join(dir, '.cache', 'admin', 'src');
|
||||
const entry = path.join(cacheDir, 'admin', 'src');
|
||||
const dest = path.join(dir, 'build');
|
||||
const env = 'development';
|
||||
|
||||
const cacheDir = path.join(dir, '.cache');
|
||||
|
||||
// Roots for the @strapi/babel-plugin-switch-ee-ce
|
||||
const roots = {
|
||||
eeRoot: path.resolve(cacheDir, 'ee', 'admin'),
|
||||
ceRoot: path.resolve(cacheDir, 'admin', 'src'),
|
||||
};
|
||||
|
||||
const pluginsPath = Object.keys(plugins).map(pluginName => plugins[pluginName].pathToPlugin);
|
||||
|
||||
const args = {
|
||||
entry,
|
||||
cacheDir,
|
||||
pluginsPath,
|
||||
dest,
|
||||
env,
|
||||
port,
|
||||
|
||||
@ -11,6 +11,8 @@ const buildAdmin = async () => {
|
||||
const args = {
|
||||
entry,
|
||||
dest,
|
||||
cacheDir: __dirname,
|
||||
pluginsPath: [path.resolve(__dirname, '../../../..')],
|
||||
env: 'production',
|
||||
optimize: true,
|
||||
options: {
|
||||
|
||||
@ -31,9 +31,9 @@ module.exports = createPolicyFactory(
|
||||
);
|
||||
|
||||
return (ctx, next) => {
|
||||
const { userAbility: ability, isAuthenticatedAdmin } = ctx.state;
|
||||
const { userAbility: ability, isAuthenticated } = ctx.state;
|
||||
|
||||
if (!isAuthenticatedAdmin || !ability) {
|
||||
if (!isAuthenticated || !ability) {
|
||||
return next();
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = (ctx, next) => {
|
||||
if (!ctx.state.isAuthenticatedAdmin) {
|
||||
if (!ctx.state.isAuthenticated) {
|
||||
return ctx.unauthorized();
|
||||
}
|
||||
|
||||
|
||||
@ -3,56 +3,51 @@
|
||||
|
||||
// const permissionsFieldsToPropertiesMigration = require('../migrations/permissions-fields-to-properties');
|
||||
|
||||
/**
|
||||
* Tries to authenticated admin user and calls next.
|
||||
* @param {KoaContext} ctx
|
||||
* @param {Middleware} next
|
||||
* @returns {undefined}
|
||||
*/
|
||||
const authMiddleware = async (ctx, next) => {
|
||||
if (!ctx.request.header.authorization) {
|
||||
return next();
|
||||
}
|
||||
const adminAuthStrategy = {
|
||||
name: 'admin',
|
||||
async authenticate(ctx) {
|
||||
const { authorization } = ctx.request.header;
|
||||
|
||||
if (
|
||||
ctx.request.header.authorization &&
|
||||
ctx.request.header.authorization.split(' ')[0] === 'Bearer'
|
||||
) {
|
||||
const token = ctx.request.header.authorization.split(' ')[1];
|
||||
if (!authorization) {
|
||||
return { authenticated: false };
|
||||
}
|
||||
|
||||
const parts = authorization.split(/\s+/);
|
||||
|
||||
if (parts[0] !== 'Bearer' || parts.length !== 2) {
|
||||
return { authenticated: false };
|
||||
}
|
||||
|
||||
const token = parts[1];
|
||||
const { payload, isValid } = strapi.admin.services.token.decodeJwtToken(token);
|
||||
|
||||
if (isValid) {
|
||||
const admin = await strapi
|
||||
const user = await strapi
|
||||
.query('admin::user')
|
||||
.findOne({ where: { id: payload.id }, populate: ['roles'] });
|
||||
|
||||
if (!admin || !(admin.isActive === true)) {
|
||||
return ctx.unauthorized('Invalid credentials');
|
||||
if (!user || !(user.isActive === true)) {
|
||||
return { error: 'Invalid credentials' };
|
||||
}
|
||||
|
||||
// TODO: use simple user & isAuthenticated
|
||||
const userAbility = await strapi.admin.services.permission.engine.generateUserAbility(user);
|
||||
|
||||
ctx.state.admin = admin;
|
||||
ctx.state.user = admin;
|
||||
ctx.state.userAbility = await strapi.admin.services.permission.engine.generateUserAbility(
|
||||
admin
|
||||
);
|
||||
ctx.state.userAbility = userAbility;
|
||||
ctx.state.user = user;
|
||||
|
||||
ctx.state.isAuthenticatedAdmin = true;
|
||||
|
||||
return next();
|
||||
return { authenticated: true, credentials: user };
|
||||
}
|
||||
}
|
||||
|
||||
ctx.unauthorized('Invalid credentials');
|
||||
return { error: 'Invalid credentials' };
|
||||
},
|
||||
// async verify() {},
|
||||
};
|
||||
|
||||
module.exports = () => {
|
||||
const passportMiddleware = strapi.admin.services.passport.init();
|
||||
|
||||
strapi.server.api('admin').use(passportMiddleware);
|
||||
strapi.server.api('admin').use(authMiddleware);
|
||||
strapi.container.get('auth').register('admin', adminAuthStrategy);
|
||||
|
||||
// FIXME: to implement
|
||||
// strapi.db.migrations.register(permissionsFieldsToPropertiesMigration);
|
||||
|
||||
63
packages/core/admin/server/routes/admin.js
Normal file
63
packages/core/admin/server/routes/admin.js
Normal file
@ -0,0 +1,63 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/init',
|
||||
handler: 'admin.init',
|
||||
config: { auth: false },
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/project-type',
|
||||
handler: 'admin.getProjectType',
|
||||
config: { auth: false },
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/information',
|
||||
handler: 'admin.information',
|
||||
config: {
|
||||
policies: ['admin::isAuthenticatedAdmin'],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/plugins',
|
||||
handler: 'admin.plugins',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{ name: 'admin::hasPermissions', options: { actions: ['admin::marketplace.read'] } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/plugins/install',
|
||||
handler: 'admin.installPlugin',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{
|
||||
name: 'admin::hasPermissions',
|
||||
options: { actions: ['admin::marketplace.plugins.install'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'DELETE',
|
||||
path: '/plugins/uninstall/:plugin',
|
||||
handler: 'admin.uninstallPlugin',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{
|
||||
name: 'admin::hasPermissions',
|
||||
options: { actions: ['admin::marketplace.plugins.uninstall'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
46
packages/core/admin/server/routes/authentication.js
Normal file
46
packages/core/admin/server/routes/authentication.js
Normal file
@ -0,0 +1,46 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/login',
|
||||
handler: 'authentication.login',
|
||||
config: { auth: false },
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/renew-token',
|
||||
handler: 'authentication.renewToken',
|
||||
config: { auth: false },
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/register-admin',
|
||||
handler: 'authentication.registerAdmin',
|
||||
config: { auth: false },
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/registration-info',
|
||||
handler: 'authentication.registrationInfo',
|
||||
config: { auth: false },
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/register',
|
||||
handler: 'authentication.register',
|
||||
config: { auth: false },
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/forgot-password',
|
||||
handler: 'authentication.forgotPassword',
|
||||
config: { auth: false },
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/reset-password',
|
||||
handler: 'authentication.resetPassword',
|
||||
config: { auth: false },
|
||||
},
|
||||
];
|
||||
@ -1,325 +1,10 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/init',
|
||||
handler: 'admin.init',
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/project-type',
|
||||
handler: 'admin.getProjectType',
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/information',
|
||||
handler: 'admin.information',
|
||||
config: {
|
||||
policies: ['admin::isAuthenticatedAdmin'],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/plugins',
|
||||
handler: 'admin.plugins',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{ name: 'admin::hasPermissions', options: { actions: ['admin::marketplace.read'] } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/plugins/install',
|
||||
handler: 'admin.installPlugin',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{
|
||||
name: 'admin::hasPermissions',
|
||||
options: { actions: ['admin::marketplace.plugins.install'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'DELETE',
|
||||
path: '/plugins/uninstall/:plugin',
|
||||
handler: 'admin.uninstallPlugin',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{
|
||||
name: 'admin::hasPermissions',
|
||||
options: { actions: ['admin::marketplace.plugins.uninstall'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/login',
|
||||
handler: 'authentication.login',
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/renew-token',
|
||||
handler: 'authentication.renewToken',
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/register-admin',
|
||||
handler: 'authentication.registerAdmin',
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/registration-info',
|
||||
handler: 'authentication.registrationInfo',
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/register',
|
||||
handler: 'authentication.register',
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/forgot-password',
|
||||
handler: 'authentication.forgotPassword',
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/reset-password',
|
||||
handler: 'authentication.resetPassword',
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/webhooks',
|
||||
handler: 'Webhooks.listWebhooks',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{ name: 'admin::hasPermissions', options: { actions: ['admin::webhooks.read'] } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/webhooks',
|
||||
handler: 'Webhooks.createWebhook',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{ name: 'admin::hasPermissions', options: { actions: ['admin::webhooks.create'] } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/webhooks/:id',
|
||||
handler: 'Webhooks.getWebhook',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{ name: 'admin::hasPermissions', options: { actions: ['admin::webhooks.read'] } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'PUT',
|
||||
path: '/webhooks/:id',
|
||||
handler: 'Webhooks.updateWebhook',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{ name: 'admin::hasPermissions', options: { actions: ['admin::webhooks.update'] } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'DELETE',
|
||||
path: '/webhooks/:id',
|
||||
handler: 'Webhooks.deleteWebhook',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{ name: 'admin::hasPermissions', options: { actions: ['admin::webhooks.delete'] } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/webhooks/batch-delete',
|
||||
handler: 'Webhooks.deleteWebhooks',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{ name: 'admin::hasPermissions', options: { actions: ['admin::webhooks.delete'] } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/webhooks/:id/trigger',
|
||||
handler: 'Webhooks.triggerWebhook',
|
||||
config: {
|
||||
policies: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/users/me',
|
||||
handler: 'authenticated-user.getMe',
|
||||
config: {
|
||||
policies: ['admin::isAuthenticatedAdmin'],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'PUT',
|
||||
path: '/users/me',
|
||||
handler: 'authenticated-user.updateMe',
|
||||
config: {
|
||||
policies: ['admin::isAuthenticatedAdmin'],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/users/me/permissions',
|
||||
handler: 'authenticated-user.getOwnPermissions',
|
||||
config: {
|
||||
policies: ['admin::isAuthenticatedAdmin'],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/users',
|
||||
handler: 'user.create',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{ name: 'admin::hasPermissions', options: { actions: ['admin::users.create'] } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/users',
|
||||
handler: 'user.find',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{ name: 'admin::hasPermissions', options: { actions: ['admin::users.read'] } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/users/:id',
|
||||
handler: 'user.findOne',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{ name: 'admin::hasPermissions', options: { actions: ['admin::users.read'] } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'PUT',
|
||||
path: '/users/:id',
|
||||
handler: 'user.update',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{ name: 'admin::hasPermissions', options: { actions: ['admin::users.update'] } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'DELETE',
|
||||
path: '/users/:id',
|
||||
handler: 'user.deleteOne',
|
||||
config: {
|
||||
policies: [{ name: 'admin::hasPermissions', options: { actions: ['admin::users.delete'] } }],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/users/batch-delete',
|
||||
handler: 'user.deleteMany',
|
||||
config: {
|
||||
policies: [{ name: 'admin::hasPermissions', options: { actions: ['admin::users.delete'] } }],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/roles/:id/permissions',
|
||||
handler: 'role.getPermissions',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{ name: 'admin::hasPermissions', options: { actions: ['admin::roles.read'] } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'PUT',
|
||||
path: '/roles/:id/permissions',
|
||||
handler: 'role.updatePermissions',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{ name: 'admin::hasPermissions', options: { actions: ['admin::roles.update'] } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/roles/:id',
|
||||
handler: 'role.findOne',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{ name: 'admin::hasPermissions', options: { actions: ['admin::roles.read'] } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/roles',
|
||||
handler: 'role.findAll',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{ name: 'admin::hasPermissions', options: { actions: ['admin::roles.read'] } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'PUT',
|
||||
path: '/roles/:id',
|
||||
handler: 'role.update',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{ name: 'admin::hasPermissions', options: { actions: ['admin::roles.update'] } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/permissions',
|
||||
handler: 'permission.getAll',
|
||||
config: {
|
||||
policies: ['admin::isAuthenticatedAdmin'],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/permissions/check',
|
||||
handler: 'permission.check',
|
||||
config: {
|
||||
policies: ['admin::isAuthenticatedAdmin'],
|
||||
},
|
||||
},
|
||||
];
|
||||
const admin = require('./admin');
|
||||
const authentication = require('./authentication');
|
||||
const permissions = require('./permissions');
|
||||
const users = require('./users');
|
||||
const roles = require('./roles');
|
||||
const webhooks = require('./webhooks');
|
||||
|
||||
module.exports = [...admin, ...authentication, ...permissions, ...users, ...roles, ...webhooks];
|
||||
|
||||
20
packages/core/admin/server/routes/permissions.js
Normal file
20
packages/core/admin/server/routes/permissions.js
Normal file
@ -0,0 +1,20 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/permissions',
|
||||
handler: 'permission.getAll',
|
||||
config: {
|
||||
policies: ['admin::isAuthenticatedAdmin'],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/permissions/check',
|
||||
handler: 'permission.check',
|
||||
config: {
|
||||
policies: ['admin::isAuthenticatedAdmin'],
|
||||
},
|
||||
},
|
||||
];
|
||||
67
packages/core/admin/server/routes/roles.js
Normal file
67
packages/core/admin/server/routes/roles.js
Normal file
@ -0,0 +1,67 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/users/batch-delete',
|
||||
handler: 'user.deleteMany',
|
||||
config: {
|
||||
policies: [{ name: 'admin::hasPermissions', options: { actions: ['admin::users.delete'] } }],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/roles/:id/permissions',
|
||||
handler: 'role.getPermissions',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{ name: 'admin::hasPermissions', options: { actions: ['admin::roles.read'] } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'PUT',
|
||||
path: '/roles/:id/permissions',
|
||||
handler: 'role.updatePermissions',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{ name: 'admin::hasPermissions', options: { actions: ['admin::roles.update'] } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/roles/:id',
|
||||
handler: 'role.findOne',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{ name: 'admin::hasPermissions', options: { actions: ['admin::roles.read'] } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/roles',
|
||||
handler: 'role.findAll',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{ name: 'admin::hasPermissions', options: { actions: ['admin::roles.read'] } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'PUT',
|
||||
path: '/roles/:id',
|
||||
handler: 'role.update',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{ name: 'admin::hasPermissions', options: { actions: ['admin::roles.update'] } },
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
88
packages/core/admin/server/routes/users.js
Normal file
88
packages/core/admin/server/routes/users.js
Normal file
@ -0,0 +1,88 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/users/me',
|
||||
handler: 'authenticated-user.getMe',
|
||||
config: {
|
||||
policies: ['admin::isAuthenticatedAdmin'],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'PUT',
|
||||
path: '/users/me',
|
||||
handler: 'authenticated-user.updateMe',
|
||||
config: {
|
||||
policies: ['admin::isAuthenticatedAdmin'],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/users/me/permissions',
|
||||
handler: 'authenticated-user.getOwnPermissions',
|
||||
config: {
|
||||
policies: ['admin::isAuthenticatedAdmin'],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/users',
|
||||
handler: 'user.create',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{ name: 'admin::hasPermissions', options: { actions: ['admin::users.create'] } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/users',
|
||||
handler: 'user.find',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{ name: 'admin::hasPermissions', options: { actions: ['admin::users.read'] } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/users/:id',
|
||||
handler: 'user.findOne',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{ name: 'admin::hasPermissions', options: { actions: ['admin::users.read'] } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'PUT',
|
||||
path: '/users/:id',
|
||||
handler: 'user.update',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{ name: 'admin::hasPermissions', options: { actions: ['admin::users.update'] } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'DELETE',
|
||||
path: '/users/:id',
|
||||
handler: 'user.deleteOne',
|
||||
config: {
|
||||
policies: [{ name: 'admin::hasPermissions', options: { actions: ['admin::users.delete'] } }],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/users/batch-delete',
|
||||
handler: 'user.deleteMany',
|
||||
config: {
|
||||
policies: [{ name: 'admin::hasPermissions', options: { actions: ['admin::users.delete'] } }],
|
||||
},
|
||||
},
|
||||
];
|
||||
78
packages/core/admin/server/routes/webhooks.js
Normal file
78
packages/core/admin/server/routes/webhooks.js
Normal file
@ -0,0 +1,78 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/webhooks',
|
||||
handler: 'Webhooks.listWebhooks',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{ name: 'admin::hasPermissions', options: { actions: ['admin::webhooks.read'] } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/webhooks',
|
||||
handler: 'Webhooks.createWebhook',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{ name: 'admin::hasPermissions', options: { actions: ['admin::webhooks.create'] } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/webhooks/:id',
|
||||
handler: 'Webhooks.getWebhook',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{ name: 'admin::hasPermissions', options: { actions: ['admin::webhooks.read'] } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'PUT',
|
||||
path: '/webhooks/:id',
|
||||
handler: 'Webhooks.updateWebhook',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{ name: 'admin::hasPermissions', options: { actions: ['admin::webhooks.update'] } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'DELETE',
|
||||
path: '/webhooks/:id',
|
||||
handler: 'Webhooks.deleteWebhook',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{ name: 'admin::hasPermissions', options: { actions: ['admin::webhooks.delete'] } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/webhooks/batch-delete',
|
||||
handler: 'Webhooks.deleteWebhooks',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{ name: 'admin::hasPermissions', options: { actions: ['admin::webhooks.delete'] } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/webhooks/:id/trigger',
|
||||
handler: 'Webhooks.triggerWebhook',
|
||||
config: {
|
||||
policies: [],
|
||||
},
|
||||
},
|
||||
];
|
||||
@ -22,6 +22,8 @@ module.exports = () => {
|
||||
|
||||
const args = {
|
||||
entry,
|
||||
cacheDir: __dirname,
|
||||
pluginsPath: [path.resolve(__dirname, '../../..')],
|
||||
dest,
|
||||
env,
|
||||
options,
|
||||
|
||||
@ -13,6 +13,8 @@ const getClientEnvironment = require('./env');
|
||||
|
||||
module.exports = ({
|
||||
entry,
|
||||
cacheDir,
|
||||
pluginsPath,
|
||||
dest,
|
||||
env,
|
||||
optimize,
|
||||
@ -98,7 +100,7 @@ module.exports = ({
|
||||
{
|
||||
test: /\.m?js$/,
|
||||
// TODO remove when plugins are built separately
|
||||
exclude: /node_modules\/(?!(@strapi\/plugin-content-type-builder|@strapi\/plugin-upload)\/).*/,
|
||||
include: [cacheDir, ...pluginsPath],
|
||||
use: {
|
||||
loader: require.resolve('babel-loader'),
|
||||
options: {
|
||||
|
||||
@ -19,14 +19,22 @@ const name = pluginPkg.strapi.name;
|
||||
export default {
|
||||
register(app) {
|
||||
app.addReducers(reducers);
|
||||
app.addCorePluginMenuLink({
|
||||
|
||||
app.addMenuLink({
|
||||
to: `/plugins/${pluginId}`,
|
||||
icon,
|
||||
intlLabel: {
|
||||
id: `${pluginId}.plugin.name`,
|
||||
defaultMessage: 'Content-Types Builder',
|
||||
defaultMessage: 'Content Types Builder',
|
||||
},
|
||||
permissions: pluginPermissions.main,
|
||||
Component: async () => {
|
||||
const component = await import(
|
||||
/* webpackChunkName: "content-type-builder" */ './pages/App'
|
||||
);
|
||||
|
||||
return component;
|
||||
},
|
||||
});
|
||||
|
||||
app.registerPlugin({
|
||||
|
||||
@ -7,7 +7,9 @@ module.exports = {
|
||||
method: 'POST',
|
||||
path: '/',
|
||||
handler: 'email.send',
|
||||
config: {},
|
||||
config: {
|
||||
policies: ['admin::isAuthenticatedAdmin'],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
|
||||
@ -18,7 +18,7 @@ const { createCoreStore, coreStoreModel } = require('./services/core-store');
|
||||
const createEntityService = require('./services/entity-service');
|
||||
const entityValidator = require('./services/entity-validator');
|
||||
const createTelemetry = require('./services/metrics');
|
||||
const createContentAPI = require('./services/content-api');
|
||||
const createAuth = require('./services/auth');
|
||||
const createUpdateNotifier = require('./utils/update-notifier');
|
||||
const createStartupLogger = require('./utils/startup-logger');
|
||||
const ee = require('./utils/ee');
|
||||
@ -53,7 +53,7 @@ class Strapi {
|
||||
this.container.register('modules', modulesRegistry(this));
|
||||
this.container.register('plugins', pluginsRegistry(this));
|
||||
this.container.register('apis', apisRegistry(this));
|
||||
this.container.register('content-api', createContentAPI(this));
|
||||
this.container.register('auth', createAuth(this));
|
||||
|
||||
this.isLoaded = false;
|
||||
this.reload = this.reload();
|
||||
|
||||
@ -3,20 +3,30 @@ const { green } = require('chalk');
|
||||
|
||||
const strapiAdmin = require('@strapi/admin');
|
||||
const { getConfigUrls } = require('@strapi/utils');
|
||||
const loadConfiguration = require('../core/app-configuration');
|
||||
const ee = require('../utils/ee');
|
||||
|
||||
const ee = require('../utils/ee');
|
||||
const addSlash = require('../utils/addSlash');
|
||||
const strapi = require('../index');
|
||||
const getEnabledPlugins = require('../core/loaders/plugins/get-enabled-plugins');
|
||||
|
||||
/**
|
||||
* `$ strapi build`
|
||||
*/
|
||||
module.exports = async ({ clean, optimization }) => {
|
||||
const dir = process.cwd();
|
||||
const config = loadConfiguration(dir);
|
||||
|
||||
const { serverUrl, adminPath } = getConfigUrls(config.server, true);
|
||||
const strapiInstance = strapi({
|
||||
dir,
|
||||
autoReload: true,
|
||||
serveAdminPanel: false,
|
||||
});
|
||||
|
||||
console.log(`Building your admin UI with ${green(config.environment)} configuration ...`);
|
||||
const plugins = await getEnabledPlugins(strapiInstance);
|
||||
|
||||
const env = strapiInstance.config.get('environment');
|
||||
const { serverUrl, adminPath } = getConfigUrls(strapiInstance.config.get('server'), true);
|
||||
|
||||
console.log(`Building your admin UI with ${green(env)} configuration ...`);
|
||||
|
||||
if (clean) {
|
||||
await strapiAdmin.clean({ dir });
|
||||
@ -27,6 +37,7 @@ module.exports = async ({ clean, optimization }) => {
|
||||
return strapiAdmin
|
||||
.build({
|
||||
dir,
|
||||
plugins,
|
||||
// front end build env is always production for now
|
||||
env: 'production',
|
||||
optimize: optimization,
|
||||
|
||||
@ -1,32 +1,42 @@
|
||||
'use strict';
|
||||
|
||||
const strapiAdmin = require('@strapi/admin');
|
||||
const { getOr } = require('lodash/fp');
|
||||
const { getConfigUrls, getAbsoluteServerUrl } = require('@strapi/utils');
|
||||
const loadConfiguration = require('../core/app-configuration');
|
||||
|
||||
const ee = require('../utils/ee');
|
||||
const addSlash = require('../utils/addSlash');
|
||||
const strapi = require('../index');
|
||||
const getEnabledPlugins = require('../core/loaders/plugins/get-enabled-plugins');
|
||||
|
||||
module.exports = async function({ browser }) {
|
||||
const dir = process.cwd();
|
||||
|
||||
const config = loadConfiguration(dir);
|
||||
const strapiInstance = strapi({
|
||||
dir,
|
||||
autoReload: true,
|
||||
serveAdminPanel: false,
|
||||
});
|
||||
|
||||
const { adminPath } = getConfigUrls(config.server, true);
|
||||
const plugins = await getEnabledPlugins(strapiInstance);
|
||||
|
||||
const adminPort = getOr(8000, 'server.admin.port')(config);
|
||||
const adminHost = getOr('localhost', 'server.admin.host')(config);
|
||||
const adminWatchIgnoreFiles = getOr([], 'server.admin.watchIgnoreFiles')(config);
|
||||
const { adminPath } = getConfigUrls(strapiInstance.config.get('server'), true);
|
||||
|
||||
const adminPort = strapiInstance.config.get('server.admin.port', 8000);
|
||||
const adminHost = strapiInstance.config.get('server.admin.host', 'localhost');
|
||||
const adminWatchIgnoreFiles = strapiInstance.config.get('server.admin.watchIgnoreFiles', []);
|
||||
|
||||
const backendURL = getAbsoluteServerUrl(strapiInstance.config, true);
|
||||
|
||||
ee({ dir });
|
||||
|
||||
strapiAdmin.watchAdmin({
|
||||
dir,
|
||||
plugins,
|
||||
port: adminPort,
|
||||
host: adminHost,
|
||||
browser,
|
||||
options: {
|
||||
backend: getAbsoluteServerUrl(config, true),
|
||||
backend: backendURL,
|
||||
adminPath: addSlash(adminPath),
|
||||
watchIgnoreFiles: adminWatchIgnoreFiles,
|
||||
features: ee.isEE ? ee.features.getEnabled() : [],
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
const { uniq, difference, get, isUndefined, merge } = require('lodash');
|
||||
|
||||
const requiredMiddlewares = [
|
||||
'auth',
|
||||
'responses',
|
||||
'router',
|
||||
'logger',
|
||||
|
||||
@ -67,11 +67,13 @@ module.exports = strapi => {
|
||||
method: 'GET',
|
||||
path: '/',
|
||||
handler: serveIndexPage,
|
||||
config: { auth: false },
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/index.html',
|
||||
handler: serveIndexPage,
|
||||
config: { auth: false },
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
@ -80,6 +82,7 @@ module.exports = strapi => {
|
||||
maxage: maxAge,
|
||||
defer: true,
|
||||
}),
|
||||
config: { auth: false },
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
@ -88,6 +91,7 @@ module.exports = strapi => {
|
||||
maxage: maxAge,
|
||||
defer: true,
|
||||
}),
|
||||
config: { auth: false },
|
||||
},
|
||||
]);
|
||||
}
|
||||
@ -118,6 +122,7 @@ module.exports = strapi => {
|
||||
serveAdmin,
|
||||
serveStatic(buildDir, { maxage: maxAge, defer: false, index: 'index.html' }),
|
||||
],
|
||||
config: { auth: false },
|
||||
},
|
||||
]);
|
||||
},
|
||||
|
||||
92
packages/core/strapi/lib/services/auth/index.js
Normal file
92
packages/core/strapi/lib/services/auth/index.js
Normal file
@ -0,0 +1,92 @@
|
||||
'use strict';
|
||||
|
||||
const { strict: assert } = require('assert');
|
||||
const { has, prop } = require('lodash/fp');
|
||||
|
||||
class UnauthorizedError extends Error {}
|
||||
class ForbiddenError extends Error {}
|
||||
|
||||
const INVALID_STRATEGY_MSG =
|
||||
'Invalid auth strategy. Expecting an object with properties {name: string, authenticate: function, verify: function}';
|
||||
|
||||
const validStrategy = strategy => {
|
||||
assert(has('authenticate', strategy), INVALID_STRATEGY_MSG);
|
||||
assert(typeof strategy.authenticate === 'function', INVALID_STRATEGY_MSG);
|
||||
|
||||
if (has('verify', strategy)) {
|
||||
assert(typeof strategy.verify === 'function', INVALID_STRATEGY_MSG);
|
||||
}
|
||||
};
|
||||
|
||||
const createAuthentication = () => {
|
||||
const strategies = {};
|
||||
|
||||
return {
|
||||
errors: {
|
||||
UnauthorizedError,
|
||||
ForbiddenError,
|
||||
},
|
||||
register(type, strategy) {
|
||||
validStrategy(strategy);
|
||||
|
||||
if (!strategies[type]) {
|
||||
strategies[type] = [];
|
||||
}
|
||||
|
||||
strategies[type].push(strategy);
|
||||
|
||||
return this;
|
||||
},
|
||||
async authenticate(ctx, next) {
|
||||
const { route } = ctx.state;
|
||||
|
||||
// use route strategy
|
||||
const config = prop('config.auth', route);
|
||||
|
||||
if (config === false) {
|
||||
return next();
|
||||
}
|
||||
|
||||
const strategiesToUse = strategies[route.info.type];
|
||||
|
||||
for (const strategy of strategiesToUse) {
|
||||
const result = await strategy.authenticate(ctx);
|
||||
|
||||
const { authenticated = false, error = null, credentials } = result || {};
|
||||
|
||||
if (error !== null) {
|
||||
return ctx.unauthorized(error);
|
||||
}
|
||||
|
||||
if (authenticated) {
|
||||
ctx.state.isAuthenticated = true;
|
||||
ctx.state.auth = {
|
||||
strategy,
|
||||
credentials,
|
||||
};
|
||||
|
||||
return next();
|
||||
}
|
||||
}
|
||||
|
||||
return ctx.unauthorized('Missing credentials');
|
||||
},
|
||||
async verify(auth, config = {}) {
|
||||
if (config === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!auth) {
|
||||
throw new UnauthorizedError();
|
||||
}
|
||||
|
||||
if (typeof auth.strategy.verify === 'function') {
|
||||
return await auth.strategy.verify(auth, config);
|
||||
}
|
||||
|
||||
return;
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = createAuthentication;
|
||||
@ -1,71 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const { strict: assert } = require('assert');
|
||||
const { has } = require('lodash/fp');
|
||||
|
||||
class UnauthorizedError extends Error {}
|
||||
class ForbiddenError extends Error {}
|
||||
|
||||
const INVALID_STRATEGY_MSG =
|
||||
'Invalid auth strategy. Expecting an object with properties {name: string, authenticate: function, verify: function}';
|
||||
|
||||
const validStrategy = strategy => {
|
||||
assert(has('name', strategy), INVALID_STRATEGY_MSG);
|
||||
assert(typeof strategy.name === 'string', INVALID_STRATEGY_MSG);
|
||||
|
||||
assert(has('authenticate', strategy), INVALID_STRATEGY_MSG);
|
||||
assert(typeof strategy.authenticate === 'function', INVALID_STRATEGY_MSG);
|
||||
|
||||
assert(has('verify', strategy), INVALID_STRATEGY_MSG);
|
||||
assert(typeof strategy.verify === 'function', INVALID_STRATEGY_MSG);
|
||||
};
|
||||
|
||||
const createAuthentication = () => {
|
||||
const strategies = [];
|
||||
|
||||
return {
|
||||
register(strategy) {
|
||||
validStrategy(strategy);
|
||||
strategies.push(strategy);
|
||||
|
||||
return () => {
|
||||
strategies.splice(strategies.indexOf(strategy), 1);
|
||||
};
|
||||
},
|
||||
async authenticate(ctx, next) {
|
||||
for (const strategy of strategies) {
|
||||
const result = await strategy.authenticate(ctx);
|
||||
|
||||
const { authenticated = false, credentials } = result || {};
|
||||
|
||||
if (authenticated) {
|
||||
ctx.state.auth = { strategy, credentials };
|
||||
return next();
|
||||
}
|
||||
}
|
||||
|
||||
return next();
|
||||
},
|
||||
async verify(auth, config = {}) {
|
||||
if (config.public) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (!auth) {
|
||||
throw new UnauthorizedError();
|
||||
}
|
||||
|
||||
return await auth.strategy.verify(auth, config);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = () => {
|
||||
return {
|
||||
auth: createAuthentication(),
|
||||
errors: {
|
||||
UnauthorizedError,
|
||||
ForbiddenError,
|
||||
},
|
||||
};
|
||||
};
|
||||
@ -5,6 +5,7 @@ const { createAPI } = require('./api');
|
||||
const createAdminAPI = strapi => {
|
||||
const opts = {
|
||||
prefix: '', // '/admin';
|
||||
type: 'admin',
|
||||
};
|
||||
|
||||
return createAPI(strapi, opts);
|
||||
|
||||
@ -5,11 +5,11 @@ const Router = require('@koa/router');
|
||||
const { createRouteManager } = require('./routing');
|
||||
|
||||
const createAPI = (strapi, opts = {}) => {
|
||||
const { prefix, defaultPolicies } = opts;
|
||||
const { prefix, type } = opts;
|
||||
|
||||
const api = new Router({ prefix });
|
||||
|
||||
const routeManager = createRouteManager(strapi, { defaultPolicies });
|
||||
const routeManager = createRouteManager(strapi, { type });
|
||||
|
||||
return {
|
||||
use(fn) {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
const { toLower, castArray, trim } = require('lodash/fp');
|
||||
const { toLower, castArray, trim, prop } = require('lodash/fp');
|
||||
|
||||
const compose = require('koa-compose');
|
||||
const { resolveMiddlewares } = require('./middleware');
|
||||
@ -9,24 +9,64 @@ const { resolvePolicies } = require('./policy');
|
||||
const getMethod = route => trim(toLower(route.method));
|
||||
const getPath = route => trim(route.path);
|
||||
|
||||
const routeInfoMiddleware = route => (ctx, next) => {
|
||||
const createRouteInfoMiddleware = routeInfo => (ctx, next) => {
|
||||
const route = {
|
||||
...routeInfo,
|
||||
config: routeInfo.config || {},
|
||||
};
|
||||
|
||||
ctx.state.route = route;
|
||||
return next();
|
||||
};
|
||||
|
||||
const getAuthConfig = prop('config.auth');
|
||||
|
||||
const createAuthorizeMiddleware = strapi => async (ctx, next) => {
|
||||
const { auth, route } = ctx.state;
|
||||
|
||||
const authService = strapi.container.get('auth');
|
||||
|
||||
try {
|
||||
await authService.verify(auth, getAuthConfig(route));
|
||||
|
||||
return next();
|
||||
} catch (error) {
|
||||
const { UnauthorizedError, ForbiddenError } = authService.errors;
|
||||
|
||||
if (error instanceof UnauthorizedError) {
|
||||
return ctx.unauthorized();
|
||||
}
|
||||
|
||||
if (error instanceof ForbiddenError) {
|
||||
return ctx.forbidden();
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const createAuthenticateMiddleware = strapi => async (ctx, next) => {
|
||||
return strapi.container.get('auth').authenticate(ctx, next);
|
||||
};
|
||||
|
||||
module.exports = strapi => {
|
||||
return (route, { pluginName, router, apiName }) => {
|
||||
const authenticate = createAuthenticateMiddleware(strapi);
|
||||
const authorize = createAuthorizeMiddleware(strapi);
|
||||
|
||||
return (route, { router }) => {
|
||||
try {
|
||||
const method = getMethod(route);
|
||||
const path = getPath(route);
|
||||
|
||||
const middlewares = resolveMiddlewares(route);
|
||||
const policies = resolvePolicies(route, { pluginName, apiName });
|
||||
const policies = resolvePolicies(route);
|
||||
|
||||
const action = getAction(route, { pluginName, apiName }, strapi);
|
||||
const action = getAction(route, strapi);
|
||||
|
||||
const routeHandler = compose([
|
||||
routeInfoMiddleware(route),
|
||||
createRouteInfoMiddleware(route),
|
||||
authenticate,
|
||||
authorize,
|
||||
...policies,
|
||||
...middlewares,
|
||||
...castArray(action),
|
||||
@ -52,7 +92,10 @@ const getController = (name, { pluginName, apiName }, strapi) => {
|
||||
return strapi.controller(name);
|
||||
};
|
||||
|
||||
const getAction = ({ handler }, { pluginName, apiName }, strapi) => {
|
||||
const getAction = (route, strapi) => {
|
||||
const { handler, info = {} } = route;
|
||||
const { pluginName, apiName } = info;
|
||||
|
||||
if (Array.isArray(handler) || typeof handler === 'function') {
|
||||
return handler;
|
||||
}
|
||||
|
||||
@ -1,50 +1,14 @@
|
||||
'use strict';
|
||||
|
||||
const { prop } = require('lodash/fp');
|
||||
const { createAPI } = require('./api');
|
||||
|
||||
const getAuthConfig = prop('config.auth');
|
||||
|
||||
const createAuthPolicy = strapi => async (ctx, next) => {
|
||||
const { auth, route } = ctx.state;
|
||||
|
||||
if (!route) {
|
||||
return ctx.unauthorized();
|
||||
}
|
||||
|
||||
try {
|
||||
await strapi.container.get('content-api').auth.verify(auth, getAuthConfig(route));
|
||||
|
||||
return next();
|
||||
} catch (error) {
|
||||
const { errors } = strapi.container.get('content-api');
|
||||
|
||||
if (error instanceof errors.UnauthorizedError) {
|
||||
return ctx.unauthorized();
|
||||
}
|
||||
|
||||
if (error instanceof errors.ForbiddenError) {
|
||||
return ctx.forbidden();
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const createContentAPI = strapi => {
|
||||
const opts = {
|
||||
prefix: strapi.config.get('api.prefix', '/api'),
|
||||
defaultPolicies: [createAuthPolicy(strapi)],
|
||||
type: 'content-api',
|
||||
};
|
||||
|
||||
const api = createAPI(strapi, opts);
|
||||
|
||||
// implement auth providers
|
||||
api.use((ctx, next) => {
|
||||
return strapi.container.get('content-api').auth.authenticate(ctx, next);
|
||||
});
|
||||
|
||||
return api;
|
||||
return createAPI(strapi, opts);
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
|
||||
@ -8,13 +8,9 @@ const { createRouteManager } = require('./routing');
|
||||
const { createAdminAPI } = require('./admin-api');
|
||||
const { createContentAPI } = require('./content-api');
|
||||
|
||||
const healthCheck = async (ctx, next) => {
|
||||
if (ctx.request.url === '/_health' && ['HEAD', 'GET'].includes(ctx.request.method)) {
|
||||
ctx.set('strapi', 'You are so French!');
|
||||
ctx.status = 204;
|
||||
} else {
|
||||
await next();
|
||||
}
|
||||
const healthCheck = async ctx => {
|
||||
ctx.set('strapi', 'You are so French!');
|
||||
ctx.status = 204;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -49,7 +45,7 @@ const createServer = strapi => {
|
||||
};
|
||||
|
||||
// init health check
|
||||
app.use(healthCheck);
|
||||
router.all('/_health', healthCheck);
|
||||
|
||||
const state = {
|
||||
mounted: false,
|
||||
|
||||
@ -7,10 +7,14 @@ const { bodyPolicy } = policy;
|
||||
|
||||
const getPoliciesConfig = propOr([], 'config.policies');
|
||||
|
||||
const resolvePolicies = (route, opts = {}) => {
|
||||
const resolvePolicies = route => {
|
||||
const { pluginName, apiName } = route.info || {};
|
||||
const policiesConfig = getPoliciesConfig(route);
|
||||
|
||||
const policies = policiesConfig.map(policyName => policy.get(policyName, opts));
|
||||
const policies = policiesConfig.map(policyName => {
|
||||
return policy.get(policyName, { pluginName, apiName });
|
||||
});
|
||||
|
||||
return [...policies, bodyPolicy];
|
||||
};
|
||||
|
||||
|
||||
@ -69,20 +69,16 @@ const validateRouteConfig = routeConfig => {
|
||||
};
|
||||
|
||||
const createRouteManager = (strapi, opts = {}) => {
|
||||
const { type } = opts;
|
||||
|
||||
const composeEndpoint = createEndpointComposer(strapi);
|
||||
|
||||
const createRoute = (route, router) => {
|
||||
validateRouteConfig(route);
|
||||
|
||||
if (opts.defaultPolicies) {
|
||||
if (has('config.policies', route)) {
|
||||
route.config.policies.unshift(...opts.defaultPolicies);
|
||||
} else {
|
||||
_.set(route, 'config.policies', [...opts.defaultPolicies]);
|
||||
}
|
||||
}
|
||||
_.set(route, 'info.type', type || 'admin');
|
||||
|
||||
composeEndpoint(route, { ...route.info, router });
|
||||
composeEndpoint(route, { router });
|
||||
};
|
||||
|
||||
const addRoutes = (routes, router) => {
|
||||
|
||||
@ -24,7 +24,7 @@ export default {
|
||||
// TODO update doc and guides
|
||||
app.addComponents({ name: 'media-library', Component: InputModalStepper });
|
||||
|
||||
app.addCorePluginMenuLink({
|
||||
app.addMenuLink({
|
||||
to: `/plugins/${pluginId}`,
|
||||
icon,
|
||||
intlLabel: {
|
||||
@ -32,6 +32,11 @@ export default {
|
||||
defaultMessage: 'Media Library',
|
||||
},
|
||||
permissions: pluginPermissions.main,
|
||||
Component: async () => {
|
||||
const component = await import(/* webpackChunkName: "upload" */ './pages/App');
|
||||
|
||||
return component;
|
||||
},
|
||||
});
|
||||
|
||||
// TODO update guide
|
||||
|
||||
@ -32,6 +32,7 @@ module.exports = {
|
||||
method: 'GET',
|
||||
path: '/uploads/(.*)',
|
||||
handler: [range, koaStatic(staticDir, { defer: true, ...localServerConfig })],
|
||||
config: { auth: false },
|
||||
},
|
||||
]);
|
||||
},
|
||||
|
||||
@ -50,8 +50,6 @@ module.exports = (projectDirectory, cliArguments) => {
|
||||
installDependencies: true,
|
||||
strapiDependencies: [
|
||||
'@strapi/strapi',
|
||||
'@strapi/admin',
|
||||
'@strapi/utils',
|
||||
'@strapi/plugin-users-permissions',
|
||||
'@strapi/plugin-i18n',
|
||||
],
|
||||
|
||||
@ -20,34 +20,34 @@ const authenticate = async ctx => {
|
||||
const { id } = await getService('jwt').getToken(ctx);
|
||||
|
||||
if (id === undefined) {
|
||||
throw new Error('Invalid token: Token did not contain required fields');
|
||||
return { error: 'Invalid token: Token did not contain required fields' };
|
||||
}
|
||||
|
||||
// fetch authenticated user
|
||||
const user = await getService('user').fetchAuthenticatedUser(id);
|
||||
|
||||
if (user) {
|
||||
return {
|
||||
authenticated: true,
|
||||
credentials: user,
|
||||
};
|
||||
if (!user) {
|
||||
return { error: 'Invalid credentials' };
|
||||
}
|
||||
|
||||
const advancedSettings = await getAdvancedSettings();
|
||||
|
||||
if (advancedSettings.email_confirmation && !user.confirmed) {
|
||||
return { error: 'Invalid credentials' };
|
||||
}
|
||||
|
||||
if (user.blocked) {
|
||||
return { error: 'Invalid credentials' };
|
||||
}
|
||||
|
||||
ctx.state.user = user;
|
||||
|
||||
return {
|
||||
authenticated: true,
|
||||
credentials: user,
|
||||
};
|
||||
} catch (err) {
|
||||
return { authenticated: false };
|
||||
}
|
||||
|
||||
if (!ctx.state.user) {
|
||||
return { authenticated: false };
|
||||
}
|
||||
|
||||
const advancedSettings = await getAdvancedSettings();
|
||||
|
||||
if (advancedSettings.email_confirmation && !ctx.state.user.confirmed) {
|
||||
return { authenticated: false };
|
||||
}
|
||||
|
||||
if (ctx.state.user.blocked) {
|
||||
return { authenticated: false };
|
||||
return { error: 'Invalid credentials' };
|
||||
}
|
||||
}
|
||||
|
||||
@ -68,7 +68,7 @@ const authenticate = async ctx => {
|
||||
};
|
||||
|
||||
const verify = async (auth, config) => {
|
||||
const { errors } = strapi.container.get('content-api');
|
||||
const { errors } = strapi.container.get('auth');
|
||||
|
||||
const { credentials: user } = auth;
|
||||
|
||||
|
||||
@ -3,5 +3,5 @@
|
||||
const authStrategy = require('./auth/strategy');
|
||||
|
||||
module.exports = strapi => {
|
||||
strapi.container.get('content-api').auth.register(authStrategy);
|
||||
strapi.container.get('auth').register('content-api', authStrategy);
|
||||
};
|
||||
|
||||
@ -20,10 +20,8 @@ const createStrapiInstance = async ({ ensureSuperAdmin = true, logLevel = 'fatal
|
||||
const options = { dir: TEST_APP_URL };
|
||||
const instance = strapi(options);
|
||||
|
||||
await instance.load();
|
||||
|
||||
instance.container.get('content-api').auth.register({
|
||||
name: 'test-strategy',
|
||||
instance.container.get('auth').register('content-api', {
|
||||
name: 'test-auth',
|
||||
authenticate() {
|
||||
return { authenticated: true };
|
||||
},
|
||||
@ -32,6 +30,8 @@ const createStrapiInstance = async ({ ensureSuperAdmin = true, logLevel = 'fatal
|
||||
},
|
||||
});
|
||||
|
||||
await instance.load();
|
||||
|
||||
instance.log.level = logLevel;
|
||||
|
||||
instance.server.mount();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user