mirror of
https://github.com/n8n-io/n8n
synced 2026-04-21 15:47:20 +00:00
fix(editor): Reset OIDC form dirty state after saving IdP settings (#28388)
This commit is contained in:
parent
f54608e6e4
commit
1042350f4e
5 changed files with 71 additions and 13 deletions
|
|
@ -115,10 +115,10 @@ const cannotSaveOidcSettings = computed(() => {
|
|||
);
|
||||
});
|
||||
|
||||
async function onOidcSettingsSave(provisioningChangesConfirmed: boolean = false) {
|
||||
async function onOidcSettingsSave(provisioningChangesConfirmed: boolean = false): Promise<boolean> {
|
||||
if (!provisioningChangesConfirmed && roleAssignmentTransition.value !== 'none') {
|
||||
showUserRoleProvisioningDialog.value = true;
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
const isLoginEnabledChanged = ssoStore.oidcConfig?.loginEnabled !== ssoStore.isOidcLoginEnabled;
|
||||
|
|
@ -140,7 +140,7 @@ async function onOidcSettingsSave(provisioningChangesConfirmed: boolean = false)
|
|||
),
|
||||
},
|
||||
);
|
||||
if (confirmAction !== MODAL_CONFIRM) return;
|
||||
if (confirmAction !== MODAL_CONFIRM) return false;
|
||||
}
|
||||
|
||||
const acrArray = authenticationContextClassReference.value
|
||||
|
|
@ -176,12 +176,13 @@ async function onOidcSettingsSave(provisioningChangesConfirmed: boolean = false)
|
|||
title: i18n.baseText('settings.sso.settings.save.success'),
|
||||
type: 'success',
|
||||
});
|
||||
return true;
|
||||
} catch (error) {
|
||||
toast.showError(error, i18n.baseText('settings.sso.settings.save.error_oidc'));
|
||||
return;
|
||||
return false;
|
||||
} finally {
|
||||
savingForm.value = false;
|
||||
await getOidcConfig();
|
||||
savingForm.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -199,7 +199,7 @@ const prompTestSamlConnectionBeforeActivating = async () => {
|
|||
return promptOpeningTestConnectionPage;
|
||||
};
|
||||
|
||||
const onSave = async (provisioningChangesConfirmed: boolean = false) => {
|
||||
const onSave = async (provisioningChangesConfirmed: boolean = false): Promise<boolean> => {
|
||||
try {
|
||||
savingForm.value = true;
|
||||
validateSamlInput();
|
||||
|
|
@ -210,13 +210,13 @@ const onSave = async (provisioningChangesConfirmed: boolean = false) => {
|
|||
if (isDisablingSamlLogin) {
|
||||
const confirmDisablingSaml = await promptConfirmDisablingSamlLogin();
|
||||
if (confirmDisablingSaml !== MODAL_CONFIRM) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!provisioningChangesConfirmed && roleAssignmentTransition.value !== 'none') {
|
||||
showUserRoleProvisioningDialog.value = true;
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
showUserRoleProvisioningDialog.value = false;
|
||||
|
||||
|
|
@ -233,7 +233,7 @@ const onSave = async (provisioningChangesConfirmed: boolean = false) => {
|
|||
|
||||
const confirmTest = await prompTestSamlConnectionBeforeActivating();
|
||||
if (confirmTest !== MODAL_CONFIRM) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -257,9 +257,10 @@ const onSave = async (provisioningChangesConfirmed: boolean = false) => {
|
|||
title: i18n.baseText('settings.sso.settings.save.success'),
|
||||
type: 'success',
|
||||
});
|
||||
return true;
|
||||
} catch (error) {
|
||||
toast.showError(error, i18n.baseText('settings.sso.settings.save.error'));
|
||||
return;
|
||||
return false;
|
||||
} finally {
|
||||
savingForm.value = false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -139,6 +139,7 @@ export const useSSOStore = defineStore('sso', () => {
|
|||
const getOidcConfig = async () => {
|
||||
const config = await ssoApi.getOidcConfig(rootStore.restApiContext);
|
||||
oidcConfig.value = config;
|
||||
oidc.value.loginEnabled = config.loginEnabled;
|
||||
return config;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
import type { OidcConfigDto } from '@n8n/api-types';
|
||||
import { createPinia, setActivePinia } from 'pinia';
|
||||
import { useSSOStore, SupportedProtocols } from '@/features/settings/sso/sso.store';
|
||||
import type { UserManagementAuthenticationMethod } from '@/Interface';
|
||||
import * as ssoApi from '@n8n/rest-api-client/api/sso';
|
||||
|
||||
vi.mock('@n8n/rest-api-client/api/sso');
|
||||
|
||||
let ssoStore: ReturnType<typeof useSSOStore>;
|
||||
|
||||
|
|
@ -150,4 +154,53 @@ describe('SSO store', () => {
|
|||
expect(ssoStore.selectedAuthProtocol).toBe(SupportedProtocols.SAML);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getOidcConfig', () => {
|
||||
it('should sync oidc.loginEnabled when fetching config', async () => {
|
||||
const oidcConfig: OidcConfigDto = {
|
||||
clientId: 'test-id',
|
||||
clientSecret: 'test-secret',
|
||||
discoveryEndpoint: 'https://example.com/.well-known/openid-configuration',
|
||||
loginEnabled: true,
|
||||
prompt: 'select_account',
|
||||
authenticationContextClassReference: [],
|
||||
};
|
||||
|
||||
vi.mocked(ssoApi.getOidcConfig).mockResolvedValue(oidcConfig);
|
||||
|
||||
// loginEnabled starts as false (default)
|
||||
expect(ssoStore.isOidcLoginEnabled).toBe(false);
|
||||
|
||||
await ssoStore.getOidcConfig();
|
||||
|
||||
// After fetching config, loginEnabled should be synced
|
||||
expect(ssoStore.isOidcLoginEnabled).toBe(true);
|
||||
expect(ssoStore.oidcConfig).toEqual(oidcConfig);
|
||||
});
|
||||
|
||||
it('should reset oidc.loginEnabled to false when server config has it disabled', async () => {
|
||||
// Start with loginEnabled = true via initialize
|
||||
ssoStore.initialize({
|
||||
authenticationMethod: 'oidc' as UserManagementAuthenticationMethod,
|
||||
config: { oidc: { loginEnabled: true } },
|
||||
features: { saml: false, ldap: false, oidc: true },
|
||||
});
|
||||
expect(ssoStore.isOidcLoginEnabled).toBe(true);
|
||||
|
||||
// Server returns config with loginEnabled = false
|
||||
vi.mocked(ssoApi.getOidcConfig).mockResolvedValue({
|
||||
clientId: 'test-id',
|
||||
clientSecret: 'test-secret',
|
||||
discoveryEndpoint: 'https://example.com/.well-known/openid-configuration',
|
||||
loginEnabled: false,
|
||||
prompt: 'select_account',
|
||||
authenticationContextClassReference: [],
|
||||
});
|
||||
|
||||
await ssoStore.getOidcConfig();
|
||||
|
||||
// loginEnabled should now be false, matching server state
|
||||
expect(ssoStore.isOidcLoginEnabled).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -77,9 +77,11 @@ onBeforeRouteLeave((_to, _from, next) => {
|
|||
|
||||
async function onSaveAndLeave() {
|
||||
showUnsavedChangesDialog.value = false;
|
||||
await activeForm.value?.onSave();
|
||||
pendingNext.value?.();
|
||||
pendingNext.value = null;
|
||||
const saved = await activeForm.value?.onSave();
|
||||
if (saved) {
|
||||
pendingNext.value?.();
|
||||
pendingNext.value = null;
|
||||
}
|
||||
}
|
||||
|
||||
function onLeaveWithoutSaving() {
|
||||
|
|
|
|||
Loading…
Reference in a new issue