Skip to content

Commit afb6dae

Browse files
author
peco-engineer-bot[bot]
committed
[coverage] Conformance findings: AUTH-013 (#909)
Signed-off-by: peco-engineer-bot[bot] <3815206+peco-engineer-bot[bot]@users.noreply.github.com>
1 parent 8f4daee commit afb6dae

2 files changed

Lines changed: 77 additions & 8 deletions

File tree

src/databricks/sql/auth/auth.py

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@ def get_auth_provider(cfg: ClientContext, http_client):
8282
PYSQL_OAUTH_AZURE_CLIENT_ID = "96eecda7-19ea-49cc-abb5-240097d554f5"
8383
PYSQL_OAUTH_REDIRECT_PORT_RANGE = list(range(8020, 8025))
8484
PYSQL_OAUTH_AZURE_REDIRECT_PORT_RANGE = [8030]
85+
# Base (app-neutral) redirect port used when a caller supplies their OWN
86+
# oauth_client_id but no redirect port: the driver must NOT pin its own
87+
# app-specific default range in that case (see AUTH-013 / PECOBLR-4039).
88+
PYSQL_OAUTH_BASE_REDIRECT_PORT_RANGE = [8030]
8589

8690

8791
def normalize_host_name(hostname: str):
@@ -102,7 +106,7 @@ def get_python_sql_connector_auth_provider(hostname: str, http_client, **kwargs)
102106
# TODO : unify all the auth mechanisms with the Python SDK
103107

104108
auth_type = kwargs.get("auth_type")
105-
client_id, redirect_port_range = get_client_id_and_redirect_port(
109+
default_client_id, default_redirect_port_range = get_client_id_and_redirect_port(
106110
auth_type == AuthType.AZURE_OAUTH.value
107111
)
108112

@@ -112,23 +116,44 @@ def get_python_sql_connector_auth_provider(hostname: str, http_client, **kwargs)
112116
"Please use OAuth or access token instead."
113117
)
114118

119+
# A caller who supplies their OWN oauth_client_id owns the rest of the U2M
120+
# bundle: the driver forwards their scopes and redirect port verbatim and must
121+
# NOT substitute its own app-specific defaults (see AUTH-013 / PECOBLR-4039).
122+
# Only when the caller relies on the driver's default client_id do the
123+
# driver's app-specific default scopes/port range apply.
124+
caller_client_id = kwargs.get("oauth_client_id")
125+
oauth_redirect_port = kwargs.get("oauth_redirect_port")
126+
oauth_scopes = kwargs.get("oauth_scopes")
127+
128+
scopes = oauth_scopes or PYSQL_OAUTH_SCOPES
129+
if caller_client_id:
130+
client_id = caller_client_id
131+
# Foreign client_id with no explicit port falls through to the base
132+
# (app-neutral) default, NOT the driver's app-specific range.
133+
redirect_port_range = (
134+
[oauth_redirect_port]
135+
if oauth_redirect_port
136+
else PYSQL_OAUTH_BASE_REDIRECT_PORT_RANGE
137+
)
138+
else:
139+
client_id = default_client_id
140+
redirect_port_range = (
141+
[oauth_redirect_port] if oauth_redirect_port else default_redirect_port_range
142+
)
143+
115144
cfg = ClientContext(
116145
hostname=normalize_host_name(hostname),
117146
auth_type=auth_type,
118147
access_token=kwargs.get("access_token"),
119148
use_cert_as_auth=kwargs.get("_use_cert_as_auth"),
120149
tls_client_cert_file=kwargs.get("_tls_client_cert_file"),
121-
oauth_scopes=PYSQL_OAUTH_SCOPES,
122-
oauth_client_id=kwargs.get("oauth_client_id") or client_id,
150+
oauth_scopes=scopes,
151+
oauth_client_id=client_id,
123152
azure_client_id=kwargs.get("azure_client_id"),
124153
azure_client_secret=kwargs.get("azure_client_secret"),
125154
azure_tenant_id=kwargs.get("azure_tenant_id"),
126155
azure_workspace_resource_id=kwargs.get("azure_workspace_resource_id"),
127-
oauth_redirect_port_range=(
128-
[kwargs["oauth_redirect_port"]]
129-
if kwargs.get("oauth_client_id") and kwargs.get("oauth_redirect_port")
130-
else redirect_port_range
131-
),
156+
oauth_redirect_port_range=redirect_port_range,
132157
oauth_persistence=kwargs.get("experimental_oauth_persistence"),
133158
credentials_provider=kwargs.get("credentials_provider"),
134159
identity_federation_client_id=kwargs.get("identity_federation_client_id"),

tests/unit/test_auth.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,50 @@ def test_get_python_sql_connector_default_auth(self, mock__initial_get_token):
207207

208208
self.assertEqual(auth_provider.external_provider._client_id, PYSQL_OAUTH_CLIENT_ID)
209209

210+
@patch.object(DatabricksOAuthProvider, "_initial_get_token")
211+
def test_get_python_sql_connector_u2m_explicit_bundle_override(
212+
self, mock__initial_get_token
213+
):
214+
# AUTH-013 Case 1: a caller who supplies their OWN client_id owns the rest
215+
# of the U2M bundle - the caller's client_id, scopes and redirect port are
216+
# forwarded verbatim, with NO driver default substitution.
217+
hostname = "foo.cloud.databricks.com"
218+
kwargs = {
219+
"oauth_client_id": "test-custom-u2m-app",
220+
"oauth_scopes": ["all-apis"],
221+
"oauth_redirect_port": 8099,
222+
}
223+
mock_http_client = MagicMock()
224+
auth_provider = get_python_sql_connector_auth_provider(
225+
hostname, mock_http_client, **kwargs
226+
)
227+
228+
provider = auth_provider.external_provider
229+
self.assertEqual(type(provider).__name__, "DatabricksOAuthProvider")
230+
self.assertEqual(provider._client_id, "test-custom-u2m-app")
231+
self.assertEqual(provider.oauth_manager.port_range, [8099])
232+
# Caller's scope set forwarded verbatim, NOT the driver default "sql offline_access".
233+
self.assertEqual(provider._scopes_as_str, "all-apis")
234+
235+
@patch.object(DatabricksOAuthProvider, "_initial_get_token")
236+
def test_get_python_sql_connector_u2m_foreign_client_id_no_port(
237+
self, mock__initial_get_token
238+
):
239+
# AUTH-013 Case 2: a foreign client_id with NO redirect port must NOT be
240+
# pinned to the driver's own app-specific default port range (8020-8024).
241+
# It falls through to the base kernel default [8030].
242+
hostname = "foo.cloud.databricks.com"
243+
kwargs = {"oauth_client_id": "test-custom-u2m-app"}
244+
mock_http_client = MagicMock()
245+
auth_provider = get_python_sql_connector_auth_provider(
246+
hostname, mock_http_client, **kwargs
247+
)
248+
249+
provider = auth_provider.external_provider
250+
self.assertEqual(type(provider).__name__, "DatabricksOAuthProvider")
251+
self.assertEqual(provider._client_id, "test-custom-u2m-app")
252+
self.assertEqual(provider.oauth_manager.port_range, [8030])
253+
210254

211255
class TestClientCredentialsTokenSource:
212256
@pytest.fixture

0 commit comments

Comments
 (0)