From ba8b9bb7d2e56315d309f7ccca532b1c51ed9b2d Mon Sep 17 00:00:00 2001 From: Alexander Polyankin <alexander.polyankin@metabase.com> Date: Tue, 4 Oct 2022 13:56:57 +0300 Subject: [PATCH] Move Google Auth toggle to the auth card (#25633) --- .../src/metabase-enterprise/auth/index.js | 6 +- .../components/SettingsGoogleForm.jsx | 92 ------------ .../SettingsGoogleForm.styled.tsx | 21 +++ .../SettingsGoogleForm/SettingsGoogleForm.tsx | 133 ++++++++++++++++++ .../components/SettingsGoogleForm/index.ts | 1 + .../src/metabase/admin/settings/settings.js | 26 +++- frontend/src/metabase/admin/settings/utils.js | 2 +- .../containers/FormikForm/FormikForm.tsx | 18 ++- .../FormikForm/FormikFormViewAdapter.tsx | 3 +- frontend/src/metabase/lib/settings.ts | 11 +- .../metabase/plugins/builtin/auth/google.js | 20 ++- frontend/src/metabase/services.js | 4 + .../admin/settings/settings.cy.spec.js | 20 --- .../admin/settings/sso/google.cy.spec.js | 84 +++++++---- 14 files changed, 272 insertions(+), 169 deletions(-) delete mode 100644 frontend/src/metabase/admin/settings/components/SettingsGoogleForm.jsx create mode 100644 frontend/src/metabase/admin/settings/components/SettingsGoogleForm/SettingsGoogleForm.styled.tsx create mode 100644 frontend/src/metabase/admin/settings/components/SettingsGoogleForm/SettingsGoogleForm.tsx create mode 100644 frontend/src/metabase/admin/settings/components/SettingsGoogleForm/index.ts diff --git a/enterprise/frontend/src/metabase-enterprise/auth/index.js b/enterprise/frontend/src/metabase-enterprise/auth/index.js index 8bd1e02f255..8c59f20a0ed 100644 --- a/enterprise/frontend/src/metabase-enterprise/auth/index.js +++ b/enterprise/frontend/src/metabase-enterprise/auth/index.js @@ -51,7 +51,7 @@ PLUGIN_ADMIN_SETTINGS_UPDATES.push(sections => description: t`When enabled, users can additionally log in with email and password.`, type: "boolean", getHidden: settings => - !settings["google-auth-client-id"] && + !settings["google-auth-enabled"] && !settings["ldap-enabled"] && !settings["saml-enabled"] && !settings["jwt-enabled"], @@ -62,7 +62,7 @@ PLUGIN_ADMIN_SETTINGS_UPDATES.push(sections => description: t`When enabled, administrators will receive an email the first time a user uses Single Sign-On.`, type: "boolean", getHidden: settings => - !settings["google-auth-client-id"] && + !settings["google-auth-enabled"] && !settings["ldap-enabled"] && !settings["saml-enabled"] && !settings["jwt-enabled"], @@ -304,6 +304,8 @@ PLUGIN_ADMIN_SETTINGS_UPDATES.push(sections => ({ settings: [ { key: "google-auth-client-id", + required: true, + autoFocus: true, }, { // Default to OSS fields if enterprise SSO is not enabled diff --git a/frontend/src/metabase/admin/settings/components/SettingsGoogleForm.jsx b/frontend/src/metabase/admin/settings/components/SettingsGoogleForm.jsx deleted file mode 100644 index 19e8cf19981..00000000000 --- a/frontend/src/metabase/admin/settings/components/SettingsGoogleForm.jsx +++ /dev/null @@ -1,92 +0,0 @@ -import React, { Component } from "react"; -import PropTypes from "prop-types"; -import { connect } from "react-redux"; -import { t, jt } from "ttag"; -import _ from "underscore"; - -import Form, { - FormField, - FormSubmit, - FormMessage, -} from "metabase/containers/FormikForm"; - -import { updateSettings } from "metabase/admin/settings/settings"; -import { settingToFormField } from "metabase/admin/settings/utils"; -import Breadcrumbs from "metabase/components/Breadcrumbs"; -import ExternalLink from "metabase/core/components/ExternalLink"; -import MetabaseSettings from "metabase/lib/settings"; - -const settingsGoogleFormPropTypes = { - elements: PropTypes.array, - settingValues: PropTypes.object, - updateSettings: PropTypes.func, -}; - -class SettingsGoogleForm extends Component { - render() { - const { elements, settingValues, updateSettings } = this.props; - - const setting = name => - _.findWhere(elements, { key: name }) || { key: name }; - const settingField = name => settingToFormField(setting(name)); - - const initialValues = { ...settingValues }; - - return ( - <Form - className="mx2" - style={{ maxWidth: 520 }} - initialValues={initialValues} - onSubmit={updateSettings} - overwriteOnInitialValuesChange - > - <Breadcrumbs - crumbs={[ - [t`Authentication`, "/admin/settings/authentication"], - [t`Google Sign-In`], - ]} - className="mb2" - /> - <h2>{t`Sign in with Google`}</h2> - <p className="text-medium"> - {t`Allows users with existing Metabase accounts to login with a Google account that matches their email address in addition to their Metabase username and password.`} - </p> - <p className="text-medium"> - {jt`To allow users to sign in with Google you'll need to give Metabase a Google Developers console application client ID. It only takes a few steps and instructions on how to create a key can be found ${( - <ExternalLink - href={MetabaseSettings.docsUrl( - "people-and-groups/google-and-ldap", - "enabling-google-sign-in", - )} - target="_blank" - > - {t`here`} - </ExternalLink> - )}.`} - </p> - <FormField - {...settingField("google-auth-client-id")} - title={t`Client ID`} - description="" - placeholder={t`{your-client-id}.apps.googleusercontent.com`} - required - autoFocus - /> - <FormField - {...settingField("google-auth-auto-create-accounts-domain")} - title={t`Domain`} - /> - <div> - <FormMessage /> - </div> - <div> - <FormSubmit>{t`Save changes`}</FormSubmit> - </div> - </Form> - ); - } -} - -export default connect(null, { updateSettings })(SettingsGoogleForm); - -SettingsGoogleForm.propTypes = settingsGoogleFormPropTypes; diff --git a/frontend/src/metabase/admin/settings/components/SettingsGoogleForm/SettingsGoogleForm.styled.tsx b/frontend/src/metabase/admin/settings/components/SettingsGoogleForm/SettingsGoogleForm.styled.tsx new file mode 100644 index 00000000000..35ad46b6fed --- /dev/null +++ b/frontend/src/metabase/admin/settings/components/SettingsGoogleForm/SettingsGoogleForm.styled.tsx @@ -0,0 +1,21 @@ +import styled from "@emotion/styled"; +import Form from "metabase/containers/FormikForm"; +import { color } from "metabase/lib/colors"; + +export const FormRoot = styled(Form)` + margin: 0 1rem; + max-width: 32.5rem; +`; + +export const FormHeader = styled.h2` + margin-top: 1rem; +`; + +export const FormCaption = styled.p` + color: ${color("text-medium")}; +`; + +export const FormSection = styled.div` + display: flex; + gap: 0.5rem; +`; diff --git a/frontend/src/metabase/admin/settings/components/SettingsGoogleForm/SettingsGoogleForm.tsx b/frontend/src/metabase/admin/settings/components/SettingsGoogleForm/SettingsGoogleForm.tsx new file mode 100644 index 00000000000..6f4dbbb4602 --- /dev/null +++ b/frontend/src/metabase/admin/settings/components/SettingsGoogleForm/SettingsGoogleForm.tsx @@ -0,0 +1,133 @@ +import React, { useRef } from "react"; +import { connect } from "react-redux"; +import { jt, t } from "ttag"; +import MetabaseSettings from "metabase/lib/settings"; +import ActionButton from "metabase/components/ActionButton"; +import ExternalLink from "metabase/core/components/ExternalLink"; +import Breadcrumbs from "metabase/components/Breadcrumbs"; +import { + FormField, + FormMessage, + FormSubmit, +} from "metabase/containers/FormikForm"; +import { updateGoogleSettings } from "metabase/admin/settings/settings"; +import { settingToFormField } from "metabase/admin/settings/utils"; +import { + FormCaption, + FormSection, + FormHeader, + FormRoot, +} from "./SettingsGoogleForm.styled"; + +export interface SettingElement { + key: string; +} + +export interface SettingsGoogleFormProps { + elements?: SettingElement[]; + settingValues?: Record<string, unknown>; + onSubmit: (settingValues: Record<string, unknown>) => void; +} + +const SettingsGoogleForm = ({ + elements = [], + settingValues = {}, + onSubmit, +}: SettingsGoogleFormProps) => { + const isEnabled = Boolean(settingValues["google-auth-enabled"]); + const isEnabledRef = useRef(isEnabled); + + const handleSubmit = (values: Record<string, unknown>) => { + return onSubmit({ ...values, "google-auth-enabled": isEnabledRef.current }); + }; + + const handleSaveAndEnableClick = (handleSubmit: () => void) => { + isEnabledRef.current = true; + return handleSubmit(); + }; + + const handleSaveAndNotEnableClick = (handleSubmit: () => void) => { + isEnabledRef.current = false; + return handleSubmit(); + }; + + return ( + <FormRoot + initialValues={settingValues} + renderSubmit={({ canSubmit, handleSubmit }) => ( + <> + <ActionButton + actionFn={() => handleSaveAndEnableClick(handleSubmit)} + primary={canSubmit} + disabled={!canSubmit} + normalText={isEnabled ? t`Save changes` : t`Save and enable`} + successText={t`Changes saved!`} + /> + {!isEnabled && ( + <ActionButton + actionFn={() => handleSaveAndNotEnableClick(handleSubmit)} + disabled={!canSubmit} + normalText={t`Save but don't enable`} + successText={t`Changes saved!`} + /> + )} + </> + )} + disablePristineSubmit + overwriteOnInitialValuesChange + onSubmit={handleSubmit} + > + <Breadcrumbs + crumbs={[ + [t`Authentication`, "/admin/settings/authentication"], + [t`Google Sign-In`], + ]} + /> + <FormHeader>{t`Sign in with Google`}</FormHeader> + <FormCaption> + {t`Allows users with existing Metabase accounts to login with a Google account that matches their email address in addition to their Metabase username and password.`} + </FormCaption> + <FormCaption> + {jt`To allow users to sign in with Google you'll need to give Metabase a Google Developers console application client ID. It only takes a few steps and instructions on how to create a key can be found ${( + <ExternalLink key="link" href={getDocsLink()}>{t`here`}</ExternalLink> + )}.`} + </FormCaption> + <FormField + {...getField("google-auth-client-id", elements)} + title={t`Client ID`} + description="" + placeholder={t`{your-client-id}.apps.googleusercontent.com`} + required + autoFocus + /> + <FormField + {...getField("google-auth-auto-create-accounts-domain", elements)} + title={t`Domain`} + /> + <FormSection> + <FormMessage /> + </FormSection> + <FormSection> + <FormSubmit>{t`Save changes`}</FormSubmit> + </FormSection> + </FormRoot> + ); +}; + +const getField = (name: string, elements: SettingElement[]) => { + const setting = elements.find(e => e.key === name) ?? { key: name }; + return settingToFormField(setting); +}; + +const getDocsLink = () => { + return MetabaseSettings.docsUrl( + "people-and-groups/google-and-ldap", + "enabling-google-sign-in", + ); +}; + +const mapDispatchToProps = { + onSubmit: updateGoogleSettings, +}; + +export default connect(null, mapDispatchToProps)(SettingsGoogleForm); diff --git a/frontend/src/metabase/admin/settings/components/SettingsGoogleForm/index.ts b/frontend/src/metabase/admin/settings/components/SettingsGoogleForm/index.ts new file mode 100644 index 00000000000..f3c8fda7595 --- /dev/null +++ b/frontend/src/metabase/admin/settings/components/SettingsGoogleForm/index.ts @@ -0,0 +1 @@ +export { default } from "./SettingsGoogleForm"; diff --git a/frontend/src/metabase/admin/settings/settings.js b/frontend/src/metabase/admin/settings/settings.js index e802330f339..e97ba898e0c 100644 --- a/frontend/src/metabase/admin/settings/settings.js +++ b/frontend/src/metabase/admin/settings/settings.js @@ -4,7 +4,13 @@ import { handleActions, combineReducers, } from "metabase/lib/redux"; -import { SettingsApi, EmailApi, SlackApi, LdapApi } from "metabase/services"; +import { + SettingsApi, + EmailApi, + SlackApi, + LdapApi, + GoogleApi, +} from "metabase/services"; import { refreshSiteSettings } from "metabase/redux/settings"; // ACITON TYPES AND ACTION CREATORS @@ -150,6 +156,24 @@ export const updateLdapSettings = createThunkAction( }, ); +export const UPDATE_GOOGLE_SETTINGS = + "metabase/admin/settings/UPDATE_GOOGLE_SETTINGS"; +export const updateGoogleSettings = createThunkAction( + UPDATE_GOOGLE_SETTINGS, + function (settings) { + return async function (dispatch, getState) { + try { + const result = await GoogleApi.updateSettings(settings); + await dispatch(reloadSettings()); + return result; + } catch (error) { + console.log("error updating Google settings", settings, error); + throw error; + } + }; + }, +); + // REDUCERS export const warnings = handleActions( diff --git a/frontend/src/metabase/admin/settings/utils.js b/frontend/src/metabase/admin/settings/utils.js index 27fd4a7d39a..cc30202dc3f 100644 --- a/frontend/src/metabase/admin/settings/utils.js +++ b/frontend/src/metabase/admin/settings/utils.js @@ -13,7 +13,7 @@ export const settingToFormField = setting => ({ placeholder: setting.is_env_setting ? t`Using ${setting.env_name}` : setting.placeholder || setting.default, - validate: setting.required ? value => !value && "required" : null, + validate: setting.required ? value => !value && "required" : undefined, }); export const settingToFormFieldId = setting => `setting-${setting.key}`; diff --git a/frontend/src/metabase/containers/FormikForm/FormikForm.tsx b/frontend/src/metabase/containers/FormikForm/FormikForm.tsx index 99856f92478..6a405cd10d7 100644 --- a/frontend/src/metabase/containers/FormikForm/FormikForm.tsx +++ b/frontend/src/metabase/containers/FormikForm/FormikForm.tsx @@ -1,10 +1,10 @@ -import React, { useCallback, useMemo, useState } from "react"; +import React, { ReactNode, useCallback, useMemo, useState } from "react"; import { t } from "ttag"; import _ from "underscore"; import { assocIn, getIn, merge } from "icepick"; // eslint-disable-next-line import/named -import { Formik, FormikProps, FormikErrors, FormikHelpers } from "formik"; +import { Formik, FormikErrors, FormikHelpers } from "formik"; import { BaseFieldValues, @@ -14,16 +14,13 @@ import { PopulatedFormObject, } from "metabase-types/forms"; -import { - makeFormObject, - cleanObject, - isNestedFieldName, - getMaybeNestedValue, -} from "../formUtils"; +import { OptionalFormViewProps } from "metabase/components/form/FormikCustomForm/types"; +import { makeFormObject, cleanObject, isNestedFieldName } from "../formUtils"; import FormikFormViewAdapter from "./FormikFormViewAdapter"; import useInlineFields from "./useInlineFields"; -interface FormContainerProps<Values extends BaseFieldValues> { +interface FormContainerProps<Values extends BaseFieldValues> + extends OptionalFormViewProps { form?: FormObject<Values>; fields?: FormFieldDefinition[]; @@ -47,7 +44,8 @@ interface FormContainerProps<Values extends BaseFieldValues> { submitTitle?: string; onClose?: () => void; footerExtraButtons?: any; - children?: (opts: any) => any; + disablePristineSubmit?: boolean; + children?: ReactNode | ((opts: any) => any); } type ServerErrorResponse = { diff --git a/frontend/src/metabase/containers/FormikForm/FormikFormViewAdapter.tsx b/frontend/src/metabase/containers/FormikForm/FormikFormViewAdapter.tsx index 1b7af57a409..9d4e5b0ef99 100644 --- a/frontend/src/metabase/containers/FormikForm/FormikFormViewAdapter.tsx +++ b/frontend/src/metabase/containers/FormikForm/FormikFormViewAdapter.tsx @@ -60,6 +60,7 @@ function FormikFormViewAdapter<Values extends BaseFieldValues>({ resetForm, initialValues, submitForm, + disablePristineSubmit = formObject.disablePristineSubmit, ...rest }: FormikFormViewAdapterProps<Values>) { const [active, setActive] = useState<string | null>(null); @@ -123,7 +124,7 @@ function FormikFormViewAdapter<Values extends BaseFieldValues>({ invalid={!isValid} valid={isValid} pristine={!dirty} - disablePristineSubmit={formObject.disablePristineSubmit} + disablePristineSubmit={disablePristineSubmit} values={values} submitting={isSubmitting} asyncValidate={validateForm} diff --git a/frontend/src/metabase/lib/settings.ts b/frontend/src/metabase/lib/settings.ts index 07a84b04459..fab48840d56 100644 --- a/frontend/src/metabase/lib/settings.ts +++ b/frontend/src/metabase/lib/settings.ts @@ -71,6 +71,7 @@ export type SettingName = | "engines" | "ga-code" | "ga-enabled" + | "google-auth-enabled" | "google-auth-client-id" | "has-sample-database?" | "has-user-setup" @@ -183,8 +184,8 @@ class Settings { return this.get("hide-embed-branding?"); } - isGoogleAuthConfigured() { - return this.get("google-auth-client-id") != null; + isGoogleAuthEnabled() { + return this.get("google-auth-enabled"); } isLdapEnabled() { @@ -201,11 +202,7 @@ class Settings { } isSsoEnabled() { - return ( - this.isGoogleAuthConfigured() || - this.isLdapEnabled() || - this.isGoogleAuthConfigured() - ); + return this.isLdapEnabled() || this.isGoogleAuthEnabled(); } isPasswordLoginEnabled() { diff --git a/frontend/src/metabase/plugins/builtin/auth/google.js b/frontend/src/metabase/plugins/builtin/auth/google.js index 2fdb34e636c..8d31ca830f7 100644 --- a/frontend/src/metabase/plugins/builtin/auth/google.js +++ b/frontend/src/metabase/plugins/builtin/auth/google.js @@ -9,7 +9,7 @@ import { import MetabaseSettings from "metabase/lib/settings"; -import AuthenticationOption from "metabase/admin/settings/components/widgets/AuthenticationOption"; +import AuthenticationWidget from "metabase/admin/settings/components/widgets/AuthenticationWidget"; import SettingsGoogleForm from "metabase/admin/settings/components/SettingsGoogleForm"; PLUGIN_AUTH_PROVIDERS.push(providers => { @@ -19,7 +19,7 @@ PLUGIN_AUTH_PROVIDERS.push(providers => { Button: require("metabase/auth/containers/GoogleButton").default, }; - return MetabaseSettings.isGoogleAuthConfigured() + return MetabaseSettings.isGoogleAuthEnabled() ? [googleProvider, ...providers] : providers; }); @@ -28,11 +28,15 @@ PLUGIN_ADMIN_SETTINGS_UPDATES.push(sections => updateIn(sections, ["authentication", "settings"], settings => [ ...settings, { - authName: t`Sign in with Google`, - authDescription: t`Allows users with existing Metabase accounts to login with a Google account that matches their email address in addition to their Metabase username and password.`, - authType: "google", - authEnabled: settings => !!settings["google-auth-client-id"], - widget: AuthenticationOption, + key: "google-auth-enabled", + description: null, + widget: AuthenticationWidget, + getProps: (setting, settings) => ({ + authType: "google", + authName: t`Sign in with Google`, + authDescription: t`Allows users with existing Metabase accounts to login with a Google account that matches their email address in addition to their Metabase username and password.`, + authConfigured: Boolean(settings["google-auth-client-id"]), + }), }, ]), ); @@ -44,6 +48,8 @@ PLUGIN_ADMIN_SETTINGS_UPDATES.push(sections => ({ settings: [ { key: "google-auth-client-id", + required: true, + autoFocus: true, }, { key: "google-auth-auto-create-accounts-domain", diff --git a/frontend/src/metabase/services.js b/frontend/src/metabase/services.js index d031b479173..8691cf6a981 100644 --- a/frontend/src/metabase/services.js +++ b/frontend/src/metabase/services.js @@ -240,6 +240,10 @@ export const LdapApi = { updateSettings: PUT("/api/ldap/settings"), }; +export const GoogleApi = { + updateSettings: PUT("/api/google/settings"), +}; + export const TimelineApi = { list: GET("/api/timeline"), listForCollection: GET("/api/collection/:collectionId/timelines"), diff --git a/frontend/test/metabase/scenarios/admin/settings/settings.cy.spec.js b/frontend/test/metabase/scenarios/admin/settings/settings.cy.spec.js index a1966a94a21..3f86cbc7c3f 100644 --- a/frontend/test/metabase/scenarios/admin/settings/settings.cy.spec.js +++ b/frontend/test/metabase/scenarios/admin/settings/settings.cy.spec.js @@ -61,19 +61,6 @@ describe("scenarios > admin > settings", () => { cy.get(".SaveStatus").contains(/^Error: Invalid site URL/); }); - it("should render the proper auth options", () => { - // Ported from `SettingsAuthenticationOptions.e2e.spec.js` - // Google sign in - cy.visit("/admin/settings/authentication"); - - configureAuth("Sign in with Google"); - - cy.contains( - "To allow users to sign in with Google you'll need to give Metabase a Google Developers console application client ID.", - ); - cy.findByText("Save changes"); - }); - it("should save a setting", () => { cy.server(); cy.route("PUT", "**/admin-email").as("saveSettings"); @@ -322,10 +309,3 @@ describeEE("scenarios > admin > settings (EE)", () => { cy.findByLabelText("store icon").should("not.exist"); }); }); - -function configureAuth(providerTitle) { - cy.findByText(providerTitle) - .closest(".rounded.bordered") - .contains("Configure") - .click(); -} diff --git a/frontend/test/metabase/scenarios/admin/settings/sso/google.cy.spec.js b/frontend/test/metabase/scenarios/admin/settings/sso/google.cy.spec.js index 73377dd6d2e..2694045c846 100644 --- a/frontend/test/metabase/scenarios/admin/settings/sso/google.cy.spec.js +++ b/frontend/test/metabase/scenarios/admin/settings/sso/google.cy.spec.js @@ -1,45 +1,73 @@ -import { restore } from "__support__/e2e/helpers"; +import { restore, typeAndBlurUsingLabel } from "__support__/e2e/helpers"; + +const CLIENT_ID_SUFFIX = "apps.googleusercontent.com"; describe("scenarios > admin > settings > SSO > Google", () => { beforeEach(() => { restore(); cy.signInAsAdmin(); + cy.intercept("PUT", "/api/setting/*").as("updateSetting"); + cy.intercept("PUT", "/api/google/settings").as("updateGoogleSettings"); + }); + + it("should save the client id on subsequent tries (metabase#15974)", () => { cy.visit("/admin/settings/authentication/google"); + + typeAndBlurUsingLabel("Client ID", "example1.apps.googleusercontent.com"); + cy.button("Save and enable").click(); + cy.wait("@updateGoogleSettings"); + cy.reload(); + cy.findByDisplayValue(`example1.${CLIENT_ID_SUFFIX}`).should("be.visible"); + + typeAndBlurUsingLabel("Client ID", `example2.${CLIENT_ID_SUFFIX}`); + cy.button("Save changes").click(); + cy.wait("@updateGoogleSettings"); + cy.findByText("Changes saved!").should("be.visible"); }); - it("Google sign-in client ID should save on subsequent tries (metabase#15974)", () => { - cy.findByLabelText("Client ID").type( - "fake-client-id.apps.googleusercontent.com", + it("should disable google auth (metabase#20442)", () => { + setupGoogleAuth(); + cy.visit("/admin/settings/authentication"); + + cy.findByRole("switch", { name: "Sign in with Google" }).click(); + cy.wait("@updateSetting"); + cy.findByRole("switch", { name: "Sign in with Google" }).should( + "not.be.checked", ); - successfullySaveSettings(); - cy.reload(); - cy.findByDisplayValue("fake-client-id.apps.googleusercontent.com") - .clear() - .type("fake-client-id2.apps.googleusercontent.com"); - successfullySaveSettings(); + cy.findByText("Saved").should("exist"); }); - it("Remove Google Sing-In Setup (metabase#20442)", () => { - cy.request("PUT", "/api/setting", { - "google-auth-client-id": "example.apps.googleusercontent.com", - "google-auth-auto-create-accounts-domain": "example.test", - }); + it("should show an error message if the client id does not end with the correct suffix (metabase#15975)", () => { cy.visit("/admin/settings/authentication/google"); - cy.findByLabelText("Client ID").clear(); - cy.findByLabelText("Domain").clear(); - successfullySaveSettings(); - }); - it("Google sign-in client ID form should show an error message if it does not end with the correct suffix (metabase#15975)", () => { - cy.findByLabelText("Client ID").type("fake-client-id"); - cy.button("Save changes").click(); + typeAndBlurUsingLabel("Client ID", "fake-client-id"); + cy.button("Save and enable").click(); cy.findByText( - 'Invalid Google Sign-In Client ID: must end with ".apps.googleusercontent.com"', - ); + `Invalid Google Sign-In Client ID: must end with ".${CLIENT_ID_SUFFIX}"`, + ).should("be.visible"); + }); + + it("should show the button to sign in via google only when enabled", () => { + setupGoogleAuth({ enabled: true }); + cy.signOut(); + cy.visit("/auth/login"); + cy.findByText("Sign in with email").should("be.visible"); + cy.findByRole("button", { name: /Google/ }).should("be.visible"); + + cy.signInAsAdmin(); + setupGoogleAuth({ enabled: false }); + cy.signOut(); + cy.visit("/auth/login"); + cy.findByText("Email address").should("be.visible"); + cy.findByText("Password").should("be.visible"); + cy.findByRole("button", { name: /Google/ }).should("not.exist"); }); }); -function successfullySaveSettings() { - cy.button("Save changes").click(); - cy.findByText("Success"); -} +const setupGoogleAuth = ({ enabled = true } = {}) => { + cy.request("PUT", "/api/google/settings", { + "google-auth-enabled": enabled, + "google-auth-client-id": `example.${CLIENT_ID_SUFFIX}`, + "google-auth-auto-create-accounts-domain": "example.test", + }); +}; -- GitLab