diff --git a/packages/core/admin/ee/server/bootstrap.js b/packages/core/admin/ee/server/bootstrap.js index 650ebb4fe5..bf116ef739 100644 --- a/packages/core/admin/ee/server/bootstrap.js +++ b/packages/core/admin/ee/server/bootstrap.js @@ -17,5 +17,7 @@ module.exports = async () => { await actionProvider.registerMany(actions.auditLogs); } + // TODO: check admin seats + await executeCEBootstrap(); }; diff --git a/packages/core/admin/ee/server/routes/features-routes.js b/packages/core/admin/ee/server/routes/features-routes.js deleted file mode 100644 index fa1448667e..0000000000 --- a/packages/core/admin/ee/server/routes/features-routes.js +++ /dev/null @@ -1,80 +0,0 @@ -'use strict'; - -module.exports = { - sso: [ - { - 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', - path: '/providers/options', - handler: 'authentication.getProviderLoginOptions', - config: { - policies: [ - 'admin::isAuthenticatedAdmin', - { name: 'admin::hasPermissions', config: { actions: ['admin::provider-login.read'] } }, - ], - }, - }, - { - method: 'PUT', - path: '/providers/options', - handler: 'authentication.updateProviderLoginOptions', - config: { - policies: [ - 'admin::isAuthenticatedAdmin', - { name: 'admin::hasPermissions', config: { actions: ['admin::provider-login.update'] } }, - ], - }, - }, - ], - 'audit-logs': [ - { - method: 'GET', - path: '/audit-logs', - handler: 'auditLogs.findMany', - config: { - policies: [ - 'admin::isAuthenticatedAdmin', - { - name: 'admin::hasPermissions', - config: { - actions: ['admin::audit-logs.read'], - }, - }, - ], - }, - }, - { - method: 'GET', - path: '/audit-logs/:id', - handler: 'auditLogs.findOne', - config: { - policies: [ - 'admin::isAuthenticatedAdmin', - { - name: 'admin::hasPermissions', - config: { - actions: ['admin::audit-logs.read'], - }, - }, - ], - }, - }, - ], -}; diff --git a/packages/core/admin/ee/server/routes/index.js b/packages/core/admin/ee/server/routes/index.js index c586b7acb0..07954c9dc8 100644 --- a/packages/core/admin/ee/server/routes/index.js +++ b/packages/core/admin/ee/server/routes/index.js @@ -1,17 +1,14 @@ 'use strict'; -// eslint-disable-next-line node/no-extraneous-require const { features } = require('@strapi/strapi/lib/utils/ee'); -const featuresRoutes = require('./features-routes'); -const getFeaturesRoutes = () => { - return Object.entries(featuresRoutes).flatMap(([featureName, featureRoutes]) => { - if (features.isEnabled(featureName)) { - return featureRoutes; - } +const enableFeatureMiddleware = (featureName) => (ctx, next) => { + console.log(featureName, features.isEnabled(featureName)); + if (features.isEnabled(featureName)) { + return next(); + } - return []; - }); + ctx.status = 404; }; module.exports = [ @@ -63,5 +60,93 @@ module.exports = [ ], }, }, - ...getFeaturesRoutes(), + + // SSO + { + method: 'GET', + path: '/providers', + handler: 'authentication.getProviders', + config: { + middlewares: [enableFeatureMiddleware('sso')], + auth: false, + }, + }, + { + method: 'GET', + path: '/connect/:provider', + handler: 'authentication.providerLogin', + config: { + middlewares: [enableFeatureMiddleware('sso')], + auth: false, + }, + }, + { + method: 'POST', + path: '/connect/:provider', + handler: 'authentication.providerLogin', + config: { + middlewares: [enableFeatureMiddleware('sso')], + auth: false, + }, + }, + { + method: 'GET', + path: '/providers/options', + handler: 'authentication.getProviderLoginOptions', + config: { + middlewares: [enableFeatureMiddleware('sso')], + policies: [ + 'admin::isAuthenticatedAdmin', + { name: 'admin::hasPermissions', config: { actions: ['admin::provider-login.read'] } }, + ], + }, + }, + { + method: 'PUT', + path: '/providers/options', + handler: 'authentication.updateProviderLoginOptions', + config: { + middlewares: [enableFeatureMiddleware('sso')], + policies: [ + 'admin::isAuthenticatedAdmin', + { name: 'admin::hasPermissions', config: { actions: ['admin::provider-login.update'] } }, + ], + }, + }, + + // Audit logs + { + method: 'GET', + path: '/audit-logs', + handler: 'auditLogs.findMany', + config: { + middlewares: [enableFeatureMiddleware('audit-logs')], + policies: [ + 'admin::isAuthenticatedAdmin', + { + name: 'admin::hasPermissions', + config: { + actions: ['admin::audit-logs.read'], + }, + }, + ], + }, + }, + { + method: 'GET', + path: '/audit-logs/:id', + handler: 'auditLogs.findOne', + config: { + middlewares: [enableFeatureMiddleware('audit-logs')], + policies: [ + 'admin::isAuthenticatedAdmin', + { + name: 'admin::hasPermissions', + config: { + actions: ['admin::audit-logs.read'], + }, + }, + ], + }, + }, ]; diff --git a/packages/core/admin/ee/server/services/passport.js b/packages/core/admin/ee/server/services/passport.js index 1e0960f7e2..2e47c4b9c0 100644 --- a/packages/core/admin/ee/server/services/passport.js +++ b/packages/core/admin/ee/server/services/passport.js @@ -6,6 +6,28 @@ const { features } = require('@strapi/strapi/lib/utils/ee'); const createLocalStrategy = require('../../../server/services/passport/local-strategy'); const sso = require('./passport/sso'); +// wrap functions with feature flag to allow execute code lazyly +// Looking at the code wrapped we probably can just add a condition in the functions +const wrapWithFeatureFlag = (flag, obj) => { + const newObj = {}; + + Object.keys(obj).forEach((key) => { + if (typeof obj[key] === 'function') { + newObj[key] = (...args) => { + if (!features.isEnabled(flag)) { + throw new Error(`${key} cannot be executed`); + } + + return obj[key].apply(newObj, ...args); + }; + } else { + newObj[key] = obj[key]; + } + }); + + return newObj; +}; + const getPassportStrategies = () => { const localStrategy = createLocalStrategy(strapi); @@ -25,8 +47,5 @@ const getPassportStrategies = () => { module.exports = { getPassportStrategies, + ...wrapWithFeatureFlag('sso', sso), }; - -if (features.isEnabled('sso')) { - Object.assign(module.exports, sso); -} diff --git a/packages/core/strapi/ee/index.js b/packages/core/strapi/ee/index.js index e46b6b7f25..930ffb1b83 100644 --- a/packages/core/strapi/ee/index.js +++ b/packages/core/strapi/ee/index.js @@ -21,7 +21,7 @@ const defaultFeatures = { gold: ['sso'], }; -module.exports = ({ dir, logger = noLog }) => { +const EEService = ({ dir, logger = noLog }) => { if (_.has(internals, 'isEE')) return internals.isEE; const warnAndReturn = (msg = 'Invalid license. Starting in CE.') => { @@ -49,6 +49,8 @@ module.exports = ({ dir, logger = noLog }) => { return false; } + // TODO: optimistically return true if license key is valid + try { const plainLicense = Buffer.from(license, 'base64').toString(); const [signatureb64, contentb64] = plainLicense.split('\n'); @@ -64,6 +66,8 @@ module.exports = ({ dir, logger = noLog }) => { if (!isValid) return warnAndReturn(); internals.licenseInfo = JSON.parse(content); + internals.licenseInfo.features = + internals.licenseInfo.features || defaultFeatures[internals.licenseInfo.type]; const expirationTime = new Date(internals.licenseInfo.expireAt).getTime(); if (expirationTime < new Date().getTime()) { @@ -77,7 +81,14 @@ module.exports = ({ dir, logger = noLog }) => { return true; }; -Object.defineProperty(module.exports, 'licenseInfo', { +EEService.checkLicense = async () => { + // TODO: online / DB check of the license info + // TODO: refresh info if the DB info is outdated + // TODO: register cron + // internals.licenseInfo = await db.getLicense(); +}; + +Object.defineProperty(EEService, 'licenseInfo', { get() { mustHaveKey('licenseInfo'); return internals.licenseInfo; @@ -86,7 +97,7 @@ Object.defineProperty(module.exports, 'licenseInfo', { enumerable: false, }); -Object.defineProperty(module.exports, 'isEE', { +Object.defineProperty(EEService, 'isEE', { get() { mustHaveKey('isEE'); return internals.isEE; @@ -95,20 +106,14 @@ Object.defineProperty(module.exports, 'isEE', { enumerable: false, }); -Object.defineProperty(module.exports, 'features', { +Object.defineProperty(EEService, 'features', { get() { - const licenseInfo = module.exports.licenseInfo; - - const { type: licenseType } = module.exports.licenseInfo; - - const features = licenseInfo.features || defaultFeatures[licenseType]; - return { isEnabled(feature) { - return features.includes(feature); + return internals.licenseInfo.features.includes(feature); }, getEnabled() { - return features; + return internals.licenseInfo.features; }, }; }, @@ -123,3 +128,5 @@ const mustHaveKey = (key) => { throw err; } }; + +module.exports = EEService; diff --git a/packages/core/strapi/lib/Strapi.js b/packages/core/strapi/lib/Strapi.js index 934a087d6f..6b7e58b188 100644 --- a/packages/core/strapi/lib/Strapi.js +++ b/packages/core/strapi/lib/Strapi.js @@ -365,6 +365,8 @@ class Strapi { } async register() { + await this.loadEE(); + await Promise.all([ this.loadApp(), this.loadSanitizers(), @@ -455,6 +457,8 @@ class Strapi { value: strapi.contentTypes, }); + await ee.checkLicense(); + await this.startWebhooks(); await this.server.initMiddlewares(); @@ -480,7 +484,6 @@ class Strapi { } async load() { - await this.loadEE(); await this.register(); await this.bootstrap(); diff --git a/packages/core/strapi/lib/services/metrics/index.js b/packages/core/strapi/lib/services/metrics/index.js index 908f2c98d8..32ad131ca3 100644 --- a/packages/core/strapi/lib/services/metrics/index.js +++ b/packages/core/strapi/lib/services/metrics/index.js @@ -47,6 +47,7 @@ const createTelemetryInstance = (strapi) => { } }, bootstrap() { + // TODO: remove if (strapi.EE === true && ee.isEE === true) { const pingDisabled = isTruthy(process.env.STRAPI_LICENSE_PING_DISABLED) && ee.licenseInfo.type === 'gold';