From 6916628a9f11e07cbcdf390f747f396fb0ef9e3c Mon Sep 17 00:00:00 2001 From: Csaba Tuncsik Date: Fri, 17 Mar 2023 21:07:08 +0100 Subject: [PATCH] feat(editor): SSO login button (#5615) * feat(editor): SSO login button * feat(editor): SSO login button * feat(editor): SSO login button --- .../src/sso/saml/routes/saml.controller.ee.ts | 6 +- .../src/components/N8nFormBox/FormBox.vue | 1 + packages/editor-ui/src/Interface.ts | 7 +++ packages/editor-ui/src/api/sso.ts | 6 ++ .../editor-ui/src/components/SSOLogin.vue | 61 +++++++++++++++++++ packages/editor-ui/src/constants.ts | 1 + .../src/plugins/i18n/locales/en.json | 5 +- packages/editor-ui/src/router.ts | 2 +- packages/editor-ui/src/stores/settings.ts | 10 ++- packages/editor-ui/src/stores/sso.ts | 42 +++++++++++++ packages/editor-ui/src/views/AuthView.vue | 12 +++- packages/editor-ui/src/views/SigninView.vue | 1 + 12 files changed, 144 insertions(+), 10 deletions(-) create mode 100644 packages/editor-ui/src/api/sso.ts create mode 100644 packages/editor-ui/src/components/SSOLogin.vue create mode 100644 packages/editor-ui/src/stores/sso.ts diff --git a/packages/cli/src/sso/saml/routes/saml.controller.ee.ts b/packages/cli/src/sso/saml/routes/saml.controller.ee.ts index ae3dc89f7f..d5cc9969d2 100644 --- a/packages/cli/src/sso/saml/routes/saml.controller.ee.ts +++ b/packages/cli/src/sso/saml/routes/saml.controller.ee.ts @@ -137,9 +137,9 @@ export class SamlController { const result = this.samlService.getLoginRequestUrl(); if (result?.binding === 'redirect') { // forced client side redirect through the use of a javascript redirect - return res.send(getInitSSOPostView(result.context)); - // TODO:SAML: If we want the frontend to handle the redirect, we will send the redirect URL instead: - // return res.status(301).send(result.context.context); + // return res.send(getInitSSOPostView(result.context)); + // Return the redirect URL directly + return res.send(result.context.context); } else if (result?.binding === 'post') { return res.send(getInitSSOFormView(result.context as PostBindingContext)); } else { diff --git a/packages/design-system/src/components/N8nFormBox/FormBox.vue b/packages/design-system/src/components/N8nFormBox/FormBox.vue index 5cedc6d9aa..465fa957f3 100644 --- a/packages/design-system/src/components/N8nFormBox/FormBox.vue +++ b/packages/design-system/src/components/N8nFormBox/FormBox.vue @@ -33,6 +33,7 @@ {{ redirectText }} + diff --git a/packages/editor-ui/src/Interface.ts b/packages/editor-ui/src/Interface.ts index 8f1021a22d..d0394414a2 100644 --- a/packages/editor-ui/src/Interface.ts +++ b/packages/editor-ui/src/Interface.ts @@ -623,10 +623,17 @@ export interface IN8nPromptResponse { updated: boolean; } +export enum UserManagementAuthenticationMethod { + Email = 'email', + Ldap = 'ldap', + Saml = 'saml', +} + export interface IUserManagementConfig { enabled: boolean; showSetupOnFirstLoad?: boolean; smtpSetup: boolean; + authenticationMethod: UserManagementAuthenticationMethod; } export interface IPermissionGroup { diff --git a/packages/editor-ui/src/api/sso.ts b/packages/editor-ui/src/api/sso.ts new file mode 100644 index 0000000000..5019335d35 --- /dev/null +++ b/packages/editor-ui/src/api/sso.ts @@ -0,0 +1,6 @@ +import { makeRestApiRequest } from '@/utils'; +import { IRestApiContext } from '@/Interface'; + +export const initSSO = (context: IRestApiContext): Promise => { + return makeRestApiRequest(context, 'GET', '/sso/saml/initsso'); +}; diff --git a/packages/editor-ui/src/components/SSOLogin.vue b/packages/editor-ui/src/components/SSOLogin.vue new file mode 100644 index 0000000000..baa5bbacfd --- /dev/null +++ b/packages/editor-ui/src/components/SSOLogin.vue @@ -0,0 +1,61 @@ + + + + + diff --git a/packages/editor-ui/src/constants.ts b/packages/editor-ui/src/constants.ts index 3607bb8f25..a7e2e4b597 100644 --- a/packages/editor-ui/src/constants.ts +++ b/packages/editor-ui/src/constants.ts @@ -448,6 +448,7 @@ export enum EnterpriseEditionFeature { Sharing = 'sharing', Ldap = 'ldap', LogStreaming = 'logStreaming', + Saml = 'saml', } export const MAIN_NODE_PANEL_WIDTH = 360; diff --git a/packages/editor-ui/src/plugins/i18n/locales/en.json b/packages/editor-ui/src/plugins/i18n/locales/en.json index 6ed0d72749..9345cbf838 100644 --- a/packages/editor-ui/src/plugins/i18n/locales/en.json +++ b/packages/editor-ui/src/plugins/i18n/locales/en.json @@ -1692,5 +1692,8 @@ "settings.ldap.form.pageSize.infoText": "Max number of records to return per page during synchronization. 0 for unlimited", "settings.ldap.form.searchTimeout.label": "Search Timeout (Seconds)", "settings.ldap.form.searchTimeout.infoText": "The timeout value for queries to the AD/LDAP server. Increase if you are getting timeout errors caused by a slow AD/LDAP server", - "settings.ldap.section.synchronization.title": "Synchronization" + "settings.ldap.section.synchronization.title": "Synchronization", + + "sso.login.divider": "or", + "sso.login.button": "Continue with SSO" } diff --git a/packages/editor-ui/src/router.ts b/packages/editor-ui/src/router.ts index 02a33d0d44..3b007a3285 100644 --- a/packages/editor-ui/src/router.ts +++ b/packages/editor-ui/src/router.ts @@ -31,7 +31,7 @@ import WorkflowsView from '@/views/WorkflowsView.vue'; import { IPermissions } from './Interface'; import { LOGIN_STATUS, ROLE } from '@/utils'; import { RouteConfigSingleView } from 'vue-router/types/router'; -import { EnterpriseEditionFeature, VIEWS } from './constants'; +import { VIEWS } from './constants'; import { useSettingsStore } from './stores/settings'; import { useTemplatesStore } from './stores/templates'; import SettingsUsageAndPlanVue from './views/SettingsUsageAndPlan.vue'; diff --git a/packages/editor-ui/src/stores/settings.ts b/packages/editor-ui/src/stores/settings.ts index 07e374a636..ee790a7571 100644 --- a/packages/editor-ui/src/stores/settings.ts +++ b/packages/editor-ui/src/stores/settings.ts @@ -15,14 +15,15 @@ import { VALUE_SURVEY_MODAL_KEY, } from '@/constants'; import { + ILdapConfig, ILogLevel, IN8nPromptResponse, IN8nPrompts, IN8nUISettings, IN8nValueSurveyData, ISettingsState, + UserManagementAuthenticationMethod, WorkflowCallerPolicyDefaultOption, - ILdapConfig, } from '@/Interface'; import { IDataObject, ITelemetrySettings } from 'n8n-workflow'; import { defineStore } from 'pinia'; @@ -40,6 +41,7 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, { enabled: false, showSetupOnFirstLoad: false, smtpSetup: false, + authenticationMethod: UserManagementAuthenticationMethod.Email, }, templatesEndpointHealthy: false, api: { @@ -169,13 +171,15 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, { workflowCallerPolicyDefaultOption(): WorkflowCallerPolicyDefaultOption { return this.settings.workflowCallerPolicyDefaultOption; }, + isDefaultAuthenticationSaml(): boolean { + return this.userManagement.authenticationMethod === UserManagementAuthenticationMethod.Saml; + }, }, actions: { setSettings(settings: IN8nUISettings): void { this.settings = settings; - this.userManagement.enabled = settings.userManagement.enabled; + this.userManagement = settings.userManagement; this.userManagement.showSetupOnFirstLoad = !!settings.userManagement.showSetupOnFirstLoad; - this.userManagement.smtpSetup = settings.userManagement.smtpSetup; this.api = settings.publicApi; this.onboardingCallPromptEnabled = settings.onboardingCallPromptEnabled; this.ldap.loginEnabled = settings.sso.ldap.loginEnabled; diff --git a/packages/editor-ui/src/stores/sso.ts b/packages/editor-ui/src/stores/sso.ts new file mode 100644 index 0000000000..52779c4608 --- /dev/null +++ b/packages/editor-ui/src/stores/sso.ts @@ -0,0 +1,42 @@ +import { computed, reactive } from 'vue'; +import { defineStore } from 'pinia'; +import { EnterpriseEditionFeature } from '@/constants'; +import { useRootStore } from '@/stores/n8nRootStore'; +import { useSettingsStore } from '@/stores/settings'; +import { initSSO } from '@/api/sso'; + +export const useSSOStore = defineStore('sso', () => { + const rootStore = useRootStore(); + const settingsStore = useSettingsStore(); + + const state = reactive({ + loading: false, + }); + + const isLoading = computed(() => state.loading); + + const setLoading = (loading: boolean) => { + state.loading = loading; + }; + + const isSamlLoginEnabled = computed(() => settingsStore.isSamlLoginEnabled); + const isEnterpriseSamlEnabled = computed(() => + settingsStore.isEnterpriseFeatureEnabled(EnterpriseEditionFeature.Saml), + ); + const isDefaultAuthenticationSaml = computed(() => settingsStore.isDefaultAuthenticationSaml); + const showSsoLoginButton = computed( + () => + isSamlLoginEnabled.value && + isEnterpriseSamlEnabled.value && + isDefaultAuthenticationSaml.value, + ); + + const getSSORedirectUrl = () => initSSO(rootStore.getRestApiContext); + + return { + isLoading, + setLoading, + showSsoLoginButton, + getSSORedirectUrl, + }; +}); diff --git a/packages/editor-ui/src/views/AuthView.vue b/packages/editor-ui/src/views/AuthView.vue index ace8b3d0ae..611090cbd4 100644 --- a/packages/editor-ui/src/views/AuthView.vue +++ b/packages/editor-ui/src/views/AuthView.vue @@ -14,7 +14,9 @@ @secondaryClick="onSecondaryClick" @submit="onSubmit" @input="onInput" - /> + > + + @@ -22,12 +24,14 @@