From 745c0c9463564deae9b7db3a10187db97152ad30 Mon Sep 17 00:00:00 2001 From: metamben <103100869+metamben@users.noreply.github.com> Date: Thu, 5 Oct 2023 02:19:52 +0300 Subject: [PATCH] Read the latest version of the secret from the DB (#34335) --- src/metabase/models/secret.clj | 20 +++++++------- test/metabase/models/secret_test.clj | 41 ++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 10 deletions(-) diff --git a/src/metabase/models/secret.clj b/src/metabase/models/secret.clj index 30d68b4d21f..3853a1e3e66 100644 --- a/src/metabase/models/secret.clj +++ b/src/metabase/models/secret.clj @@ -143,6 +143,12 @@ "Regex for parsing base64 encoded file uploads." #"^data:application/([^;]*);base64,") +(defn latest-for-id + "Returns the latest Secret instance for the given `id` (meaning the one with the highest `version`)." + {:added "0.42.0"} + [id] + (t2/select-one Secret :id id {:order-by [[:version :desc]]})) + (defn db-details-prop->secret-map "Returns a map containing `:value` and `:source` for the given `conn-prop-nm`. `conn-prop-nm` is expected to be the name of a connection property having `:type` `:secret`, and the relevant sub-properties (ex: -value, -path, etc.) will @@ -185,14 +191,14 @@ {:invalid-db-details-entry (select-keys details [path-kw])})))) (id-kw details) - (:value (t2/select-one Secret :id (id-kw details)))) + (:value (latest-for-id (id-kw details)))) source (cond ;; set the :source due to the -path suffix (see above)) (and (not= "uploaded" (options-kw details)) (path-kw details)) :file-path (id-kw details) - (:source (t2/select-one Secret :id (id-kw details))))] + (:source (latest-for-id (id-kw details))))] (cond-> {:connection-property-name conn-prop-nm, :subprops [path-kw value-kw id-kw]} value (assoc :value value @@ -209,7 +215,7 @@ value (String. ^bytes value "UTF-8"))) (when id - (String. ^bytes (:value (t2/select-one Secret :id id)) "UTF-8")))] + (String. ^bytes (:value (latest-for-id id)) "UTF-8")))] (case (options-kw details) "uploaded" (try ;; When a secret is updated, the value has already been decoded @@ -226,12 +232,6 @@ bump-version-keys [:kind :source :value]) -(defn latest-for-id - "Returns the latest Secret instance for the given `id` (meaning the one with the highest `version`)." - {:added "0.42.0"} - [id] - (t2/select-one Secret :id id {:order-by [[:version :desc]]})) - (defn upsert-secret-value! "Inserts a new secret value, or updates an existing one, for the given parameters. * if there is no existing Secret instance, inserts with the given field values @@ -305,7 +305,7 @@ (let [subprop (fn [prop-nm] (keyword (str conn-prop-nm prop-nm))) secret* (cond (int? secret-or-id) - (t2/select-one Secret :id secret-or-id) + (latest-for-id secret-or-id) (mi/instance-of? Secret secret-or-id) secret-or-id diff --git a/test/metabase/models/secret_test.clj b/test/metabase/models/secret_test.clj index cce30abfa0f..fac83f2f604 100644 --- a/test/metabase/models/secret_test.clj +++ b/test/metabase/models/secret_test.clj @@ -210,3 +210,44 @@ (is (bytes? decoded)) (is (= content (String. ^bytes decoded "UTF-8")))))))))) + +(deftest use-latest-version-test + (testing "when reading a secret from the DB, the latest version is taken (#33116)" + (let [initial-value "vnetillo" + latest-value (str initial-value "s") + initial-source "source" + latest-source (str initial-source "s") + secret-property "secret" + secret-map1 {:name secret-property + :kind ::secret/password + :value initial-value + :source initial-source} + secret-map2 {:name secret-property + :kind ::secret/password + :value latest-value + :source latest-source} + crowberto-id (mt/user->id :crowberto) + by-crowberto #(assoc % :creator_id crowberto-id) + timestamp-string? (fn [s] + (try + (java.time.Instant/parse s) + true + (catch Exception _ + false)))] + (t2.with-temp/with-temp [Secret {:keys [id version]} (by-crowberto secret-map1) + Secret _ (-> secret-map2 + by-crowberto + (assoc :id id :version (inc version)))] + (let [details {:secret-id id}] + (testing "db-details-prop->secret-map" + (let [secret (secret/db-details-prop->secret-map details secret-property)] + (is (= latest-value (-> secret :value bytes (String. "UTF8")))) + (is (= (keyword latest-source) (:source secret))))) + (testing "get-secret-string" + (is (= latest-value (secret/get-secret-string details secret-property)))) + (testing "expand-inferred-secret-values" + (is (=? {:secret-id id + :secret-source (keyword latest-source) + :secret-creator-id crowberto-id + :secret-created-at timestamp-string?} + (secret/expand-inferred-secret-values details secret-property id))))))))) -- GitLab