From edf5687aa0d2093681e1c4ffb00589a32314108b Mon Sep 17 00:00:00 2001
From: metamben <103100869+metamben@users.noreply.github.com>
Date: Mon, 6 Jun 2022 21:50:20 +0300
Subject: [PATCH] Properly decode uploaded Postgres SSL fields (#23176)

Fixes #22626.
---
 src/metabase/models/secret.clj       | 13 ++++---
 test/metabase/models/secret_test.clj | 51 ++++++++++++++++++----------
 2 files changed, 43 insertions(+), 21 deletions(-)

diff --git a/src/metabase/models/secret.clj b/src/metabase/models/secret.clj
index c17e862fa09..0e0402ae143 100644
--- a/src/metabase/models/secret.clj
+++ b/src/metabase/models/secret.clj
@@ -100,7 +100,7 @@
           (.write out v)))
       tmp-file)))
 
-(def ^:private uploaded-base-64-prefix "data:application/x-x509-ca-cert;base64,")
+(def ^:private uploaded-base-64-pattern #"^data:application/([^;]*);base64,")
 
 (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
@@ -126,9 +126,14 @@
         id-kw      (sub-prop "-id")
         value      (cond
                      ;; ssl-root-certs will need their prefix removed, and to be base 64 decoded (#20319)
-                     (and (value-kw details) (= "ssl-root-cert" conn-prop-nm)
-                          (str/starts-with? (value-kw details) uploaded-base-64-prefix))
-                     (-> (value-kw details) (str/replace-first uploaded-base-64-prefix "") u/decode-base64)
+                     (and (value-kw details) (#{"ssl-client-cert" "ssl-root-cert"} conn-prop-nm)
+                          (re-find uploaded-base-64-pattern (value-kw details)))
+                     (-> (value-kw details) (str/replace-first uploaded-base-64-pattern "") u/decode-base64)
+
+                     (and (value-kw details) (#{"ssl-key"} conn-prop-nm)
+                          (re-find uploaded-base-64-pattern (value-kw details)))
+                     (.decode (java.util.Base64/getDecoder)
+                              (str/replace-first (value-kw details) uploaded-base-64-pattern ""))
 
                      ;; the -value suffix was specified; use that
                      (value-kw details)
diff --git a/test/metabase/models/secret_test.clj b/test/metabase/models/secret_test.clj
index a5e6f2d666a..1f765204d10 100644
--- a/test/metabase/models/secret_test.clj
+++ b/test/metabase/models/secret_test.clj
@@ -75,21 +75,38 @@
                                       (.readFully in result))
                                     result))))))))))
 
-(deftest ssl-root-cert-base
+(defn- decode-ssl-db-property [content mime-type property]
+  (let [value-key (keyword (str property "-value"))
+        options-key (keyword (str property "-options"))]
+    (:value (secret/db-details-prop->secret-map
+                  {:ssl true
+                   :ssl-mode "verify-ca"
+                   value-key (format "data:%s;base64,%s" mime-type (u/encode-base64 content))
+                   options-key "uploaded"
+                   :port 5432,
+                   :advanced-options false
+                   :dbname "the-bean-base"
+                   :host "localhost"
+                   :tunnel-enabled false
+                   :engine :postgres
+                   :user "human-bean"}
+                  property))))
+
+(deftest ssl-cert-base
   (testing "db-details-prop->secret-map"
-    (testing "decodes root cert value properly (#20319)"
-      (is (= "<Certificate text goes here>"
-             (:value (secret/db-details-prop->secret-map
-                      {:ssl true
-                       :ssl-mode "verify-ca"
-                       :ssl-root-cert-value (str "data:application/x-x509-ca-cert;base64,"
-                                                 (u/encode-base64 "<Certificate text goes here>"))
-                       :ssl-root-cert-options "uploaded"
-                       :port 5432,
-                       :advanced-options false
-                       :dbname "the-bean-base"
-                       :host "localhost"
-                       :tunnel-enabled false
-                       :engine :postgres
-                       :user "human-bean"}
-                      "ssl-root-cert")))))))
+    (let [content "<Certificate text goes here>"
+          mime-types ["application/x-x509-ca-cert" "application/octet-stream"]]
+      (testing "decodes root cert value properly (#20319, #22626)"
+        (doseq [property ["ssl-root-cert" "ssl-client-cert"]
+                mime-type mime-types]
+          (testing (format "property %s with mime-type %s" property mime-type)
+            (is (= content
+                   (decode-ssl-db-property content mime-type property))))))
+      (testing "decodes client key value properly (#22626)"
+        (doseq [property ["ssl-key"]
+                mime-type mime-types]
+          (testing (format "property %s with mime-type %s" property mime-type)
+            (let [decoded (decode-ssl-db-property content mime-type property)]
+              (is (instance? (Class/forName "[B") decoded))
+              (is (= content
+                     (String. decoded "UTF-8"))))))))))
-- 
GitLab