Repository: dbt-labs/dbt-core

Issue:
add optional SSL parameters to the Postgres connector
### Describe the feature
Add `sslcert`, `sslkey`, and `sslrootcert` parameters to the `PostgresCredentials` class, allowing their values to be used when connecting to a Postgres database.

### Describe alternatives you've considered
I guess an alternative is to not allow this! I don't think there's a specific alternative that would achieve the same goal.

### Additional context
This was originally requested for the community-supported `dbt-materialize` adapter. I [implemented this change for that adapter](https://github.com/MaterializeInc/materialize/commit/fb7f4aac10b310833608ae5ad6e93f9fe3c70e9f), but since it borrows heavily from the Postgres adapter, I thought it would be best to submit the change here as well! (Folks need this in order to connect to Materialize Cloud, which requires certificates.)

### Who will this benefit?
It will at least benefit dbt + Materialize users, but hopefully folks using dbt + Postgres, as well!

### Are you interested in contributing this feature?
Yes, I am! 



Discussion:

Resolving patch:
diff --git a/plugins/postgres/dbt/adapters/postgres/connections.py b/plugins/postgres/dbt/adapters/postgres/connections.py
--- a/plugins/postgres/dbt/adapters/postgres/connections.py
+++ b/plugins/postgres/dbt/adapters/postgres/connections.py
@@ -23,6 +23,9 @@ class PostgresCredentials(Credentials):
     search_path: Optional[str] = None
     keepalives_idle: int = 0  # 0 means to use the default value
     sslmode: Optional[str] = None
+    sslcert: Optional[str] = None
+    sslkey: Optional[str] = None
+    sslrootcert: Optional[str] = None
     application_name: Optional[str] = 'dbt'
 
     _ALIASES = {
@@ -94,6 +97,15 @@ def open(cls, connection):
         if credentials.sslmode:
             kwargs['sslmode'] = credentials.sslmode
 
+        if credentials.sslcert is not None:
+            kwargs["sslcert"] = credentials.sslcert
+
+        if credentials.sslkey is not None:
+            kwargs["sslkey"] = credentials.sslkey
+
+        if credentials.sslrootcert is not None:
+            kwargs["sslrootcert"] = credentials.sslrootcert
+
         if credentials.application_name:
             kwargs['application_name'] = credentials.application_name
 


Test patch:
diff --git a/test/unit/test_postgres_adapter.py b/test/unit/test_postgres_adapter.py
--- a/test/unit/test_postgres_adapter.py
+++ b/test/unit/test_postgres_adapter.py
@@ -214,6 +214,29 @@ def test_sslmode(self, psycopg2):
             sslmode="require",
             application_name='dbt')
 
+    @mock.patch('dbt.adapters.postgres.connections.psycopg2')
+    def test_ssl_parameters(self, psycopg2):
+        self.config.credentials = self.config.credentials.replace(sslmode="verify-ca")
+        self.config.credentials = self.config.credentials.replace(sslcert="service.crt")
+        self.config.credentials = self.config.credentials.replace(sslkey="service.key")
+        self.config.credentials = self.config.credentials.replace(sslrootcert="ca.crt")
+        connection = self.adapter.acquire_connection('dummy')
+
+        psycopg2.connect.assert_not_called()
+        connection.handle
+        psycopg2.connect.assert_called_once_with(
+            dbname='postgres',
+            user='root',
+            host='thishostshouldnotexist',
+            password='password',
+            port=5432,
+            connect_timeout=10,
+            sslmode="verify-ca",
+            sslcert="service.crt",
+            sslkey="service.key",
+            sslrootcert="ca.crt",
+            application_name='dbt')
+
     @mock.patch('dbt.adapters.postgres.connections.psycopg2')
     def test_schema_with_space(self, psycopg2):
         self.config.credentials = self.config.credentials.replace(search_path="test test")