Skip to content
Snippets Groups Projects
Unverified Commit 1b821778 authored by Cam Saul's avatar Cam Saul Committed by GitHub
Browse files

Move `metabase.config.file` code => enterprise directory (#26108)

* Move metabase.config.file code => enterprise directory

* Test fix :wrench:

* Fix Kondo error

* Fix Kondo errors

* Remove unused namespace
parent 590759f8
No related branches found
No related tags found
No related merge requests found
Showing
with 521 additions and 442 deletions
......@@ -271,7 +271,7 @@
metabase.plugins.initialize plugins.init
metabase.public-settings public-settings
metabase.public-settings.premium-features premium-features
metabase.pulse pulse ; NB some conflicts with metabase.models.pulse
metabase.pulse pulse ; NB some conflicts with metabase.models.pulse
metabase.pulse.markdown markdown
metabase.pulse.render render
metabase.pulse.render.body body
......@@ -581,9 +581,10 @@
metabase.test.data/run-mbql-query hooks.metabase.test.data/mbql-query
metabase.test.util.async/with-chans hooks.common/let-with-optional-value-for-last-binding
metabase.test.util.async/with-open-channels hooks.common/let-with-optional-value-for-last-binding
metabase.test.util.log/with-log-level hooks.common/with-ignored-first-arg
metabase.test.util.log/with-log-messages-for-level hooks.common/with-ignored-first-arg
metabase.test.util/discard-setting-changes hooks.common/with-ignored-first-arg
metabase.test.util/with-column-remappings hooks.common/with-ignored-first-arg
metabase.test.util/with-log-level hooks.common/with-ignored-first-arg
metabase.test.util/with-non-admin-groups-no-root-collection-perms hooks.common/do*
metabase.test.util/with-temp-file hooks.metabase.test.util/with-temp-file
metabase.test.util/with-temporary-setting-values hooks.metabase.test.util/with-temporary-setting-values
......@@ -597,6 +598,7 @@
metabase.test/with-column-remappings hooks.common/with-ignored-first-arg
metabase.test/with-group hooks.common/let-one-with-optional-value
metabase.test/with-log-level hooks.common/with-ignored-first-arg
metabase.test/with-log-messages-for-level hooks.common/with-ignored-first-arg
metabase.test/with-non-admin-groups-no-root-collection-perms hooks.common/do*
metabase.test/with-temp hooks.toucan.util.test/with-temp
metabase.test/with-temp* hooks.toucan.util.test/with-temp*
......
(ns ^{:added "0.45.0"}
metabase.config.file
metabase-enterprise.config-from-file.core
"Support for initializing Metabase with configuration from a `config.yml` file located in the current working
directory. See https://github.com/metabase/metabase/issues/2052 for more information.
......@@ -99,6 +99,9 @@
[clojure.tools.logging :as log]
[clojure.walk :as walk]
[environ.core :as env]
[metabase-enterprise.config-from-file.databases]
[metabase-enterprise.config-from-file.interface :as config-from-file.i]
[metabase-enterprise.config-from-file.users]
[metabase.driver.common.parameters]
[metabase.driver.common.parameters.parse :as params.parse]
[metabase.util :as u]
......@@ -106,32 +109,22 @@
[metabase.util.i18n :refer [trs]]
[yaml.core :as yaml]))
(comment metabase.driver.common.parameters/keep-me)
(comment
;; for parameter parsing
metabase.driver.common.parameters/keep-me
;; for `:databases:` section code
metabase-enterprise.config-from-file.databases/keep-me
;; for `users:` section code
metabase-enterprise.config-from-file.users/keep-me)
(set! *warn-on-reflection* true)
(defmulti section-spec
"Spec that should be used to validate the config section with `section-name`, e.g. `:users`. Default spec
is [[any?]].
Sections are validated BEFORE template expansion, so as to avoid leaking any sensitive values in spec errors. Write
your specs accordingly!
Implementations of this method live in other namespaces. For example, the section spec for the `:users` section
lives in [[metabase.models.user]]."
{:arglists '([section-name])}
keyword)
(defmethod section-spec :default
[_section-name]
any?)
(s/def :metabase.config.file.config/config
(s/and
map?
(fn validate-section-configs [m]
(doseq [[section-name section-config] m
:let [spec (section-spec section-name)]]
:let [spec (config-from-file.i/section-spec section-name)]]
(s/assert* spec section-config))
true)))
......@@ -241,28 +234,14 @@
(s/assert* ::config m)
(expand-templates m)))
(defmulti initialize-section!
"Execute initialization code for the section of the init config file with the key `section-name` and value
`section-config`.
Implementations of this method live in other namespaces, for example the method for the `:users` section (to
initialize Users) lives in [[metabase.models.user]]."
{:arglists '([section-name section-config])}
(fn [section-name _section-config]
(keyword section-name)))
;;; if we don't know how to initialize a particular section, just log a warning and proceed. This way we can be
;;; forward-compatible and handle sections that might be unknown in a particular version of Metabase.
(defmethod initialize-section! :default
[section-name _section-config]
(log/warn (u/colorize :yellow (trs "Ignoring unknown config section {0}." (pr-str section-name)))))
(defn ^{:added "0.45.0"} initialize!
"Initialize Metabase according to the directives in the config file, if it exists."
[]
;; TODO -- this should only do anything if we have an appropriate token (we should get a token for testing this before
;; enabling that check tho)
(when-let [m (config)]
(doseq [[section-name section-config] (:config m)]
(log/info (u/colorize :magenta (trs "Initializing {0} from config file..." section-name)) (u/emoji "🗄️"))
(initialize-section! section-name section-config))
(config-from-file.i/initialize-section! section-name section-config))
(log/info (u/colorize :magenta (trs "Done initializing from file.")) (u/emoji "🗄️")))
:ok)
(ns metabase-enterprise.config-from-file.databases
(:require
[clojure.spec.alpha :as s]
[clojure.tools.logging :as log]
[metabase-enterprise.config-from-file.interface :as config-from-file.i]
[metabase.driver.util :as driver.u]
[metabase.models.database :refer [Database]]
[metabase.util :as u]
[metabase.util.i18n :refer [trs]]
[toucan.db :as db]))
(s/def :metabase-enterprise.config-from-file.databases.config-file-spec/name
string?)
(s/def :metabase-enterprise.config-from-file.databases.config-file-spec/engine
string?)
(s/def :metabase-enterprise.config-from-file.databases.config-file-spec/details
map?)
(s/def ::config-file-spec
(s/keys :req-un [:metabase-enterprise.config-from-file.databases.config-file-spec/engine
:metabase-enterprise.config-from-file.databases.config-file-spec/name
:metabase-enterprise.config-from-file.databases.config-file-spec/details]))
(defmethod config-from-file.i/section-spec :databases
[_section]
(s/spec (s/* ::config-file-spec)))
(defn- init-from-config-file!
[database]
;; assert that we are able to connect to this Database. Otherwise, throw an Exception.
(driver.u/can-connect-with-details? (keyword (:engine database)) (:details database) :throw-exceptions)
(if-let [existing-database-id (db/select-one-id Database :engine (:engine database), :name (:name database))]
(do
(log/info (u/colorize :blue (trs "Updating Database {0} {1}" (:engine database) (pr-str (:name database)))))
(db/update! Database existing-database-id database))
(do
(log/info (u/colorize :green (trs "Creating new {0} Database {1}" (:engine database) (pr-str (:name database)))))
(let [db (db/insert! Database database)]
((requiring-resolve 'metabase.sync/sync-database!) db)))))
(defmethod config-from-file.i/initialize-section! :databases
[_section-name databases]
(doseq [database databases]
(init-from-config-file! database)))
(ns metabase-enterprise.config-from-file.interface
(:require
[clojure.tools.logging :as log]
[metabase.util :as u]
[metabase.util.i18n :refer [trs]]))
(defmulti section-spec
"Spec that should be used to validate the config section with `section-name`, e.g. `:users`. Default spec
is [[any?]].
Sections are validated BEFORE template expansion, so as to avoid leaking any sensitive values in spec errors. Write
your specs accordingly!
Implementations of this method live in other namespaces. For example, the section spec for the `:users` section
lives in [[metabase.models.user]]."
{:arglists '([section-name])}
keyword)
(defmethod section-spec :default
[_section-name]
any?)
(defmulti initialize-section!
"Execute initialization code for the section of the init config file with the key `section-name` and value
`section-config`.
Implementations of this method live in other namespaces, for example the method for the `:users` section (to
initialize Users) lives in [[metabase.models.user]]."
{:arglists '([section-name section-config])}
(fn [section-name _section-config]
(keyword section-name)))
;;; if we don't know how to initialize a particular section, just log a warning and proceed. This way we can be
;;; forward-compatible and handle sections that might be unknown in a particular version of Metabase.
(defmethod initialize-section! :default
[section-name _section-config]
(log/warn (u/colorize :yellow (trs "Ignoring unknown config section {0}." (pr-str section-name)))))
(ns metabase-enterprise.config-from-file.users
(:require
[clojure.spec.alpha :as s]
[clojure.tools.logging :as log]
[metabase-enterprise.config-from-file.interface :as config-from-file.i]
[metabase.models.user :refer [User]]
[metabase.util :as u]
[metabase.util.i18n :as i18n :refer [trs]]
[toucan.db :as db]))
(s/def :metabase-enterprise.config-from-file.users.config-file-spec/first_name
string?)
(s/def :metabase-enterprise.config-from-file.users.config-file-spec/last_name
string?)
(s/def :metabase-enterprise.config-from-file.users.config-file-spec/password
string?)
(s/def :metabase-enterprise.config-from-file.users.config-file-spec/email
string?)
(s/def ::config-file-spec
(s/keys :req-un [:metabase-enterprise.config-from-file.users.config-file-spec/first_name
:metabase-enterprise.config-from-file.users.config-file-spec/last_name
:metabase-enterprise.config-from-file.users.config-file-spec/password
:metabase-enterprise.config-from-file.users.config-file-spec/email]))
(defmethod config-from-file.i/section-spec :users
[_section]
(s/spec (s/* ::config-file-spec)))
(defn- init-from-config-file-is-first-user?
"For [[init-from-config-file!]]: `true` if this the first User being created for this instance. If so, we will ALWAYS
create that User as a superuser, regardless of what is specified in the config file. (It doesn't make sense to
create the first User as anything other than a superuser)."
[]
(zero? (db/count User)))
(defn- init-from-config-file!
[user]
;; TODO -- if this is the FIRST user, we should probably make them a superuser, right?
(if-let [existing-user-id (db/select-one-id User :email (:email user))]
(do
(log/info (u/colorize :blue (trs "Updating User with email {0}" (pr-str (:email user)))))
(db/update! User existing-user-id user))
;; create a new user. If they are the first User, force them to be an admin.
(let [user (cond-> user
(init-from-config-file-is-first-user?) (assoc :is_superuser true))]
(log/info (u/colorize :green (trs "Creating the first User for this instance. The first user is always created as an admin.")))
(log/info (u/colorize :green (trs "Creating new User {0} with email {1}"
(pr-str (str (:first_name user) \space (:last_name user)))
(pr-str (:email user)))))
(db/insert! User user))))
(defmethod config-from-file.i/initialize-section! :users
[_section-name users]
(doseq [user users]
(init-from-config-file! user)))
(ns metabase.config.file-test
(ns metabase-enterprise.config-from-file.core-test
(:require
[clojure.string :as str]
[clojure.test :refer :all]
[clojure.walk :as walk]
[metabase.config.file :as config.file]
[metabase-enterprise.config-from-file.core :as config-from-file]
[metabase.test :as mt]
[metabase.util :as u]
[yaml.core :as yaml]))
(use-fixtures :each (fn [thunk]
(binding [config.file/*supported-versions* {:min 1.0, :max 1.999}]
(binding [config-from-file/*supported-versions* {:min 1.0, :max 1.999}]
(thunk))))
(defn- re-quote [^String s]
......@@ -23,46 +23,46 @@
(testing "Specify a custom path and read from YAML"
(mt/with-temp-file [filename "temp-config-file.yml"]
(spit filename (yaml/generate-string mock-yaml))
(binding [config.file/*env* (assoc @#'config.file/*env* :mb-config-file-path filename)]
(binding [config-from-file/*env* (assoc @#'config-from-file/*env* :mb-config-file-path filename)]
(is (= {:version 1
:config {:settings {:my-setting "abc123"}}}
(#'config.file/config))))))
(#'config-from-file/config))))))
(testing "Support overriding config with dynamic var for mocking purposes"
(binding [config.file/*config* mock-yaml]
(binding [config-from-file/*config* mock-yaml]
(is (= {:version 1
:config {:settings {:my-setting "abc123"}}}
(#'config.file/config))))))
(#'config-from-file/config))))))
(deftest ^:parallel validate-config-test
(testing "Config should throw an error"
(testing "if it is not a map"
(binding [config.file/*config* [1 2 3 4]]
(binding [config-from-file/*config* [1 2 3 4]]
(is (thrown-with-msg?
clojure.lang.ExceptionInfo
(re-quote "failed: map?")
(#'config.file/config)))))
(#'config-from-file/config)))))
(testing "if version"
(testing "is not included"
(binding [config.file/*config* {:config {:settings {}}}]
(binding [config-from-file/*config* {:config {:settings {}}}]
(is (thrown-with-msg?
clojure.lang.ExceptionInfo
(re-quote "failed: (contains? % :version)")
(#'config.file/config)))))
(#'config-from-file/config)))))
(testing "is unsupported"
(testing "because it is too old"
(binding [config.file/*supported-versions* {:min 2.0, :max 3.0}
config.file/*config* {:version 1.0, :config {}}]
(binding [config-from-file/*supported-versions* {:min 2.0, :max 3.0}
config-from-file/*config* {:version 1.0, :config {}}]
(is (thrown-with-msg?
clojure.lang.ExceptionInfo
(re-quote "failed: supported-version?")
(#'config.file/config)))))
(#'config-from-file/config)))))
(testing "because it is too new"
(binding [config.file/*supported-versions* {:min 2.0, :max 3.0}
config.file/*config* {:version 4.0, :config {}}]
(binding [config-from-file/*supported-versions* {:min 2.0, :max 3.0}
config-from-file/*config* {:version 4.0, :config {}}]
(is (thrown-with-msg?
clojure.lang.ExceptionInfo
(re-quote "failed: supported-version?")
(#'config.file/config)))))))))
(#'config-from-file/config)))))))))
(defn- mock-config-with-setting [s]
{:version 1.0, :config {:settings {:my-setting s}}})
......@@ -70,8 +70,8 @@
(deftest ^:parallel expand-template-forms-test
(testing "Ignore single curly brackets, or brackets with spaces between them"
(are [s] (= (mock-config-with-setting s)
(binding [config.file/*config* (mock-config-with-setting s)]
(#'config.file/config)))
(binding [config-from-file/*config* (mock-config-with-setting s)]
(#'config-from-file/config)))
"{}}"
"{}}"
"{ {}}"))
......@@ -79,8 +79,8 @@
(are [template error-pattern] (thrown-with-msg?
clojure.lang.ExceptionInfo
error-pattern
(binding [config.file/*config* (mock-config-with-setting template)]
(#'config.file/config)))
(binding [config-from-file/*config* (mock-config-with-setting template)]
(#'config-from-file/config)))
;; {{ without a corresponding }}
"{{}" (re-quote "Invalid query: found [[ or {{ with no matching ]] or }}")
"{{} }" (re-quote "Invalid query: found [[ or {{ with no matching ]] or }}")
......@@ -93,36 +93,36 @@
(deftest ^:parallel recursive-template-form-expansion-test
(testing "Recursive expansion is unsupported, for now."
(binding [config.file/*env* (assoc @#'config.file/*env* :x "{{env Y}}", :y "Y")
config.file/*config* (mock-config-with-setting "{{env X}}")]
(binding [config-from-file/*env* (assoc @#'config-from-file/*env* :x "{{env Y}}", :y "Y")
config-from-file/*config* (mock-config-with-setting "{{env X}}")]
(is (= (mock-config-with-setting "{{env Y}}")
(#'config.file/config))))))
(#'config-from-file/config))))))
(deftest ^:parallel expand-template-env-var-values-test
(testing "env var values"
(binding [config.file/*env* (assoc @#'config.file/*env* :config-file-bird-name "Parrot Hilton")]
(binding [config-from-file/*env* (assoc @#'config-from-file/*env* :config-file-bird-name "Parrot Hilton")]
(testing "Nothing weird"
(binding [config.file/*config* (mock-config-with-setting "{{env CONFIG_FILE_BIRD_NAME}}")]
(binding [config-from-file/*config* (mock-config-with-setting "{{env CONFIG_FILE_BIRD_NAME}}")]
(is (= (mock-config-with-setting "Parrot Hilton")
(#'config.file/config)))))
(#'config-from-file/config)))))
(testing "Should handle multiple templates in one string"
(binding [config.file/*config* (mock-config-with-setting "{{env CONFIG_FILE_BIRD_NAME}}-{{env CONFIG_FILE_BIRD_NAME}}")]
(binding [config-from-file/*config* (mock-config-with-setting "{{env CONFIG_FILE_BIRD_NAME}}-{{env CONFIG_FILE_BIRD_NAME}}")]
(is (= (mock-config-with-setting "Parrot Hilton-Parrot Hilton")
(#'config.file/config)))))
(#'config-from-file/config)))))
(testing "Ignore whitespace inside the template brackets"
(binding [config.file/*config* (mock-config-with-setting "{{ env CONFIG_FILE_BIRD_NAME }}")]
(binding [config-from-file/*config* (mock-config-with-setting "{{ env CONFIG_FILE_BIRD_NAME }}")]
(is (= (mock-config-with-setting "Parrot Hilton")
(#'config.file/config)))))
(#'config-from-file/config)))))
(testing "Ignore excess brackets"
(are [template expected] (= (mock-config-with-setting expected)
(binding [config.file/*config* (mock-config-with-setting template)]
(#'config.file/config)))
(binding [config-from-file/*config* (mock-config-with-setting template)]
(#'config-from-file/config)))
"{{{env CONFIG_FILE_BIRD_NAME}}" "{Parrot Hilton"
"{{env CONFIG_FILE_BIRD_NAME}}}" "Parrot Hilton}"))
(testing "handle lisp-case/snake-case and case variations"
(binding [config.file/*config* (mock-config-with-setting "{{env config-file-bird-name}}")]
(binding [config-from-file/*config* (mock-config-with-setting "{{env config-file-bird-name}}")]
(is (= (mock-config-with-setting "Parrot Hilton")
(#'config.file/config))))))))
(#'config-from-file/config))))))))
(deftest ^:parallel expand-template-env-var-values-validation-test
(testing "(config) should walk the config map and expand {{template}} forms"
......@@ -131,8 +131,8 @@
(are [template error-pattern] (thrown-with-msg?
clojure.lang.ExceptionInfo
error-pattern
(binding [config.file/*config* (mock-config-with-setting template)]
(#'config.file/config)))
(binding [config-from-file/*config* (mock-config-with-setting template)]
(#'config-from-file/config)))
;; missing env var name
"{{env}}" #"Insufficient input"
;; too many args
......@@ -142,51 +142,51 @@
(deftest ^:parallel optional-template-test
(testing "[[optional {{template}}]] values"
(binding [config.file/*env* (assoc @#'config.file/*env* :my-sensitive-password "~~~SeCrEt123~~~")]
(binding [config-from-file/*env* (assoc @#'config-from-file/*env* :my-sensitive-password "~~~SeCrEt123~~~")]
(testing "env var exists"
(binding [config.file/*config* (mock-config-with-setting "[[{{env MY_SENSITIVE_PASSWORD}}]]")]
(binding [config-from-file/*config* (mock-config-with-setting "[[{{env MY_SENSITIVE_PASSWORD}}]]")]
(is (= (mock-config-with-setting "~~~SeCrEt123~~~")
(#'config.file/config))))
(binding [config.file/*config* (mock-config-with-setting "password__[[{{env MY_SENSITIVE_PASSWORD}}]]")]
(#'config-from-file/config))))
(binding [config-from-file/*config* (mock-config-with-setting "password__[[{{env MY_SENSITIVE_PASSWORD}}]]")]
(is (= (mock-config-with-setting "password__~~~SeCrEt123~~~")
(#'config.file/config))))
(#'config-from-file/config))))
(testing "with text inside optional brackets before/after the templated part"
(binding [config.file/*config* (mock-config-with-setting "[[before__{{env MY_SENSITIVE_PASSWORD}}__after]]")]
(binding [config-from-file/*config* (mock-config-with-setting "[[before__{{env MY_SENSITIVE_PASSWORD}}__after]]")]
(is (= (mock-config-with-setting "before__~~~SeCrEt123~~~__after")
(#'config.file/config))))))
(#'config-from-file/config))))))
(testing "env var does not exist"
(binding [config.file/*config* (mock-config-with-setting "[[{{env MY_OTHER_SENSITIVE_PASSWORD}}]]")]
(binding [config-from-file/*config* (mock-config-with-setting "[[{{env MY_OTHER_SENSITIVE_PASSWORD}}]]")]
(is (= (mock-config-with-setting "")
(#'config.file/config))))
(binding [config.file/*config* (mock-config-with-setting "password__[[{{env MY_OTHER_SENSITIVE_PASSWORD}}]]")]
(#'config-from-file/config))))
(binding [config-from-file/*config* (mock-config-with-setting "password__[[{{env MY_OTHER_SENSITIVE_PASSWORD}}]]")]
(is (= (mock-config-with-setting "password__")
(#'config.file/config))))
(#'config-from-file/config))))
(testing "with text inside optional brackets before/after the templated part"
(binding [config.file/*config* (mock-config-with-setting "[[before__{{env MY_OTHER_SENSITIVE_PASSWORD}}__after]]")]
(binding [config-from-file/*config* (mock-config-with-setting "[[before__{{env MY_OTHER_SENSITIVE_PASSWORD}}__after]]")]
(is (= (mock-config-with-setting "")
(#'config.file/config)))))))))
(#'config-from-file/config)))))))))
(deftest initialize-section-test
(testing "Ignore unknown sections"
(binding [config.file/*config* {:version 1.0, :config {:unknown-section {}}}]
(let [log-messages (mt/with-log-messages-for-level [metabase.config.file :warn]
(binding [config-from-file/*config* {:version 1.0, :config {:unknown-section {}}}]
(let [log-messages (mt/with-log-messages-for-level [metabase-enterprise.config-from-file.interface :warn]
(is (= :ok
(config.file/initialize!))))]
(config-from-file/initialize!))))]
(is (= [[:warn nil (u/colorize :yellow "Ignoring unknown config section :unknown-section.")]]
log-messages))))))
(deftest ^:parallel error-validation-do-not-leak-env-vars-test
(testing "spec errors should not include contents of env vars -- expand templates after spec validation."
(binding [config.file/*env* (assoc @#'config.file/*env* :my-sensitive-password "~~~SeCrEt123~~~")
config.file/*config* {:version 1
(binding [config-from-file/*env* (assoc @#'config-from-file/*env* :my-sensitive-password "~~~SeCrEt123~~~")
config-from-file/*config* {:version 1
:config {:users [{:first_name "Cam"
:last_name "Era"
:password "{{env MY_SENSITIVE_PASSWORD}}"}]}}]
(is (thrown?
clojure.lang.ExceptionInfo
(#'config.file/config)))
(#'config-from-file/config)))
(try
(#'config.file/config)
(#'config-from-file/config)
(catch Throwable e
(letfn [(contains-password? [form]
(let [seen-password? (atom false)]
......
(ns metabase-enterprise.config-from-file.databases-test
(:require
[clojure.test :refer :all]
[metabase-enterprise.config-from-file.core :as config-from-file]
[metabase.db.connection :as mdb.connection]
[metabase.models :refer [Database Table]]
[metabase.test :as mt]
[metabase.util :as u]
[toucan.db :as db]))
(deftest init-from-config-file-test
(let [db-type (mdb.connection/db-type)
original-db (mt/with-driver db-type (mt/db))]
(try
(binding [config-from-file/*supported-versions* {:min 1, :max 1}
config-from-file/*config* {:version 1
:config {:databases [{:name "init-from-config-file-test/test-data"
:engine (name db-type)
:details (:details original-db)}]}}]
(testing "Create a Database if it does not already exist"
(is (= :ok
(config-from-file/initialize!)))
(let [db (db/select-one Database :name "init-from-config-file-test/test-data")]
(is (partial= {:engine db-type}
db))
(is (= 1
(db/count Database :name "init-from-config-file-test/test-data")))
(testing "do not duplicate if Database already exists"
(is (= :ok
(config-from-file/initialize!)))
(is (= 1
(db/count Database :name "init-from-config-file-test/test-data")))
(is (partial= {:engine db-type}
(db/select-one Database :name "init-from-config-file-test/test-data"))))
(testing "Database should have been synced"
(is (= (db/count Table :db_id (u/the-id original-db))
(db/count Table :db_id (u/the-id db))))))))
(finally
(db/delete! Database :name "init-from-config-file-test/test-data")))))
(deftest ^:parallel init-from-config-file-connection-validation-test
(testing "Validate connection details when creating a Database from a config file, and error if they are invalid"
(binding [config-from-file/*supported-versions* {:min 1, :max 1}
config-from-file/*config* {:version 1
:config {:databases [{:name "inist-from-config-file-test/test-data-in-memory"
:engine "h2"
:details {:db "mem:some-in-memory-db"}}]}}]
(testing "Create a Database if it does not already exist"
(is (thrown-with-msg?
clojure.lang.ExceptionInfo
#"Database cannot be found\."
(config-from-file/initialize!)))))))
(ns metabase-enterprise.config-from-file.users-test
(:require
[clojure.test :refer :all]
[metabase-enterprise.config-from-file.core :as config-from-file]
[metabase-enterprise.config-from-file.users :as config-from-file.users]
[metabase.models :refer [User]]
[metabase.util.password :as u.password]
[toucan.db :as db]))
(deftest init-from-config-file-test
(try
(binding [config-from-file/*supported-versions* {:min 1, :max 1}
config-from-file/*config* {:version 1
:config {:users [{:first_name "Cam"
:last_name "Era"
:email "cam+config-file-test@metabase.com"
:password "2cans"}]}}]
(testing "Create a User if it does not already exist"
(is (= :ok
(config-from-file/initialize!)))
(is (partial= {:first_name "Cam"
:last_name "Era"
:email "cam+config-file-test@metabase.com"}
(db/select-one User :email "cam+config-file-test@metabase.com")))
(is (= 1
(db/count User :email "cam+config-file-test@metabase.com"))))
(testing "upsert if User already exists"
(binding [config-from-file/*config* {:version 1
:config {:users [{:first_name "Cam"
:last_name "Saul"
:email "cam+config-file-test@metabase.com"
:password "2cans"}]}}]
(is (= :ok
(config-from-file/initialize!)))
(is (= 1
(db/count User :email "cam+config-file-test@metabase.com")))
(is (partial= {:first_name "Cam"
:last_name "Saul"
:email "cam+config-file-test@metabase.com"}
(db/select-one User :email "cam+config-file-test@metabase.com"))))))
(finally
(db/delete! User :email "cam+config-file-test@metabase.com"))))
(deftest init-from-config-file-force-admin-for-first-user-test
(testing "If this is the first user being created, always make the user a superuser regardless of what is specified"
(try
(binding [config-from-file/*supported-versions* {:min 1, :max 1}]
(testing "Create the first User"
(binding [config-from-file/*config* {:version 1
:config {:users [{:first_name "Cam"
:last_name "Era"
:email "cam+config-file-admin-test@metabase.com"
:password "2cans"
:is_superuser false}]}}]
(with-redefs [config-from-file.users/init-from-config-file-is-first-user? (constantly true)]
(is (= :ok
(config-from-file/initialize!)))
(is (partial= {:first_name "Cam"
:last_name "Era"
:email "cam+config-file-admin-test@metabase.com"
:is_superuser true}
(db/select-one User :email "cam+config-file-admin-test@metabase.com")))
(is (= 1
(db/count User :email "cam+config-file-admin-test@metabase.com"))))))
(testing "Create the another User, DO NOT force them to be an admin"
(binding [config-from-file/*config* {:version 1
:config {:users [{:first_name "Cam"
:last_name "Saul"
:email "cam+config-file-admin-test-2@metabase.com"
:password "2cans"
:is_superuser false}]}}]
(is (= :ok
(config-from-file/initialize!)))
(is (partial= {:first_name "Cam"
:last_name "Saul"
:email "cam+config-file-admin-test-2@metabase.com"
:is_superuser false}
(db/select-one User :email "cam+config-file-admin-test-2@metabase.com")))
(is (= 1
(db/count User :email "cam+config-file-admin-test-2@metabase.com"))))))
(finally (db/delete! User :email [:in #{"cam+config-file-admin-test@metabase.com"
"cam+config-file-admin-test-2@metabase.com"}])))))
(deftest init-from-config-file-env-var-for-password-test
(testing "Ensure that we can set User password using {{env ...}} templates"
(try
(binding [config-from-file/*supported-versions* {:min 1, :max 1}
config-from-file/*config* {:version 1
:config {:users [{:first_name "Cam"
:last_name "Era"
:email "cam+config-file-password-test@metabase.com"
:password "{{env USER_PASSWORD}}"}]}}
config-from-file/*env* (assoc @#'config-from-file/*env* :user-password "1234cans")]
(testing "Create a User if it does not already exist"
(is (= :ok
(config-from-file/initialize!)))
(let [user (db/select-one [User :first_name :last_name :email :password_salt :password]
:email "cam+config-file-password-test@metabase.com")]
(is (partial= {:first_name "Cam"
:last_name "Era"
:email "cam+config-file-password-test@metabase.com"}
user))
(is (u.password/verify-password "1234cans" (:password_salt user) (:password user))))))
(finally
(db/delete! User :email "cam+config-file-password-test@metabase.com")))))
(deftest ^:parallel init-from-config-file-validation-test
(binding [config-from-file/*supported-versions* {:min 1, :max 1}]
(are [user error-pattern] (thrown-with-msg?
clojure.lang.ExceptionInfo
error-pattern
(binding [config-from-file/*config* {:version 1
:config {:users [user]}}]
(#'config-from-file/config)))
;; missing email
{:first_name "Cam"
:last_name "Era"
:password "2cans"}
(re-pattern (java.util.regex.Pattern/quote "failed: (contains? % :email)"))
;; missing first name
{:last_name "Era"
:email "cam+config-file-admin-test@metabase.com"
:password "2cans"}
(re-pattern (java.util.regex.Pattern/quote "failed: (contains? % :first_name)"))
;; missing last name
{:first_name "Cam"
:email "cam+config-file-admin-test@metabase.com"
:password "2cans"}
(re-pattern (java.util.regex.Pattern/quote "failed: (contains? % :last_name)"))
;; missing password
{:first_name "Cam"
:last_name "Era"
:email "cam+config-file-test@metabase.com"}
(re-pattern (java.util.regex.Pattern/quote "failed: (contains? % :password)")))))
......@@ -5,7 +5,7 @@
[java-time :as t]
[metabase.analytics.prometheus :as prometheus]
[metabase.config :as config]
[metabase.config.file :as config.file]
[metabase.core.config-from-file :as config-from-file]
[metabase.core.initialization-status :as init-status]
[metabase.db :as mdb]
metabase.driver.h2
......@@ -105,8 +105,8 @@
(log/info (trs "Setting up prometheus metrics"))
(prometheus/setup!)
(init-status/set-progress! 0.6))
;; initialize Metabase from an `config.yml` file as needed.
(config.file/initialize!)
;; initialize Metabase from an `config.yml` file if present (Enterprise Edition™ only)
(config-from-file/init-from-file-if-code-available!)
(init-status/set-progress! 0.65)
;; Bootstrap the event system
(events/initialize-events!)
......
(ns metabase.core.config-from-file
(:require
[clojure.tools.logging :as log]
[metabase.plugins.classloader :as classloader]))
(defn init-from-file-if-code-available!
"Shim for running the config-from-file code, used by [[metabase.core]]. The config-from-file code only ships in the
Enterprise Edition™ JAR, so this checks whether the namespace exists, and if it does,
invokes [[metabase-enterprise.config-from-file.core/initialize!]]; otherwise, this no-ops."
[]
(when (try
(classloader/require 'metabase-enterprise.config-from-file.core)
:ok
(catch Throwable _
(log/debug "metabase-enterprise.config-from-file.core not available; cannot initialize from file.")
nil))
((resolve 'metabase-enterprise.config-from-file.core/initialize!))))
(ns metabase.models.database
(:require
[cheshire.generate :refer [add-encoder encode-map]]
[clojure.spec.alpha :as s]
[clojure.tools.logging :as log]
[medley.core :as m]
[metabase.config.file :as config.file]
[metabase.db.util :as mdb.u]
[metabase.driver :as driver]
[metabase.driver.impl :as driver.impl]
......@@ -314,42 +312,3 @@
(not (:details database)) (assoc :details "{}"))
serdes.base/load-xform-basics
(update :creator_id serdes.util/import-user)))
;;;; initialization from files
(s/def :metabase.models.database.config-file-spec/name
string?)
(s/def :metabase.models.database.config-file-spec/engine
string?)
(s/def :metabase.models.database.config-file-spec/details
map?)
(s/def ::config-file-spec
(s/keys :req-un [:metabase.models.database.config-file-spec/engine
:metabase.models.database.config-file-spec/name
:metabase.models.database.config-file-spec/details]))
(defmethod config.file/section-spec :databases
[_section]
(s/spec (s/* ::config-file-spec)))
(defn- init-from-config-file!
[database]
;; assert that we are able to connect to this Database. Otherwise, throw an Exception.
(driver.u/can-connect-with-details? (keyword (:engine database)) (:details database) :throw-exceptions)
(if-let [existing-database-id (db/select-one-id Database :engine (:engine database), :name (:name database))]
(do
(log/info (u/colorize :blue (trs "Updating Database {0} {1}" (:engine database) (pr-str (:name database)))))
(db/update! Database existing-database-id database))
(do
(log/info (u/colorize :green (trs "Creating new {0} Database {1}" (:engine database) (pr-str (:name database)))))
(let [db (db/insert! Database database)]
((requiring-resolve 'metabase.sync/sync-database!) db)))))
(defmethod config.file/initialize-section! :databases
[_section-name databases]
(doseq [database databases]
(init-from-config-file! database)))
(ns metabase.models.user
(:require
[clojure.data :as data]
[clojure.spec.alpha :as s]
[clojure.string :as str]
[clojure.tools.logging :as log]
[metabase.config.file :as config.file]
[metabase.models.collection :as collection]
[metabase.models.permissions :as perms]
[metabase.models.permissions-group :as perms-group]
......@@ -367,56 +365,3 @@
(doseq [group-id to-add]
(db/insert! PermissionsGroupMembership {:user_id user-id, :group_id group-id}))))
true))
;;;; initialization from files
(s/def :metabase.models.user.config-file-spec/first_name
string?)
(s/def :metabase.models.user.config-file-spec/last_name
string?)
(s/def :metabase.models.user.config-file-spec/password
string?)
(s/def :metabase.models.user.config-file-spec/email
string?)
(s/def ::config-file-spec
(s/keys :req-un [:metabase.models.user.config-file-spec/first_name
:metabase.models.user.config-file-spec/last_name
:metabase.models.user.config-file-spec/password
:metabase.models.user.config-file-spec/email]))
(defmethod config.file/section-spec :users
[_section]
(s/spec (s/* ::config-file-spec)))
(defn- init-from-config-file-is-first-user?
"For [[init-from-config-file!]]: `true` if this the first User being created for this instance. If so, we will ALWAYS
create that User as a superuser, regardless of what is specified in the config file. (It doesn't make sense to
create the first User as anything other than a superuser)."
[]
(zero? (db/count User)))
(defn- init-from-config-file!
[user]
;; TODO -- if this is the FIRST user, we should probably make them a superuser, right?
(if-let [existing-user-id (db/select-one-id User :email (:email user))]
(do
(log/info (u/colorize :blue (trs "Updating User with email {0}" (pr-str (:email user)))))
(db/update! User existing-user-id user))
;; create a new user. If they are the first User, force them to be an admin.
(let [user (cond-> user
(init-from-config-file-is-first-user?) (assoc :is_superuser true))]
(log/info (u/colorize :green (trs "Creating the first User for this instance. The first user is always created as an admin.")))
(log/info (u/colorize :green (trs "Creating new User {0} with email {1}"
(pr-str (str (:first_name user) \space (:last_name user)))
(pr-str (:email user)))))
(db/insert! User user))))
(defmethod config.file/initialize-section! :users
[_section-name users]
(doseq [user users]
(init-from-config-file! user)))
(ns metabase.email.messages-test
(:require [clojure.string :as str]
[clojure.test :refer :all]
[metabase.email-test :as et]
[metabase.email.messages :as messages]
[metabase.test.util :as tu])
(:import java.io.IOException))
(:require
[clojure.string :as str]
[clojure.test :refer :all]
[metabase.email-test :as et]
[metabase.email.messages :as messages]
[metabase.test :as mt]
[metabase.test.util :as tu])
(:import
(java.io IOException)))
(deftest new-user-email
(is (= [{:from "notifications@metabase.com",
......@@ -90,7 +93,7 @@
(deftest render-pulse-email-test
(testing "Email with few rows and columns can be rendered when tracing (#21166)"
(tu/with-log-level [metabase.email :trace]
(mt/with-log-level [metabase.email :trace]
(let [result {:card {:name "card-name"
:visualization_settings
{:table.column_formatting []}}
......
......@@ -4,11 +4,9 @@
[clojure.string :as str]
[clojure.test :refer :all]
[metabase.api.common :as api]
[metabase.config.file :as config.file]
[metabase.db.connection :as mdb.connection]
[metabase.driver :as driver]
[metabase.driver.util :as driver.u]
[metabase.models :refer [Database Permissions Table]]
[metabase.models :refer [Database Permissions]]
[metabase.models.database :as database]
[metabase.models.interface :as mi]
[metabase.models.permissions :as perms]
......@@ -263,46 +261,3 @@
(let [db (db/insert! Database (dissoc (mt/with-temp-defaults Database) :details))]
(is (partial= {:details {}}
db))))))
(deftest init-from-config-file-test
(let [db-type (mdb.connection/db-type)
original-db (mt/with-driver db-type (mt/db))]
(try
(binding [config.file/*supported-versions* {:min 1, :max 1}
config.file/*config* {:version 1
:config {:databases [{:name "init-from-config-file-test/test-data"
:engine (name db-type)
:details (:details original-db)}]}}]
(testing "Create a Database if it does not already exist"
(is (= :ok
(config.file/initialize!)))
(let [db (db/select-one Database :name "init-from-config-file-test/test-data")]
(is (partial= {:engine db-type}
db))
(is (= 1
(db/count Database :name "init-from-config-file-test/test-data")))
(testing "do not duplicate if Database already exists"
(is (= :ok
(config.file/initialize!)))
(is (= 1
(db/count Database :name "init-from-config-file-test/test-data")))
(is (partial= {:engine db-type}
(db/select-one Database :name "init-from-config-file-test/test-data"))))
(testing "Database should have been synced"
(is (= (db/count Table :db_id (u/the-id original-db))
(db/count Table :db_id (u/the-id db))))))))
(finally
(db/delete! Database :name "init-from-config-file-test/test-data")))))
(deftest ^:parallel init-from-config-file-connection-validation-test
(testing "Validate connection details when creating a Database from a config file, and error if they are invalid"
(binding [config.file/*supported-versions* {:min 1, :max 1}
config.file/*config* {:version 1
:config {:databases [{:name "inist-from-config-file-test/test-data-in-memory"
:engine "h2"
:details {:db "mem:some-in-memory-db"}}]}}]
(testing "Create a Database if it does not already exist"
(is (thrown-with-msg?
clojure.lang.ExceptionInfo
#"Database cannot be found\."
(config.file/initialize!)))))))
......@@ -3,7 +3,6 @@
[clojure.set :as set]
[clojure.string :as str]
[clojure.test :refer :all]
[metabase.config.file :as config.file]
[metabase.http-client :as client]
[metabase.models
:refer [Collection
......@@ -477,132 +476,3 @@
(is (= "e8d63472"
(serdes.hash/raw-hash ["fred@flintston.es"])
(serdes.hash/identity-hash user))))))
(deftest init-from-config-file-test
(try
(binding [config.file/*supported-versions* {:min 1, :max 1}
config.file/*config* {:version 1
:config {:users [{:first_name "Cam"
:last_name "Era"
:email "cam+config-file-test@metabase.com"
:password "2cans"}]}}]
(testing "Create a User if it does not already exist"
(is (= :ok
(config.file/initialize!)))
(is (partial= {:first_name "Cam"
:last_name "Era"
:email "cam+config-file-test@metabase.com"}
(db/select-one User :email "cam+config-file-test@metabase.com")))
(is (= 1
(db/count User :email "cam+config-file-test@metabase.com"))))
(testing "upsert if User already exists"
(binding [config.file/*config* {:version 1
:config {:users [{:first_name "Cam"
:last_name "Saul"
:email "cam+config-file-test@metabase.com"
:password "2cans"}]}}]
(is (= :ok
(config.file/initialize!)))
(is (= 1
(db/count User :email "cam+config-file-test@metabase.com")))
(is (partial= {:first_name "Cam"
:last_name "Saul"
:email "cam+config-file-test@metabase.com"}
(db/select-one User :email "cam+config-file-test@metabase.com"))))))
(finally
(db/delete! User :email "cam+config-file-test@metabase.com"))))
(deftest init-from-config-file-force-admin-for-first-user-test
(testing "If this is the first user being created, always make the user a superuser regardless of what is specified"
(try
(binding [config.file/*supported-versions* {:min 1, :max 1}]
(testing "Create the first User"
(binding [config.file/*config* {:version 1
:config {:users [{:first_name "Cam"
:last_name "Era"
:email "cam+config-file-admin-test@metabase.com"
:password "2cans"
:is_superuser false}]}}]
(with-redefs [user/init-from-config-file-is-first-user? (constantly true)]
(is (= :ok
(config.file/initialize!)))
(is (partial= {:first_name "Cam"
:last_name "Era"
:email "cam+config-file-admin-test@metabase.com"
:is_superuser true}
(db/select-one User :email "cam+config-file-admin-test@metabase.com")))
(is (= 1
(db/count User :email "cam+config-file-admin-test@metabase.com"))))))
(testing "Create the another User, DO NOT force them to be an admin"
(binding [config.file/*config* {:version 1
:config {:users [{:first_name "Cam"
:last_name "Saul"
:email "cam+config-file-admin-test-2@metabase.com"
:password "2cans"
:is_superuser false}]}}]
(is (= :ok
(config.file/initialize!)))
(is (partial= {:first_name "Cam"
:last_name "Saul"
:email "cam+config-file-admin-test-2@metabase.com"
:is_superuser false}
(db/select-one User :email "cam+config-file-admin-test-2@metabase.com")))
(is (= 1
(db/count User :email "cam+config-file-admin-test-2@metabase.com"))))))
(finally (db/delete! User :email [:in #{"cam+config-file-admin-test@metabase.com"
"cam+config-file-admin-test-2@metabase.com"}])))))
(deftest init-from-config-file-env-var-for-password-test
(testing "Ensure that we can set User password using {{env ...}} templates"
(try
(binding [config.file/*supported-versions* {:min 1, :max 1}
config.file/*config* {:version 1
:config {:users [{:first_name "Cam"
:last_name "Era"
:email "cam+config-file-password-test@metabase.com"
:password "{{env USER_PASSWORD}}"}]}}
config.file/*env* (assoc @#'config.file/*env* :user-password "1234cans")]
(testing "Create a User if it does not already exist"
(is (= :ok
(config.file/initialize!)))
(let [user (db/select-one [User :first_name :last_name :email :password_salt :password]
:email "cam+config-file-password-test@metabase.com")]
(is (partial= {:first_name "Cam"
:last_name "Era"
:email "cam+config-file-password-test@metabase.com"}
user))
(is (u.password/verify-password "1234cans" (:password_salt user) (:password user))))))
(finally
(db/delete! User :email "cam+config-file-password-test@metabase.com")))))
(deftest ^:parallel init-from-config-file-validation-test
(binding [config.file/*supported-versions* {:min 1, :max 1}]
(are [user error-pattern] (thrown-with-msg?
clojure.lang.ExceptionInfo
error-pattern
(binding [config.file/*config* {:version 1
:config {:users [user]}}]
(#'config.file/config)))
;; missing email
{:first_name "Cam"
:last_name "Era"
:password "2cans"}
(re-pattern (java.util.regex.Pattern/quote "failed: (contains? % :email)"))
;; missing first name
{:last_name "Era"
:email "cam+config-file-admin-test@metabase.com"
:password "2cans"}
(re-pattern (java.util.regex.Pattern/quote "failed: (contains? % :first_name)"))
;; missing last name
{:first_name "Cam"
:email "cam+config-file-admin-test@metabase.com"
:password "2cans"}
(re-pattern (java.util.regex.Pattern/quote "failed: (contains? % :last_name)"))
;; missing password
{:first_name "Cam"
:last_name "Era"
:email "cam+config-file-test@metabase.com"}
(re-pattern (java.util.regex.Pattern/quote "failed: (contains? % :password)")))))
(ns metabase.public-settings.premium-features-test
(:require [cheshire.core :as json]
[clj-http.client :as http]
[clj-http.fake :as http-fake]
[clojure.test :refer :all]
[metabase.config :as config]
[metabase.models.user :refer [User]]
[metabase.public-settings :as public-settings]
[metabase.public-settings.premium-features :as premium-features :refer [defenterprise defenterprise-schema]]
[metabase.test :as mt]
[metabase.test.util :as tu]
[schema.core :as s]
[toucan.util.test :as tt]))
(:require
[cheshire.core :as json]
[clj-http.client :as http]
[clj-http.fake :as http-fake]
[clojure.test :refer :all]
[metabase.config :as config]
[metabase.models.user :refer [User]]
[metabase.public-settings :as public-settings]
[metabase.public-settings.premium-features
:as premium-features
:refer [defenterprise defenterprise-schema]]
[metabase.test :as mt]
[schema.core :as s]
[toucan.util.test :as tt]))
(defn do-with-premium-features [features f]
(let [features (set (map name features))]
......@@ -99,7 +101,7 @@
(is (contains? (set (:features result)) "test")))))))
(deftest not-found-test
(tu/with-log-level :fatal
(mt/with-log-level :fatal
;; `partial=` here in case the Cloud API starts including extra keys... this is a "dangerous" test since changes
;; upstream in Cloud could break this. We probably want to catch that stuff anyway tho in tests rather than waiting
;; for bug reports to come in
......
(ns metabase.test.util
"Helper functions and macros for writing unit tests."
(:require [cheshire.core :as json]
[clojure.java.io :as io]
[clojure.set :as set]
[clojure.string :as str]
[clojure.test :refer :all]
[clojure.walk :as walk]
[clojurewerkz.quartzite.scheduler :as qs]
[colorize.core :as colorize]
[environ.core :as env]
[java-time :as t]
[metabase.models :refer [Card Collection Dashboard DashboardCardSeries Database Dimension Field FieldValues
LoginHistory Metric NativeQuerySnippet Permissions PermissionsGroup PermissionsGroupMembership
PersistedInfo Pulse PulseCard PulseChannel Revision Segment Setting
Table TaskHistory Timeline TimelineEvent User]]
[metabase.models.collection :as collection]
[metabase.models.interface :as mi]
[metabase.models.permissions :as perms]
[metabase.models.permissions-group :as perms-group]
[metabase.models.setting :as setting]
[metabase.models.setting.cache :as setting.cache]
[metabase.models.timeline :as timeline]
[metabase.plugins.classloader :as classloader]
[metabase.task :as task]
[metabase.test-runner.assert-exprs :as test-runner.assert-exprs]
[metabase.test-runner.parallel :as test-runner.parallel]
[metabase.test.data :as data]
[metabase.test.fixtures :as fixtures]
[metabase.test.initialize :as initialize]
[metabase.test.util.log :as tu.log]
[metabase.util :as u]
[metabase.util.files :as u.files]
[potemkin :as p]
[toucan.db :as db]
[toucan.models :as models]
[toucan.util.test :as tt])
(:require
[cheshire.core :as json]
[clojure.java.io :as io]
[clojure.set :as set]
[clojure.string :as str]
[clojure.test :refer :all]
[clojure.walk :as walk]
[clojurewerkz.quartzite.scheduler :as qs]
[colorize.core :as colorize]
[environ.core :as env]
[java-time :as t]
[metabase.models
:refer [Card
Collection
Dashboard
DashboardCardSeries
Database
Dimension
Field
FieldValues
LoginHistory
Metric
NativeQuerySnippet
Permissions
PermissionsGroup
PermissionsGroupMembership
PersistedInfo
Pulse
PulseCard
PulseChannel
Revision
Segment
Setting
Table
TaskHistory
Timeline
TimelineEvent
User]]
[metabase.models.collection :as collection]
[metabase.models.interface :as mi]
[metabase.models.permissions :as perms]
[metabase.models.permissions-group :as perms-group]
[metabase.models.setting :as setting]
[metabase.models.setting.cache :as setting.cache]
[metabase.models.timeline :as timeline]
[metabase.plugins.classloader :as classloader]
[metabase.task :as task]
[metabase.test-runner.assert-exprs :as test-runner.assert-exprs]
[metabase.test-runner.parallel :as test-runner.parallel]
[metabase.test.data :as data]
[metabase.test.fixtures :as fixtures]
[metabase.test.initialize :as initialize]
[metabase.test.util.log :as tu.log]
[metabase.util :as u]
[metabase.util.files :as u.files]
[toucan.db :as db]
[toucan.models :as models]
[toucan.util.test :as tt])
(:import
(java.io File FileInputStream)
(java.net ServerSocket)
......@@ -48,14 +71,6 @@
(use-fixtures :once (fixtures/initialize :db))
;; these are imported because these functions originally lived in this namespace, and some tests might still be
;; referencing them from here. We can remove the imports once everyone is using `metabase.test` instead of using this
;; namespace directly.
(p/import-vars
[tu.log
with-log-level
with-log-messages-for-level])
(defn- random-uppercase-letter []
(char (+ (int \A) (rand-int 26))))
......
(ns metabase.util.encryption-test
"Tests for encryption of Metabase DB details."
(:require [clojure.string :as str]
[clojure.test :refer :all]
[metabase.models.setting.cache :as setting.cache]
[metabase.test.initialize :as initialize]
[metabase.test.util :as tu]
[metabase.util.encryption :as encryption]))
(:require
[clojure.string :as str]
[clojure.test :refer :all]
[metabase.models.setting.cache :as setting.cache]
[metabase.test :as mt]
[metabase.test.initialize :as initialize]
[metabase.util.encryption :as encryption]))
(defn do-with-secret-key [^String secret-key thunk]
;; flush the Setting cache so unencrypted values have to be fetched from the DB again
......@@ -88,7 +89,7 @@
(deftest no-errors-for-unencrypted-test
(testing "Something obviously not encrypted should avoiding trying to decrypt it (and thus not log an error)"
(is (empty? (tu/with-log-messages-for-level :warn
(is (empty? (mt/with-log-messages-for-level :warn
(encryption/maybe-decrypt secret "abc"))))))
(def ^:private fake-ciphertext
......@@ -101,10 +102,10 @@
(testing (str "Something that is not encrypted, but might be (is the correct shape etc) should attempt to be "
"decrypted. If unable to decrypt it, log a warning.")
(is (includes-encryption-warning?
(tu/with-log-messages-for-level :warn
(mt/with-log-messages-for-level :warn
(encryption/maybe-decrypt secret fake-ciphertext))))
(is (includes-encryption-warning?
(tu/with-log-messages-for-level :warn
(mt/with-log-messages-for-level :warn
(encryption/maybe-decrypt secret-2 (encryption/encrypt secret "WOW")))))))
(deftest ^:parallel possibly-encrypted-test
......
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