diff --git a/frontend/src/metabase/admin/settings/components/SettingsSetupList.jsx b/frontend/src/metabase/admin/settings/components/SettingsSetupList.jsx index 4b99f2ea18ceb1e0ca6bd4dcea40244767a5ba6a..01f537a4565972115fadd55c96200fa1164ae594 100644 --- a/frontend/src/metabase/admin/settings/components/SettingsSetupList.jsx +++ b/frontend/src/metabase/admin/settings/components/SettingsSetupList.jsx @@ -85,7 +85,7 @@ export default class SettingsSetupList extends Component { async componentDidMount() { try { const tasks = await SetupApi.admin_checklist(); - this.setState({ tasks: tasks }); + this.setState({ tasks }); } catch (e) { this.setState({ error: e }); } diff --git a/frontend/src/metabase/services.js b/frontend/src/metabase/services.js index 1fe823f45d69c872106cc5a6c0eb8335161b4cb4..c52c5757add064aca53aa850056870e9918e421c 100644 --- a/frontend/src/metabase/services.js +++ b/frontend/src/metabase/services.js @@ -393,6 +393,7 @@ export const SetupApi = { create: POST("/api/setup"), validate_db: POST("/api/setup/validate"), admin_checklist: GET("/api/setup/admin_checklist"), + user_defaults: GET("/api/setup/user_defaults"), }; export const UserApi = { diff --git a/frontend/src/metabase/setup/components/Setup.jsx b/frontend/src/metabase/setup/components/Setup.jsx index baf6f8c9ddd6c87f0e570c00ebe6cb6645274988..937c280ae3b2b9330507b593521ad2daf29cfdeb 100644 --- a/frontend/src/metabase/setup/components/Setup.jsx +++ b/frontend/src/metabase/setup/components/Setup.jsx @@ -6,7 +6,6 @@ import { t } from "ttag"; import { color } from "metabase/lib/colors"; import { trackStructEvent } from "metabase/lib/analytics"; import MetabaseSettings from "metabase/lib/settings"; -import { b64hash_to_utf8 } from "metabase/lib/encoding"; import AddDatabaseHelpCard from "metabase/components/AddDatabaseHelpCard"; import DriverWarning from "metabase/components/DriverWarning"; @@ -21,6 +20,9 @@ import UserStep from "./UserStep"; import DatabaseConnectionStep from "./DatabaseConnectionStep"; import PreferencesStep from "./PreferencesStep"; import { AddDatabaseHelpCardHolder } from "./Setup.styled"; + +import { SetupApi } from "metabase/services"; + import { COMPLETED_STEP_NUMBER, DATABASE_CONNECTION_STEP_NUMBER, @@ -56,10 +58,10 @@ export default class Setup extends Component { trackStructEvent("Setup", "Welcome"); } - componentDidMount() { - this.setDefaultLanguage(); - this.setDefaultDetails(); + async componentDidMount() { this.trackStepSeen(); + this.setDefaultLanguage(); + await this.setDefaultDetails(); } setDefaultLanguage() { @@ -81,14 +83,11 @@ export default class Setup extends Component { } } - setDefaultDetails() { - const { hash } = this.props.location; - - try { - const userDetails = hash && JSON.parse(b64hash_to_utf8(hash)); + async setDefaultDetails() { + const token = this.props.location.hash.replace(/^#/, ""); + if (token) { + const userDetails = await SetupApi.user_defaults({ token }); this.setState({ defaultDetails: userDetails }); - } catch (e) { - this.setState({ defaultDetails: undefined }); } } diff --git a/frontend/test/__runner__/backend.js b/frontend/test/__runner__/backend.js index bc9d7844878c8ec849573a9bfeabe5dd8045768b..21393b10e94aa7c4f2a51899a1fa5d20045ae464 100644 --- a/frontend/test/__runner__/backend.js +++ b/frontend/test/__runner__/backend.js @@ -68,6 +68,15 @@ export const BackendResource = createSharedResource("BackendResource", { process.env["ENTERPRISE_TOKEN"]) || undefined, MB_FIELD_FILTER_OPERATORS_ENABLED: "true", + MB_USER_DEFAULTS: JSON.stringify({ + token: "123456", + user: { + first_name: "Testy", + last_name: "McTestface", + email: "testy@metabase.test", + site_name: "Epic Team", + }, + }), }, stdio: process.env["DISABLE_LOGGING"] || diff --git a/frontend/test/metabase/scenarios/onboarding/setup/setup.cy.spec.js b/frontend/test/metabase/scenarios/onboarding/setup/setup.cy.spec.js index 77f597cf153c214b933f14cf694a4a7a59d601d4..36620367a60f8f70c28c86f602ad05ff58e7afde 100644 --- a/frontend/test/metabase/scenarios/onboarding/setup/setup.cy.spec.js +++ b/frontend/test/metabase/scenarios/onboarding/setup/setup.cy.spec.js @@ -179,16 +179,7 @@ describe("scenarios > setup", () => { }); it("should allow pre-filling user details", () => { - const details = { - user: { - first_name: "Testy", - last_name: "McTestface", - email: "testy@metabase.test", - site_name: "Epic Team", - }, - }; - - cy.visit(`/setup#${btoa(JSON.stringify(details))}`); + cy.visit(`/setup#123456`); cy.findByText("Welcome to Metabase"); cy.findByText("Let's get started").click(); diff --git a/src/metabase/api/setup.clj b/src/metabase/api/setup.clj index ec8b9d0dee717d61f0e39c7cbc7a1d62c9aa964b..fc280fecb7fb1b6b788b53a20ce164851901d7f6 100644 --- a/src/metabase/api/setup.clj +++ b/src/metabase/api/setup.clj @@ -2,6 +2,7 @@ (:require [compojure.core :refer [GET POST]] [metabase.api.common :as api] [metabase.api.database :as database-api :refer [DBEngineString]] + [metabase.config :as config] [metabase.driver :as driver] [metabase.email :as email] [metabase.events :as events] @@ -256,5 +257,15 @@ (api/check-superuser) (admin-checklist)) +;; User defaults endpoint + +(api/defendpoint GET "/user_defaults" + "Returns object containing default user details for initial setup, if configured, + and if the provided token value matches the token in the configuration value." + [token] + (let [{config-token :token :as defaults} (config/mb-user-defaults)] + (api/check-404 config-token) + (api/check-403 (= token config-token)) + (dissoc defaults :token))) (api/define-routes) diff --git a/src/metabase/config.clj b/src/metabase/config.clj index d8e2eae2325cd09f9c4f5bf0001a1ed19e847cb6..341ea67b88b08c366b3dccd611e337fc10c6b4b4 100644 --- a/src/metabase/config.clj +++ b/src/metabase/config.clj @@ -1,5 +1,6 @@ (ns metabase.config - (:require [clojure.java.io :as io] + (:require [cheshire.core :as json] + [clojure.java.io :as io] [clojure.string :as str] [clojure.tools.logging :as log] [environ.core :as environ] @@ -143,3 +144,9 @@ "Environ will use values from it in preference to env var or Java system properties you've specified.\n" "You should delete it; it will be recreated as needed when switching to a branch still using Leiningen.\n" "See https://github.com/metabase/metabase/wiki/Migrating-from-Leiningen-to-tools.deps#custom-env-var-values for more details."))) + +(defn mb-user-defaults + "Default user details provided as a JSON string at launch time for first-user setup flow." + [] + (when-let [user-json (environ/env :mb-user-defaults)] + (json/parse-string user-json true))) diff --git a/test/metabase/api/setup_test.clj b/test/metabase/api/setup_test.clj index b5a5afeeac36d28322e9f67af6a8e137389a46a2..2f6335292c3ee6fd01c8277dcc81df4da039e61a 100644 --- a/test/metabase/api/setup_test.clj +++ b/test/metabase/api/setup_test.clj @@ -363,3 +363,19 @@ :tasks (for [task tasks] (-> (select-keys task [:title :completed :triggered :is_next_step]) (update :title str)))}))))) + +(deftest user-defaults-test + (testing "with no user defaults configured" + (mt/with-temp-env-var-value [mb-user-defaults nil] + (is (= "Not found." (http/client :get "setup/user_defaults"))))) + + (testing "with defaults containing no token" + (mt/with-temp-env-var-value [mb-user-defaults "{}"] + (is (= "Not found." (http/client :get "setup/user_defaults"))))) + + (testing "with valid configuration" + (mt/with-temp-env-var-value [mb-user-defaults "{\"token\":\"123456\",\"email\":\"john.doe@example.com\"}"] + (testing "with mismatched token" + (is (= "You don't have permissions to do that." (http/client :get "setup/user_defaults?token=987654")))) + (testing "with valid token" + (is (= {:email "john.doe@example.com"} (http/client :get "setup/user_defaults?token=123456")))))))