From 1647552dfd49b5e4b7f3053b7003f82742dccea8 Mon Sep 17 00:00:00 2001
From: john-metabase <92878045+john-metabase@users.noreply.github.com>
Date: Thu, 15 Jun 2023 16:35:24 -0400
Subject: [PATCH] Fix Snowflake sync with uploaded private key (#31593)

---
 .../snowflake/resources/metabase-plugin.yaml  |  2 +-
 .../src/metabase/driver/snowflake.clj         | 49 ++++++++++---------
 src/metabase/driver/util.clj                  |  2 +-
 3 files changed, 27 insertions(+), 26 deletions(-)

diff --git a/modules/drivers/snowflake/resources/metabase-plugin.yaml b/modules/drivers/snowflake/resources/metabase-plugin.yaml
index 9a82cd386bf..2c0f6bd750a 100644
--- a/modules/drivers/snowflake/resources/metabase-plugin.yaml
+++ b/modules/drivers/snowflake/resources/metabase-plugin.yaml
@@ -10,7 +10,7 @@ driver:
   connection-properties:
     - name: account
       display-name: Account name
-      helper-text: Enter your Account ID with the region that your Snowflake cluster is running on e.g. "xxxxxxxx.us-east-2.aws". Some regions don't have this suffix.
+      helper-text: Enter your Account ID with the region that your Snowflake cluster is running on e.g. "xxxxxxxx.us-east-2.aws". Some regions don't have this suffix.
       placeholder: xxxxxxxx.us-east-2.aws
       required: true
     - user
diff --git a/modules/drivers/snowflake/src/metabase/driver/snowflake.clj b/modules/drivers/snowflake/src/metabase/driver/snowflake.clj
index 2a347c6bcd5..a166d59405d 100644
--- a/modules/drivers/snowflake/src/metabase/driver/snowflake.clj
+++ b/modules/drivers/snowflake/src/metabase/driver/snowflake.clj
@@ -20,7 +20,6 @@
    [metabase.driver.sql.util :as sql.u]
    [metabase.driver.sql.util.unprepare :as unprepare]
    [metabase.driver.sync :as driver.s]
-   [metabase.driver.util :as driver.u]
    [metabase.models.secret :as secret]
    [metabase.query-processor.error-type :as qp.error-type]
    [metabase.query-processor.store :as qp.store]
@@ -34,7 +33,6 @@
    [ring.util.codec :as codec])
   (:import
    (java.io File)
-   (java.nio.charset StandardCharsets)
    (java.sql Connection DatabaseMetaData ResultSet Types)
    (java.time OffsetDateTime ZonedDateTime)))
 
@@ -76,34 +74,37 @@
                                                           {:user (codec/url-encode user)
                                                            :private_key_file (codec/url-encode (.getCanonicalPath ^File private-key-file))})
         new-conn-uri (sql-jdbc.common/conn-str-with-additional-opts existing-conn-uri :url opts-str)]
-    (assoc details :connection-uri new-conn-uri)))
+    (-> details
+        (assoc :connection-uri new-conn-uri)
+        ;; The Snowflake driver uses the :account property, but we need to drop the region from it first
+        (assoc :account (first (str/split account #"\."))))))
 
 (defn- resolve-private-key
   "Convert the private-key secret properties into a private_key_file property in `details`.
   Setting the Snowflake driver property privatekey would be easier, but that doesn't work
   because clojure.java.jdbc (properly) converts the property values into strings while the
   Snowflake driver expects a java.security.PrivateKey instance."
-  [{:keys [user password account private-key-value private-key-path] :as details}]
-  (cond
-    password
-    details
-
-    private-key-path
-    (let [secret-map       (secret/db-details-prop->secret-map details "private-key")
-          private-key-file (when (some? (:value secret-map))
-                             (secret/value->file! secret-map :snowflake))]
-      (cond-> (apply dissoc details (vals (secret/get-sub-props "private-key")))
-        private-key-file (handle-conn-uri user account private-key-file)))
-
-    private-key-value
-    (let [private-key-str      (if (bytes? private-key-value)
-                                 (String. ^bytes private-key-value StandardCharsets/UTF_8)
-                                 private-key-value)
-          private-key-file-val (cond-> private-key-str
-                                 (re-find driver.u/data-url-pattern private-key-str) driver.u/decode-uploaded)
-          private-key-file     (secret/value->file! {:connection-property-name "private-key-file"
-                                                     :value                    private-key-file-val})]
-      (handle-conn-uri details user account private-key-file))))
+  [{:keys [user password account private-key-path]
+    :as   details}]
+  (let [base-details (apply dissoc details (vals (secret/get-sub-props "private-key")))]
+    (cond
+      password
+      details
+
+      private-key-path
+      (let [secret-map       (secret/db-details-prop->secret-map details "private-key")
+            private-key-file (when (some? (:value secret-map))
+                               (secret/value->file! secret-map :snowflake))]
+        (cond-> base-details
+          private-key-file (handle-conn-uri user account private-key-file)))
+
+      :else
+      ;; get-secret-string should get the right value (using private-key-value or private-key-id) and decode it automatically
+      (let [decoded (secret/get-secret-string details "private-key")
+            file    (secret/value->file! {:connection-property-name "private-key-file"
+                                          :value                    decoded})]
+        (assoc (handle-conn-uri base-details user account file)
+               :private_key_file file)))))
 
 (defmethod sql-jdbc.conn/connection-details->spec :snowflake
   [_ {:keys [account additional-options], :as details}]
diff --git a/src/metabase/driver/util.clj b/src/metabase/driver/util.clj
index 6a5a3d75bc5..1b427f46a62 100644
--- a/src/metabase/driver/util.clj
+++ b/src/metabase/driver/util.clj
@@ -427,7 +427,7 @@
                                           ;; version of the -value property (the :type "textFile" one)
                                           (let [textfile-prop (val-kw secrets-server->client)]
                                             (:treat-before-posting textfile-prop)))))
-                         value      (let [^String v (val-kw acc)]
+                         value      (when-let [^String v (val-kw acc)]
                                       (case (get-treat)
                                         "base64" (decode-uploaded v)
                                         v))]
-- 
GitLab