mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 10:02:05 +00:00
refactor(core): Port SSO config (#17044)
This commit is contained in:
48
packages/@n8n/config/src/configs/sso.config.ts
Normal file
48
packages/@n8n/config/src/configs/sso.config.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import { Config, Env, Nested } from '../decorators';
|
||||||
|
|
||||||
|
@Config
|
||||||
|
class SamlConfig {
|
||||||
|
/** Whether to enable SAML SSO. */
|
||||||
|
@Env('N8N_SSO_SAML_LOGIN_ENABLED')
|
||||||
|
loginEnabled: boolean = false;
|
||||||
|
|
||||||
|
@Env('N8N_SSO_SAML_LOGIN_LABEL')
|
||||||
|
loginLabel: string = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
@Config
|
||||||
|
class OidcConfig {
|
||||||
|
/** Whether to enable OIDC SSO. */
|
||||||
|
@Env('N8N_SSO_OIDC_LOGIN_ENABLED')
|
||||||
|
loginEnabled: boolean = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Config
|
||||||
|
class LdapConfig {
|
||||||
|
/** Whether to enable LDAP SSO. */
|
||||||
|
@Env('N8N_SSO_LDAP_LOGIN_ENABLED')
|
||||||
|
loginEnabled: boolean = false;
|
||||||
|
|
||||||
|
@Env('N8N_SSO_LDAP_LOGIN_LABEL')
|
||||||
|
loginLabel: string = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
@Config
|
||||||
|
export class SsoConfig {
|
||||||
|
/** Whether to create users when they log in via SSO. */
|
||||||
|
@Env('N8N_SSO_JUST_IN_TIME_PROVISIONING')
|
||||||
|
justInTimeProvisioning: boolean = true;
|
||||||
|
|
||||||
|
/** Whether to redirect users from the login dialog to initialize SSO flow. */
|
||||||
|
@Env('N8N_SSO_REDIRECT_LOGIN_TO_SSO')
|
||||||
|
redirectLoginToSso: boolean = true;
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
saml: SamlConfig;
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
oidc: OidcConfig;
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
ldap: LdapConfig;
|
||||||
|
}
|
||||||
@@ -25,6 +25,7 @@ import { TaskRunnersConfig } from './configs/runners.config';
|
|||||||
import { ScalingModeConfig } from './configs/scaling-mode.config';
|
import { ScalingModeConfig } from './configs/scaling-mode.config';
|
||||||
import { SecurityConfig } from './configs/security.config';
|
import { SecurityConfig } from './configs/security.config';
|
||||||
import { SentryConfig } from './configs/sentry.config';
|
import { SentryConfig } from './configs/sentry.config';
|
||||||
|
import { SsoConfig } from './configs/sso.config';
|
||||||
import { TagsConfig } from './configs/tags.config';
|
import { TagsConfig } from './configs/tags.config';
|
||||||
import { TemplatesConfig } from './configs/templates.config';
|
import { TemplatesConfig } from './configs/templates.config';
|
||||||
import { UserManagementConfig } from './configs/user-management.config';
|
import { UserManagementConfig } from './configs/user-management.config';
|
||||||
@@ -167,6 +168,9 @@ export class GlobalConfig {
|
|||||||
@Nested
|
@Nested
|
||||||
personalization: PersonalizationConfig;
|
personalization: PersonalizationConfig;
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
sso: SsoConfig;
|
||||||
|
|
||||||
/** Default locale for the UI. */
|
/** Default locale for the UI. */
|
||||||
@Env('N8N_DEFAULT_LOCALE')
|
@Env('N8N_DEFAULT_LOCALE')
|
||||||
defaultLocale: string = 'en';
|
defaultLocale: string = 'en';
|
||||||
|
|||||||
@@ -331,6 +331,21 @@ describe('GlobalConfig', () => {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
pruneTime: -1,
|
pruneTime: -1,
|
||||||
},
|
},
|
||||||
|
sso: {
|
||||||
|
justInTimeProvisioning: true,
|
||||||
|
redirectLoginToSso: true,
|
||||||
|
saml: {
|
||||||
|
loginEnabled: false,
|
||||||
|
loginLabel: '',
|
||||||
|
},
|
||||||
|
oidc: {
|
||||||
|
loginEnabled: false,
|
||||||
|
},
|
||||||
|
ldap: {
|
||||||
|
loginEnabled: false,
|
||||||
|
loginLabel: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
it('should use all default values when no env variables are defined', () => {
|
it('should use all default values when no env variables are defined', () => {
|
||||||
|
|||||||
@@ -146,47 +146,6 @@ export const schema = {
|
|||||||
env: 'EXTERNAL_FRONTEND_HOOKS_URLS',
|
env: 'EXTERNAL_FRONTEND_HOOKS_URLS',
|
||||||
},
|
},
|
||||||
|
|
||||||
sso: {
|
|
||||||
justInTimeProvisioning: {
|
|
||||||
format: Boolean,
|
|
||||||
default: true,
|
|
||||||
doc: 'Whether to automatically create users when they login via SSO.',
|
|
||||||
},
|
|
||||||
redirectLoginToSso: {
|
|
||||||
format: Boolean,
|
|
||||||
default: true,
|
|
||||||
doc: 'Whether to automatically redirect users from login dialog to initialize SSO flow.',
|
|
||||||
},
|
|
||||||
saml: {
|
|
||||||
loginEnabled: {
|
|
||||||
format: Boolean,
|
|
||||||
default: false,
|
|
||||||
doc: 'Whether to enable SAML SSO.',
|
|
||||||
},
|
|
||||||
loginLabel: {
|
|
||||||
format: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
oidc: {
|
|
||||||
loginEnabled: {
|
|
||||||
format: Boolean,
|
|
||||||
default: false,
|
|
||||||
doc: 'Whether to enable OIDC SSO.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ldap: {
|
|
||||||
loginEnabled: {
|
|
||||||
format: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
loginLabel: {
|
|
||||||
format: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
redis: {
|
redis: {
|
||||||
prefix: {
|
prefix: {
|
||||||
doc: 'Prefix for all n8n related keys',
|
doc: 'Prefix for all n8n related keys',
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { mockLogger } from '@n8n/backend-test-utils';
|
import { mockLogger, mockInstance } from '@n8n/backend-test-utils';
|
||||||
import { mockInstance } from '@n8n/backend-test-utils';
|
import { GlobalConfig } from '@n8n/config';
|
||||||
import { LDAP_FEATURE_NAME, type LdapConfig } from '@n8n/constants';
|
import { LDAP_FEATURE_NAME, type LdapConfig } from '@n8n/constants';
|
||||||
import type { Settings } from '@n8n/db';
|
import type { Settings } from '@n8n/db';
|
||||||
import { AuthIdentityRepository, SettingsRepository } from '@n8n/db';
|
import { AuthIdentityRepository, SettingsRepository } from '@n8n/db';
|
||||||
|
import { Container } from '@n8n/di';
|
||||||
import { QueryFailedError } from '@n8n/typeorm';
|
import { QueryFailedError } from '@n8n/typeorm';
|
||||||
import { mock } from 'jest-mock-extended';
|
import { mock } from 'jest-mock-extended';
|
||||||
import { Client } from 'ldapts';
|
import { Client } from 'ldapts';
|
||||||
@@ -12,7 +13,7 @@ import { randomString } from 'n8n-workflow';
|
|||||||
import config from '@/config';
|
import config from '@/config';
|
||||||
import type { EventService } from '@/events/event.service';
|
import type { EventService } from '@/events/event.service';
|
||||||
|
|
||||||
import { BINARY_AD_ATTRIBUTES, LDAP_LOGIN_ENABLED, LDAP_LOGIN_LABEL } from '../constants';
|
import { BINARY_AD_ATTRIBUTES } from '../constants';
|
||||||
import {
|
import {
|
||||||
getLdapIds,
|
getLdapIds,
|
||||||
createFilter,
|
createFilter,
|
||||||
@@ -51,6 +52,15 @@ jest.mock('n8n-workflow', () => ({
|
|||||||
randomString: jest.fn(),
|
randomString: jest.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
mockInstance(GlobalConfig, {
|
||||||
|
sso: {
|
||||||
|
ldap: {
|
||||||
|
loginEnabled: true,
|
||||||
|
loginLabel: 'fakeLoginLabel',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
describe('LdapService', () => {
|
describe('LdapService', () => {
|
||||||
const ldapConfig: LdapConfig = {
|
const ldapConfig: LdapConfig = {
|
||||||
loginEnabled: true,
|
loginEnabled: true,
|
||||||
@@ -74,7 +84,7 @@ describe('LdapService', () => {
|
|||||||
searchTimeout: 6,
|
searchTimeout: 6,
|
||||||
};
|
};
|
||||||
|
|
||||||
let settingsRepository = mockInstance(SettingsRepository);
|
const settingsRepository = mockInstance(SettingsRepository);
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
// Need fake timers to avoid setInterval
|
// Need fake timers to avoid setInterval
|
||||||
@@ -116,13 +126,12 @@ describe('LdapService', () => {
|
|||||||
|
|
||||||
await ldapService.init();
|
await ldapService.init();
|
||||||
|
|
||||||
expect(configSetSpy).toHaveBeenNthCalledWith(1, LDAP_LOGIN_ENABLED, ldapConfig.loginEnabled);
|
|
||||||
expect(configSetSpy).toHaveBeenNthCalledWith(
|
expect(configSetSpy).toHaveBeenNthCalledWith(
|
||||||
2,
|
1,
|
||||||
'userManagement.authenticationMethod',
|
'userManagement.authenticationMethod',
|
||||||
'ldap',
|
'ldap',
|
||||||
);
|
);
|
||||||
expect(configSetSpy).toHaveBeenNthCalledWith(3, LDAP_LOGIN_LABEL, ldapConfig.loginLabel);
|
expect(Container.get(GlobalConfig).sso.ldap.loginLabel).toBe(ldapConfig.loginLabel);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set expected configuration variables from LDAP config if LDAP is disabled', async () => {
|
it('should set expected configuration variables from LDAP config if LDAP is disabled', async () => {
|
||||||
@@ -134,13 +143,12 @@ describe('LdapService', () => {
|
|||||||
|
|
||||||
await ldapService.init();
|
await ldapService.init();
|
||||||
|
|
||||||
expect(configSetSpy).toHaveBeenNthCalledWith(1, LDAP_LOGIN_ENABLED, givenConfig.loginEnabled);
|
|
||||||
expect(configSetSpy).toHaveBeenNthCalledWith(
|
expect(configSetSpy).toHaveBeenNthCalledWith(
|
||||||
2,
|
1,
|
||||||
'userManagement.authenticationMethod',
|
'userManagement.authenticationMethod',
|
||||||
'email',
|
'email',
|
||||||
);
|
);
|
||||||
expect(configSetSpy).toHaveBeenNthCalledWith(3, LDAP_LOGIN_LABEL, givenConfig.loginLabel);
|
expect(Container.get(GlobalConfig).sso.ldap.loginLabel).toBe(ldapConfig.loginLabel);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should show logger warning if authentication method is not ldap or email', async () => {
|
it('should show logger warning if authentication method is not ldap or email', async () => {
|
||||||
@@ -356,17 +364,14 @@ describe('LdapService', () => {
|
|||||||
|
|
||||||
it('should update the LDAP login label in the config', async () => {
|
it('should update the LDAP login label in the config', async () => {
|
||||||
mockSettingsRespositoryFindOneByOrFail(ldapConfig);
|
mockSettingsRespositoryFindOneByOrFail(ldapConfig);
|
||||||
|
|
||||||
mockInstance(AuthIdentityRepository, {
|
mockInstance(AuthIdentityRepository, {
|
||||||
find: jest.fn().mockResolvedValue([{ user: { id: 'userId' } }]),
|
find: jest.fn().mockResolvedValue([{ user: { id: 'userId' } }]),
|
||||||
delete: jest.fn(),
|
delete: jest.fn(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const cipherMock = mock<Cipher>({
|
const cipherMock = mock<Cipher>({
|
||||||
encrypt: jest.fn().mockReturnValue('encryptedPassword'),
|
encrypt: jest.fn().mockReturnValue('encryptedPassword'),
|
||||||
});
|
});
|
||||||
const configSetSpy = jest.spyOn(config, 'set');
|
const globalConfig = Container.get(GlobalConfig);
|
||||||
|
|
||||||
config.set('userManagement.authenticationMethod', 'email');
|
config.set('userManagement.authenticationMethod', 'email');
|
||||||
const ldapService = new LdapService(mockLogger(), settingsRepository, cipherMock, mock());
|
const ldapService = new LdapService(mockLogger(), settingsRepository, cipherMock, mock());
|
||||||
|
|
||||||
@@ -377,8 +382,7 @@ describe('LdapService', () => {
|
|||||||
loginLabel: 'newLoginLabel',
|
loginLabel: 'newLoginLabel',
|
||||||
};
|
};
|
||||||
await ldapService.updateConfig(newConfig);
|
await ldapService.updateConfig(newConfig);
|
||||||
|
expect(globalConfig.sso.ldap.loginLabel).toBe(newConfig.loginLabel);
|
||||||
expect(configSetSpy).toHaveBeenNthCalledWith(4, LDAP_LOGIN_LABEL, newConfig.loginLabel);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { GlobalConfig } from '@n8n/config';
|
||||||
import type { LdapConfig, ConnectionSecurity } from '@n8n/constants';
|
import type { LdapConfig, ConnectionSecurity } from '@n8n/constants';
|
||||||
import type { AuthProviderSyncHistory } from '@n8n/db';
|
import type { AuthProviderSyncHistory } from '@n8n/db';
|
||||||
import {
|
import {
|
||||||
@@ -13,15 +14,9 @@ import type { Entry as LdapUser } from 'ldapts';
|
|||||||
import { Filter } from 'ldapts/filters/Filter';
|
import { Filter } from 'ldapts/filters/Filter';
|
||||||
import { randomString } from 'n8n-workflow';
|
import { randomString } from 'n8n-workflow';
|
||||||
|
|
||||||
import config from '@/config';
|
|
||||||
import { License } from '@/license';
|
import { License } from '@/license';
|
||||||
|
|
||||||
import {
|
import { BINARY_AD_ATTRIBUTES, LDAP_CONFIG_SCHEMA } from './constants';
|
||||||
BINARY_AD_ATTRIBUTES,
|
|
||||||
LDAP_CONFIG_SCHEMA,
|
|
||||||
LDAP_LOGIN_ENABLED,
|
|
||||||
LDAP_LOGIN_LABEL,
|
|
||||||
} from './constants';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether the LDAP feature is disabled in the instance
|
* Check whether the LDAP feature is disabled in the instance
|
||||||
@@ -33,12 +28,12 @@ export const isLdapEnabled = () => {
|
|||||||
/**
|
/**
|
||||||
* Retrieve the LDAP login label from the configuration object
|
* Retrieve the LDAP login label from the configuration object
|
||||||
*/
|
*/
|
||||||
export const getLdapLoginLabel = (): string => config.getEnv(LDAP_LOGIN_LABEL);
|
export const getLdapLoginLabel = (): string => Container.get(GlobalConfig).sso.ldap.loginLabel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the LDAP login enabled from the configuration object
|
* Retrieve the LDAP login enabled from the configuration object
|
||||||
*/
|
*/
|
||||||
export const isLdapLoginEnabled = (): boolean => config.getEnv(LDAP_LOGIN_ENABLED);
|
export const isLdapLoginEnabled = (): boolean => Container.get(GlobalConfig).sso.ldap.loginEnabled;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate the structure of the LDAP configuration schema
|
* Validate the structure of the LDAP configuration schema
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Logger } from '@n8n/backend-common';
|
import { Logger } from '@n8n/backend-common';
|
||||||
|
import { GlobalConfig } from '@n8n/config';
|
||||||
import type { LdapConfig } from '@n8n/constants';
|
import type { LdapConfig } from '@n8n/constants';
|
||||||
import { LDAP_FEATURE_NAME } from '@n8n/constants';
|
import { LDAP_FEATURE_NAME } from '@n8n/constants';
|
||||||
import { SettingsRepository } from '@n8n/db';
|
import { SettingsRepository } from '@n8n/db';
|
||||||
@@ -12,7 +13,6 @@ import { Cipher } from 'n8n-core';
|
|||||||
import { jsonParse, UnexpectedError } from 'n8n-workflow';
|
import { jsonParse, UnexpectedError } from 'n8n-workflow';
|
||||||
import type { ConnectionOptions } from 'tls';
|
import type { ConnectionOptions } from 'tls';
|
||||||
|
|
||||||
import config from '@/config';
|
|
||||||
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
||||||
import { InternalServerError } from '@/errors/response-errors/internal-server.error';
|
import { InternalServerError } from '@/errors/response-errors/internal-server.error';
|
||||||
import { EventService } from '@/events/event.service';
|
import { EventService } from '@/events/event.service';
|
||||||
@@ -45,7 +45,7 @@ import {
|
|||||||
setCurrentAuthenticationMethod,
|
setCurrentAuthenticationMethod,
|
||||||
} from '@/sso.ee/sso-helpers';
|
} from '@/sso.ee/sso-helpers';
|
||||||
|
|
||||||
import { BINARY_AD_ATTRIBUTES, LDAP_LOGIN_ENABLED, LDAP_LOGIN_LABEL } from './constants';
|
import { BINARY_AD_ATTRIBUTES } from './constants';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class LdapService {
|
export class LdapService {
|
||||||
@@ -141,7 +141,7 @@ export class LdapService {
|
|||||||
/** Take the LDAP configuration and set login enabled and login label to the config object */
|
/** Take the LDAP configuration and set login enabled and login label to the config object */
|
||||||
private async setGlobalLdapConfigVariables(ldapConfig: LdapConfig): Promise<void> {
|
private async setGlobalLdapConfigVariables(ldapConfig: LdapConfig): Promise<void> {
|
||||||
await this.setLdapLoginEnabled(ldapConfig.loginEnabled);
|
await this.setLdapLoginEnabled(ldapConfig.loginEnabled);
|
||||||
config.set(LDAP_LOGIN_LABEL, ldapConfig.loginLabel);
|
Container.get(GlobalConfig).sso.ldap.loginLabel = ldapConfig.loginLabel;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Set the LDAP login enabled to the configuration object */
|
/** Set the LDAP login enabled to the configuration object */
|
||||||
@@ -153,7 +153,7 @@ export class LdapService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
config.set(LDAP_LOGIN_ENABLED, enabled);
|
Container.get(GlobalConfig).sso.ldap.loginEnabled = enabled;
|
||||||
|
|
||||||
const targetAuthenticationMethod =
|
const targetAuthenticationMethod =
|
||||||
!enabled && currentAuthenticationMethod === 'ldap' ? 'email' : currentAuthenticationMethod;
|
!enabled && currentAuthenticationMethod === 'ldap' ? 'email' : currentAuthenticationMethod;
|
||||||
|
|||||||
@@ -343,20 +343,20 @@ export class FrontendService {
|
|||||||
if (this.license.isLdapEnabled()) {
|
if (this.license.isLdapEnabled()) {
|
||||||
Object.assign(this.settings.sso.ldap, {
|
Object.assign(this.settings.sso.ldap, {
|
||||||
loginLabel: getLdapLoginLabel(),
|
loginLabel: getLdapLoginLabel(),
|
||||||
loginEnabled: config.getEnv('sso.ldap.loginEnabled'),
|
loginEnabled: this.globalConfig.sso.ldap.loginEnabled,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.license.isSamlEnabled()) {
|
if (this.license.isSamlEnabled()) {
|
||||||
Object.assign(this.settings.sso.saml, {
|
Object.assign(this.settings.sso.saml, {
|
||||||
loginLabel: getSamlLoginLabel(),
|
loginLabel: getSamlLoginLabel(),
|
||||||
loginEnabled: config.getEnv('sso.saml.loginEnabled'),
|
loginEnabled: this.globalConfig.sso.saml.loginEnabled,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.licenseState.isOidcLicensed()) {
|
if (this.licenseState.isOidcLicensed()) {
|
||||||
Object.assign(this.settings.sso.oidc, {
|
Object.assign(this.settings.sso.oidc, {
|
||||||
loginEnabled: config.getEnv('sso.oidc.loginEnabled'),
|
loginEnabled: this.globalConfig.sso.oidc.loginEnabled,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,22 +8,17 @@ import {
|
|||||||
type User,
|
type User,
|
||||||
UserRepository,
|
UserRepository,
|
||||||
} from '@n8n/db';
|
} from '@n8n/db';
|
||||||
import { Service } from '@n8n/di';
|
import { Container, Service } from '@n8n/di';
|
||||||
import { Cipher } from 'n8n-core';
|
import { Cipher } from 'n8n-core';
|
||||||
import { jsonParse } from 'n8n-workflow';
|
import { jsonParse } from 'n8n-workflow';
|
||||||
import * as client from 'openid-client';
|
import * as client from 'openid-client';
|
||||||
|
|
||||||
import config from '@/config';
|
|
||||||
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
||||||
import { ForbiddenError } from '@/errors/response-errors/forbidden.error';
|
import { ForbiddenError } from '@/errors/response-errors/forbidden.error';
|
||||||
import { InternalServerError } from '@/errors/response-errors/internal-server.error';
|
import { InternalServerError } from '@/errors/response-errors/internal-server.error';
|
||||||
import { UrlService } from '@/services/url.service';
|
import { UrlService } from '@/services/url.service';
|
||||||
|
|
||||||
import {
|
import { OIDC_CLIENT_SECRET_REDACTED_VALUE, OIDC_PREFERENCES_DB_KEY } from './constants';
|
||||||
OIDC_CLIENT_SECRET_REDACTED_VALUE,
|
|
||||||
OIDC_LOGIN_ENABLED,
|
|
||||||
OIDC_PREFERENCES_DB_KEY,
|
|
||||||
} from './constants';
|
|
||||||
import {
|
import {
|
||||||
getCurrentAuthenticationMethod,
|
getCurrentAuthenticationMethod,
|
||||||
isEmailCurrentAuthenticationMethod,
|
isEmailCurrentAuthenticationMethod,
|
||||||
@@ -235,7 +230,7 @@ export class OidcService {
|
|||||||
const targetAuthenticationMethod =
|
const targetAuthenticationMethod =
|
||||||
!enabled && currentAuthenticationMethod === 'oidc' ? 'email' : currentAuthenticationMethod;
|
!enabled && currentAuthenticationMethod === 'oidc' ? 'email' : currentAuthenticationMethod;
|
||||||
|
|
||||||
config.set(OIDC_LOGIN_ENABLED, enabled);
|
Container.get(GlobalConfig).sso.oidc.loginEnabled = enabled;
|
||||||
await setCurrentAuthenticationMethod(enabled ? 'oidc' : targetAuthenticationMethod);
|
await setCurrentAuthenticationMethod(enabled ? 'oidc' : targetAuthenticationMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,16 @@
|
|||||||
import type { SamlAcsDto, SamlPreferences } from '@n8n/api-types';
|
import type { SamlAcsDto, SamlPreferences } from '@n8n/api-types';
|
||||||
|
import { GlobalConfig } from '@n8n/config';
|
||||||
import type { User } from '@n8n/db';
|
import type { User } from '@n8n/db';
|
||||||
import { AuthIdentity, AuthIdentityRepository, UserRepository } from '@n8n/db';
|
import { AuthIdentity, AuthIdentityRepository, UserRepository } from '@n8n/db';
|
||||||
import { Container } from '@n8n/di';
|
import { Container } from '@n8n/di';
|
||||||
import { randomString } from 'n8n-workflow';
|
import { randomString } from 'n8n-workflow';
|
||||||
import type { FlowResult } from 'samlify/types/src/flow';
|
import type { FlowResult } from 'samlify/types/src/flow';
|
||||||
|
|
||||||
import config from '@/config';
|
|
||||||
import { AuthError } from '@/errors/response-errors/auth.error';
|
import { AuthError } from '@/errors/response-errors/auth.error';
|
||||||
import { InternalServerError } from '@/errors/response-errors/internal-server.error';
|
import { InternalServerError } from '@/errors/response-errors/internal-server.error';
|
||||||
import { License } from '@/license';
|
import { License } from '@/license';
|
||||||
import { PasswordUtility } from '@/services/password.utility';
|
import { PasswordUtility } from '@/services/password.utility';
|
||||||
|
|
||||||
import { SAML_LOGIN_ENABLED, SAML_LOGIN_LABEL } from './constants';
|
|
||||||
import { getServiceProviderConfigTestReturnUrl } from './service-provider.ee';
|
import { getServiceProviderConfigTestReturnUrl } from './service-provider.ee';
|
||||||
import type { SamlAttributeMapping, SamlUserAttributes } from './types';
|
import type { SamlAttributeMapping, SamlUserAttributes } from './types';
|
||||||
import {
|
import {
|
||||||
@@ -25,11 +24,11 @@ import {
|
|||||||
* Check whether the SAML feature is licensed and enabled in the instance
|
* Check whether the SAML feature is licensed and enabled in the instance
|
||||||
*/
|
*/
|
||||||
export function isSamlLoginEnabled(): boolean {
|
export function isSamlLoginEnabled(): boolean {
|
||||||
return config.getEnv(SAML_LOGIN_ENABLED);
|
return Container.get(GlobalConfig).sso.saml.loginEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getSamlLoginLabel(): string {
|
export function getSamlLoginLabel(): string {
|
||||||
return config.getEnv(SAML_LOGIN_LABEL);
|
return Container.get(GlobalConfig).sso.saml.loginLabel;
|
||||||
}
|
}
|
||||||
|
|
||||||
// can only toggle between email and saml, not directly to e.g. ldap
|
// can only toggle between email and saml, not directly to e.g. ldap
|
||||||
@@ -44,12 +43,12 @@ export async function setSamlLoginEnabled(enabled: boolean): Promise<void> {
|
|||||||
const targetAuthenticationMethod =
|
const targetAuthenticationMethod =
|
||||||
!enabled && currentAuthenticationMethod === 'saml' ? 'email' : currentAuthenticationMethod;
|
!enabled && currentAuthenticationMethod === 'saml' ? 'email' : currentAuthenticationMethod;
|
||||||
|
|
||||||
config.set(SAML_LOGIN_ENABLED, enabled);
|
Container.get(GlobalConfig).sso.saml.loginEnabled = enabled;
|
||||||
await setCurrentAuthenticationMethod(enabled ? 'saml' : targetAuthenticationMethod);
|
await setCurrentAuthenticationMethod(enabled ? 'saml' : targetAuthenticationMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setSamlLoginLabel(label: string): void {
|
export function setSamlLoginLabel(label: string): void {
|
||||||
config.set(SAML_LOGIN_LABEL, label);
|
Container.get(GlobalConfig).sso.saml.loginLabel = label;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isSamlLicensed(): boolean {
|
export function isSamlLicensed(): boolean {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { GlobalConfig } from '@n8n/config';
|
||||||
import { SettingsRepository, type AuthProviderType } from '@n8n/db';
|
import { SettingsRepository, type AuthProviderType } from '@n8n/db';
|
||||||
import { Container } from '@n8n/di';
|
import { Container } from '@n8n/di';
|
||||||
|
|
||||||
@@ -44,9 +45,9 @@ export function isEmailCurrentAuthenticationMethod(): boolean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function isSsoJustInTimeProvisioningEnabled(): boolean {
|
export function isSsoJustInTimeProvisioningEnabled(): boolean {
|
||||||
return config.getEnv('sso.justInTimeProvisioning');
|
return Container.get(GlobalConfig).sso.justInTimeProvisioning;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function doRedirectUsersFromLoginToSsoFlow(): boolean {
|
export function doRedirectUsersFromLoginToSsoFlow(): boolean {
|
||||||
return config.getEnv('sso.redirectLoginToSso');
|
return Container.get(GlobalConfig).sso.redirectLoginToSso;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,10 @@ const testServer = utils.setupTestServer({ endpointGroups: ['me'] });
|
|||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await testDb.truncate(['User']);
|
await testDb.truncate(['User']);
|
||||||
mockInstance(GlobalConfig, { publicApi: { disabled: false } });
|
mockInstance(GlobalConfig, {
|
||||||
|
publicApi: { disabled: false },
|
||||||
|
sso: { saml: { loginEnabled: true } },
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Owner shell', () => {
|
describe('Owner shell', () => {
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { randomEmail, randomName, randomValidPassword } from '@n8n/backend-test-utils';
|
import { randomEmail, randomName, randomValidPassword } from '@n8n/backend-test-utils';
|
||||||
|
import { GlobalConfig } from '@n8n/config';
|
||||||
import type { User } from '@n8n/db';
|
import type { User } from '@n8n/db';
|
||||||
|
import { Container } from '@n8n/di';
|
||||||
|
|
||||||
import { setSamlLoginEnabled } from '@/sso.ee/saml/saml-helpers';
|
import { setSamlLoginEnabled } from '@/sso.ee/saml/saml-helpers';
|
||||||
import {
|
import {
|
||||||
@@ -33,6 +35,7 @@ beforeAll(async () => {
|
|||||||
someUser = await createUser({ password: memberPassword });
|
someUser = await createUser({ password: memberPassword });
|
||||||
authOwnerAgent = testServer.authAgentFor(owner);
|
authOwnerAgent = testServer.authAgentFor(owner);
|
||||||
authMemberAgent = testServer.authAgentFor(someUser);
|
authMemberAgent = testServer.authAgentFor(someUser);
|
||||||
|
Container.get(GlobalConfig).sso.saml.loginEnabled = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(async () => await enableSaml(false));
|
beforeEach(async () => await enableSaml(false));
|
||||||
|
|||||||
Reference in New Issue
Block a user