mirror of
https://github.com/n8n-io/n8n.git
synced 2026-05-31 16:57:08 +02:00
feat(core): Shorten copy text on confirm provisioning dialog (#22086)
This commit is contained in:
parent
68a81c2ed6
commit
4ddd7089b3
|
|
@ -2542,27 +2542,21 @@
|
|||
"settings.provisioning.toggle": "Provision instance and project roles",
|
||||
"settings.provisioning.toggle.help": "Project access can only be defined on external provider. Any existing project access configured in n8n, but not on the provider, will be removed once a user logs in.",
|
||||
"settings.provisioningConfirmDialog.enable.title": "Enable user role provisioning",
|
||||
"settings.provisioningConfirmDialog.disable.title": "Disable user role provisioning",
|
||||
"settings.provisioningConfirmDialog.breakingChangeDescription.firstLine": "When you enable Just-in-time provisioning, your external SSO provider becomes the source of truth for all instance and project roles in n8n.",
|
||||
"settings.provisioningConfirmDialog.breakingChangeDescription.list.one": "If your SSO provider doesn't specify a role for a member, we'll automatically assign the default role: global:member.",
|
||||
"settings.provisioningConfirmDialog.breakingChangeDescription.list.two": "Any existing instance and project roles in n8n will be replaced by the roles defined in your SSO provider once the user logs in via SSO.",
|
||||
"settings.provisioningConfirmDialog.breakingChangeRequiredSteps": "To enable you to migrate your current access settings to your SSO provider, download the two CSV files below. This step is mandatory before enabling JIT.",
|
||||
"settings.provisioningConfirmDialog.disable.description": "You're switching instance role management back to n8n.",
|
||||
"settings.provisioningConfirmDialog.disable.whatWillHappen": "What will happen:",
|
||||
"settings.provisioningConfirmDialog.disable.list.one": "The SSO n8n_instance_role attribute will be ignored.",
|
||||
"settings.provisioningConfirmDialog.disable.list.two": "Instance roles must be reassigned manually inside n8n.",
|
||||
"settings.provisioningConfirmDialog.disable.beforeSaving": "Before saving, make sure:",
|
||||
"settings.provisioningConfirmDialog.disable.checklist.one": "You are ready to reassign instance roles for all users inside n8n.",
|
||||
"settings.provisioningConfirmDialog.disable.checklist.two": "You understand that role changes made in SSO will no longer be applied.",
|
||||
"settings.provisioningConfirmDialog.enable.checkbox": "I have downloaded and reviewed the CSV export. My SSO provider is correctly configured to become the source of truth for user role provisioning on this n8n instance.",
|
||||
"settings.provisioningConfirmDialog.disable.title": "Manage user role provisioning in n8n only",
|
||||
"settings.provisioningConfirmDialog.breakingChangeDescription.firstSentence.partOne": "Your SSO provider will control all user roles in n8n.",
|
||||
"settings.provisioningConfirmDialog.breakingChangeDescription.firstSentence.partOne.withProjectRoles": "Your SSO provider will control all user and project roles in n8n.",
|
||||
"settings.provisioningConfirmDialog.breakingChangeDescription.firstSentence.partTwo": "Roles not assigned by your SSO provider will default to global:member.",
|
||||
"settings.provisioningConfirmDialog.breakingChangeDescription.secondLine": "<b>Before enabling:</b> Download and review your current access settings below to ensure your SSO provider is configured correctly.",
|
||||
"settings.provisioningConfirmDialog.disable.description": "You're switching instance role management from SSO back to n8n.",
|
||||
"settings.provisioningConfirmDialog.enable.checkbox": "I have downloaded and reviewed the CSV export. My SSO provider is correctly configured to control user roles on this n8n instance.",
|
||||
"settings.provisioningConfirmDialog.disable.checkbox": "I confirm that I want to no longer provision user roles from my SSO provider.",
|
||||
"settings.provisioningConfirmDialog.link.docs": "Link to docs",
|
||||
"settings.provisioningConfirmDialog.button.enable.confirm": "Save and enable",
|
||||
"settings.provisioningConfirmDialog.button.disable.confirm": "Save and disable",
|
||||
"settings.provisioningConfirmDialog.button.disable.confirm": "Save",
|
||||
"settings.provisioningConfirmDialog.button.cancel": "Cancel",
|
||||
"settings.provisioningConfirmDialog.button.generateCsvExport": "Generate access settings CSV export",
|
||||
"settings.provisioningConfirmDialog.button.downloadProjectRolesCsv": "Download existing project access settings csv",
|
||||
"settings.provisioningConfirmDialog.button.downloadInstanceRolesCsv": "Download existing instance role settings csv",
|
||||
"settings.provisioningConfirmDialog.button.downloadProjectRolesCsv": "Existing project access settings csv",
|
||||
"settings.provisioningConfirmDialog.button.downloadInstanceRolesCsv": "Existing instance role settings csv",
|
||||
"settings.provisioningInstanceRolesHandledBySsoProvider.description": "User management and instance roles are controlled by your SSO provider. Contact your n8n instance owner or admin to make changes.",
|
||||
"settings.provisioningProjectRolesHandledBySsoProvider.description": "User management and project roles are controlled by your SSO provider. Contact your n8n instance owner or admin to make changes.",
|
||||
"settings.externalSecrets.title": "External Secrets",
|
||||
|
|
@ -3470,9 +3464,7 @@
|
|||
"settings.sso.subtitle": "SAML 2.0 Configuration",
|
||||
"settings.sso.info": "Activate SAML or OIDC 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 or OIDC.",
|
||||
"settings.sso.activation.tooltip": "You need to save the settings first before activating SAML",
|
||||
"settings.sso.activated": "Activated",
|
||||
"settings.sso.deactivated": "Deactivated",
|
||||
"settings.sso.activated": "Enable Single Sign On",
|
||||
"settings.sso.settings.redirectUrl.label": "Redirect URL",
|
||||
"settings.sso.settings.redirectUrl.copied": "Redirect URL copied to clipboard",
|
||||
"settings.sso.settings.redirectUrl.help": "Copy the Redirect URL to configure your SAML provider",
|
||||
|
|
@ -3494,26 +3486,25 @@
|
|||
"settings.sso.settings.userRoleProvisioning.label": "User role provisioning",
|
||||
"settings.sso.settings.userRoleProvisioning.help": "Manage instance and project roles from your SSO provider.",
|
||||
"settings.sso.settings.userRoleProvisioning.help.linkText": "Link to docs",
|
||||
"settings.sso.settings.userRoleProvisioning.option.disabled.label": "Disabled",
|
||||
"settings.sso.settings.userRoleProvisioning.option.disabled.description": "User and project roles are managed inside the n8n settings.",
|
||||
"settings.sso.settings.userRoleProvisioning.option.disabled.label": "Managed in n8n",
|
||||
"settings.sso.settings.userRoleProvisioning.option.instanceRole.label": "Instance role",
|
||||
"settings.sso.settings.userRoleProvisioning.option.instanceRole.description": "The instance role of a user is configured in the \"n8n_instance_role\" attribute on your SSO provider. If none is set on the SSO provider, the member role is used as fallback.",
|
||||
"settings.sso.settings.userRoleProvisioning.option.instanceAndProjectRoles.label": "Instance and project roles",
|
||||
"settings.sso.settings.userRoleProvisioning.option.instanceAndProjectRoles.description": "The list of projects a user has access to is configured on the \"n8n_projects\" string array attribute on your SSO provider. Project access cannot be granted from within n8n.",
|
||||
"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.testConnection.title": "Test and activate SAML SSO",
|
||||
"settings.sso.settings.save.testConnection.message": "You are about to activate SSO via SAML. Test your SAML SSO settings first before proceeding.",
|
||||
"settings.sso.settings.save.testConnection.test": "Test settings",
|
||||
"settings.sso.settings.save.confirmTestConnection.title": "Confirm successful connection test",
|
||||
"settings.sso.settings.save.confirmTestConnection.message": "Was the connection test successful? Confirm to activate SSO via SAML.",
|
||||
"settings.sso.settings.save.confirmTestConnection.confirm": "Confirm",
|
||||
"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.save.error_oidc": "Error saving OIDC 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 the 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",
|
||||
"settings.oidc.confirmMessage.beforeSaveForm.headline": "Are you sure you want to disable OIDC login?",
|
||||
"settings.oidc.confirmMessage.beforeSaveForm.message": "If you do so, all OIDC users will be converted to email users.",
|
||||
"settings.sso.confirmMessage.beforeSaveForm.headline": "Are you sure you want to disable {protocol} login?",
|
||||
"settings.sso.confirmMessage.beforeSaveForm.message": "If you do so, all {protocol} users will be converted to email users.",
|
||||
"settings.mfa.secret": "Secret {secret}",
|
||||
"settings.mfa": "MFA",
|
||||
"settings.mfa.title": "Multi-factor Authentication",
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { MODAL_CONFIRM } from '@/app/constants';
|
|||
import { SupportedProtocols, useSSOStore } from '../sso.store';
|
||||
import { useI18n } from '@n8n/i18n';
|
||||
|
||||
import { ElSwitch } from 'element-plus';
|
||||
import { ElCheckbox } from 'element-plus';
|
||||
import { N8nActionBox, N8nButton, N8nInput, N8nOption, N8nSelect } from '@n8n/design-system';
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { useToast } from '@/app/composables/useToast';
|
||||
|
|
@ -26,6 +26,8 @@ const toast = useToast();
|
|||
const message = useMessage();
|
||||
const pageRedirectionHelper = usePageRedirectionHelper();
|
||||
|
||||
const savingForm = ref<boolean>(false);
|
||||
|
||||
const discoveryEndpoint = ref('');
|
||||
const clientId = ref('');
|
||||
const clientSecret = ref('');
|
||||
|
|
@ -60,12 +62,6 @@ const promptDescriptions: PromptDescription[] = [
|
|||
{ label: i18n.baseText('settings.sso.settings.oidc.prompt.create'), value: 'create' },
|
||||
];
|
||||
|
||||
const oidcActivatedLabel = computed(() =>
|
||||
ssoStore.isOidcLoginEnabled
|
||||
? i18n.baseText('settings.sso.activated')
|
||||
: i18n.baseText('settings.sso.deactivated'),
|
||||
);
|
||||
|
||||
const authenticationContextClassReference = ref('');
|
||||
|
||||
const getOidcConfig = async () => {
|
||||
|
|
@ -114,8 +110,12 @@ const cannotSaveOidcSettings = computed(() => {
|
|||
async function onOidcSettingsSave(provisioningChangesConfirmed: boolean = false) {
|
||||
if (ssoStore.oidcConfig?.loginEnabled && !ssoStore.isOidcLoginEnabled) {
|
||||
const confirmAction = await message.confirm(
|
||||
i18n.baseText('settings.oidc.confirmMessage.beforeSaveForm.message'),
|
||||
i18n.baseText('settings.oidc.confirmMessage.beforeSaveForm.headline'),
|
||||
i18n.baseText('settings.sso.confirmMessage.beforeSaveForm.message', {
|
||||
interpolate: { protocol: 'OIDC' },
|
||||
}),
|
||||
i18n.baseText('settings.sso.confirmMessage.beforeSaveForm.headline', {
|
||||
interpolate: { protocol: 'OIDC' },
|
||||
}),
|
||||
{
|
||||
cancelButtonText: i18n.baseText(
|
||||
'settings.ldap.confirmMessage.beforeSaveForm.cancelButtonText',
|
||||
|
|
@ -139,6 +139,7 @@ async function onOidcSettingsSave(provisioningChangesConfirmed: boolean = false)
|
|||
.filter(Boolean);
|
||||
|
||||
try {
|
||||
savingForm.value = true;
|
||||
const newConfig = await ssoStore.saveOidcConfig({
|
||||
clientId: clientId.value,
|
||||
clientSecret: clientSecret.value,
|
||||
|
|
@ -163,6 +164,7 @@ async function onOidcSettingsSave(provisioningChangesConfirmed: boolean = false)
|
|||
toast.showError(error, i18n.baseText('settings.sso.settings.save.error_oidc'));
|
||||
return;
|
||||
} finally {
|
||||
savingForm.value = false;
|
||||
await getOidcConfig();
|
||||
}
|
||||
}
|
||||
|
|
@ -252,6 +254,7 @@ onMounted(async () => {
|
|||
:new-provisioning-setting="userRoleProvisioning"
|
||||
auth-protocol="oidc"
|
||||
@confirm-provisioning="onOidcSettingsSave(true)"
|
||||
@cancel="showUserRoleProvisioningDialog = false"
|
||||
/>
|
||||
<div :class="$style.group">
|
||||
<label>Authentication Context Class Reference</label>
|
||||
|
|
@ -267,20 +270,18 @@ onMounted(async () => {
|
|||
commas in order of preference.</small
|
||||
>
|
||||
</div>
|
||||
<div :class="$style.group">
|
||||
<ElSwitch
|
||||
v-model="ssoStore.isOidcLoginEnabled"
|
||||
data-test-id="sso-oidc-toggle"
|
||||
:class="$style.switch"
|
||||
:inactive-text="oidcActivatedLabel"
|
||||
/>
|
||||
<div :class="[$style.group, $style.checkboxGroup]">
|
||||
<ElCheckbox v-model="ssoStore.isOidcLoginEnabled" data-test-id="sso-oidc-toggle">{{
|
||||
i18n.baseText('settings.sso.activated')
|
||||
}}</ElCheckbox>
|
||||
</div>
|
||||
|
||||
<div :class="$style.buttons">
|
||||
<N8nButton
|
||||
data-test-id="sso-oidc-save"
|
||||
size="large"
|
||||
:disabled="cannotSaveOidcSettings"
|
||||
:loading="savingForm"
|
||||
:disabled="savingForm || cannotSaveOidcSettings"
|
||||
@click="onOidcSettingsSave(false)"
|
||||
>
|
||||
{{ i18n.baseText('settings.sso.settings.save') }}
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ import { SupportedProtocols, useSSOStore } from '../sso.store';
|
|||
import { useI18n } from '@n8n/i18n';
|
||||
import { captureMessage } from '@sentry/vue';
|
||||
|
||||
import { ElSwitch } from 'element-plus';
|
||||
import { N8nActionBox, N8nButton, N8nInput, N8nRadioButtons, N8nTooltip } from '@n8n/design-system';
|
||||
import { ElCheckbox } from 'element-plus';
|
||||
import { N8nActionBox, N8nButton, N8nInput, N8nRadioButtons } from '@n8n/design-system';
|
||||
import { useToast } from '@/app/composables/useToast';
|
||||
import { usePageRedirectionHelper } from '@/app/composables/usePageRedirectionHelper';
|
||||
import { useMessage } from '@/app/composables/useMessage';
|
||||
|
|
@ -18,6 +18,7 @@ import { useUserRoleProvisioningForm } from '../provisioning/composables/useUser
|
|||
import { useRootStore } from '@n8n/stores/useRootStore';
|
||||
import { useTelemetry } from '@/app/composables/useTelemetry';
|
||||
import ConfirmProvisioningDialog from '../provisioning/components/ConfirmProvisioningDialog.vue';
|
||||
import { MODAL_CONFIRM } from '@/app/constants/modals';
|
||||
|
||||
const i18n = useI18n();
|
||||
const ssoStore = useSSOStore();
|
||||
|
|
@ -26,7 +27,10 @@ const toast = useToast();
|
|||
const message = useMessage();
|
||||
const pageRedirectionHelper = usePageRedirectionHelper();
|
||||
|
||||
const savingForm = ref<boolean>(false);
|
||||
|
||||
const redirectUrl = ref();
|
||||
const samlLoginEnabled = ref<boolean>(false);
|
||||
|
||||
const IdentityProviderSettingsType = {
|
||||
URL: 'url',
|
||||
|
|
@ -45,17 +49,9 @@ const ipsOptions = ref([
|
|||
]);
|
||||
const ipsType = ref(IdentityProviderSettingsType.URL);
|
||||
|
||||
const ssoActivatedLabel = computed(() =>
|
||||
ssoStore.isSamlLoginEnabled
|
||||
? i18n.baseText('settings.sso.activated')
|
||||
: i18n.baseText('settings.sso.deactivated'),
|
||||
);
|
||||
|
||||
const metadataUrl = ref();
|
||||
const metadata = ref();
|
||||
|
||||
const ssoSettingsSaved = ref(false);
|
||||
|
||||
const entityId = ref();
|
||||
|
||||
const showUserRoleProvisioningDialog = ref(false);
|
||||
|
|
@ -90,25 +86,32 @@ const getSamlConfig = async () => {
|
|||
|
||||
metadata.value = config?.metadata;
|
||||
metadataUrl.value = config?.metadataUrl;
|
||||
ssoSettingsSaved.value = !!config?.metadata;
|
||||
samlLoginEnabled.value = config.loginEnabled ?? false;
|
||||
};
|
||||
|
||||
const isSaveEnabled = computed(() => {
|
||||
if (isUserRoleProvisioningChanged()) {
|
||||
return true;
|
||||
} else if (ipsType.value === IdentityProviderSettingsType.URL) {
|
||||
return !!metadataUrl.value && metadataUrl.value !== ssoStore.samlConfig?.metadataUrl;
|
||||
} else if (ipsType.value === IdentityProviderSettingsType.XML) {
|
||||
return !!metadata.value && metadata.value !== ssoStore.samlConfig?.metadata;
|
||||
if (savingForm.value) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
const isIdentityProviderChanged = () => {
|
||||
if (ipsType.value === IdentityProviderSettingsType.URL) {
|
||||
return !!metadataUrl.value && metadataUrl.value !== ssoStore.samlConfig?.metadataUrl;
|
||||
} else if (ipsType.value === IdentityProviderSettingsType.XML) {
|
||||
return !!metadata.value && metadata.value !== ssoStore.samlConfig?.metadata;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
const isSamlLoginEnabledChanged = ssoStore.isSamlLoginEnabled !== samlLoginEnabled.value;
|
||||
return (
|
||||
isUserRoleProvisioningChanged() || isIdentityProviderChanged() || isSamlLoginEnabledChanged
|
||||
);
|
||||
});
|
||||
|
||||
const isTestEnabled = computed(() => {
|
||||
if (ipsType.value === IdentityProviderSettingsType.URL) {
|
||||
return !!metadataUrl.value && ssoSettingsSaved.value;
|
||||
return !!metadataUrl.value;
|
||||
} else if (ipsType.value === IdentityProviderSettingsType.XML) {
|
||||
return !!metadata.value && ssoSettingsSaved.value;
|
||||
return !!metadata.value;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
|
@ -127,20 +130,94 @@ const sendTrackingEvent = (config?: SamlPreferences) => {
|
|||
telemetry.track('User updated single sign on settings', trackingMetadata);
|
||||
};
|
||||
|
||||
const promptConfirmDisablingSamlLogin = async () => {
|
||||
const confirmAction = await message.confirm(
|
||||
i18n.baseText('settings.sso.confirmMessage.beforeSaveForm.message', {
|
||||
interpolate: { protocol: 'SAML' },
|
||||
}),
|
||||
i18n.baseText('settings.sso.confirmMessage.beforeSaveForm.headline', {
|
||||
interpolate: { protocol: 'SAML' },
|
||||
}),
|
||||
{
|
||||
cancelButtonText: i18n.baseText(
|
||||
'settings.ldap.confirmMessage.beforeSaveForm.cancelButtonText',
|
||||
),
|
||||
confirmButtonText: i18n.baseText(
|
||||
'settings.ldap.confirmMessage.beforeSaveForm.confirmButtonText',
|
||||
),
|
||||
},
|
||||
);
|
||||
return confirmAction;
|
||||
};
|
||||
|
||||
const prompTestSamlConnectionBeforeActivating = async () => {
|
||||
const promptOpeningTestConnectionPage = await message.confirm(
|
||||
i18n.baseText('settings.sso.settings.save.testConnection.message'),
|
||||
i18n.baseText('settings.sso.settings.save.testConnection.title'),
|
||||
{
|
||||
confirmButtonText: i18n.baseText('settings.sso.settings.save.testConnection.test'),
|
||||
cancelButtonText: i18n.baseText('settings.sso.settings.save.activate.cancel'),
|
||||
},
|
||||
);
|
||||
|
||||
if (promptOpeningTestConnectionPage === MODAL_CONFIRM) {
|
||||
await onTest();
|
||||
|
||||
const promptConfirmingSuccessfulTest = await message.confirm(
|
||||
i18n.baseText('settings.sso.settings.save.confirmTestConnection.message'),
|
||||
i18n.baseText('settings.sso.settings.save.confirmTestConnection.title'),
|
||||
{
|
||||
confirmButtonText: i18n.baseText(
|
||||
'settings.sso.settings.save.confirmTestConnection.confirm',
|
||||
),
|
||||
cancelButtonText: i18n.baseText('settings.sso.settings.save.activate.cancel'),
|
||||
},
|
||||
);
|
||||
return promptConfirmingSuccessfulTest;
|
||||
}
|
||||
return promptOpeningTestConnectionPage;
|
||||
};
|
||||
|
||||
const onSave = async (provisioningChangesConfirmed: boolean = false) => {
|
||||
try {
|
||||
savingForm.value = true;
|
||||
validateSamlInput();
|
||||
|
||||
if (isUserRoleProvisioningChanged() && !provisioningChangesConfirmed) {
|
||||
const isDisablingSamlLogin = ssoStore.isSamlLoginEnabled && !samlLoginEnabled.value;
|
||||
|
||||
if (isDisablingSamlLogin) {
|
||||
const confirmDisablingSaml = await promptConfirmDisablingSamlLogin();
|
||||
if (confirmDisablingSaml !== MODAL_CONFIRM) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isDisablingSamlLogin && isUserRoleProvisioningChanged() && !provisioningChangesConfirmed) {
|
||||
showUserRoleProvisioningDialog.value = true;
|
||||
return;
|
||||
}
|
||||
|
||||
const config: Partial<SamlPreferences> =
|
||||
const metaDataConfig: Partial<SamlPreferences> =
|
||||
ipsType.value === IdentityProviderSettingsType.URL
|
||||
? { metadataUrl: metadataUrl.value }
|
||||
: { metadata: metadata.value };
|
||||
const configResponse = await ssoStore.saveSamlConfig(config);
|
||||
|
||||
const isActivatingSamlLogin = !ssoStore.isSamlLoginEnabled && samlLoginEnabled.value;
|
||||
|
||||
if (isActivatingSamlLogin) {
|
||||
// metadata settings need to be saved for test to work
|
||||
await ssoStore.saveSamlConfig(metaDataConfig);
|
||||
|
||||
const confirmTest = await prompTestSamlConnectionBeforeActivating();
|
||||
if (confirmTest !== MODAL_CONFIRM) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const configResponse = await ssoStore.saveSamlConfig({
|
||||
...metaDataConfig,
|
||||
loginEnabled: samlLoginEnabled.value,
|
||||
});
|
||||
|
||||
if (isUserRoleProvisioningChanged()) {
|
||||
await saveProvisioningConfig();
|
||||
|
|
@ -149,30 +226,14 @@ const onSave = async (provisioningChangesConfirmed: boolean = false) => {
|
|||
|
||||
// Update store with saved protocol selection
|
||||
ssoStore.selectedAuthProtocol = SupportedProtocols.SAML;
|
||||
// Update store with saved metadata config
|
||||
ssoStore.samlConfig!.metadata = config.metadata;
|
||||
ssoStore.samlConfig!.metadataUrl = config.metadataUrl;
|
||||
|
||||
if (!ssoStore.isSamlLoginEnabled) {
|
||||
const answer = await message.confirm(
|
||||
i18n.baseText('settings.sso.settings.save.activate.message'),
|
||||
i18n.baseText('settings.sso.settings.save.activate.title'),
|
||||
{
|
||||
confirmButtonText: i18n.baseText('settings.sso.settings.save.activate.test'),
|
||||
cancelButtonText: i18n.baseText('settings.sso.settings.save.activate.cancel'),
|
||||
},
|
||||
);
|
||||
|
||||
if (answer === 'confirm') {
|
||||
await onTest();
|
||||
}
|
||||
}
|
||||
|
||||
await getSamlConfig();
|
||||
sendTrackingEvent(configResponse);
|
||||
} catch (error) {
|
||||
toast.showError(error, i18n.baseText('settings.sso.settings.save.error'));
|
||||
return;
|
||||
} finally {
|
||||
savingForm.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -207,15 +268,6 @@ const validateSamlInput = () => {
|
|||
}
|
||||
};
|
||||
|
||||
const isToggleSsoDisabled = computed(() => {
|
||||
/** Allow users to disable SSO even if config request fails */
|
||||
if (ssoStore.isSamlLoginEnabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !ssoSettingsSaved.value;
|
||||
});
|
||||
|
||||
const goToUpgrade = () => {
|
||||
void pageRedirectionHelper.goToUpgrade('sso', 'upgrade-sso');
|
||||
};
|
||||
|
|
@ -276,30 +328,18 @@ onMounted(async () => {
|
|||
:new-provisioning-setting="userRoleProvisioning"
|
||||
auth-protocol="saml"
|
||||
@confirm-provisioning="onSave(true)"
|
||||
@cancel="showUserRoleProvisioningDialog = false"
|
||||
/>
|
||||
<div :class="$style.group">
|
||||
<N8nTooltip
|
||||
v-if="ssoStore.isEnterpriseSamlEnabled"
|
||||
:disabled="ssoStore.isSamlLoginEnabled || ssoSettingsSaved"
|
||||
>
|
||||
<template #content>
|
||||
<span>
|
||||
{{ i18n.baseText('settings.sso.activation.tooltip') }}
|
||||
</span>
|
||||
</template>
|
||||
<ElSwitch
|
||||
v-model="ssoStore.isSamlLoginEnabled"
|
||||
data-test-id="sso-toggle"
|
||||
:disabled="isToggleSsoDisabled"
|
||||
:class="$style.switch"
|
||||
:inactive-text="ssoActivatedLabel"
|
||||
/>
|
||||
</N8nTooltip>
|
||||
<div :class="[$style.group, $style.checkboxGroup]">
|
||||
<ElCheckbox v-model="samlLoginEnabled" data-test-id="sso-toggle">{{
|
||||
i18n.baseText('settings.sso.activated')
|
||||
}}</ElCheckbox>
|
||||
</div>
|
||||
</div>
|
||||
<div :class="$style.buttons">
|
||||
<N8nButton
|
||||
:disabled="!isSaveEnabled"
|
||||
:loading="savingForm"
|
||||
size="large"
|
||||
data-test-id="sso-save"
|
||||
@click="onSave(false)"
|
||||
|
|
@ -316,10 +356,6 @@ onMounted(async () => {
|
|||
{{ i18n.baseText('settings.sso.settings.test') }}
|
||||
</N8nButton>
|
||||
</div>
|
||||
|
||||
<footer :class="$style.footer">
|
||||
{{ i18n.baseText('settings.sso.settings.footer.hint') }}
|
||||
</footer>
|
||||
</div>
|
||||
<N8nActionBox
|
||||
v-else
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts" setup>
|
||||
import { useI18n } from '@n8n/i18n';
|
||||
import { ElDialog } from 'element-plus';
|
||||
import { N8nButton, N8nCheckbox, N8nIcon, N8nText } from '@n8n/design-system';
|
||||
import { N8nButton, N8nCard, N8nCheckbox, N8nIcon, N8nText } from '@n8n/design-system';
|
||||
import { ref, watch, computed } from 'vue';
|
||||
import { useAccessSettingsCsvExport } from '@/features/settings/sso/provisioning/composables/useAccessSettingsCsvExport';
|
||||
import type { UserRoleProvisioningSetting } from './UserRoleProvisioningDropdown.vue';
|
||||
|
|
@ -77,22 +77,23 @@ const onConfirmProvisioningSetting = () => {
|
|||
>
|
||||
<template v-if="!isDisablingProvisioning">
|
||||
<div class="mb-s">
|
||||
<N8nText color="text-base">{{
|
||||
locale.baseText('settings.provisioningConfirmDialog.breakingChangeDescription.firstLine')
|
||||
}}</N8nText>
|
||||
<N8nText color="text-base"
|
||||
>{{
|
||||
locale.baseText(
|
||||
newProvisioningSetting === 'instance_and_project_roles'
|
||||
? 'settings.provisioningConfirmDialog.breakingChangeDescription.firstSentence.partOne.withProjectRoles'
|
||||
: 'settings.provisioningConfirmDialog.breakingChangeDescription.firstSentence.partOne',
|
||||
)
|
||||
}}
|
||||
</N8nText>
|
||||
<N8nText :class="$style.descriptionTextPartTwo" color="text-base">
|
||||
{{
|
||||
locale.baseText(
|
||||
'settings.provisioningConfirmDialog.breakingChangeDescription.firstSentence.partTwo',
|
||||
)
|
||||
}}</N8nText
|
||||
>
|
||||
</div>
|
||||
<ul :class="$style.list" class="mb-s">
|
||||
<li>
|
||||
<N8nText color="text-base">{{
|
||||
locale.baseText('settings.provisioningConfirmDialog.breakingChangeDescription.list.one')
|
||||
}}</N8nText>
|
||||
</li>
|
||||
<li v-if="newProvisioningSetting === 'instance_and_project_roles'">
|
||||
<N8nText color="text-base">{{
|
||||
locale.baseText('settings.provisioningConfirmDialog.breakingChangeDescription.list.two')
|
||||
}}</N8nText>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="mb-s">
|
||||
<N8nText color="text-base"
|
||||
><a
|
||||
|
|
@ -103,50 +104,51 @@ const onConfirmProvisioningSetting = () => {
|
|||
>
|
||||
</div>
|
||||
<div class="mb-s">
|
||||
<N8nText color="text-base">{{
|
||||
locale.baseText('settings.provisioningConfirmDialog.breakingChangeRequiredSteps')
|
||||
}}</N8nText>
|
||||
<N8nText
|
||||
color="text-base"
|
||||
v-n8n-html="
|
||||
locale.baseText(
|
||||
'settings.provisioningConfirmDialog.breakingChangeDescription.secondLine',
|
||||
)
|
||||
"
|
||||
></N8nText>
|
||||
</div>
|
||||
<div class="mb-s" :class="$style.buttonRow">
|
||||
<N8nButton
|
||||
type="secondary"
|
||||
native-type="button"
|
||||
data-test-id="provisioning-download-instance-roles-csv-button"
|
||||
:disabled="downloadingInstanceRolesCsv"
|
||||
:loading="downloadingInstanceRolesCsv"
|
||||
:class="$style.button"
|
||||
@click="onDownloadInstanceRolesCsv"
|
||||
>{{
|
||||
<ul :class="$style.list" class="mb-s">
|
||||
<li>
|
||||
<N8nText color="text-base">{{
|
||||
locale.baseText('settings.provisioningConfirmDialog.button.downloadInstanceRolesCsv')
|
||||
}}</N8nButton
|
||||
>
|
||||
<N8nIcon
|
||||
v-if="hasDownloadedInstanceRoleCsv"
|
||||
icon="check"
|
||||
color="success"
|
||||
:class="$style.icon"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="shouldShowProjectRolesCsv" class="mb-s" :class="$style.buttonRow">
|
||||
<N8nButton
|
||||
type="secondary"
|
||||
native-type="button"
|
||||
data-test-id="provisioning-download-project-roles-csv-button"
|
||||
:disabled="downloadingProjectRolesCsv"
|
||||
:loading="downloadingProjectRolesCsv"
|
||||
:class="$style.button"
|
||||
@click="onDownloadProjectRolesCsv"
|
||||
>{{
|
||||
}}</N8nText>
|
||||
<N8nButton
|
||||
v-if="!hasDownloadedInstanceRoleCsv"
|
||||
type="highlight"
|
||||
native-type="button"
|
||||
:icon="'file-download' as any"
|
||||
data-test-id="provisioning-download-instance-roles-csv-button"
|
||||
:disabled="downloadingInstanceRolesCsv"
|
||||
:loading="downloadingInstanceRolesCsv"
|
||||
:class="$style.button"
|
||||
@click="onDownloadInstanceRolesCsv"
|
||||
></N8nButton>
|
||||
<N8nIcon v-else icon="check" color="success" :class="$style.icon"></N8nIcon>
|
||||
</li>
|
||||
<li v-if="shouldShowProjectRolesCsv">
|
||||
<N8nText color="text-base">{{
|
||||
locale.baseText('settings.provisioningConfirmDialog.button.downloadProjectRolesCsv')
|
||||
}}</N8nButton
|
||||
>
|
||||
<N8nIcon
|
||||
v-if="hasDownloadedProjectRoleCsv"
|
||||
icon="check"
|
||||
color="success"
|
||||
:class="$style.icon"
|
||||
/>
|
||||
</div>
|
||||
}}</N8nText>
|
||||
<N8nButton
|
||||
v-if="!hasDownloadedProjectRoleCsv"
|
||||
type="highlight"
|
||||
native-type="button"
|
||||
:icon="'file-download' as any"
|
||||
data-test-id="provisioning-download-project-roles-csv-button"
|
||||
:disabled="downloadingProjectRolesCsv"
|
||||
:loading="downloadingProjectRolesCsv"
|
||||
:class="$style.button"
|
||||
@click="onDownloadProjectRolesCsv"
|
||||
></N8nButton>
|
||||
<N8nIcon v-else icon="check" color="success" :class="$style.icon"></N8nIcon>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="mb-s">
|
||||
|
|
@ -154,40 +156,6 @@ const onConfirmProvisioningSetting = () => {
|
|||
locale.baseText('settings.provisioningConfirmDialog.disable.description')
|
||||
}}</N8nText>
|
||||
</div>
|
||||
<div class="mb-s">
|
||||
<N8nText color="text-base">{{
|
||||
locale.baseText('settings.provisioningConfirmDialog.disable.whatWillHappen')
|
||||
}}</N8nText>
|
||||
</div>
|
||||
<ul :class="$style.list" class="mb-s">
|
||||
<li>
|
||||
<N8nText color="text-base">{{
|
||||
locale.baseText('settings.provisioningConfirmDialog.disable.list.one')
|
||||
}}</N8nText>
|
||||
</li>
|
||||
<li>
|
||||
<N8nText color="text-base">{{
|
||||
locale.baseText('settings.provisioningConfirmDialog.disable.list.two')
|
||||
}}</N8nText>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="mb-s">
|
||||
<N8nText color="text-base">{{
|
||||
locale.baseText('settings.provisioningConfirmDialog.disable.beforeSaving')
|
||||
}}</N8nText>
|
||||
</div>
|
||||
<ul :class="$style.list" class="mb-s">
|
||||
<li>
|
||||
<N8nText color="text-base">{{
|
||||
locale.baseText('settings.provisioningConfirmDialog.disable.checklist.one')
|
||||
}}</N8nText>
|
||||
</li>
|
||||
<li>
|
||||
<N8nText color="text-base">{{
|
||||
locale.baseText('settings.provisioningConfirmDialog.disable.checklist.two')
|
||||
}}</N8nText>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="mb-s">
|
||||
<N8nText color="text-base"
|
||||
><a
|
||||
|
|
@ -199,19 +167,21 @@ const onConfirmProvisioningSetting = () => {
|
|||
</div>
|
||||
</template>
|
||||
<div class="mb-s">
|
||||
<N8nCheckbox
|
||||
v-model="confirmationChecked"
|
||||
:disabled="
|
||||
!isDisablingProvisioning &&
|
||||
(!hasDownloadedInstanceRoleCsv ||
|
||||
(shouldShowProjectRolesCsv && !hasDownloadedProjectRoleCsv))
|
||||
"
|
||||
data-test-id="provisioning-confirmation-checkbox"
|
||||
>
|
||||
<N8nText color="text-base">{{
|
||||
locale.baseText(`settings.provisioningConfirmDialog.${messagingKey}.checkbox`)
|
||||
}}</N8nText>
|
||||
</N8nCheckbox>
|
||||
<N8nCard :class="$style.card">
|
||||
<N8nCheckbox
|
||||
v-model="confirmationChecked"
|
||||
:disabled="
|
||||
!isDisablingProvisioning &&
|
||||
(!hasDownloadedInstanceRoleCsv ||
|
||||
(shouldShowProjectRolesCsv && !hasDownloadedProjectRoleCsv))
|
||||
"
|
||||
data-test-id="provisioning-confirmation-checkbox"
|
||||
>
|
||||
<N8nText color="text-base">{{
|
||||
locale.baseText(`settings.provisioningConfirmDialog.${messagingKey}.checkbox`)
|
||||
}}</N8nText>
|
||||
</N8nCheckbox>
|
||||
</N8nCard>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
|
|
@ -242,24 +212,35 @@ const onConfirmProvisioningSetting = () => {
|
|||
</template>
|
||||
|
||||
<style lang="scss" module>
|
||||
.buttonRow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.button {
|
||||
min-width: 340px;
|
||||
}
|
||||
|
||||
.icon {
|
||||
margin-left: var(--spacing--xs);
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: var(--color--background--light-1);
|
||||
}
|
||||
|
||||
.descriptionTextPartTwo {
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.icon {
|
||||
height: 32px; // to match height of download button
|
||||
margin: 0 var(--spacing--lg);
|
||||
}
|
||||
|
||||
.list {
|
||||
padding: 0 var(--spacing--sm);
|
||||
padding: 0 var(--spacing--2xs);
|
||||
|
||||
li {
|
||||
list-style: disc outside;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&::before {
|
||||
content: '•';
|
||||
margin-right: var(--spacing--3xs);
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -49,7 +49,6 @@ const getUserRoleProvisioningValueFromConfig = (
|
|||
|
||||
type UserRoleProvisioningDescription = {
|
||||
label: string;
|
||||
description: string;
|
||||
value: UserRoleProvisioningSetting;
|
||||
};
|
||||
|
||||
|
|
@ -57,25 +56,16 @@ const userRoleProvisioningDescriptions: UserRoleProvisioningDescription[] = [
|
|||
{
|
||||
label: i18n.baseText('settings.sso.settings.userRoleProvisioning.option.disabled.label'),
|
||||
value: 'disabled',
|
||||
description: i18n.baseText(
|
||||
'settings.sso.settings.userRoleProvisioning.option.disabled.description',
|
||||
),
|
||||
},
|
||||
{
|
||||
label: i18n.baseText('settings.sso.settings.userRoleProvisioning.option.instanceRole.label'),
|
||||
value: 'instance_role',
|
||||
description: i18n.baseText(
|
||||
'settings.sso.settings.userRoleProvisioning.option.instanceRole.description',
|
||||
),
|
||||
},
|
||||
{
|
||||
label: i18n.baseText(
|
||||
'settings.sso.settings.userRoleProvisioning.option.instanceAndProjectRoles.label',
|
||||
),
|
||||
value: 'instance_and_project_roles',
|
||||
description: i18n.baseText(
|
||||
'settings.sso.settings.userRoleProvisioning.option.instanceAndProjectRoles.description',
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
|
|
@ -104,12 +94,7 @@ onMounted(async () => {
|
|||
:label="option.label"
|
||||
data-test-id="oidc-user-role-provisioning-option"
|
||||
:value="option.value"
|
||||
>
|
||||
<div class="list-option">
|
||||
<div class="option-headline">{{ option.label }}</div>
|
||||
<div class="option-description">{{ option.description }}</div>
|
||||
</div>
|
||||
</N8nOption>
|
||||
/>
|
||||
</N8nSelect>
|
||||
<small
|
||||
>{{ i18n.baseText('settings.sso.settings.userRoleProvisioning.help') }}
|
||||
|
|
|
|||
|
|
@ -88,7 +88,6 @@ export const useSSOStore = defineStore('sso', () => {
|
|||
get: () => saml.value.loginEnabled,
|
||||
set: (value: boolean) => {
|
||||
saml.value.loginEnabled = value;
|
||||
void toggleLoginEnabled(value);
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -98,14 +97,13 @@ export const useSSOStore = defineStore('sso', () => {
|
|||
() => authenticationMethod.value === UserManagementAuthenticationMethod.Saml,
|
||||
);
|
||||
|
||||
const toggleLoginEnabled = async (enabled: boolean) =>
|
||||
await ssoApi.toggleSamlConfig(rootStore.restApiContext, { loginEnabled: enabled });
|
||||
|
||||
const getSamlMetadata = async () => await ssoApi.getSamlMetadata(rootStore.restApiContext);
|
||||
|
||||
const getSamlConfig = async () => {
|
||||
const config = await ssoApi.getSamlConfig(rootStore.restApiContext);
|
||||
samlConfig.value = config;
|
||||
saml.value.loginEnabled = config.loginEnabled;
|
||||
saml.value.loginLabel = config.loginLabel;
|
||||
return config;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -38,6 +38,11 @@
|
|||
}
|
||||
}
|
||||
|
||||
.checkboxGroup label > *:first-child {
|
||||
// center checkbox next to label
|
||||
vertical-align: text-top;
|
||||
}
|
||||
|
||||
.actionBox {
|
||||
margin: var(--spacing--2xl) 0 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -121,31 +121,43 @@ describe('SettingsSso View', () => {
|
|||
const { getByTestId } = renderView();
|
||||
|
||||
const toggle = getByTestId('sso-toggle');
|
||||
const checkbox = toggle.querySelector('input[type="checkbox"]') as HTMLInputElement;
|
||||
|
||||
expect(toggle.textContent).toContain('Deactivated');
|
||||
expect(checkbox).not.toBeChecked();
|
||||
|
||||
await userEvent.click(toggle);
|
||||
expect(toggle.textContent).toContain('Activated');
|
||||
expect(checkbox).toBeChecked();
|
||||
|
||||
await userEvent.click(toggle);
|
||||
expect(toggle.textContent).toContain('Deactivated');
|
||||
expect(checkbox).not.toBeChecked();
|
||||
});
|
||||
|
||||
it("allows user to fill Identity Provider's URL", async () => {
|
||||
confirmMessage.mockResolvedValueOnce('confirm');
|
||||
// Mock two confirm dialogs: 1) test connection prompt, 2) confirm successful test
|
||||
confirmMessage.mockResolvedValueOnce('confirm').mockResolvedValueOnce('confirm');
|
||||
|
||||
const windowOpenSpy = vi.spyOn(window, 'open');
|
||||
|
||||
ssoStore.isEnterpriseSamlEnabled = true;
|
||||
ssoStore.isEnterpriseOidcEnabled = true;
|
||||
ssoStore.isSamlLoginEnabled = false;
|
||||
ssoStore.samlConfig = { ...samlConfig, metadataUrl: undefined, metadata: undefined };
|
||||
ssoStore.samlConfig = {
|
||||
...samlConfig,
|
||||
metadataUrl: undefined,
|
||||
metadata: undefined,
|
||||
loginEnabled: false,
|
||||
};
|
||||
ssoStore.getSamlConfig.mockResolvedValue({
|
||||
...samlConfig,
|
||||
metadataUrl: undefined,
|
||||
metadata: undefined,
|
||||
loginEnabled: false,
|
||||
});
|
||||
ssoStore.saveSamlConfig.mockResolvedValue({
|
||||
...samlConfig,
|
||||
metadata: undefined,
|
||||
loginEnabled: true,
|
||||
});
|
||||
ssoStore.saveSamlConfig.mockResolvedValue({ ...samlConfig, metadata: undefined });
|
||||
ssoStore.testSamlConfig.mockResolvedValue('https://test-url.com');
|
||||
|
||||
const { getByTestId } = renderView();
|
||||
|
|
@ -158,11 +170,18 @@ describe('SettingsSso View', () => {
|
|||
expect(urlInput).toBeVisible();
|
||||
await userEvent.type(urlInput, samlConfig.metadataUrl as string);
|
||||
|
||||
// Enable SSO toggle
|
||||
const toggle = getByTestId('sso-toggle');
|
||||
await userEvent.click(toggle);
|
||||
|
||||
expect(saveButton).not.toBeDisabled();
|
||||
await userEvent.click(saveButton);
|
||||
|
||||
expect(ssoStore.saveSamlConfig).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ metadataUrl: samlConfig.metadataUrl }),
|
||||
expect.objectContaining({
|
||||
metadataUrl: samlConfig.metadataUrl,
|
||||
loginEnabled: true,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(ssoStore.testSamlConfig).toHaveBeenCalled();
|
||||
|
|
@ -177,7 +196,8 @@ describe('SettingsSso View', () => {
|
|||
});
|
||||
|
||||
it("allows user to fill Identity Provider's XML", async () => {
|
||||
confirmMessage.mockResolvedValueOnce('confirm');
|
||||
// Mock two confirm dialogs: 1) test connection prompt, 2) confirm successful test
|
||||
confirmMessage.mockResolvedValueOnce('confirm').mockResolvedValueOnce('confirm');
|
||||
|
||||
const windowOpenSpy = vi.spyOn(window, 'open');
|
||||
|
||||
|
|
@ -185,8 +205,18 @@ describe('SettingsSso View', () => {
|
|||
ssoStore.isEnterpriseOidcEnabled = true;
|
||||
ssoStore.isSamlLoginEnabled = false;
|
||||
ssoStore.samlConfig = { ...samlConfig, metadataUrl: undefined, metadata: undefined };
|
||||
ssoStore.getSamlConfig.mockResolvedValue({
|
||||
...samlConfig,
|
||||
metadataUrl: undefined,
|
||||
metadata: undefined,
|
||||
loginEnabled: false,
|
||||
});
|
||||
// Mock should return config with metadata but WITHOUT metadataUrl (since user filled XML)
|
||||
ssoStore.saveSamlConfig.mockResolvedValue({ ...samlConfig, metadataUrl: undefined });
|
||||
ssoStore.saveSamlConfig.mockResolvedValue({
|
||||
...samlConfig,
|
||||
metadataUrl: undefined,
|
||||
loginEnabled: true,
|
||||
});
|
||||
ssoStore.testSamlConfig.mockResolvedValue('https://test-url.com');
|
||||
|
||||
const { getByTestId } = renderView();
|
||||
|
|
@ -201,11 +231,18 @@ describe('SettingsSso View', () => {
|
|||
expect(xmlInput).toBeVisible();
|
||||
await userEvent.type(xmlInput, samlConfig.metadata!);
|
||||
|
||||
// Enable SSO toggle
|
||||
const toggle = getByTestId('sso-toggle');
|
||||
await userEvent.click(toggle);
|
||||
|
||||
expect(saveButton).not.toBeDisabled();
|
||||
await userEvent.click(saveButton);
|
||||
|
||||
expect(ssoStore.saveSamlConfig).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ metadata: samlConfig.metadata }),
|
||||
expect.objectContaining({
|
||||
metadata: samlConfig.metadata,
|
||||
loginEnabled: true,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(ssoStore.testSamlConfig).toHaveBeenCalled();
|
||||
|
|
@ -281,19 +318,21 @@ describe('SettingsSso View', () => {
|
|||
ssoStore.isEnterpriseOidcEnabled = true;
|
||||
ssoStore.isOidcLoginEnabled = false;
|
||||
|
||||
const error = new Error('Request failed with status code 404');
|
||||
ssoStore.getSamlConfig.mockRejectedValue(error);
|
||||
ssoStore.getSamlConfig.mockResolvedValue({
|
||||
...samlConfig,
|
||||
loginEnabled: true,
|
||||
});
|
||||
|
||||
const { getByTestId } = renderView();
|
||||
|
||||
expect(ssoStore.getSamlConfig).toHaveBeenCalledTimes(1);
|
||||
|
||||
await waitFor(async () => {
|
||||
expect(showError).toHaveBeenCalledWith(error, 'error');
|
||||
const toggle = getByTestId('sso-toggle');
|
||||
expect(toggle.textContent).toContain('Activated');
|
||||
const checkbox = toggle.querySelector('input[type="checkbox"]') as HTMLInputElement;
|
||||
expect(checkbox).toBeChecked();
|
||||
await userEvent.click(toggle);
|
||||
expect(toggle.textContent).toContain('Deactivated');
|
||||
expect(checkbox).not.toBeChecked();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -312,7 +351,7 @@ describe('SettingsSso View', () => {
|
|||
|
||||
expect(container.querySelector('textarea[name="metadata"]')).toHaveValue(samlConfig.metadata);
|
||||
|
||||
expect(getByRole('switch')).toBeEnabled();
|
||||
expect(getByRole('checkbox')).toBeEnabled();
|
||||
expect(getByTestId('sso-test')).toBeEnabled();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user