From e3a53fd19d8c258a08baab9c090968104327a13b Mon Sep 17 00:00:00 2001 From: Alex Grozav Date: Tue, 23 May 2023 16:25:28 +0300 Subject: [PATCH] feat: Add SSO SAML metadataUrl support and various improvements (#6139) * feat: add various sso improvements * fix: remove test button assertion * fix: fix type imports * test: attempt fixing unit tests * fix: changed to using useToast for error toasts * Minor copy tweaks and swapped buttons position. * fix locale ref * align error with UI wording * simplify saving ux * fix pretty * fix: update saml sso setting saving * fix: undo try/catch changes when saving saml config * metadata url tab selected at first * chore: fix linting issue * test: fix activation checkbox test --------- Co-authored-by: Giulio Andreini Co-authored-by: Michael Auerswald Co-authored-by: Romain Minaud --- .../cli/src/controllers/auth.controller.ts | 2 +- .../src/__tests__/server/endpoints/index.ts | 2 + .../src/__tests__/server/endpoints/sso.ts | 36 ++++ packages/editor-ui/src/__tests__/utils.ts | 2 +- .../src/plugins/i18n/locales/en.json | 22 +- packages/editor-ui/src/stores/sso.store.ts | 11 +- packages/editor-ui/src/views/SettingsSso.vue | 198 +++++++++++++----- .../src/views/__tests__/SettingsSso.test.ts | 109 +++++----- 8 files changed, 264 insertions(+), 118 deletions(-) create mode 100644 packages/editor-ui/src/__tests__/server/endpoints/sso.ts diff --git a/packages/cli/src/controllers/auth.controller.ts b/packages/cli/src/controllers/auth.controller.ts index 3492fe7c145df..f025b60eea0b2 100644 --- a/packages/cli/src/controllers/auth.controller.ts +++ b/packages/cli/src/controllers/auth.controller.ts @@ -80,7 +80,7 @@ export class AuthController { user = preliminaryUser; usedAuthenticationMethod = 'email'; } else { - throw new AuthError('SAML is enabled, please log in with SAML'); + throw new AuthError('SSO is enabled, please log in with SSO'); } } else if (isLdapCurrentAuthenticationMethod()) { user = await handleLdapLogin(email, password); diff --git a/packages/editor-ui/src/__tests__/server/endpoints/index.ts b/packages/editor-ui/src/__tests__/server/endpoints/index.ts index 4ba3315e425fb..d79a81d30883b 100644 --- a/packages/editor-ui/src/__tests__/server/endpoints/index.ts +++ b/packages/editor-ui/src/__tests__/server/endpoints/index.ts @@ -4,6 +4,7 @@ import { routesForCredentials } from './credential'; import { routesForCredentialTypes } from './credentialType'; import { routesForVariables } from './variable'; import { routesForSettings } from './settings'; +import { routesForSSO } from './sso'; const endpoints: Array<(server: Server) => void> = [ routesForCredentials, @@ -11,6 +12,7 @@ const endpoints: Array<(server: Server) => void> = [ routesForUsers, routesForVariables, routesForSettings, + routesForSSO, ]; export { endpoints }; diff --git a/packages/editor-ui/src/__tests__/server/endpoints/sso.ts b/packages/editor-ui/src/__tests__/server/endpoints/sso.ts new file mode 100644 index 0000000000000..61896ca1f7426 --- /dev/null +++ b/packages/editor-ui/src/__tests__/server/endpoints/sso.ts @@ -0,0 +1,36 @@ +import type { Server, Request } from 'miragejs'; +import { Response } from 'miragejs'; +import type { SamlPreferences, SamlPreferencesExtractedData } from '@/Interface'; +import { faker } from '@faker-js/faker'; +import type { AppSchema } from '@/__tests__/server/types'; +import { jsonParse } from 'n8n-workflow'; + +let samlConfig: SamlPreferences & SamlPreferencesExtractedData = { + metadata: '', + metadataUrl: '', + entityID: faker.internet.url(), + returnUrl: faker.internet.url(), +}; + +export function routesForSSO(server: Server) { + server.get('/rest/sso/saml/config', () => { + return new Response(200, {}, { data: samlConfig }); + }); + + server.post('/rest/sso/saml/config', (schema: AppSchema, request: Request) => { + const requestBody = jsonParse(request.requestBody) as Partial< + SamlPreferences & SamlPreferencesExtractedData + >; + + samlConfig = { + ...samlConfig, + ...requestBody, + }; + + return new Response(200, {}, { data: samlConfig }); + }); + + server.get('/rest/sso/saml/config/test', () => { + return new Response(200, {}, { data: '' }); + }); +} diff --git a/packages/editor-ui/src/__tests__/utils.ts b/packages/editor-ui/src/__tests__/utils.ts index f6f779709072a..9fa734192de18 100644 --- a/packages/editor-ui/src/__tests__/utils.ts +++ b/packages/editor-ui/src/__tests__/utils.ts @@ -3,7 +3,7 @@ import { UserManagementAuthenticationMethod } from '@/Interface'; import { render } from '@testing-library/vue'; import { PiniaVuePlugin } from 'pinia'; -export const retry = async (assertion: () => any, { interval = 20, timeout = 200 } = {}) => { +export const retry = async (assertion: () => any, { interval = 20, timeout = 1000 } = {}) => { return new Promise((resolve, reject) => { const startTime = Date.now(); diff --git a/packages/editor-ui/src/plugins/i18n/locales/en.json b/packages/editor-ui/src/plugins/i18n/locales/en.json index 20c5415fbe0a6..99df8a2ae5722 100644 --- a/packages/editor-ui/src/plugins/i18n/locales/en.json +++ b/packages/editor-ui/src/plugins/i18n/locales/en.json @@ -1797,22 +1797,32 @@ "settings.ldap.section.synchronization.title": "Synchronization", "settings.sso": "SSO", "settings.sso.title": "Single Sign On", - "settings.sso.subtitle": "SAML 2.0", - "settings.sso.info": "SAML SSO (Security Assertion Markup Language Single Sign-On) is a type of authentication process that enables users to access multiple applications with a single set of login credentials. {link}", - "settings.sso.info.link": "More info.", + "settings.sso.subtitle": "SAML 2.0 Configuration", + "settings.sso.info": "Activate SAML SSO to enable passwordless login via your existing user management tool and enhance security through unified authentication.", + "settings.sso.info.link": "Learn how to configure SAML 2.0.", "settings.sso.activation.tooltip": "You need to save the settings first before activating SAML", "settings.sso.activated": "Activated", "settings.sso.deactivated": "Deactivated", "settings.sso.settings.redirectUrl.label": "Redirect URL", "settings.sso.settings.redirectUrl.copied": "Redirect URL copied to clipboard", - "settings.sso.settings.redirectUrl.help": "Save the Redirect URL as you’ll need it to configure these in the SAML provider’s settings.", + "settings.sso.settings.redirectUrl.help": "Copy the Redirect URL to configure your SAML provider", "settings.sso.settings.entityId.label": "Entity ID", "settings.sso.settings.entityId.copied": "Entity ID copied to clipboard", - "settings.sso.settings.entityId.help": "Save the Entity URL as you’ll need it to configure these in the SAML provider’s settings.", + "settings.sso.settings.entityId.help": "Copy the Entity ID URL to configure your SAML provider", "settings.sso.settings.ips.label": "Identity Provider Settings", - "settings.sso.settings.ips.help": "Add the raw Metadata XML provided by your Identity Provider", + "settings.sso.settings.ips.xml.help": "Paste here the raw Metadata XML provided by your Identity Provider", + "settings.sso.settings.ips.url.help": "Paste here the Internet Provider Metadata URL", + "settings.sso.settings.ips.url.placeholder": "e.g. https://samltest.id/saml/idp", + "settings.sso.settings.ips.options.url": "Metadata URL", + "settings.sso.settings.ips.options.xml": "XML", "settings.sso.settings.test": "Test settings", "settings.sso.settings.save": "Save settings", + "settings.sso.settings.save.activate.title": "Test and activate SAML SSO", + "settings.sso.settings.save.activate.message": "SAML SSO configuration saved successfully. Test your SAML SSO settings first, then activate to enable single sign-on for your organization.", + "settings.sso.settings.save.activate.cancel": "Cancel", + "settings.sso.settings.save.activate.test": "Test settings", + "settings.sso.settings.save.error": "Error saving SAML SSO configuration", + "settings.sso.settings.footer.hint": "Don't forget to activate SAML SSO once you've saved the settings.", "settings.sso.actionBox.title": "Available on Enterprise plan", "settings.sso.actionBox.description": "Use Single Sign On to consolidate authentication into a single platform to improve security and agility.", "settings.sso.actionBox.buttonText": "See plans", diff --git a/packages/editor-ui/src/stores/sso.store.ts b/packages/editor-ui/src/stores/sso.store.ts index 27b21de7f5a92..be8b90c2f9942 100644 --- a/packages/editor-ui/src/stores/sso.store.ts +++ b/packages/editor-ui/src/stores/sso.store.ts @@ -6,6 +6,7 @@ import { useSettingsStore } from '@/stores/settings.store'; import * as ssoApi from '@/api/sso'; import type { SamlPreferences } from '@/Interface'; import { updateCurrentUser } from '@/api/users'; +import type { SamlPreferencesExtractedData } from '@/Interface'; import { useUsersStore } from '@/stores/users.store'; export const useSSOStore = defineStore('sso', () => { @@ -15,10 +16,13 @@ export const useSSOStore = defineStore('sso', () => { const state = reactive({ loading: false, + samlConfig: undefined as (SamlPreferences & SamlPreferencesExtractedData) | undefined, }); const isLoading = computed(() => state.loading); + const samlConfig = computed(() => state.samlConfig); + const setLoading = (loading: boolean) => { state.loading = loading; }; @@ -56,7 +60,11 @@ export const useSSOStore = defineStore('sso', () => { ssoApi.toggleSamlConfig(rootStore.getRestApiContext, { loginEnabled: enabled }); const getSamlMetadata = async () => ssoApi.getSamlMetadata(rootStore.getRestApiContext); - const getSamlConfig = async () => ssoApi.getSamlConfig(rootStore.getRestApiContext); + const getSamlConfig = async () => { + const samlConfig = await ssoApi.getSamlConfig(rootStore.getRestApiContext); + state.samlConfig = samlConfig; + return samlConfig; + }; const saveSamlConfig = async (config: SamlPreferences) => ssoApi.saveSamlConfig(rootStore.getRestApiContext, config); const testSamlConfig = async () => ssoApi.testSamlConfig(rootStore.getRestApiContext); @@ -77,6 +85,7 @@ export const useSSOStore = defineStore('sso', () => { isEnterpriseSamlEnabled, isDefaultAuthenticationSaml, showSsoLoginButton, + samlConfig, getSSORedirectUrl, getSamlMetadata, getSamlConfig, diff --git a/packages/editor-ui/src/views/SettingsSso.vue b/packages/editor-ui/src/views/SettingsSso.vue index 0365378f2b9bb..4e7b045bb6521 100644 --- a/packages/editor-ui/src/views/SettingsSso.vue +++ b/packages/editor-ui/src/views/SettingsSso.vue @@ -1,55 +1,120 @@