diff --git a/CHANGELOG.md b/CHANGELOG.md index e50c3f4a2b3..42a996b3364 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,7 @@ The types of changes are: - Changed "allow user to dismiss" toggle to show on config form for TCF experience [#4755](https://github.com/ethyca/fides/pull/4755) - Fixed issue when loading the privacy request detail page [#4775](https://github.com/ethyca/fides/pull/4775) - Fixed connection test for Aircall [#4756](https://github.com/ethyca/fides/pull/4756/pull) +- Fixed issues connecting to Redshift due to character encoding and SSL requirements [#4790](https://github.com/ethyca/fides/pull/4790) ### Developer Experience - Build a `fides-types.d.ts` type declaration file to include alongside our FidesJS developer docs [#4772](https://github.com/ethyca/fides/pull/4772) diff --git a/src/fides/api/service/connectors/sql_connector.py b/src/fides/api/service/connectors/sql_connector.py index 828e5c5d2bb..b8c2c15227b 100644 --- a/src/fides/api/service/connectors/sql_connector.py +++ b/src/fides/api/service/connectors/sql_connector.py @@ -1,6 +1,7 @@ import io from abc import abstractmethod from typing import Any, Dict, List, Optional, Type +from urllib.parse import quote_plus import paramiko import sshtunnel # type: ignore @@ -398,15 +399,17 @@ def build_uri(self) -> str: """Build URI of format redshift+psycopg2://user:password@[host][:port][/database]""" config = self.secrets_schema(**self.configuration.secrets or {}) + url_encoded_password = quote_plus(config.password) port = f":{config.port}" if config.port else "" database = f"/{config.database}" if config.database else "" - url = f"redshift+psycopg2://{config.user}:{config.password}@{config.host}{port}{database}" + url = f"redshift+psycopg2://{config.user}:{url_encoded_password}@{config.host}{port}{database}" return url # Overrides SQLConnector.create_client def create_client(self) -> Engine: """Returns a SQLAlchemy Engine that can be used to interact with a database""" connect_args = {} + connect_args["sslmode"] = "prefer" if ( self.configuration.secrets and self.configuration.secrets.get("ssh_required", False) @@ -416,7 +419,6 @@ def create_client(self) -> Engine: self.create_ssh_tunnel(host=config.host, port=config.port) self.ssh_server.start() uri = self.build_ssh_uri(local_address=self.ssh_server.local_bind_address) - connect_args["sslmode"] = "prefer" else: uri = (self.configuration.secrets or {}).get("url") or self.build_uri() return create_engine(