From 5ddfd277c5430b45d35f1991a4b7077a1eccb6ef Mon Sep 17 00:00:00 2001 From: Michael Auerswald Date: Fri, 24 Mar 2023 17:46:06 +0100 Subject: [PATCH] feat(core): Improve ldap/saml toggle and tests (#5771) * improve ldap/saml toggle and tests * import cleanup * reject regular login users when saml is enabled * lint fix --- packages/cli/src/Ldap/helpers.ts | 33 +++++++-- .../cli/src/controllers/auth.controller.ts | 21 +++--- packages/cli/src/sso/saml/samlHelpers.ts | 18 +++-- packages/cli/src/sso/ssoHelpers.ts | 40 +++++++---- .../test/integration/ldap/ldap.api.test.ts | 5 ++ .../test/integration/saml/saml.api.test.ts | 67 ++++++++++++++++++- .../test/integration/saml/sampleMetadata.ts | 30 +++++++++ .../cli/test/integration/shared/types.d.ts | 1 + packages/cli/test/integration/shared/utils.ts | 8 +++ 9 files changed, 183 insertions(+), 40 deletions(-) create mode 100644 packages/cli/test/integration/saml/sampleMetadata.ts diff --git a/packages/cli/src/Ldap/helpers.ts b/packages/cli/src/Ldap/helpers.ts index fd7fb1450eda4..6ed83242e001a 100644 --- a/packages/cli/src/Ldap/helpers.ts +++ b/packages/cli/src/Ldap/helpers.ts @@ -26,6 +26,11 @@ import type { ConnectionSecurity, LdapConfig } from './types'; import { jsonParse, LoggerProxy as Logger } from 'n8n-workflow'; import { License } from '@/License'; import { InternalHooks } from '@/InternalHooks'; +import { + isEmailCurrentAuthenticationMethod, + isLdapCurrentAuthenticationMethod, + setCurrentAuthenticationMethod, +} from '@/sso/ssoHelpers'; /** * Check whether the LDAP feature is disabled in the instance @@ -50,8 +55,24 @@ export const setLdapLoginLabel = (value: string): void => { /** * Set the LDAP login enabled to the configuration object */ -export const setLdapLoginEnabled = (value: boolean): void => { - config.set(LDAP_LOGIN_ENABLED, value); +export const setLdapLoginEnabled = async (value: boolean): Promise => { + if (config.get(LDAP_LOGIN_ENABLED) === value) { + return; + } + // only one auth method can be active at a time, with email being the default + if (value && isEmailCurrentAuthenticationMethod()) { + // enable ldap login and disable email login, but only if email is the current auth method + config.set(LDAP_LOGIN_ENABLED, true); + await setCurrentAuthenticationMethod('ldap'); + } else if (!value && isLdapCurrentAuthenticationMethod()) { + // disable ldap login, but only if ldap is the current auth method + config.set(LDAP_LOGIN_ENABLED, false); + await setCurrentAuthenticationMethod('email'); + } else { + Logger.warn( + 'Cannot switch LDAP login enabled state when an authentication method other than email is active', + ); + } }; /** @@ -126,8 +147,8 @@ export const getLdapConfig = async (): Promise => { /** * Take the LDAP configuration and set login enabled and login label to the config object */ -export const setGlobalLdapConfigVariables = (ldapConfig: LdapConfig): void => { - setLdapLoginEnabled(ldapConfig.loginEnabled); +export const setGlobalLdapConfigVariables = async (ldapConfig: LdapConfig): Promise => { + await setLdapLoginEnabled(ldapConfig.loginEnabled); setLdapLoginLabel(ldapConfig.loginLabel); }; @@ -175,7 +196,7 @@ export const updateLdapConfig = async (ldapConfig: LdapConfig): Promise => { key: LDAP_FEATURE_NAME }, { value: JSON.stringify(ldapConfig), loadOnStartup: true }, ); - setGlobalLdapConfigVariables(ldapConfig); + await setGlobalLdapConfigVariables(ldapConfig); }; /** @@ -197,7 +218,7 @@ export const handleLdapInit = async (): Promise => { const ldapConfig = await getLdapConfig(); - setGlobalLdapConfigVariables(ldapConfig); + await setGlobalLdapConfigVariables(ldapConfig); // init LDAP manager with the current // configuration diff --git a/packages/cli/src/controllers/auth.controller.ts b/packages/cli/src/controllers/auth.controller.ts index 5092d1ea66b15..7a6405de36479 100644 --- a/packages/cli/src/controllers/auth.controller.ts +++ b/packages/cli/src/controllers/auth.controller.ts @@ -19,8 +19,10 @@ import type { } from '@/Interfaces'; import { handleEmailLogin, handleLdapLogin } from '@/auth'; import type { PostHogClient } from '@/posthog'; -import { isSamlCurrentAuthenticationMethod } from '../sso/ssoHelpers'; -import { SamlUrls } from '../sso/saml/constants'; +import { + isLdapCurrentAuthenticationMethod, + isSamlCurrentAuthenticationMethod, +} from '@/sso/ssoHelpers'; @RestController() export class AuthController { @@ -73,19 +75,12 @@ export class AuthController { if (preliminaryUser?.globalRole?.name === 'owner') { user = preliminaryUser; } else { - // TODO:SAML - uncomment this block when we have a way to redirect users to the SSO flow - // if (doRedirectUsersFromLoginToSsoFlow()) { - res.redirect(SamlUrls.restInitSSO); - return; - // return withFeatureFlags(this.postHog, sanitizeUser(preliminaryUser)); - // } else { - // throw new AuthError( - // 'Login with username and password is disabled due to SAML being the default authentication method. Please use SAML to log in.', - // ); - // } + throw new AuthError('SAML is enabled, please log in with SAML'); } + } else if (isLdapCurrentAuthenticationMethod()) { + user = await handleLdapLogin(email, password); } else { - user = (await handleLdapLogin(email, password)) ?? (await handleEmailLogin(email, password)); + user = await handleEmailLogin(email, password); } if (user) { await issueCookie(res, user); diff --git a/packages/cli/src/sso/saml/samlHelpers.ts b/packages/cli/src/sso/saml/samlHelpers.ts index ffa6132f1f40a..57c710e7071a4 100644 --- a/packages/cli/src/sso/saml/samlHelpers.ts +++ b/packages/cli/src/sso/saml/samlHelpers.ts @@ -16,6 +16,7 @@ import { isSamlCurrentAuthenticationMethod, setCurrentAuthenticationMethod, } from '../ssoHelpers'; +import { LoggerProxy } from 'n8n-workflow'; /** * Check whether the SAML feature is licensed and enabled in the instance */ @@ -29,14 +30,19 @@ export function getSamlLoginLabel(): string { // can only toggle between email and saml, not directly to e.g. ldap export async function setSamlLoginEnabled(enabled: boolean): Promise { - if (enabled) { - if (isEmailCurrentAuthenticationMethod()) { - config.set(SAML_LOGIN_ENABLED, true); - await setCurrentAuthenticationMethod('saml'); - } - } else { + if (config.get(SAML_LOGIN_ENABLED) === enabled) { + return; + } + if (enabled && isEmailCurrentAuthenticationMethod()) { + config.set(SAML_LOGIN_ENABLED, true); + await setCurrentAuthenticationMethod('saml'); + } else if (!enabled && isSamlCurrentAuthenticationMethod()) { config.set(SAML_LOGIN_ENABLED, false); await setCurrentAuthenticationMethod('email'); + } else { + LoggerProxy.warn( + 'Cannot switch SAML login enabled state when an authentication method other than email is active', + ); } } diff --git a/packages/cli/src/sso/ssoHelpers.ts b/packages/cli/src/sso/ssoHelpers.ts index 99cf43cfd85fc..70c375b6c44a4 100644 --- a/packages/cli/src/sso/ssoHelpers.ts +++ b/packages/cli/src/sso/ssoHelpers.ts @@ -2,12 +2,37 @@ import config from '@/config'; import * as Db from '@/Db'; import type { AuthProviderType } from '@/databases/entities/AuthIdentity'; +/** + * Only one authentication method can be active at a time. This function sets the current authentication method + * and saves it to the database. + * SSO methods should only switch to email and then to another method. Email can switch to any method. + * @param authenticationMethod + */ +export async function setCurrentAuthenticationMethod( + authenticationMethod: AuthProviderType, +): Promise { + config.set('userManagement.authenticationMethod', authenticationMethod); + await Db.collections.Settings.save({ + key: 'userManagement.authenticationMethod', + value: authenticationMethod, + loadOnStartup: true, + }); +} + +export function getCurrentAuthenticationMethod(): AuthProviderType { + return config.getEnv('userManagement.authenticationMethod'); +} + export function isSamlCurrentAuthenticationMethod(): boolean { - return config.getEnv('userManagement.authenticationMethod') === 'saml'; + return getCurrentAuthenticationMethod() === 'saml'; +} + +export function isLdapCurrentAuthenticationMethod(): boolean { + return getCurrentAuthenticationMethod() === 'ldap'; } export function isEmailCurrentAuthenticationMethod(): boolean { - return config.getEnv('userManagement.authenticationMethod') === 'email'; + return getCurrentAuthenticationMethod() === 'email'; } export function isSsoJustInTimeProvisioningEnabled(): boolean { @@ -17,14 +42,3 @@ export function isSsoJustInTimeProvisioningEnabled(): boolean { export function doRedirectUsersFromLoginToSsoFlow(): boolean { return config.getEnv('sso.redirectLoginToSso'); } - -export async function setCurrentAuthenticationMethod( - authenticationMethod: AuthProviderType, -): Promise { - config.set('userManagement.authenticationMethod', authenticationMethod); - await Db.collections.Settings.save({ - key: 'userManagement.authenticationMethod', - value: authenticationMethod, - loadOnStartup: true, - }); -} diff --git a/packages/cli/test/integration/ldap/ldap.api.test.ts b/packages/cli/test/integration/ldap/ldap.api.test.ts index 9c2358e3e01ff..fcecb25d8fcfb 100644 --- a/packages/cli/test/integration/ldap/ldap.api.test.ts +++ b/packages/cli/test/integration/ldap/ldap.api.test.ts @@ -16,6 +16,7 @@ import { randomEmail, randomName, uniqueId } from './../shared/random'; import * as testDb from './../shared/testDb'; import type { AuthAgent } from '../shared/types'; import * as utils from '../shared/utils'; +import { getCurrentAuthenticationMethod, setCurrentAuthenticationMethod } from '@/sso/ssoHelpers'; jest.mock('@/telemetry'); jest.mock('@/UserManagement/email/NodeMailer'); @@ -55,6 +56,8 @@ beforeAll(async () => { ); utils.initConfigFile(); + + await setCurrentAuthenticationMethod('email'); }); beforeEach(async () => { @@ -174,6 +177,7 @@ describe('PUT /ldap/config', () => { const emailUser = await Db.collections.User.findOneByOrFail({ id: member.id }); const localLdapIdentities = await testDb.getLdapIdentities(); + expect(getCurrentAuthenticationMethod()).toBe('email'); expect(emailUser.email).toBe(member.email); expect(emailUser.lastName).toBe(member.lastName); expect(emailUser.firstName).toBe(member.firstName); @@ -190,6 +194,7 @@ test('GET /ldap/config route should retrieve current configuration', async () => let response = await authAgent(owner).put('/ldap/config').send(validPayload); expect(response.statusCode).toBe(200); + expect(getCurrentAuthenticationMethod()).toBe('ldap'); response = await authAgent(owner).get('/ldap/config'); diff --git a/packages/cli/test/integration/saml/saml.api.test.ts b/packages/cli/test/integration/saml/saml.api.test.ts index 32b5f7fa6392a..a3abb177aa2d7 100644 --- a/packages/cli/test/integration/saml/saml.api.test.ts +++ b/packages/cli/test/integration/saml/saml.api.test.ts @@ -2,10 +2,11 @@ import type { SuperAgentTest } from 'supertest'; import config from '@/config'; import type { User } from '@db/entities/User'; import { setSamlLoginEnabled } from '@/sso/saml/samlHelpers'; -import { setCurrentAuthenticationMethod } from '@/sso/ssoHelpers'; +import { getCurrentAuthenticationMethod, setCurrentAuthenticationMethod } from '@/sso/ssoHelpers'; import { randomEmail, randomName, randomValidPassword } from '../shared/random'; import * as testDb from '../shared/testDb'; import * as utils from '../shared/utils'; +import { sampleConfig } from './sampleMetadata'; let owner: User; let authOwnerAgent: SuperAgentTest; @@ -16,7 +17,7 @@ async function enableSaml(enable: boolean) { } beforeAll(async () => { - const app = await utils.initTestServer({ endpointGroups: ['me'] }); + const app = await utils.initTestServer({ endpointGroups: ['me', 'saml'] }); owner = await testDb.createOwner(); authOwnerAgent = utils.createAuthAgent(app)(owner); }); @@ -67,4 +68,66 @@ describe('Instance owner', () => { }); }); }); + + describe('POST /sso/saml/config', () => { + test('should post saml config', async () => { + await authOwnerAgent + .post('/sso/saml/config') + .send({ + ...sampleConfig, + loginEnabled: true, + }) + .expect(200); + expect(getCurrentAuthenticationMethod()).toBe('saml'); + }); + }); + + describe('POST /sso/saml/config/toggle', () => { + test('should toggle saml as default authentication method', async () => { + await enableSaml(true); + expect(getCurrentAuthenticationMethod()).toBe('saml'); + + await authOwnerAgent + .post('/sso/saml/config/toggle') + .send({ + loginEnabled: false, + }) + .expect(200); + expect(getCurrentAuthenticationMethod()).toBe('email'); + + await authOwnerAgent + .post('/sso/saml/config/toggle') + .send({ + loginEnabled: true, + }) + .expect(200); + expect(getCurrentAuthenticationMethod()).toBe('saml'); + }); + }); + + describe('POST /sso/saml/config/toggle', () => { + test('should fail enable saml if default authentication is not email', async () => { + await enableSaml(true); + + await authOwnerAgent + .post('/sso/saml/config/toggle') + .send({ + loginEnabled: false, + }) + .expect(200); + expect(getCurrentAuthenticationMethod()).toBe('email'); + + await setCurrentAuthenticationMethod('ldap'); + expect(getCurrentAuthenticationMethod()).toBe('ldap'); + + await authOwnerAgent + .post('/sso/saml/config/toggle') + .send({ + loginEnabled: true, + }) + .expect(200); + + expect(getCurrentAuthenticationMethod()).toBe('ldap'); + }); + }); }); diff --git a/packages/cli/test/integration/saml/sampleMetadata.ts b/packages/cli/test/integration/saml/sampleMetadata.ts new file mode 100644 index 0000000000000..fd7968c2fbf62 --- /dev/null +++ b/packages/cli/test/integration/saml/sampleMetadata.ts @@ -0,0 +1,30 @@ +export const sampleMetadata = + '\n\n\n\n\n\n\n\n\n\n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\n7EzvuRaQc7pNrduO1pTKoPAy+2SLgqo=\n\n\nMIIFUzCCAzugAwIBAgIRAJ1peD6pO0pygujUcWb85QswDQYJKoZIhvcNAQELBQAwHTEbMBkGA1UEAwwSYXV0aGVudGlrIDIwMjMuMi4yMB4XDTIzMDIyNzEzMTQ0MFoXDTI0MDIyODEzMTQ0MFowVjEqMCgGA1UEAwwhYXV0aGVudGlrIFNlbGYtc2lnbmVkIENlcnRpZmljYXRlMRIwEAYDVQQKDAlhdXRoZW50aWsxFDASBgNVBAsMC1NlbGYtc2lnbmVkMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA3thve9UWPL09ouGwUPlCxfrBDDKmDdvoMc3eahfuop2tSP38EvdBcnPCVYTtu2hhHNqN/QtoyAZcTvwD8oDjwiYxdO6VbNjMZAnMD4W84l2niGnG7ATy/niNcZoge4xy+OmCJKXsolbsXT+hQGQ2oiUDnbX8QwMQCMN8FBF+EvYoHXKvRjmjO75DHyHY9JP05HZTO3lycVLWGrIq4oJfp60PN/0z5tbpk/Tyst21o4lcESAM4fkmndonPmoKMr7q9g+CFYRT+As6iB+L38J44YNWs0Qm42tHAlveinBRuLLMi+eMC2L0sckvyJKB1qHG+bKl7jVXNDJg5KWKEHdM4CBg3dJkign+12EO205ruLYSBydZErAb2NKd2htgYs/zGHSgb3LhQ3vEuHiTIcq828PWmVM7l3B8CJ+ZyPLixywT0pKgkb8lrDqzXIffRljCYMT2pIR4FNuy+CzXMYm+N30qVO8h9+cl3YRSHpFBk9KJ0/+HQp1k6ELnaYW+LryS8Jr1uPxhwyMqGu+4bxCF8JfZncojMhlQghXCQUvOaboNlBWv5jtsoZ9mN266V1EJpnF064UimQ1foN1O4l4292NvkChcmiQf2YDE5PrMWm10gQg401oulE9o91OsxLRmyw/qZTJvA06KgVamNLfhN/St/CVfl8q6ldgoHmWaxY8CAwEAAaNVMFMwUQYDVR0RAQH/BEcwRYJDT1BRVVpWNW1qdWFvQ01hdEVvenU5ajNoUnlhU0UyQThaTjd4WlZqUy5zZWxmLXNpZ25lZC5nb2F1dGhlbnRpay5pbzANBgkqhkiG9w0BAQsFAAOCAgEAwaQtK4s2DnJxjg6i6BSo/rhNg7ClXgnOyF79T7JO3gexVjzboY2UTi1ut/DEII01PI0qgQ62+q9lTloWd1SpxPOrOVeu2uVgTK0LkGb63q355iJ2myfhFYYPPprNDzvUhnX8cVY979MaiqAOCJW7irlHAH2bLAujanRdlcgFtmoe5lZ+qnS5iOUmp5tehPsDJGlPZ3nCWJcRQHDLLSOp3TvR5no8nj0cWxUWnNeaGoJy1GsJlGapLXS5pUKpxVg9GeEcQxjBkFgMLWrkWBsQDvC5+GlmHgSkdRvuYBlB6CRK2eGY7G06v7ZRPhf82LvEFRBwzJvGdM0g491OTTJquTN2wyq45UlJK4anMYrUbpi8p8MOW7IUw6a+SvZyJab9gNoLTUzA6MlzQP9bPrEALpwNhmHsmD09zNyYiNfpkpLJog96wPscx4b+gsg+5PcilET8qvth6VYDup8TdsonPvDPH0oyo66SAYoyOgAeB+BHTicjtVt+UnrhXYj92BHDXfmfdTzA8QcY7reLPIOQVk1zV24cwySiLh4F2Hr8z8V1wMRVNVHcezMsVBvCzxQ15XlMq9X2wBujfED93dXJVs+WuzbpTIoXvHHT3zWnzykX8hVbrj9ddzF8TuJW4NYis0cH5SLzvtPj7EzvuRaQc7pNrduO1pTKoPAy+2SLgqo=urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddressurn:oasis:names:tc:SAML:2.0:nameid-format:persistenturn:oasis:names:tc:SAML:2.0:nameid-format:X509SubjectNameurn:oasis:names:tc:SAML:2.0:nameid-format:transient'; + +export const sampleConfig = { + mapping: { + email: 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress', + firstName: 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/firstname', + lastName: 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/lastname', + userPrincipalName: 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn', + }, + metadata: sampleMetadata, + metadataUrl: '', + ignoreSSL: true, + loginBinding: 'redirect', + acsBinding: 'post', + authnRequestsSigned: false, + loginEnabled: false, + loginLabel: 'SAML Login', + wantAssertionsSigned: true, + wantMessageSigned: true, + signatureConfig: { + prefix: 'ds', + location: { + reference: '/samlp:Response/saml:Issuer', + action: 'after', + }, + }, + entityID: 'https://n8n-tunnel.localhost.dev/rest/sso/saml/metadata', + returnUrl: 'https://n8n-tunnel.localhost.dev/rest/sso/saml/acs', +}; diff --git a/packages/cli/test/integration/shared/types.d.ts b/packages/cli/test/integration/shared/types.d.ts index bd739897588bd..97bb13843ddbd 100644 --- a/packages/cli/test/integration/shared/types.d.ts +++ b/packages/cli/test/integration/shared/types.d.ts @@ -22,6 +22,7 @@ type EndpointGroup = | 'publicApi' | 'nodes' | 'ldap' + | 'saml' | 'eventBus' | 'license'; diff --git a/packages/cli/test/integration/shared/utils.ts b/packages/cli/test/integration/shared/utils.ts index d827f1562d1aa..1fdb77f34aeef 100644 --- a/packages/cli/test/integration/shared/utils.ts +++ b/packages/cli/test/integration/shared/utils.ts @@ -78,6 +78,9 @@ import { LdapManager } from '@/Ldap/LdapManager.ee'; import { LDAP_ENABLED } from '@/Ldap/constants'; import { handleLdapInit } from '@/Ldap/helpers'; import { Push } from '@/push'; +import { setSamlLoginEnabled } from '@/sso/saml/samlHelpers'; +import { SamlService } from '@/sso/saml/saml.service.ee'; +import { SamlController } from '@/sso/saml/routes/saml.controller.ee'; export const mockInstance = ( ctor: new (...args: any[]) => T, @@ -190,6 +193,11 @@ export async function initTestServer({ new LdapController(service, sync, internalHooks), ); break; + case 'saml': + await setSamlLoginEnabled(true); + const samlService = Container.get(SamlService); + registerController(testServer.app, config, new SamlController(samlService)); + break; case 'nodes': registerController( testServer.app,