diff --git a/packages/core/admin/admin/src/pages/Admin/index.js b/packages/core/admin/admin/src/pages/Admin/index.js
index f7dd1ecb2c..4960112fbd 100644
--- a/packages/core/admin/admin/src/pages/Admin/index.js
+++ b/packages/core/admin/admin/src/pages/Admin/index.js
@@ -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 = () => {
-
-
{routes}
diff --git a/packages/core/admin/admin/src/plugins.js b/packages/core/admin/admin/src/plugins.js
index 981437e347..b5e257e446 100644
--- a/packages/core/admin/admin/src/plugins.js
+++ b/packages/core/admin/admin/src/plugins.js
@@ -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;
diff --git a/packages/core/admin/ee/server/routes/features-routes.js b/packages/core/admin/ee/server/routes/features-routes.js
index 1612012426..b44a1b9cff 100644
--- a/packages/core/admin/ee/server/routes/features-routes.js
+++ b/packages/core/admin/ee/server/routes/features-routes.js
@@ -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',
diff --git a/packages/core/admin/index.js b/packages/core/admin/index.js
index 30d04cde20..74ddf13fdf 100644
--- a/packages/core/admin/index.js
+++ b/packages/core/admin/index.js
@@ -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,
diff --git a/packages/core/admin/scripts/build.js b/packages/core/admin/scripts/build.js
index f9f7056f8c..04f4e4b00c 100644
--- a/packages/core/admin/scripts/build.js
+++ b/packages/core/admin/scripts/build.js
@@ -11,6 +11,8 @@ const buildAdmin = async () => {
const args = {
entry,
dest,
+ cacheDir: __dirname,
+ pluginsPath: [path.resolve(__dirname, '../../../..')],
env: 'production',
optimize: true,
options: {
diff --git a/packages/core/admin/server/policies/hasPermissions.js b/packages/core/admin/server/policies/hasPermissions.js
index 6b77c47de3..4fda9bee29 100644
--- a/packages/core/admin/server/policies/hasPermissions.js
+++ b/packages/core/admin/server/policies/hasPermissions.js
@@ -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();
}
diff --git a/packages/core/admin/server/policies/isAuthenticatedAdmin.js b/packages/core/admin/server/policies/isAuthenticatedAdmin.js
index 90dc786865..3d3bc8b7b6 100644
--- a/packages/core/admin/server/policies/isAuthenticatedAdmin.js
+++ b/packages/core/admin/server/policies/isAuthenticatedAdmin.js
@@ -1,7 +1,7 @@
'use strict';
module.exports = (ctx, next) => {
- if (!ctx.state.isAuthenticatedAdmin) {
+ if (!ctx.state.isAuthenticated) {
return ctx.unauthorized();
}
diff --git a/packages/core/admin/server/register.js b/packages/core/admin/server/register.js
index e2d7d9c86b..23ca2dc11c 100644
--- a/packages/core/admin/server/register.js
+++ b/packages/core/admin/server/register.js
@@ -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);
diff --git a/packages/core/admin/server/routes/admin.js b/packages/core/admin/server/routes/admin.js
new file mode 100644
index 0000000000..35ab8a9027
--- /dev/null
+++ b/packages/core/admin/server/routes/admin.js
@@ -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'] },
+ },
+ ],
+ },
+ },
+];
diff --git a/packages/core/admin/server/routes/authentication.js b/packages/core/admin/server/routes/authentication.js
new file mode 100644
index 0000000000..9b031e07ca
--- /dev/null
+++ b/packages/core/admin/server/routes/authentication.js
@@ -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 },
+ },
+];
diff --git a/packages/core/admin/server/routes/index.js b/packages/core/admin/server/routes/index.js
index f559f0946b..10fdf88795 100644
--- a/packages/core/admin/server/routes/index.js
+++ b/packages/core/admin/server/routes/index.js
@@ -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];
diff --git a/packages/core/admin/server/routes/permissions.js b/packages/core/admin/server/routes/permissions.js
new file mode 100644
index 0000000000..5e279338e2
--- /dev/null
+++ b/packages/core/admin/server/routes/permissions.js
@@ -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'],
+ },
+ },
+];
diff --git a/packages/core/admin/server/routes/roles.js b/packages/core/admin/server/routes/roles.js
new file mode 100644
index 0000000000..e181adac75
--- /dev/null
+++ b/packages/core/admin/server/routes/roles.js
@@ -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'] } },
+ ],
+ },
+ },
+];
diff --git a/packages/core/admin/server/routes/users.js b/packages/core/admin/server/routes/users.js
new file mode 100644
index 0000000000..272fddc470
--- /dev/null
+++ b/packages/core/admin/server/routes/users.js
@@ -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'] } }],
+ },
+ },
+];
diff --git a/packages/core/admin/server/routes/webhooks.js b/packages/core/admin/server/routes/webhooks.js
new file mode 100644
index 0000000000..6e5eaa6574
--- /dev/null
+++ b/packages/core/admin/server/routes/webhooks.js
@@ -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: [],
+ },
+ },
+];
diff --git a/packages/core/admin/webpack.config.dev.js b/packages/core/admin/webpack.config.dev.js
index d2a4f9255c..e30d25008f 100644
--- a/packages/core/admin/webpack.config.dev.js
+++ b/packages/core/admin/webpack.config.dev.js
@@ -22,6 +22,8 @@ module.exports = () => {
const args = {
entry,
+ cacheDir: __dirname,
+ pluginsPath: [path.resolve(__dirname, '../../..')],
dest,
env,
options,
diff --git a/packages/core/admin/webpack.config.js b/packages/core/admin/webpack.config.js
index 6285c61c6f..edac4c7cfe 100644
--- a/packages/core/admin/webpack.config.js
+++ b/packages/core/admin/webpack.config.js
@@ -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: {
diff --git a/packages/core/content-type-builder/admin/src/index.js b/packages/core/content-type-builder/admin/src/index.js
index f233b48f32..1f4676bc2d 100644
--- a/packages/core/content-type-builder/admin/src/index.js
+++ b/packages/core/content-type-builder/admin/src/index.js
@@ -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({
diff --git a/packages/core/email/server/routes/admin.js b/packages/core/email/server/routes/admin.js
index 341550bb48..70b94e589b 100644
--- a/packages/core/email/server/routes/admin.js
+++ b/packages/core/email/server/routes/admin.js
@@ -7,7 +7,9 @@ module.exports = {
method: 'POST',
path: '/',
handler: 'email.send',
- config: {},
+ config: {
+ policies: ['admin::isAuthenticatedAdmin'],
+ },
},
{
method: 'POST',
diff --git a/packages/core/strapi/lib/Strapi.js b/packages/core/strapi/lib/Strapi.js
index 40c0699f96..e2dbba44cc 100644
--- a/packages/core/strapi/lib/Strapi.js
+++ b/packages/core/strapi/lib/Strapi.js
@@ -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();
diff --git a/packages/core/strapi/lib/commands/build.js b/packages/core/strapi/lib/commands/build.js
index 256d7f1e78..79545a76eb 100644
--- a/packages/core/strapi/lib/commands/build.js
+++ b/packages/core/strapi/lib/commands/build.js
@@ -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,
diff --git a/packages/core/strapi/lib/commands/watchAdmin.js b/packages/core/strapi/lib/commands/watchAdmin.js
index 9a68367b6a..b259316913 100644
--- a/packages/core/strapi/lib/commands/watchAdmin.js
+++ b/packages/core/strapi/lib/commands/watchAdmin.js
@@ -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() : [],
diff --git a/packages/core/strapi/lib/middlewares/index.js b/packages/core/strapi/lib/middlewares/index.js
index 506ed3734e..6aab681925 100644
--- a/packages/core/strapi/lib/middlewares/index.js
+++ b/packages/core/strapi/lib/middlewares/index.js
@@ -3,6 +3,7 @@
const { uniq, difference, get, isUndefined, merge } = require('lodash');
const requiredMiddlewares = [
+ 'auth',
'responses',
'router',
'logger',
diff --git a/packages/core/strapi/lib/middlewares/public/index.js b/packages/core/strapi/lib/middlewares/public/index.js
index e273850d5b..25479a8fcc 100644
--- a/packages/core/strapi/lib/middlewares/public/index.js
+++ b/packages/core/strapi/lib/middlewares/public/index.js
@@ -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 },
},
]);
},
diff --git a/packages/core/strapi/lib/services/auth/index.js b/packages/core/strapi/lib/services/auth/index.js
new file mode 100644
index 0000000000..093ad98de0
--- /dev/null
+++ b/packages/core/strapi/lib/services/auth/index.js
@@ -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;
diff --git a/packages/core/strapi/lib/services/content-api/index.js b/packages/core/strapi/lib/services/content-api/index.js
deleted file mode 100644
index e500ea53d8..0000000000
--- a/packages/core/strapi/lib/services/content-api/index.js
+++ /dev/null
@@ -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,
- },
- };
-};
diff --git a/packages/core/strapi/lib/services/server/admin-api.js b/packages/core/strapi/lib/services/server/admin-api.js
index 23f5b6e7b8..86c915a023 100644
--- a/packages/core/strapi/lib/services/server/admin-api.js
+++ b/packages/core/strapi/lib/services/server/admin-api.js
@@ -5,6 +5,7 @@ const { createAPI } = require('./api');
const createAdminAPI = strapi => {
const opts = {
prefix: '', // '/admin';
+ type: 'admin',
};
return createAPI(strapi, opts);
diff --git a/packages/core/strapi/lib/services/server/api.js b/packages/core/strapi/lib/services/server/api.js
index 02fadb0f1d..52c5dcab83 100644
--- a/packages/core/strapi/lib/services/server/api.js
+++ b/packages/core/strapi/lib/services/server/api.js
@@ -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) {
diff --git a/packages/core/strapi/lib/services/server/compose-endpoint.js b/packages/core/strapi/lib/services/server/compose-endpoint.js
index 97e65ba5d8..73777b4419 100644
--- a/packages/core/strapi/lib/services/server/compose-endpoint.js
+++ b/packages/core/strapi/lib/services/server/compose-endpoint.js
@@ -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;
}
diff --git a/packages/core/strapi/lib/services/server/content-api.js b/packages/core/strapi/lib/services/server/content-api.js
index e1ec4a6fda..201ebb6c6b 100644
--- a/packages/core/strapi/lib/services/server/content-api.js
+++ b/packages/core/strapi/lib/services/server/content-api.js
@@ -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 = {
diff --git a/packages/core/strapi/lib/services/server/index.js b/packages/core/strapi/lib/services/server/index.js
index d5ae9453cb..9d549f28d8 100644
--- a/packages/core/strapi/lib/services/server/index.js
+++ b/packages/core/strapi/lib/services/server/index.js
@@ -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,
diff --git a/packages/core/strapi/lib/services/server/policy.js b/packages/core/strapi/lib/services/server/policy.js
index 984f24bda1..f35668dda1 100644
--- a/packages/core/strapi/lib/services/server/policy.js
+++ b/packages/core/strapi/lib/services/server/policy.js
@@ -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];
};
diff --git a/packages/core/strapi/lib/services/server/routing.js b/packages/core/strapi/lib/services/server/routing.js
index e696063845..7f7c442644 100644
--- a/packages/core/strapi/lib/services/server/routing.js
+++ b/packages/core/strapi/lib/services/server/routing.js
@@ -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) => {
diff --git a/packages/core/upload/admin/src/index.js b/packages/core/upload/admin/src/index.js
index f784d02912..e743075f7f 100644
--- a/packages/core/upload/admin/src/index.js
+++ b/packages/core/upload/admin/src/index.js
@@ -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
diff --git a/packages/core/upload/server/middlewares/upload.js b/packages/core/upload/server/middlewares/upload.js
index 8756833ccd..a96bf57db3 100644
--- a/packages/core/upload/server/middlewares/upload.js
+++ b/packages/core/upload/server/middlewares/upload.js
@@ -32,6 +32,7 @@ module.exports = {
method: 'GET',
path: '/uploads/(.*)',
handler: [range, koaStatic(staticDir, { defer: true, ...localServerConfig })],
+ config: { auth: false },
},
]);
},
diff --git a/packages/generators/app/lib/index.js b/packages/generators/app/lib/index.js
index 3857b53949..f5d2f03458 100644
--- a/packages/generators/app/lib/index.js
+++ b/packages/generators/app/lib/index.js
@@ -50,8 +50,6 @@ module.exports = (projectDirectory, cliArguments) => {
installDependencies: true,
strapiDependencies: [
'@strapi/strapi',
- '@strapi/admin',
- '@strapi/utils',
'@strapi/plugin-users-permissions',
'@strapi/plugin-i18n',
],
diff --git a/packages/plugins/users-permissions/server/auth/strategy.js b/packages/plugins/users-permissions/server/auth/strategy.js
index 9f261f313b..01304e66b3 100644
--- a/packages/plugins/users-permissions/server/auth/strategy.js
+++ b/packages/plugins/users-permissions/server/auth/strategy.js
@@ -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;
diff --git a/packages/plugins/users-permissions/server/register.js b/packages/plugins/users-permissions/server/register.js
index c6454c832c..dd1a060fa1 100644
--- a/packages/plugins/users-permissions/server/register.js
+++ b/packages/plugins/users-permissions/server/register.js
@@ -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);
};
diff --git a/test/helpers/strapi.js b/test/helpers/strapi.js
index 6dbdf5955f..528d10c662 100644
--- a/test/helpers/strapi.js
+++ b/test/helpers/strapi.js
@@ -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();