Skip to content

Commit 5ac8f00

Browse files
Luis Garzatimm4205
authored andcommitted
fix: use proper OIDC host for China partition
1 parent 0c90e58 commit 5ac8f00

File tree

2 files changed

+69
-4
lines changed

2 files changed

+69
-4
lines changed

redshift_connector/plugin/browser_idc_auth_plugin.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ class OAuthParamNames(Enum):
5151
CURRENT_INTERACTION_SCHEMA = "https"
5252
OIDC_SCHEMA = "oidc"
5353
AMAZON_COM_SCHEMA = "amazonaws.com"
54+
AMAZON_CHINA_SCHEMA = "amazonaws.com.cn"
5455
REDSHIFT_IDC_CONNECT_SCOPE = "redshift:connect"
5556
AUTH_CODE_GRANT_TYPE = "authorization_code"
5657
REDIRECT_URI = "http://127.0.0.1"
@@ -389,7 +390,7 @@ def get_authorization_token_url(
389390
}
390391

391392
encoded_params: str = urlencode(params)
392-
idc_host = self.OIDC_SCHEMA + "." + str(self.idc_region) + "." + self.AMAZON_COM_SCHEMA
393+
idc_host = self._build_oidc_host_url(self.idc_region)
393394

394395
return urlunsplit(
395396
(
@@ -401,6 +402,31 @@ def get_authorization_token_url(
401402
)
402403
)
403404

405+
def _build_oidc_host_url(self: "BrowserIdcAuthPlugin", idc_region: str) -> str:
406+
"""
407+
Builds the OpenID Connect (OIDC) host URL based on the provided region.
408+
409+
:param idc_region: The AWS region identifier (e.g., "us-west-2", "cn-north-1")
410+
:return: The formatted OIDC host URL
411+
:raises InterfaceError: if the region is null, empty, or not a valid AWS region
412+
"""
413+
if not idc_region:
414+
raise InterfaceError("Region cannot be null or empty")
415+
416+
normalized_region = idc_region.strip().lower()
417+
if not normalized_region:
418+
raise InterfaceError("Region cannot be empty")
419+
420+
# Validate region format to prevent injection attacks
421+
import re
422+
# AWS region format: partition-region-az (e.g., us-west-2, cn-north-1, us-gov-east-1)
423+
region_pattern = r'^[a-z]{2,3}(-[a-z]+)+-[0-9]+$'
424+
if not re.match(region_pattern, normalized_region):
425+
raise InterfaceError(f"Invalid AWS region format: {normalized_region}")
426+
427+
domain = self.AMAZON_CHINA_SCHEMA if normalized_region.startswith("cn-") else self.AMAZON_COM_SCHEMA
428+
return f"{self.OIDC_SCHEMA}.{normalized_region}.{domain}"
429+
404430
def get_listen_socket(self: "BrowserIdcAuthPlugin", listen_port: int) -> socket.socket:
405431
"""
406432
Returns a listen socket used for user authentication

test/unit/plugin/test_browser_idc_auth_plugin.py

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
def make_valid_browser_idc_provider() -> typing.Tuple[BrowserIdcAuthPlugin, RedshiftProperty]:
1616
rp: RedshiftProperty = RedshiftProperty()
17-
rp.idc_region = "some_region"
17+
rp.idc_region = "us-west-2"
1818
rp.issuer_url = "some_url"
1919
rp.idp_response_timeout = 100
2020
rp.listen_port = 8000
@@ -25,7 +25,7 @@ def make_valid_browser_idc_provider() -> typing.Tuple[BrowserIdcAuthPlugin, Reds
2525

2626
def valid_browser_without_optional_parameter() -> typing.Tuple[BrowserIdcAuthPlugin, RedshiftProperty]:
2727
rp: RedshiftProperty = RedshiftProperty()
28-
rp.idc_region = "some_region"
28+
rp.idc_region = "us-west-2"
2929
rp.issuer_url = "some_url"
3030
cp: BrowserIdcAuthPlugin = BrowserIdcAuthPlugin()
3131
cp.add_parameter(rp)
@@ -261,7 +261,7 @@ def test_authorization_token_url():
261261
mocked_state: str = "mockedState"
262262
mocked_client_id: str = "mockedClientId"
263263
mocked_code_challenge: str = "mockedCodeChallenge"
264-
expected_url = "https://oidc.some_region.amazonaws.com/authorize?response_type=code&client_id=mockedClientId&redirect_uri=None&state=mockedState&scopes=redshift%3Aconnect&code_challenge=mockedCodeChallenge&code_challenge_method=S256"
264+
expected_url = "https://oidc.us-west-2.amazonaws.com/authorize?response_type=code&client_id=mockedClientId&redirect_uri=None&state=mockedState&scopes=redshift%3Aconnect&code_challenge=mockedCodeChallenge&code_challenge_method=S256"
265265

266266
url: str = idc_credentials_provider.get_authorization_token_url(
267267
mocked_state, mocked_client_id, mocked_code_challenge
@@ -292,3 +292,42 @@ def test_open_browser():
292292

293293
listen_socket: socket.socket = idc_credentials_provider.get_listen_socket(mocked_port)
294294
assert str(listen_socket.getsockname()) == expected_socket
295+
296+
297+
@pytest.mark.parametrize("region,expected_host", [
298+
("cn-north-1", "oidc.cn-north-1.amazonaws.com.cn"),
299+
("CN-NORTH-1", "oidc.cn-north-1.amazonaws.com.cn"),
300+
(" cn-north-1 ", "oidc.cn-north-1.amazonaws.com.cn"), # Test with spaces
301+
("us-west-2", "oidc.us-west-2.amazonaws.com"),
302+
(" US-WEST-2 ", "oidc.us-west-2.amazonaws.com"), # Test with upper case and spaces
303+
("us-gov-west-1", "oidc.us-gov-west-1.amazonaws.com"),
304+
("us-gov-east-1", "oidc.us-gov-east-1.amazonaws.com"),
305+
("us-east-1", "oidc.us-east-1.amazonaws.com"),
306+
("eu-west-1", "oidc.eu-west-1.amazonaws.com"), # Dublin
307+
("ap-southeast-2", "oidc.ap-southeast-2.amazonaws.com"), # Multi-word region
308+
("eu-central-1", "oidc.eu-central-1.amazonaws.com"), # Frankfurt
309+
])
310+
def test_build_oidc_host_url_valid_regions(region, expected_host):
311+
"""Test that valid regions produce correct OIDC host URLs"""
312+
idc_credentials_provider, rp = make_valid_browser_idc_provider()
313+
314+
result = idc_credentials_provider._build_oidc_host_url(region)
315+
assert result == expected_host
316+
317+
318+
@pytest.mark.parametrize("invalid_region", [
319+
None,
320+
"",
321+
" ",
322+
"invalid-region",
323+
"../../etc/passwd",
324+
"us-west-2; rm -rf /",
325+
"us-west-2.evil.com",
326+
"evil.com#us-west-2"
327+
])
328+
def test_build_oidc_host_url_invalid_regions(invalid_region):
329+
"""Test that invalid regions raise InterfaceError"""
330+
idc_credentials_provider, rp = make_valid_browser_idc_provider()
331+
332+
with pytest.raises(InterfaceError):
333+
idc_credentials_provider._build_oidc_host_url(invalid_region)

0 commit comments

Comments
 (0)