Skip to content
Snippets Groups Projects
Unverified Commit c6c8ec0d authored by Sloan Sparger's avatar Sloan Sparger Committed by GitHub
Browse files

Correct issue where EE only LDAP user provisioning feature is breaking OSS instances (#40685)

parent 70ec6d86
No related branches found
No related tags found
No related merge requests found
......@@ -52,7 +52,7 @@ describeEE("scenarios > admin > settings > SSO > JWT", () => {
setupJwt();
cy.visit("/admin/settings/authentication/jwt");
getUserProvisioningInput().click();
getUserProvisioningInput().label.click();
cy.button("Save changes").click();
cy.wait("@updateSettings");
......
......@@ -65,15 +65,13 @@ describe(
getLdapCard().findByText("Active").should("exist");
});
it("should allow the user to enable/disable user provisioning", () => {
it("should not show the user provision UI to OSS users", () => {
setupLdap();
cy.visit("/admin/settings/authentication/ldap");
getUserProvisioningInput().click();
cy.button("Save changes").click();
cy.wait("@updateLdapSettings");
getSuccessUi().should("exist");
cy.findByTestId("admin-layout-content")
.findByText("User Provisioning")
.should("not.exist");
});
it("should allow to reset ldap settings", () => {
......@@ -172,6 +170,20 @@ describeEE(
restore();
cy.signInAsAdmin();
setTokenFeatures("all");
cy.intercept("PUT", "/api/ldap/settings").as("updateLdapSettings");
});
it("should allow the user to enable/disable user provisioning", () => {
setupLdap();
cy.visit("/admin/settings/authentication/ldap");
const { label, input } = getUserProvisioningInput();
input.should("be.checked");
label.click();
cy.button("Save changes").click();
cy.wait("@updateLdapSettings");
getSuccessUi().should("exist");
});
it("should show the login form when ldap is enabled but password login isn't (metabase#25661)", () => {
......
......@@ -81,7 +81,7 @@ describeEE("scenarios > admin > settings > SSO > SAML", () => {
setupSaml();
cy.visit("/admin/settings/authentication/saml");
getUserProvisioningInput().click();
getUserProvisioningInput().label.click();
cy.button("Save changes").click();
cy.wait("@updateSamlSettings");
......
export function getUserProvisioningInput() {
return cy
.findByTestId("admin-layout-content")
.findByText("User Provisioning");
return {
label: cy
.findByTestId("admin-layout-content")
.findByText("User Provisioning"),
input: cy
.findByTestId("admin-layout-content")
.findByLabelText("User Provisioning"),
};
}
export function getSuccessUi() {
......
/* eslint-disable react/prop-types */
import { updateIn } from "icepick";
import { t } from "ttag";
import _ from "underscore";
import * as Yup from "yup";
import SettingHeader from "metabase/admin/settings/components/SettingHeader";
import GroupMappingsWidget from "metabase/admin/settings/containers/GroupMappingsWidget";
import { LOGIN, LOGIN_GOOGLE } from "metabase/auth/actions";
import { FormSwitch } from "metabase/forms";
import MetabaseSettings from "metabase/lib/settings";
import {
PLUGIN_ADMIN_SETTINGS_UPDATES,
PLUGIN_AUTH_PROVIDERS,
PLUGIN_LDAP_FORM_FIELDS,
PLUGIN_IS_PASSWORD_USER,
PLUGIN_REDUX_MIDDLEWARES,
} from "metabase/plugins";
import { Stack } from "metabase/ui";
import SessionTimeoutSetting from "metabase-enterprise/auth/components/SessionTimeoutSetting";
import {
hasAnySsoPremiumFeature,
......@@ -270,8 +277,36 @@ if (hasPremiumFeature("disable_password_login")) {
}
if (hasPremiumFeature("sso_ldap")) {
Object.assign(PLUGIN_LDAP_FORM_FIELDS, {
formFieldAttributes: ["ldap-user-provisioning-enabled?"],
defaultableFormFieldAttributes: ["ldap-user-provisioning-enabled?"],
formFieldsSchemas: {
"ldap-user-provisioning-enabled?": Yup.boolean().default(null),
},
UserProvisioning: ({ fields, settings }) => (
<Stack spacing="0.75rem" m="2.5rem 0">
<SettingHeader
id="ldap-user-provisioning-enabled?"
setting={settings["ldap-user-provisioning-enabled?"]}
/>
<FormSwitch
id="ldap-user-provisioning-enabled?"
name={fields["ldap-user-provisioning-enabled?"].name}
defaultChecked={fields["ldap-user-provisioning-enabled?"].default}
/>
</Stack>
),
});
PLUGIN_ADMIN_SETTINGS_UPDATES.push(sections =>
updateIn(sections, ["authentication/ldap", "settings"], settings => [
{
key: "ldap-user-provisioning-enabled?",
display_name: t`User Provisioning`,
// eslint-disable-next-line no-literal-metabase-strings -- This string only shows for admins.
description: t`When a user logs in via LDAP, create a Metabase account for them automatically if they don't have one.`,
type: "boolean",
},
...settings,
{
key: "ldap-group-membership-filter",
......
import * as Yup from "yup";
import * as Errors from "metabase/lib/errors";
import { PLUGIN_LDAP_FORM_FIELDS } from "metabase/plugins";
import type { SettingDefinition } from "metabase-types/api";
const REQUIRED_SCHEMA = {
......@@ -21,8 +22,8 @@ export const GOOGLE_SCHEMA = Yup.object({
});
export const LDAP_SCHEMA = Yup.object({
...PLUGIN_LDAP_FORM_FIELDS.formFieldsSchemas,
"ldap-enabled": Yup.boolean().nullable().default(false),
"ldap-user-provisioning-enabled?": Yup.boolean().default(null),
"ldap-host": Yup.string().nullable().default(null),
"ldap-port": Yup.number().nullable().default(null),
"ldap-security": Yup.string().nullable().default("none"),
......
......@@ -5,7 +5,6 @@ import _ from "underscore";
import type { TestConfig } from "yup";
import * as Yup from "yup";
import SettingHeader from "metabase/admin/settings/components/SettingHeader";
import GroupMappingsWidget from "metabase/admin/settings/containers/GroupMappingsWidget";
import { updateLdapSettings } from "metabase/admin/settings/settings";
import type { SettingElement } from "metabase/admin/settings/types";
......@@ -20,6 +19,7 @@ import {
FormSwitch,
FormTextInput,
} from "metabase/forms";
import { PLUGIN_LDAP_FORM_FIELDS } from "metabase/plugins";
import { Group, Radio, Stack } from "metabase/ui";
import type { SettingValue } from "metabase-types/api";
......@@ -82,9 +82,46 @@ export const SettingsLdapFormView = ({
}));
}, [settings]);
const defaultableAttrs = useMemo(() => {
return new Set(PLUGIN_LDAP_FORM_FIELDS.defaultableFormFieldAttributes);
}, []);
const ldapAttributes = useMemo(
() => [
...PLUGIN_LDAP_FORM_FIELDS.formFieldAttributes,
// Server Settings
"ldap-host",
"ldap-port",
"ldap-security",
"ldap-bind-dn",
"ldap-password",
// User Schema
"ldap-user-base",
"ldap-user-filter",
// Attributes
"ldap-attribute-email",
"ldap-attribute-firstname",
"ldap-attribute-lastname",
// Group Schema
"ldap-group-sync",
"ldap-group-base",
"ldap-group-membership-filter",
"ldap-sync-admin-group",
],
[],
);
const attributeValues = useMemo(() => {
return getAttributeValues(settings, settingValues);
}, [settings, settingValues]);
return getAttributeValues(
ldapAttributes,
settings,
settingValues,
defaultableAttrs,
);
}, [settings, settingValues, ldapAttributes, defaultableAttrs]);
const handleSubmit = useCallback(
values => {
......@@ -113,17 +150,10 @@ export const SettingsLdapFormView = ({
[t`LDAP`],
]}
/>
<Stack spacing="0.75rem" m="2.5rem 0">
<SettingHeader
id="ldap-user-provisioning-enabled?"
setting={settings["ldap-user-provisioning-enabled?"]}
/>
<FormSwitch
id="ldap-user-provisioning-enabled?"
name={fields["ldap-user-provisioning-enabled?"].name}
defaultChecked={fields["ldap-user-provisioning-enabled?"].default}
/>
</Stack>
<PLUGIN_LDAP_FORM_FIELDS.UserProvisioning
fields={fields}
settings={settings}
/>
<FormSection title={"Server Settings"}>
<Stack spacing="md">
<FormTextInput {...fields["ldap-host"]} />
......@@ -188,43 +218,16 @@ export const SettingsLdapFormView = ({
);
};
const LDAP_ATTRS = [
// User Provision Settings
"ldap-user-provisioning-enabled?",
// Server Settings
"ldap-host",
"ldap-port",
"ldap-security",
"ldap-bind-dn",
"ldap-password",
// User Schema
"ldap-user-base",
"ldap-user-filter",
// Attributes
"ldap-attribute-email",
"ldap-attribute-firstname",
"ldap-attribute-lastname",
// Group Schema
"ldap-group-sync",
"ldap-group-base",
"ldap-group-membership-filter",
"ldap-sync-admin-group",
];
const DEFAULTABLE_LDAP_ATTRS = new Set(["ldap-user-provisioning-enabled?"]);
const getAttributeValues = (
ldapAttributes: string[],
settings: Record<string, LdapFormSettingElement>,
values: SettingValues,
defaultableAttrs: Set<string>,
) => {
return Object.fromEntries(
LDAP_ATTRS.map(key => [
ldapAttributes.map(key => [
key,
DEFAULTABLE_LDAP_ATTRS.has(key)
defaultableAttrs.has(key)
? values[key] ?? settings[key]?.default
: values[key],
]),
......
......@@ -28,16 +28,6 @@ const elements = [
display_name: "LDAP Authentication",
type: "boolean",
},
{
key: "ldap-user-provisioning-enabled?",
value: null,
is_env_setting: false,
env_name: "MB_LDAP_USER_PROVISIONING_ENABLED",
display_name: "User Provisioning",
description:
"When we enable LDAP user provisioning, we automatically create a Metabase account on LDAP signin for users who\ndon't have one.",
default: true,
},
{
placeholder: "ldap.yourdomain.org",
key: "ldap-host",
......@@ -247,7 +237,6 @@ describe("SettingsLdapForm", () => {
});
const ATTRS = {
"ldap-user-provisioning-enabled?": false,
"ldap-host": "example.com",
"ldap-port": "123",
"ldap-security": "ssl",
......@@ -265,7 +254,6 @@ describe("SettingsLdapForm", () => {
"ldap-sync-admin-group": true,
};
userEvent.click(screen.getByLabelText(/User Provisioning/));
userEvent.type(
await screen.findByRole("textbox", { name: /LDAP Host/ }),
ATTRS["ldap-host"],
......
......@@ -33,13 +33,6 @@ PLUGIN_ADMIN_SETTINGS_UPDATES.push(
type: "boolean",
getHidden: () => true,
},
{
key: "ldap-user-provisioning-enabled?",
display_name: t`User Provisioning`,
// eslint-disable-next-line no-literal-metabase-strings -- This string only shows for admins.
description: t`When a user logs in via LDAP, create a Metabase account for them automatically if they don't have one.`,
type: "boolean",
},
{
key: "ldap-host",
display_name: t`LDAP Host`,
......
import type { ComponentType, HTMLAttributes, ReactNode } from "react";
import { t } from "ttag";
import type { AnySchema } from "yup";
import { UNABLE_TO_CHANGE_ADMIN_PERMISSIONS } from "metabase/admin/permissions/constants/messages";
import type {
......@@ -126,6 +127,28 @@ export const PLUGIN_ADMIN_USER_MENU_ROUTES = [];
// authentication providers
export const PLUGIN_AUTH_PROVIDERS: GetAuthProviders[] = [];
export const PLUGIN_LDAP_FORM_FIELDS = {
formFieldAttributes: [] as string[],
defaultableFormFieldAttributes: [] as string[],
formFieldsSchemas: {} as Record<string, AnySchema>,
UserProvisioning: (() => null) as ComponentType<{
settings: {
[setting: string]: {
display_name?: string | undefined;
warningMessage?: string | undefined;
description?: string | undefined;
note?: string | undefined;
};
};
fields: {
[field: string]: {
name: string;
default: boolean;
};
};
}>,
};
// Only show the password tab in account settings if these functions all return true.
// Otherwise, the user is logged in via SSO and should hide first name, last name, and email field in profile settings metabase#23298.
export const PLUGIN_IS_PASSWORD_USER: ((user: User) => boolean)[] = [];
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment