feat(core): Read ephemeral license from environment and clean up ee flags (#5797)
* remove enterprise feature schema for license.cert * bump license sdk version * Update packages/cli/package.json Co-authored-by: Cornelius Suermann <cornelius@n8n.io> --------- Co-authored-by: Cornelius Suermann <cornelius@n8n.io>
This commit is contained in:
committed by
GitHub
parent
5f6183a031
commit
a81ca7c19c
@@ -2,8 +2,6 @@ import type { LdapConfig } from './types';
|
||||
|
||||
export const LDAP_FEATURE_NAME = 'features.ldap';
|
||||
|
||||
export const LDAP_ENABLED = 'enterprise.features.ldap';
|
||||
|
||||
export const LDAP_LOGIN_LABEL = 'sso.ldap.loginLabel';
|
||||
|
||||
export const LDAP_LOGIN_ENABLED = 'sso.ldap.loginEnabled';
|
||||
|
||||
@@ -17,7 +17,6 @@ import { LdapManager } from './LdapManager.ee';
|
||||
import {
|
||||
BINARY_AD_ATTRIBUTES,
|
||||
LDAP_CONFIG_SCHEMA,
|
||||
LDAP_ENABLED,
|
||||
LDAP_FEATURE_NAME,
|
||||
LDAP_LOGIN_ENABLED,
|
||||
LDAP_LOGIN_LABEL,
|
||||
@@ -37,7 +36,7 @@ import {
|
||||
*/
|
||||
export const isLdapEnabled = (): boolean => {
|
||||
const license = Container.get(License);
|
||||
return isUserManagementEnabled() && (config.getEnv(LDAP_ENABLED) || license.isLdapEnabled());
|
||||
return isUserManagementEnabled() && license.isLdapEnabled();
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -8,6 +8,11 @@ import { LICENSE_FEATURES, N8N_VERSION, SETTINGS_LICENSE_CERT_KEY } from './cons
|
||||
import { Service } from 'typedi';
|
||||
|
||||
async function loadCertStr(): Promise<TLicenseContainerStr> {
|
||||
// if we have an ephemeral license, we don't want to load it from the database
|
||||
const ephemeralLicense = config.get('license.cert');
|
||||
if (ephemeralLicense) {
|
||||
return ephemeralLicense;
|
||||
}
|
||||
const databaseSettings = await Db.collections.Settings.findOne({
|
||||
where: {
|
||||
key: SETTINGS_LICENSE_CERT_KEY,
|
||||
@@ -18,6 +23,8 @@ async function loadCertStr(): Promise<TLicenseContainerStr> {
|
||||
}
|
||||
|
||||
async function saveCertStr(value: TLicenseContainerStr): Promise<void> {
|
||||
// if we have an ephemeral license, we don't want to save it to the database
|
||||
if (config.get('license.cert')) return;
|
||||
await Db.collections.Settings.upsert(
|
||||
{
|
||||
key: SETTINGS_LICENSE_CERT_KEY,
|
||||
|
||||
@@ -311,8 +311,8 @@ class Server extends AbstractServer {
|
||||
sharing: false,
|
||||
ldap: false,
|
||||
saml: false,
|
||||
logStreaming: config.getEnv('enterprise.features.logStreaming'),
|
||||
advancedExecutionFilters: config.getEnv('enterprise.features.advancedExecutionFilters'),
|
||||
logStreaming: false,
|
||||
advancedExecutionFilters: false,
|
||||
},
|
||||
hideUsagePage: config.getEnv('hideUsagePage'),
|
||||
license: {
|
||||
|
||||
@@ -57,10 +57,7 @@ export function isUserManagementEnabled(): boolean {
|
||||
|
||||
export function isSharingEnabled(): boolean {
|
||||
const license = Container.get(License);
|
||||
return (
|
||||
isUserManagementEnabled() &&
|
||||
(config.getEnv('enterprise.features.sharing') || license.isSharingEnabled())
|
||||
);
|
||||
return isUserManagementEnabled() && license.isSharingEnabled();
|
||||
}
|
||||
|
||||
export async function getRoleId(scope: Role['scope'], name: Role['name']): Promise<Role['id']> {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import { Router } from 'express';
|
||||
import type { Request } from 'express';
|
||||
import bodyParser from 'body-parser';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import config from '@/config';
|
||||
@@ -12,12 +13,26 @@ import * as Db from '@/Db';
|
||||
import type { Role } from '@db/entities/Role';
|
||||
import { hashPassword } from '@/UserManagement/UserManagementHelper';
|
||||
import { eventBus } from '@/eventbus/MessageEventBus/MessageEventBus';
|
||||
import Container from 'typedi';
|
||||
import { License } from '../License';
|
||||
|
||||
if (process.env.E2E_TESTS !== 'true') {
|
||||
console.error('E2E endpoints only allowed during E2E tests');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const enabledFeatures = {
|
||||
sharing: true, //default to true here instead of setting it in config/index.ts for e2e
|
||||
ldap: false,
|
||||
saml: false,
|
||||
logStreaming: false,
|
||||
advancedExecutionFilters: false,
|
||||
};
|
||||
|
||||
type Feature = keyof typeof enabledFeatures;
|
||||
|
||||
Container.get(License).isFeatureEnabled = (feature: Feature) => enabledFeatures[feature] ?? false;
|
||||
|
||||
const tablesToTruncate = [
|
||||
'auth_identity',
|
||||
'auth_provider_sync_history',
|
||||
@@ -78,7 +93,7 @@ const setupUserManagement = async () => {
|
||||
};
|
||||
|
||||
const resetLogStreaming = async () => {
|
||||
config.set('enterprise.features.logStreaming', false);
|
||||
enabledFeatures.logStreaming = false;
|
||||
for (const id in eventBus.destinations) {
|
||||
await eventBus.removeDestination(id);
|
||||
}
|
||||
@@ -127,7 +142,8 @@ e2eController.post('/db/setup-owner', bodyParser.json(), async (req, res) => {
|
||||
res.writeHead(204).end();
|
||||
});
|
||||
|
||||
e2eController.post('/enable-feature/:feature', async (req, res) => {
|
||||
config.set(`enterprise.features.${req.params.feature}`, true);
|
||||
e2eController.post('/enable-feature/:feature', async (req: Request<{ feature: Feature }>, res) => {
|
||||
const { feature } = req.params;
|
||||
enabledFeatures[feature] = true;
|
||||
res.writeHead(204).end();
|
||||
});
|
||||
|
||||
@@ -26,10 +26,6 @@ if (inE2ETests) {
|
||||
|
||||
const config = convict(schema, { args: [] });
|
||||
|
||||
if (inE2ETests) {
|
||||
config.set('enterprise.features.sharing', true);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||
config.getEnv = config.get;
|
||||
|
||||
|
||||
@@ -990,31 +990,6 @@ export const schema = {
|
||||
},
|
||||
},
|
||||
|
||||
enterprise: {
|
||||
features: {
|
||||
sharing: {
|
||||
format: Boolean,
|
||||
default: false,
|
||||
},
|
||||
ldap: {
|
||||
format: Boolean,
|
||||
default: false,
|
||||
},
|
||||
saml: {
|
||||
format: Boolean,
|
||||
default: false,
|
||||
},
|
||||
logStreaming: {
|
||||
format: Boolean,
|
||||
default: false,
|
||||
},
|
||||
advancedExecutionFilters: {
|
||||
format: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
sso: {
|
||||
justInTimeProvisioning: {
|
||||
format: Boolean,
|
||||
@@ -1166,6 +1141,12 @@ export const schema = {
|
||||
env: 'N8N_LICENSE_TENANT_ID',
|
||||
doc: 'Tenant id used by the license manager',
|
||||
},
|
||||
cert: {
|
||||
format: String,
|
||||
default: '',
|
||||
env: 'N8N_LICENSE_CERT',
|
||||
doc: 'Ephemeral license certificate',
|
||||
},
|
||||
},
|
||||
|
||||
hideUsagePage: {
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import config from '@/config';
|
||||
import { License } from '@/License';
|
||||
import { Container } from 'typedi';
|
||||
|
||||
export function isLogStreamingEnabled(): boolean {
|
||||
const license = Container.get(License);
|
||||
return config.getEnv('enterprise.features.logStreaming') || license.isLogStreamingEnabled();
|
||||
return license.isLogStreamingEnabled();
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ import { Container } from 'typedi';
|
||||
import type { IExecutionFlattedDb } from '@/Interfaces';
|
||||
import type { ExecutionStatus } from 'n8n-workflow';
|
||||
import { License } from '@/License';
|
||||
import config from '@/config';
|
||||
|
||||
export function getStatusUsingPreviousExecutionStatusMethod(
|
||||
execution: IExecutionFlattedDb,
|
||||
@@ -22,8 +21,5 @@ export function getStatusUsingPreviousExecutionStatusMethod(
|
||||
|
||||
export function isAdvancedExecutionFiltersEnabled(): boolean {
|
||||
const license = Container.get(License);
|
||||
return (
|
||||
config.getEnv('enterprise.features.advancedExecutionFilters') ||
|
||||
license.isAdvancedExecutionFiltersEnabled()
|
||||
);
|
||||
return license.isAdvancedExecutionFiltersEnabled();
|
||||
}
|
||||
|
||||
@@ -28,8 +28,6 @@ export class SamlUrls {
|
||||
|
||||
export const SAML_PREFERENCES_DB_KEY = 'features.saml';
|
||||
|
||||
export const SAML_ENTERPRISE_FEATURE_ENABLED = 'enterprise.features.saml';
|
||||
|
||||
export const SAML_LOGIN_LABEL = 'sso.saml.loginLabel';
|
||||
|
||||
export const SAML_LOGIN_ENABLED = 'sso.saml.loginEnabled';
|
||||
|
||||
@@ -10,7 +10,7 @@ import type { SamlPreferences } from './types/samlPreferences';
|
||||
import type { SamlUserAttributes } from './types/samlUserAttributes';
|
||||
import type { FlowResult } from 'samlify/types/src/flow';
|
||||
import type { SamlAttributeMapping } from './types/samlAttributeMapping';
|
||||
import { SAML_ENTERPRISE_FEATURE_ENABLED, SAML_LOGIN_ENABLED, SAML_LOGIN_LABEL } from './constants';
|
||||
import { SAML_LOGIN_ENABLED, SAML_LOGIN_LABEL } from './constants';
|
||||
import {
|
||||
isEmailCurrentAuthenticationMethod,
|
||||
isSamlCurrentAuthenticationMethod,
|
||||
@@ -52,10 +52,7 @@ export function setSamlLoginLabel(label: string): void {
|
||||
|
||||
export function isSamlLicensed(): boolean {
|
||||
const license = Container.get(License);
|
||||
return (
|
||||
isUserManagementEnabled() &&
|
||||
(license.isSamlEnabled() || config.getEnv(SAML_ENTERPRISE_FEATURE_ENABLED))
|
||||
);
|
||||
return isUserManagementEnabled() && license.isSamlEnabled();
|
||||
}
|
||||
|
||||
export function isSamlLicensedAndEnabled(): boolean {
|
||||
|
||||
Reference in New Issue
Block a user