mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 17:46:45 +00:00
refactor(editor): Extract SAML, OIDC, and LDAP from Settings Store into SSO Store (no-changelog) (#16276)
This commit is contained in:
@@ -2,19 +2,21 @@
|
||||
import { useSSOStore } from '@/stores/sso.store';
|
||||
import { useI18n } from '@n8n/i18n';
|
||||
import { useToast } from '@/composables/useToast';
|
||||
import { useSettingsStore } from '@/stores/settings.store';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
const i18n = useI18n();
|
||||
const ssoStore = useSSOStore();
|
||||
const toast = useToast();
|
||||
const settingsStore = useSettingsStore();
|
||||
const route = useRoute();
|
||||
|
||||
const onSSOLogin = async () => {
|
||||
try {
|
||||
const redirectUrl = ssoStore.isDefaultAuthenticationSaml
|
||||
? await ssoStore.getSSORedirectUrl()
|
||||
: settingsStore.settings.sso.oidc.loginUrl;
|
||||
window.location.href = redirectUrl;
|
||||
? await ssoStore.getSSORedirectUrl(
|
||||
typeof route.query?.redirect === 'string' ? route.query.redirect : '',
|
||||
)
|
||||
: ssoStore.oidc.loginUrl;
|
||||
window.location.href = redirectUrl ?? '';
|
||||
} catch (error) {
|
||||
toast.showError(error, 'Error', error.message);
|
||||
}
|
||||
|
||||
@@ -3,12 +3,18 @@ import { useCloudPlanStore } from '@/stores/cloudPlan.store';
|
||||
import { useSourceControlStore } from '@/stores/sourceControl.store';
|
||||
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
||||
import { useRootStore } from '@n8n/stores/useRootStore';
|
||||
import { initializeAuthenticatedFeatures, initializeCore } from '@/init';
|
||||
import { state, initializeAuthenticatedFeatures, initializeCore } from '@/init';
|
||||
import { createTestingPinia } from '@pinia/testing';
|
||||
import { setActivePinia } from 'pinia';
|
||||
import { useSettingsStore } from '@/stores/settings.store';
|
||||
import { useVersionsStore } from '@/stores/versions.store';
|
||||
import { AxiosError } from 'axios';
|
||||
import merge from 'lodash/merge';
|
||||
import { SETTINGS_STORE_DEFAULT_STATE } from '@/__tests__/utils';
|
||||
import { STORES } from '@n8n/stores';
|
||||
import { useSSOStore } from '@/stores/sso.store';
|
||||
import { UserManagementAuthenticationMethod } from '@/Interface';
|
||||
import { EnterpriseEditionFeature } from '@/constants';
|
||||
|
||||
const showMessage = vi.fn();
|
||||
|
||||
@@ -31,9 +37,17 @@ describe('Init', () => {
|
||||
let usersStore: ReturnType<typeof useUsersStore>;
|
||||
let nodeTypesStore: ReturnType<typeof useNodeTypesStore>;
|
||||
let versionsStore: ReturnType<typeof useVersionsStore>;
|
||||
let ssoStore: ReturnType<typeof useSSOStore>;
|
||||
|
||||
beforeEach(() => {
|
||||
setActivePinia(createTestingPinia());
|
||||
setActivePinia(
|
||||
createTestingPinia({
|
||||
initialState: {
|
||||
[STORES.SETTINGS]: merge({}, SETTINGS_STORE_DEFAULT_STATE),
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
settingsStore = useSettingsStore();
|
||||
cloudPlanStore = useCloudPlanStore();
|
||||
sourceControlStore = useSourceControlStore();
|
||||
@@ -41,9 +55,14 @@ describe('Init', () => {
|
||||
usersStore = useUsersStore();
|
||||
versionsStore = useVersionsStore();
|
||||
versionsStore = useVersionsStore();
|
||||
ssoStore = useSSOStore();
|
||||
});
|
||||
|
||||
describe('initializeCore()', () => {
|
||||
beforeEach(() => {
|
||||
state.initialized = false;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
@@ -63,6 +82,28 @@ describe('Init', () => {
|
||||
|
||||
expect(settingsStoreSpy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should initialize ssoStore with settings SSO configuration', async () => {
|
||||
const saml = { loginEnabled: true, loginLabel: '' };
|
||||
const ldap = { loginEnabled: false, loginLabel: '' };
|
||||
const oidc = { loginEnabled: false, loginUrl: '', callbackUrl: '' };
|
||||
|
||||
settingsStore.userManagement.authenticationMethod = UserManagementAuthenticationMethod.Saml;
|
||||
settingsStore.settings.sso = { saml, ldap, oidc };
|
||||
settingsStore.isEnterpriseFeatureEnabled[EnterpriseEditionFeature.Saml] = true;
|
||||
|
||||
await initializeCore();
|
||||
|
||||
expect(ssoStore.initialize).toHaveBeenCalledWith({
|
||||
authenticationMethod: UserManagementAuthenticationMethod.Saml,
|
||||
config: { saml, ldap, oidc },
|
||||
features: {
|
||||
saml: true,
|
||||
ldap: false,
|
||||
oidc: false,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('initializeAuthenticatedFeatures()', () => {
|
||||
|
||||
@@ -13,8 +13,13 @@ import { useInsightsStore } from '@/features/insights/insights.store';
|
||||
import { useToast } from '@/composables/useToast';
|
||||
import { useI18n } from '@n8n/i18n';
|
||||
import SourceControlInitializationErrorMessage from '@/components/SourceControlInitializationErrorMessage.vue';
|
||||
import { useSSOStore } from '@/stores/sso.store';
|
||||
import { EnterpriseEditionFeature } from '@/constants';
|
||||
import type { UserManagementAuthenticationMethod } from '@/Interface';
|
||||
|
||||
let coreInitialized = false;
|
||||
export const state = {
|
||||
initialized: false,
|
||||
};
|
||||
let authenticatedFeaturesInitialized = false;
|
||||
|
||||
/**
|
||||
@@ -22,16 +27,28 @@ let authenticatedFeaturesInitialized = false;
|
||||
* This is called once, when the first route is loaded.
|
||||
*/
|
||||
export async function initializeCore() {
|
||||
if (coreInitialized) {
|
||||
if (state.initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
const settingsStore = useSettingsStore();
|
||||
const usersStore = useUsersStore();
|
||||
const versionsStore = useVersionsStore();
|
||||
const ssoStore = useSSOStore();
|
||||
|
||||
await settingsStore.initialize();
|
||||
|
||||
ssoStore.initialize({
|
||||
authenticationMethod: settingsStore.userManagement
|
||||
.authenticationMethod as UserManagementAuthenticationMethod,
|
||||
config: settingsStore.settings.sso,
|
||||
features: {
|
||||
saml: settingsStore.isEnterpriseFeatureEnabled[EnterpriseEditionFeature.Saml],
|
||||
ldap: settingsStore.isEnterpriseFeatureEnabled[EnterpriseEditionFeature.Ldap],
|
||||
oidc: settingsStore.isEnterpriseFeatureEnabled[EnterpriseEditionFeature.Oidc],
|
||||
},
|
||||
});
|
||||
|
||||
void useExternalHooks().run('app.mount');
|
||||
|
||||
if (!settingsStore.isPreviewMode) {
|
||||
@@ -40,7 +57,7 @@ export async function initializeCore() {
|
||||
void versionsStore.checkForNewVersions();
|
||||
}
|
||||
|
||||
coreInitialized = true;
|
||||
state.initialized = true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,11 +3,9 @@ import Bowser from 'bowser';
|
||||
import type { IUserManagementSettings, FrontendSettings } from '@n8n/api-types';
|
||||
|
||||
import * as eventsApi from '@n8n/rest-api-client/api/events';
|
||||
import * as ldapApi from '@n8n/rest-api-client/api/ldap';
|
||||
import * as settingsApi from '@n8n/rest-api-client/api/settings';
|
||||
import * as promptsApi from '@n8n/rest-api-client/api/prompts';
|
||||
import { testHealthEndpoint } from '@/api/templates';
|
||||
import type { LdapConfig } from '@n8n/rest-api-client/api/ldap';
|
||||
import {
|
||||
INSECURE_CONNECTION_WARNING,
|
||||
LOCAL_STORAGE_EXPERIMENTAL_MIN_ZOOM_NODE_SETTINGS_IN_CANVAS,
|
||||
@@ -44,9 +42,6 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, () => {
|
||||
enabled: false,
|
||||
},
|
||||
});
|
||||
const ldap = ref({ loginLabel: '', loginEnabled: false });
|
||||
const saml = ref({ loginLabel: '', loginEnabled: false });
|
||||
const oidc = ref({ loginEnabled: false, loginUrl: '', callbackUrl: '' });
|
||||
const mfa = ref({ enabled: false });
|
||||
const folders = ref({ enabled: false });
|
||||
|
||||
@@ -90,16 +85,6 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, () => {
|
||||
|
||||
const publicApiPath = computed(() => api.value.path);
|
||||
|
||||
const isLdapLoginEnabled = computed(() => ldap.value.loginEnabled);
|
||||
|
||||
const ldapLoginLabel = computed(() => ldap.value.loginLabel);
|
||||
|
||||
const isSamlLoginEnabled = computed(() => saml.value.loginEnabled);
|
||||
|
||||
const isOidcLoginEnabled = computed(() => oidc.value.loginEnabled);
|
||||
|
||||
const oidcCallBackUrl = computed(() => oidc.value.callbackUrl);
|
||||
|
||||
const isAiAssistantEnabled = computed(() => settings.value.aiAssistant?.enabled);
|
||||
|
||||
const isAskAiEnabled = computed(() => settings.value.askAi?.enabled);
|
||||
@@ -182,14 +167,6 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, () => {
|
||||
() => settings.value.workflowCallerPolicyDefaultOption,
|
||||
);
|
||||
|
||||
const isDefaultAuthenticationSaml = computed(
|
||||
() => userManagement.value.authenticationMethod === UserManagementAuthenticationMethod.Saml,
|
||||
);
|
||||
|
||||
const isDefaultAuthenticationOidc = computed(
|
||||
() => userManagement.value.authenticationMethod === UserManagementAuthenticationMethod.Oidc,
|
||||
);
|
||||
|
||||
const permanentlyDismissedBanners = computed(() => settings.value.banners?.dismissed ?? []);
|
||||
|
||||
const isBelowUserQuota = computed(
|
||||
@@ -210,21 +187,6 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, () => {
|
||||
!!settings.value.userManagement.showSetupOnFirstLoad;
|
||||
}
|
||||
api.value = settings.value.publicApi;
|
||||
if (settings.value.sso?.ldap) {
|
||||
ldap.value.loginEnabled = settings.value.sso.ldap.loginEnabled;
|
||||
ldap.value.loginLabel = settings.value.sso.ldap.loginLabel;
|
||||
}
|
||||
if (settings.value.sso?.saml) {
|
||||
saml.value.loginEnabled = settings.value.sso.saml.loginEnabled;
|
||||
saml.value.loginLabel = settings.value.sso.saml.loginLabel;
|
||||
}
|
||||
|
||||
if (settings.value.sso?.oidc) {
|
||||
oidc.value.loginEnabled = settings.value.sso.oidc.loginEnabled;
|
||||
oidc.value.loginUrl = settings.value.sso.oidc.loginUrl || '';
|
||||
oidc.value.callbackUrl = settings.value.sso.oidc.callbackUrl || '';
|
||||
}
|
||||
|
||||
mfa.value.enabled = settings.value.mfa?.enabled;
|
||||
folders.value.enabled = settings.value.folders?.enabled;
|
||||
|
||||
@@ -364,31 +326,6 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, () => {
|
||||
templatesEndpointHealthy.value = true;
|
||||
};
|
||||
|
||||
const getLdapConfig = async () => {
|
||||
const rootStore = useRootStore();
|
||||
return await ldapApi.getLdapConfig(rootStore.restApiContext);
|
||||
};
|
||||
|
||||
const getLdapSynchronizations = async (pagination: { page: number }) => {
|
||||
const rootStore = useRootStore();
|
||||
return await ldapApi.getLdapSynchronizations(rootStore.restApiContext, pagination);
|
||||
};
|
||||
|
||||
const testLdapConnection = async () => {
|
||||
const rootStore = useRootStore();
|
||||
return await ldapApi.testLdapConnection(rootStore.restApiContext);
|
||||
};
|
||||
|
||||
const updateLdapConfig = async (ldapConfig: LdapConfig) => {
|
||||
const rootStore = useRootStore();
|
||||
return await ldapApi.updateLdapConfig(rootStore.restApiContext, ldapConfig);
|
||||
};
|
||||
|
||||
const runLdapSync = async (data: IDataObject) => {
|
||||
const rootStore = useRootStore();
|
||||
return await ldapApi.runLdapSync(rootStore.restApiContext, data);
|
||||
};
|
||||
|
||||
const getTimezones = async (): Promise<IDataObject> => {
|
||||
const rootStore = useRootStore();
|
||||
return await makeRestApiRequest(rootStore.restApiContext, 'GET', '/options/timezones');
|
||||
@@ -412,8 +349,6 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, () => {
|
||||
userManagement,
|
||||
templatesEndpointHealthy,
|
||||
api,
|
||||
ldap,
|
||||
saml,
|
||||
mfa,
|
||||
isDocker,
|
||||
isDevRelease,
|
||||
@@ -432,10 +367,6 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, () => {
|
||||
isPreviewMode,
|
||||
publicApiLatestVersion,
|
||||
publicApiPath,
|
||||
isLdapLoginEnabled,
|
||||
ldapLoginLabel,
|
||||
isSamlLoginEnabled,
|
||||
isOidcLoginEnabled,
|
||||
showSetupPage,
|
||||
deploymentType,
|
||||
isCloudDeployment,
|
||||
@@ -459,8 +390,6 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, () => {
|
||||
isQueueModeEnabled,
|
||||
isMultiMain,
|
||||
isWorkerViewAvailable,
|
||||
isDefaultAuthenticationSaml,
|
||||
isDefaultAuthenticationOidc,
|
||||
workflowCallerPolicyDefaultOption,
|
||||
permanentlyDismissedBanners,
|
||||
isBelowUserQuota,
|
||||
@@ -474,13 +403,7 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, () => {
|
||||
aiCreditsQuota,
|
||||
experimental__minZoomNodeSettingsInCanvas,
|
||||
partialExecutionVersion,
|
||||
oidcCallBackUrl,
|
||||
reset,
|
||||
testLdapConnection,
|
||||
getLdapConfig,
|
||||
getLdapSynchronizations,
|
||||
updateLdapConfig,
|
||||
runLdapSync,
|
||||
getTimezones,
|
||||
testTemplatesEndpoint,
|
||||
submitContactInfo,
|
||||
|
||||
@@ -1,75 +1,19 @@
|
||||
import type { OidcConfigDto } from '@n8n/api-types';
|
||||
import { type SamlPreferences } from '@n8n/api-types';
|
||||
import { computed, reactive } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { computed, ref } from 'vue';
|
||||
import { defineStore } from 'pinia';
|
||||
import { EnterpriseEditionFeature } from '@/constants';
|
||||
import { useRootStore } from '@n8n/stores/useRootStore';
|
||||
import { useSettingsStore } from '@/stores/settings.store';
|
||||
import * as ssoApi from '@n8n/rest-api-client/api/sso';
|
||||
import type { SamlPreferencesExtractedData } from '@n8n/rest-api-client/api/sso';
|
||||
import { updateCurrentUser } from '@/api/users';
|
||||
import { useUsersStore } from '@/stores/users.store';
|
||||
import * as ldapApi from '@n8n/rest-api-client/api/ldap';
|
||||
import type { LdapConfig } from '@n8n/rest-api-client/api/ldap';
|
||||
import type { IDataObject } from 'n8n-workflow';
|
||||
import { UserManagementAuthenticationMethod } from '@/Interface';
|
||||
|
||||
export const useSSOStore = defineStore('sso', () => {
|
||||
const rootStore = useRootStore();
|
||||
const settingsStore = useSettingsStore();
|
||||
const usersStore = useUsersStore();
|
||||
const route = useRoute();
|
||||
|
||||
const state = reactive({
|
||||
samlConfig: undefined as (SamlPreferences & SamlPreferencesExtractedData) | undefined,
|
||||
oidcConfig: undefined as OidcConfigDto | undefined,
|
||||
});
|
||||
|
||||
const samlConfig = computed(() => state.samlConfig);
|
||||
|
||||
const oidcConfig = computed(() => state.oidcConfig);
|
||||
|
||||
const isSamlLoginEnabled = computed({
|
||||
get: () => settingsStore.isSamlLoginEnabled,
|
||||
set: (value: boolean) => {
|
||||
settingsStore.setSettings({
|
||||
...settingsStore.settings,
|
||||
sso: {
|
||||
...settingsStore.settings.sso,
|
||||
saml: {
|
||||
...settingsStore.settings.sso.saml,
|
||||
loginEnabled: value,
|
||||
},
|
||||
},
|
||||
});
|
||||
void toggleLoginEnabled(value);
|
||||
},
|
||||
});
|
||||
|
||||
const isOidcLoginEnabled = computed({
|
||||
get: () => settingsStore.isOidcLoginEnabled,
|
||||
set: (value: boolean) => {
|
||||
settingsStore.setSettings({
|
||||
...settingsStore.settings,
|
||||
sso: {
|
||||
...settingsStore.settings.sso,
|
||||
oidc: {
|
||||
...settingsStore.settings.sso.oidc,
|
||||
loginEnabled: value,
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const isEnterpriseSamlEnabled = computed(
|
||||
() => settingsStore.isEnterpriseFeatureEnabled[EnterpriseEditionFeature.Saml],
|
||||
);
|
||||
|
||||
const isEnterpriseOidcEnabled = computed(
|
||||
() => settingsStore.isEnterpriseFeatureEnabled[EnterpriseEditionFeature.Oidc],
|
||||
);
|
||||
|
||||
const isDefaultAuthenticationSaml = computed(() => settingsStore.isDefaultAuthenticationSaml);
|
||||
|
||||
const isDefaultAuthenticationOidc = computed(() => settingsStore.isDefaultAuthenticationOidc);
|
||||
const authenticationMethod = ref<UserManagementAuthenticationMethod | undefined>(undefined);
|
||||
|
||||
const showSsoLoginButton = computed(
|
||||
() =>
|
||||
@@ -81,66 +25,201 @@ export const useSSOStore = defineStore('sso', () => {
|
||||
isDefaultAuthenticationOidc.value),
|
||||
);
|
||||
|
||||
const getSSORedirectUrl = async () =>
|
||||
await ssoApi.initSSO(
|
||||
rootStore.restApiContext,
|
||||
typeof route.query?.redirect === 'string' ? route.query.redirect : '',
|
||||
);
|
||||
const getSSORedirectUrl = async (existingRedirect?: string) =>
|
||||
await ssoApi.initSSO(rootStore.restApiContext, existingRedirect);
|
||||
|
||||
const initialize = (options: {
|
||||
authenticationMethod: UserManagementAuthenticationMethod;
|
||||
config: {
|
||||
ldap?: Pick<LdapConfig, 'loginLabel' | 'loginEnabled'>;
|
||||
saml?: Pick<SamlPreferences, 'loginLabel' | 'loginEnabled'>;
|
||||
oidc?: Pick<OidcConfigDto, 'loginEnabled'> & {
|
||||
loginUrl?: string;
|
||||
callbackUrl?: string;
|
||||
};
|
||||
};
|
||||
features: {
|
||||
saml: boolean;
|
||||
ldap: boolean;
|
||||
oidc: boolean;
|
||||
};
|
||||
}) => {
|
||||
authenticationMethod.value = options.authenticationMethod;
|
||||
|
||||
isEnterpriseLdapEnabled.value = options.features.ldap;
|
||||
if (options.config.ldap) {
|
||||
ldap.value.loginEnabled = options.config.ldap.loginEnabled;
|
||||
ldap.value.loginLabel = options.config.ldap.loginLabel;
|
||||
}
|
||||
|
||||
isEnterpriseSamlEnabled.value = options.features.saml;
|
||||
if (options.config.saml) {
|
||||
saml.value.loginEnabled = options.config.saml.loginEnabled;
|
||||
saml.value.loginLabel = options.config.saml.loginLabel;
|
||||
}
|
||||
|
||||
isEnterpriseOidcEnabled.value = options.features.oidc;
|
||||
if (options.config.oidc) {
|
||||
oidc.value.loginEnabled = options.config.oidc.loginEnabled;
|
||||
oidc.value.loginUrl = options.config.oidc.loginUrl || '';
|
||||
oidc.value.callbackUrl = options.config.oidc.callbackUrl || '';
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* SAML
|
||||
*/
|
||||
|
||||
const saml = ref<Pick<SamlPreferences, 'loginLabel' | 'loginEnabled'>>({
|
||||
loginLabel: '',
|
||||
loginEnabled: false,
|
||||
});
|
||||
|
||||
const samlConfig = ref<SamlPreferences & SamlPreferencesExtractedData>();
|
||||
|
||||
const isSamlLoginEnabled = computed({
|
||||
get: () => saml.value.loginEnabled,
|
||||
set: (value: boolean) => {
|
||||
saml.value.loginEnabled = value;
|
||||
void toggleLoginEnabled(value);
|
||||
},
|
||||
});
|
||||
|
||||
const isEnterpriseSamlEnabled = ref(false);
|
||||
|
||||
const isDefaultAuthenticationSaml = computed(
|
||||
() => 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 getOidcRedirectLUrl = async () => await ssoApi)
|
||||
|
||||
const getSamlConfig = async () => {
|
||||
const samlConfig = await ssoApi.getSamlConfig(rootStore.restApiContext);
|
||||
state.samlConfig = samlConfig;
|
||||
return samlConfig;
|
||||
const config = await ssoApi.getSamlConfig(rootStore.restApiContext);
|
||||
samlConfig.value = config;
|
||||
return config;
|
||||
};
|
||||
|
||||
const saveSamlConfig = async (config: Partial<SamlPreferences>) =>
|
||||
await ssoApi.saveSamlConfig(rootStore.restApiContext, config);
|
||||
|
||||
const testSamlConfig = async () => await ssoApi.testSamlConfig(rootStore.restApiContext);
|
||||
|
||||
const updateUser = async (params: { firstName: string; lastName: string }) =>
|
||||
await updateCurrentUser(rootStore.restApiContext, {
|
||||
email: usersStore.currentUser!.email!,
|
||||
...params,
|
||||
});
|
||||
/**
|
||||
* OIDC
|
||||
*/
|
||||
|
||||
const userData = computed(() => usersStore.currentUser);
|
||||
const oidc = ref<
|
||||
Pick<OidcConfigDto, 'loginEnabled'> & {
|
||||
loginUrl?: string;
|
||||
callbackUrl?: string;
|
||||
}
|
||||
>({
|
||||
loginUrl: '',
|
||||
loginEnabled: false,
|
||||
callbackUrl: '',
|
||||
});
|
||||
|
||||
const oidcConfig = ref<OidcConfigDto | undefined>();
|
||||
|
||||
const isEnterpriseOidcEnabled = ref(false);
|
||||
|
||||
const getOidcConfig = async () => {
|
||||
const oidcConfig = await ssoApi.getOidcConfig(rootStore.restApiContext);
|
||||
state.oidcConfig = oidcConfig;
|
||||
return oidcConfig;
|
||||
const config = await ssoApi.getOidcConfig(rootStore.restApiContext);
|
||||
oidcConfig.value = config;
|
||||
return config;
|
||||
};
|
||||
|
||||
const saveOidcConfig = async (config: OidcConfigDto) => {
|
||||
const savedConfig = await ssoApi.saveOidcConfig(rootStore.restApiContext, config);
|
||||
state.oidcConfig = savedConfig;
|
||||
oidcConfig.value = savedConfig;
|
||||
return savedConfig;
|
||||
};
|
||||
|
||||
const isOidcLoginEnabled = computed({
|
||||
get: () => oidc.value.loginEnabled,
|
||||
set: (value: boolean) => {
|
||||
oidc.value.loginEnabled = value;
|
||||
},
|
||||
});
|
||||
|
||||
const isDefaultAuthenticationOidc = computed(
|
||||
() => authenticationMethod.value === UserManagementAuthenticationMethod.Oidc,
|
||||
);
|
||||
|
||||
/**
|
||||
* LDAP Configuration
|
||||
*/
|
||||
|
||||
const ldap = ref<Pick<LdapConfig, 'loginLabel' | 'loginEnabled'>>({
|
||||
loginLabel: '',
|
||||
loginEnabled: false,
|
||||
});
|
||||
|
||||
const isEnterpriseLdapEnabled = ref(false);
|
||||
|
||||
const isLdapLoginEnabled = computed(() => ldap.value.loginEnabled);
|
||||
|
||||
const ldapLoginLabel = computed(() => ldap.value.loginLabel);
|
||||
|
||||
const getLdapConfig = async () => {
|
||||
const rootStore = useRootStore();
|
||||
return await ldapApi.getLdapConfig(rootStore.restApiContext);
|
||||
};
|
||||
|
||||
const getLdapSynchronizations = async (pagination: { page: number }) => {
|
||||
const rootStore = useRootStore();
|
||||
return await ldapApi.getLdapSynchronizations(rootStore.restApiContext, pagination);
|
||||
};
|
||||
|
||||
const testLdapConnection = async () => {
|
||||
const rootStore = useRootStore();
|
||||
return await ldapApi.testLdapConnection(rootStore.restApiContext);
|
||||
};
|
||||
|
||||
const updateLdapConfig = async (ldapConfig: LdapConfig) => {
|
||||
const rootStore = useRootStore();
|
||||
return await ldapApi.updateLdapConfig(rootStore.restApiContext, ldapConfig);
|
||||
};
|
||||
|
||||
const runLdapSync = async (data: IDataObject) => {
|
||||
const rootStore = useRootStore();
|
||||
return await ldapApi.runLdapSync(rootStore.restApiContext, data);
|
||||
};
|
||||
|
||||
return {
|
||||
isEnterpriseOidcEnabled,
|
||||
showSsoLoginButton,
|
||||
getSSORedirectUrl,
|
||||
initialize,
|
||||
|
||||
saml,
|
||||
samlConfig,
|
||||
isSamlLoginEnabled,
|
||||
isOidcLoginEnabled,
|
||||
isEnterpriseSamlEnabled,
|
||||
isDefaultAuthenticationSaml,
|
||||
isDefaultAuthenticationOidc,
|
||||
showSsoLoginButton,
|
||||
samlConfig,
|
||||
oidcConfig,
|
||||
userData,
|
||||
getSSORedirectUrl,
|
||||
getSamlMetadata,
|
||||
getSamlConfig,
|
||||
saveSamlConfig,
|
||||
testSamlConfig,
|
||||
updateUser,
|
||||
|
||||
oidc,
|
||||
oidcConfig,
|
||||
isOidcLoginEnabled,
|
||||
isEnterpriseOidcEnabled,
|
||||
isDefaultAuthenticationOidc,
|
||||
getOidcConfig,
|
||||
saveOidcConfig,
|
||||
|
||||
ldap,
|
||||
isLdapLoginEnabled,
|
||||
isEnterpriseLdapEnabled,
|
||||
ldapLoginLabel,
|
||||
getLdapConfig,
|
||||
getLdapSynchronizations,
|
||||
testLdapConnection,
|
||||
updateLdapConfig,
|
||||
runLdapSync,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -1,20 +1,13 @@
|
||||
import { createPinia, setActivePinia } from 'pinia';
|
||||
import { useSettingsStore } from '@/stores/settings.store';
|
||||
import { useSSOStore } from '@/stores/sso.store';
|
||||
import merge from 'lodash/merge';
|
||||
import { SETTINGS_STORE_DEFAULT_STATE } from '@/__tests__/utils';
|
||||
import type { FrontendSettings } from '@n8n/api-types';
|
||||
import type { UserManagementAuthenticationMethod } from '@/Interface';
|
||||
|
||||
let ssoStore: ReturnType<typeof useSSOStore>;
|
||||
let settingsStore: ReturnType<typeof useSettingsStore>;
|
||||
|
||||
const DEFAULT_SETTINGS: FrontendSettings = SETTINGS_STORE_DEFAULT_STATE.settings;
|
||||
|
||||
describe('SSO store', () => {
|
||||
beforeEach(() => {
|
||||
setActivePinia(createPinia());
|
||||
ssoStore = useSSOStore();
|
||||
settingsStore = useSettingsStore();
|
||||
});
|
||||
|
||||
test.each([
|
||||
@@ -26,21 +19,19 @@ describe('SSO store', () => {
|
||||
])(
|
||||
'should check SSO login button availability when authenticationMethod is %s and enterprise feature is %s and sso login is set to %s',
|
||||
(authenticationMethod, saml, loginEnabled, expectation) => {
|
||||
settingsStore.setSettings(
|
||||
merge({}, DEFAULT_SETTINGS, {
|
||||
userManagement: {
|
||||
authenticationMethod,
|
||||
ssoStore.initialize({
|
||||
authenticationMethod: authenticationMethod as UserManagementAuthenticationMethod,
|
||||
config: {
|
||||
saml: {
|
||||
loginEnabled,
|
||||
},
|
||||
enterprise: {
|
||||
saml,
|
||||
},
|
||||
sso: {
|
||||
saml: {
|
||||
loginEnabled,
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
},
|
||||
features: {
|
||||
saml,
|
||||
ldap: false,
|
||||
oidc: false,
|
||||
},
|
||||
});
|
||||
|
||||
expect(ssoStore.showSsoLoginButton).toBe(expectation);
|
||||
},
|
||||
|
||||
@@ -264,6 +264,18 @@ export const useUsersStore = defineStore(STORES.USERS, () => {
|
||||
const updateUser = async (params: UserUpdateRequestDto) => {
|
||||
const user = await usersApi.updateCurrentUser(rootStore.restApiContext, params);
|
||||
addUsers([user]);
|
||||
return user;
|
||||
};
|
||||
|
||||
const updateUserName = async (params: { firstName: string; lastName: string }) => {
|
||||
if (!currentUser.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
return await updateUser({
|
||||
email: currentUser.value.email as string,
|
||||
...params,
|
||||
});
|
||||
};
|
||||
|
||||
const updateUserSettings = async (settings: SettingsUpdateRequestDto) => {
|
||||
@@ -430,6 +442,7 @@ export const useUsersStore = defineStore(STORES.USERS, () => {
|
||||
validatePasswordToken,
|
||||
changePassword,
|
||||
updateUser,
|
||||
updateUserName,
|
||||
updateUserSettings,
|
||||
updateOtherUserSettings,
|
||||
updateCurrentUserPassword,
|
||||
|
||||
@@ -3,10 +3,10 @@ import { useRouter } from 'vue-router';
|
||||
import { createTestingPinia } from '@pinia/testing';
|
||||
import merge from 'lodash/merge';
|
||||
import SamlOnboarding from '@/views/SamlOnboarding.vue';
|
||||
import { useSSOStore } from '@/stores/sso.store';
|
||||
import { STORES } from '@n8n/stores';
|
||||
import { SETTINGS_STORE_DEFAULT_STATE, waitAllPromises } from '@/__tests__/utils';
|
||||
import { createComponentRenderer } from '@/__tests__/render';
|
||||
import { useUsersStore } from '@/stores/users.store';
|
||||
|
||||
vi.mock('vue-router', () => {
|
||||
const push = vi.fn();
|
||||
@@ -20,7 +20,7 @@ vi.mock('vue-router', () => {
|
||||
});
|
||||
|
||||
let pinia: ReturnType<typeof createTestingPinia>;
|
||||
let ssoStore: ReturnType<typeof useSSOStore>;
|
||||
let usersStore: ReturnType<typeof useUsersStore>;
|
||||
let router: ReturnType<typeof useRouter>;
|
||||
|
||||
const renderComponent = createComponentRenderer(SamlOnboarding);
|
||||
@@ -34,7 +34,7 @@ describe('SamlOnboarding', () => {
|
||||
},
|
||||
},
|
||||
});
|
||||
ssoStore = useSSOStore(pinia);
|
||||
usersStore = useUsersStore(pinia);
|
||||
router = useRouter();
|
||||
});
|
||||
|
||||
@@ -43,7 +43,7 @@ describe('SamlOnboarding', () => {
|
||||
});
|
||||
|
||||
it('should submit filled in form only and redirect', async () => {
|
||||
vi.spyOn(ssoStore, 'updateUser').mockResolvedValue({
|
||||
vi.spyOn(usersStore, 'updateUserName').mockResolvedValue({
|
||||
id: '1',
|
||||
isPending: false,
|
||||
});
|
||||
@@ -56,14 +56,14 @@ describe('SamlOnboarding', () => {
|
||||
await userEvent.click(submit);
|
||||
await waitAllPromises();
|
||||
|
||||
expect(ssoStore.updateUser).not.toHaveBeenCalled();
|
||||
expect(usersStore.updateUserName).not.toHaveBeenCalled();
|
||||
expect(router.push).not.toHaveBeenCalled();
|
||||
|
||||
await userEvent.type(inputs[0], 'test');
|
||||
await userEvent.type(inputs[1], 'test');
|
||||
await userEvent.click(submit);
|
||||
|
||||
expect(ssoStore.updateUser).toHaveBeenCalled();
|
||||
expect(usersStore.updateUserName).toHaveBeenCalled();
|
||||
expect(router.push).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,13 +6,13 @@ import AuthView from '@/views/AuthView.vue';
|
||||
import { VIEWS } from '@/constants';
|
||||
import { useI18n } from '@n8n/i18n';
|
||||
import { useToast } from '@/composables/useToast';
|
||||
import { useSSOStore } from '@/stores/sso.store';
|
||||
import { useUsersStore } from '@/stores/users.store';
|
||||
|
||||
const router = useRouter();
|
||||
const locale = useI18n();
|
||||
const toast = useToast();
|
||||
|
||||
const ssoStore = useSSOStore();
|
||||
const usersStore = useUsersStore();
|
||||
|
||||
const loading = ref(false);
|
||||
const FORM_CONFIG: IFormBoxConfig = reactive({
|
||||
@@ -21,7 +21,7 @@ const FORM_CONFIG: IFormBoxConfig = reactive({
|
||||
inputs: [
|
||||
{
|
||||
name: 'firstName',
|
||||
initialValue: ssoStore.userData?.firstName,
|
||||
initialValue: usersStore.currentUser?.firstName,
|
||||
properties: {
|
||||
label: locale.baseText('auth.firstName'),
|
||||
maxlength: 32,
|
||||
@@ -32,7 +32,7 @@ const FORM_CONFIG: IFormBoxConfig = reactive({
|
||||
},
|
||||
{
|
||||
name: 'lastName',
|
||||
initialValue: ssoStore.userData?.lastName,
|
||||
initialValue: usersStore.currentUser?.lastName,
|
||||
properties: {
|
||||
label: locale.baseText('auth.lastName'),
|
||||
maxlength: 32,
|
||||
@@ -54,7 +54,7 @@ const onSubmit = async (values: { [key: string]: string }) => {
|
||||
if (!isFormWithFirstAndLastName(values)) return;
|
||||
try {
|
||||
loading.value = true;
|
||||
await ssoStore.updateUser(values);
|
||||
await usersStore.updateUserName(values);
|
||||
await router.push({ name: VIEWS.HOMEPAGE });
|
||||
} catch (error) {
|
||||
loading.value = false;
|
||||
|
||||
@@ -19,6 +19,7 @@ import { createFormEventBus } from '@n8n/design-system/utils';
|
||||
import type { TableColumnCtx } from 'element-plus';
|
||||
import { useI18n } from '@n8n/i18n';
|
||||
import { usePageRedirectionHelper } from '@/composables/usePageRedirectionHelper';
|
||||
import { useSSOStore } from '@/stores/sso.store';
|
||||
|
||||
type TableRow = {
|
||||
status: string;
|
||||
@@ -65,6 +66,7 @@ const documentTitle = useDocumentTitle();
|
||||
const pageRedirectionHelper = usePageRedirectionHelper();
|
||||
|
||||
const settingsStore = useSettingsStore();
|
||||
const ssoStore = useSSOStore();
|
||||
|
||||
const dataTable = ref<LdapSyncTable[]>([]);
|
||||
const tableKey = ref(0);
|
||||
@@ -192,7 +194,7 @@ const onSubmit = async () => {
|
||||
hasAnyChanges.value = true;
|
||||
}
|
||||
|
||||
adConfig.value = await settingsStore.updateLdapConfig(newConfiguration);
|
||||
adConfig.value = await ssoStore.updateLdapConfig(newConfiguration);
|
||||
toast.showToast({
|
||||
title: i18n.baseText('settings.ldap.updateConfiguration'),
|
||||
message: '',
|
||||
@@ -214,7 +216,7 @@ const onSaveClick = () => {
|
||||
const onTestConnectionClick = async () => {
|
||||
loadingTestConnection.value = true;
|
||||
try {
|
||||
await settingsStore.testLdapConnection();
|
||||
await ssoStore.testLdapConnection();
|
||||
toast.showToast({
|
||||
title: i18n.baseText('settings.ldap.connectionTest'),
|
||||
message: i18n.baseText('settings.ldap.toast.connection.success'),
|
||||
@@ -234,7 +236,7 @@ const onTestConnectionClick = async () => {
|
||||
const onDryRunClick = async () => {
|
||||
loadingDryRun.value = true;
|
||||
try {
|
||||
await settingsStore.runLdapSync({ type: 'dry' });
|
||||
await ssoStore.runLdapSync({ type: 'dry' });
|
||||
toast.showToast({
|
||||
title: i18n.baseText('settings.ldap.runSync.title'),
|
||||
message: i18n.baseText('settings.ldap.toast.sync.success'),
|
||||
@@ -251,7 +253,7 @@ const onDryRunClick = async () => {
|
||||
const onLiveRunClick = async () => {
|
||||
loadingLiveRun.value = true;
|
||||
try {
|
||||
await settingsStore.runLdapSync({ type: 'live' });
|
||||
await ssoStore.runLdapSync({ type: 'live' });
|
||||
toast.showToast({
|
||||
title: i18n.baseText('settings.ldap.runSync.title'),
|
||||
message: i18n.baseText('settings.ldap.toast.sync.success'),
|
||||
@@ -267,7 +269,7 @@ const onLiveRunClick = async () => {
|
||||
|
||||
const getLdapConfig = async () => {
|
||||
try {
|
||||
adConfig.value = await settingsStore.getLdapConfig();
|
||||
adConfig.value = await ssoStore.getLdapConfig();
|
||||
loginEnabled.value = adConfig.value.loginEnabled;
|
||||
syncEnabled.value = adConfig.value.synchronizationEnabled;
|
||||
const whenLoginEnabled: IFormInput['shouldDisplay'] = (values) => values.loginEnabled === true;
|
||||
@@ -554,7 +556,7 @@ const getLdapConfig = async () => {
|
||||
const getLdapSynchronizations = async (state: Parameters<Events['infinite']>[0]) => {
|
||||
try {
|
||||
loadingTable.value = true;
|
||||
const data = await settingsStore.getLdapSynchronizations({
|
||||
const data = await ssoStore.getLdapSynchronizations({
|
||||
page: page.value,
|
||||
});
|
||||
|
||||
|
||||
@@ -9,9 +9,12 @@ import { setupServer } from '@/__tests__/server';
|
||||
import { ROLE } from '@n8n/api-types';
|
||||
import { useUIStore } from '@/stores/ui.store';
|
||||
import { useCloudPlanStore } from '@/stores/cloudPlan.store';
|
||||
import { useSSOStore } from '@/stores/sso.store';
|
||||
import { UserManagementAuthenticationMethod } from '@/Interface';
|
||||
|
||||
let pinia: ReturnType<typeof createPinia>;
|
||||
let settingsStore: ReturnType<typeof useSettingsStore>;
|
||||
let ssoStore: ReturnType<typeof useSSOStore>;
|
||||
let usersStore: ReturnType<typeof useUsersStore>;
|
||||
let uiStore: ReturnType<typeof useUIStore>;
|
||||
let cloudPlanStore: ReturnType<typeof useCloudPlanStore>;
|
||||
@@ -41,6 +44,7 @@ describe('SettingsPersonalView', () => {
|
||||
pinia = createPinia();
|
||||
|
||||
settingsStore = useSettingsStore(pinia);
|
||||
ssoStore = useSSOStore(pinia);
|
||||
usersStore = useUsersStore(pinia);
|
||||
uiStore = useUIStore(pinia);
|
||||
cloudPlanStore = useCloudPlanStore(pinia);
|
||||
@@ -49,6 +53,15 @@ describe('SettingsPersonalView', () => {
|
||||
usersStore.currentUserId = currentUser.id;
|
||||
|
||||
await settingsStore.getSettings();
|
||||
ssoStore.initialize({
|
||||
authenticationMethod: UserManagementAuthenticationMethod.Email,
|
||||
config: settingsStore.settings.sso,
|
||||
features: {
|
||||
saml: true,
|
||||
ldap: true,
|
||||
oidc: true,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
@@ -96,7 +109,9 @@ describe('SettingsPersonalView', () => {
|
||||
});
|
||||
|
||||
it('should commit the theme change after clicking save', async () => {
|
||||
vi.spyOn(usersStore, 'updateUser').mockReturnValue(Promise.resolve());
|
||||
vi.spyOn(usersStore, 'updateUser').mockReturnValue(
|
||||
Promise.resolve({ id: '123', isPending: false }),
|
||||
);
|
||||
const { getByPlaceholderText, findByText, getByTestId } = renderComponent({ pinia });
|
||||
await waitAllPromises();
|
||||
|
||||
@@ -116,8 +131,8 @@ describe('SettingsPersonalView', () => {
|
||||
|
||||
describe('when external auth is enabled, email and password change', () => {
|
||||
beforeEach(() => {
|
||||
vi.spyOn(settingsStore, 'isSamlLoginEnabled', 'get').mockReturnValue(true);
|
||||
vi.spyOn(settingsStore, 'isDefaultAuthenticationSaml', 'get').mockReturnValue(true);
|
||||
vi.spyOn(ssoStore, 'isSamlLoginEnabled', 'get').mockReturnValue(true);
|
||||
vi.spyOn(ssoStore, 'isDefaultAuthenticationSaml', 'get').mockReturnValue(true);
|
||||
vi.spyOn(settingsStore, 'isMfaFeatureEnabled', 'get').mockReturnValue(true);
|
||||
});
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import { createFormEventBus } from '@n8n/design-system/utils';
|
||||
import type { MfaModalEvents } from '@/event-bus/mfa';
|
||||
import { promptMfaCodeBus } from '@/event-bus/mfa';
|
||||
import type { BaseTextKey } from '@n8n/i18n';
|
||||
import { useSSOStore } from '@/stores/sso.store';
|
||||
|
||||
type UserBasicDetailsForm = {
|
||||
firstName: string;
|
||||
@@ -62,32 +63,38 @@ const themeOptions = ref<Array<{ name: ThemeOption; label: BaseTextKey }>>([
|
||||
const uiStore = useUIStore();
|
||||
const usersStore = useUsersStore();
|
||||
const settingsStore = useSettingsStore();
|
||||
const ssoStore = useSSOStore();
|
||||
const cloudPlanStore = useCloudPlanStore();
|
||||
|
||||
const currentUser = computed((): IUser | null => {
|
||||
return usersStore.currentUser;
|
||||
});
|
||||
|
||||
const isExternalAuthEnabled = computed((): boolean => {
|
||||
const isLdapEnabled =
|
||||
settingsStore.settings.enterprise.ldap && currentUser.value?.signInType === 'ldap';
|
||||
const isSamlEnabled =
|
||||
settingsStore.isSamlLoginEnabled && settingsStore.isDefaultAuthenticationSaml;
|
||||
ssoStore.isEnterpriseLdapEnabled && currentUser.value?.signInType === 'ldap';
|
||||
const isSamlEnabled = ssoStore.isSamlLoginEnabled && ssoStore.isDefaultAuthenticationSaml;
|
||||
const isOidcEnabled =
|
||||
settingsStore.settings.enterprise.oidc && currentUser.value?.signInType === 'oidc';
|
||||
ssoStore.isEnterpriseOidcEnabled && currentUser.value?.signInType === 'oidc';
|
||||
return isLdapEnabled || isSamlEnabled || isOidcEnabled;
|
||||
});
|
||||
|
||||
const isPersonalSecurityEnabled = computed((): boolean => {
|
||||
return usersStore.isInstanceOwner || !isExternalAuthEnabled.value;
|
||||
});
|
||||
|
||||
const mfaDisabled = computed((): boolean => {
|
||||
return !usersStore.mfaEnabled;
|
||||
});
|
||||
|
||||
const isMfaFeatureEnabled = computed((): boolean => {
|
||||
return settingsStore.isMfaFeatureEnabled;
|
||||
});
|
||||
|
||||
const hasAnyPersonalisationChanges = computed((): boolean => {
|
||||
return currentSelectedTheme.value !== uiStore.theme;
|
||||
});
|
||||
|
||||
const hasAnyChanges = computed(() => {
|
||||
return hasAnyBasicInfoChanges.value || hasAnyPersonalisationChanges.value;
|
||||
});
|
||||
|
||||
@@ -9,7 +9,6 @@ import { useSettingsStore } from '@/stores/settings.store';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { useSSOStore } from '@/stores/sso.store';
|
||||
import { createComponentRenderer } from '@/__tests__/render';
|
||||
import { EnterpriseEditionFeature } from '@/constants';
|
||||
import { nextTick } from 'vue';
|
||||
import { usePageRedirectionHelper } from '@/composables/usePageRedirectionHelper';
|
||||
import type { SamlPreferencesExtractedData } from '@n8n/rest-api-client/api/sso';
|
||||
@@ -328,7 +327,7 @@ describe('SettingsSso', () => {
|
||||
});
|
||||
|
||||
it('should render licensed content', async () => {
|
||||
settingsStore.settings.enterprise[EnterpriseEditionFeature.Saml] = true;
|
||||
ssoStore.isEnterpriseSamlEnabled = true;
|
||||
await nextTick();
|
||||
|
||||
const { getByTestId, queryByTestId, getByRole } = renderComponent({
|
||||
@@ -342,7 +341,7 @@ describe('SettingsSso', () => {
|
||||
|
||||
it('should enable activation checkbox and test button if data is already saved', async () => {
|
||||
await ssoStore.getSamlConfig();
|
||||
settingsStore.settings.enterprise[EnterpriseEditionFeature.Saml] = true;
|
||||
ssoStore.isEnterpriseSamlEnabled = true;
|
||||
await nextTick();
|
||||
|
||||
const { container, getByTestId, getByRole } = renderComponent({
|
||||
|
||||
@@ -10,7 +10,6 @@ import { useDocumentTitle } from '@/composables/useDocumentTitle';
|
||||
import { useRootStore } from '@n8n/stores/useRootStore';
|
||||
import { usePageRedirectionHelper } from '@/composables/usePageRedirectionHelper';
|
||||
import { MODAL_CONFIRM } from '@/constants';
|
||||
import { useSettingsStore } from '@/stores/settings.store';
|
||||
|
||||
type SupportedProtocolType = (typeof SupportedProtocols)[keyof typeof SupportedProtocols];
|
||||
|
||||
@@ -29,7 +28,6 @@ const telemetry = useTelemetry();
|
||||
const rootStore = useRootStore();
|
||||
const ssoStore = useSSOStore();
|
||||
const message = useMessage();
|
||||
const settingsStore = useSettingsStore();
|
||||
const toast = useToast();
|
||||
const documentTitle = useDocumentTitle();
|
||||
const pageRedirectionHelper = usePageRedirectionHelper();
|
||||
@@ -398,7 +396,7 @@ async function onOidcSettingsSave() {
|
||||
<div :class="$style.group">
|
||||
<label>Redirect URL</label>
|
||||
<CopyInput
|
||||
:value="settingsStore.oidcCallBackUrl"
|
||||
:value="ssoStore.oidc.callbackUrl"
|
||||
:copy-button-text="i18n.baseText('generic.clickToCopy')"
|
||||
toast-title="Redirect URL copied to clipboard"
|
||||
/>
|
||||
|
||||
@@ -91,8 +91,7 @@ describe('SettingsUsersView', () => {
|
||||
|
||||
it('hides invite button visibility based on user permissions', async () => {
|
||||
const pinia = createTestingPinia({ initialState: getInitialState() });
|
||||
const userStore = useUsersStore(pinia);
|
||||
// @ts-expect-error: mocked getter
|
||||
const userStore = mockedStore(useUsersStore);
|
||||
userStore.currentUser = createUser({ isDefaultUser: true });
|
||||
|
||||
const { queryByTestId } = renderView({ pinia });
|
||||
@@ -103,8 +102,7 @@ describe('SettingsUsersView', () => {
|
||||
describe('Below quota', () => {
|
||||
const pinia = createTestingPinia({ initialState: getInitialState() });
|
||||
|
||||
const settingsStore = useSettingsStore(pinia);
|
||||
// @ts-expect-error: mocked getter
|
||||
const settingsStore = mockedStore(useSettingsStore);
|
||||
settingsStore.isBelowUserQuota = false;
|
||||
|
||||
it('disables the invite button', async () => {
|
||||
@@ -180,8 +178,7 @@ describe('SettingsUsersView', () => {
|
||||
|
||||
const pinia = createTestingPinia({ initialState: getInitialState() });
|
||||
|
||||
const settingsStore = useSettingsStore(pinia);
|
||||
// @ts-expect-error: mocked getter
|
||||
const settingsStore = mockedStore(useSettingsStore);
|
||||
settingsStore.isSmtpSetup = true;
|
||||
|
||||
const userStore = useUsersStore();
|
||||
@@ -236,9 +233,8 @@ describe('SettingsUsersView', () => {
|
||||
|
||||
const pinia = createTestingPinia({ initialState: getInitialState() });
|
||||
|
||||
const settingsStore = useSettingsStore(pinia);
|
||||
// @ts-expect-error: mocked getter
|
||||
settingsStore.isSamlLoginEnabled = true;
|
||||
const ssoStore = useSSOStore(pinia);
|
||||
ssoStore.isSamlLoginEnabled = true;
|
||||
|
||||
const userStore = useUsersStore();
|
||||
|
||||
@@ -256,9 +252,8 @@ describe('SettingsUsersView', () => {
|
||||
|
||||
const pinia = createTestingPinia({ initialState: getInitialState() });
|
||||
|
||||
const settingsStore = useSettingsStore(pinia);
|
||||
// @ts-expect-error: mocked getter
|
||||
settingsStore.isSamlLoginEnabled = true;
|
||||
const ssoStore = useSSOStore(pinia);
|
||||
ssoStore.isSamlLoginEnabled = true;
|
||||
|
||||
const userStore = useUsersStore();
|
||||
|
||||
|
||||
@@ -71,13 +71,12 @@ const usersListActions = computed((): IUserListAction[] => {
|
||||
{
|
||||
label: i18n.baseText('settings.users.actions.allowSSOManualLogin'),
|
||||
value: 'allowSSOManualLogin',
|
||||
guard: (user) => settingsStore.isSamlLoginEnabled && !user.settings?.allowSSOManualLogin,
|
||||
guard: (user) => !!ssoStore.isSamlLoginEnabled && !user.settings?.allowSSOManualLogin,
|
||||
},
|
||||
{
|
||||
label: i18n.baseText('settings.users.actions.disallowSSOManualLogin'),
|
||||
value: 'disallowSSOManualLogin',
|
||||
guard: (user) =>
|
||||
settingsStore.isSamlLoginEnabled && user.settings?.allowSSOManualLogin === true,
|
||||
guard: (user) => !!ssoStore.isSamlLoginEnabled && user.settings?.allowSSOManualLogin === true,
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
@@ -12,6 +12,7 @@ import { useTelemetry } from '@/composables/useTelemetry';
|
||||
import { useUsersStore } from '@/stores/users.store';
|
||||
import { useSettingsStore } from '@/stores/settings.store';
|
||||
import { useCloudPlanStore } from '@/stores/cloudPlan.store';
|
||||
import { useSSOStore } from '@/stores/sso.store';
|
||||
|
||||
import type { IFormBoxConfig } from '@/Interface';
|
||||
import { MFA_AUTHENTICATION_REQUIRED_ERROR_CODE, VIEWS, MFA_FORM } from '@/constants';
|
||||
@@ -27,6 +28,7 @@ export type MfaCodeOrMfaRecoveryCode = Pick<LoginRequestDto, 'mfaCode' | 'mfaRec
|
||||
const usersStore = useUsersStore();
|
||||
const settingsStore = useSettingsStore();
|
||||
const cloudPlanStore = useCloudPlanStore();
|
||||
const ssoStore = useSSOStore();
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
@@ -41,8 +43,8 @@ const emailOrLdapLoginId = ref('');
|
||||
const password = ref('');
|
||||
const reportError = ref(false);
|
||||
|
||||
const ldapLoginLabel = computed(() => settingsStore.ldapLoginLabel);
|
||||
const isLdapLoginEnabled = computed(() => settingsStore.isLdapLoginEnabled);
|
||||
const ldapLoginLabel = computed(() => ssoStore.ldapLoginLabel);
|
||||
const isLdapLoginEnabled = computed(() => ssoStore.isLdapLoginEnabled);
|
||||
const emailLabel = computed(() => {
|
||||
let label = locale.baseText('auth.email');
|
||||
if (isLdapLoginEnabled.value && ldapLoginLabel.value) {
|
||||
|
||||
Reference in New Issue
Block a user