Skip to content

Commit 251d59c

Browse files
authored
feat(python/adbc_driver_flightsql): add constants for OAuth options (#3849)
This pull request adds OAuth options introduced in #2651 to Python. ## Changes Made - Added enums in python with available OAuth options - Updated documentation - Added python recipes with working mock oauth server ## Related Issues Closes #2714.
1 parent 5672c87 commit 251d59c

File tree

12 files changed

+653
-27
lines changed

12 files changed

+653
-27
lines changed

.env

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,15 @@ ADBC_JDBC_POSTGRESQL_PASSWORD=password
5757
ADBC_JDBC_POSTGRESQL_DATABASE=postgres
5858
ADBC_POSTGRESQL_TEST_URI="postgresql://localhost:5432/postgres?user=postgres&password=password"
5959
ADBC_SQLITE_FLIGHTSQL_URI=grpc+tcp://localhost:8080
60-
ADBC_TEST_FLIGHTSQL_URI=grpc+tcp://localhost:41414
60+
ADBC_TEST_FLIGHTSQL_URI=grpc+tls://localhost:41414
6161
ADBC_GIZMOSQL_URI=grpc+tcp://localhost:31337
6262
ADBC_GIZMOSQL_USER=adbc_test_user
6363
ADBC_GIZMOSQL_PASSWORD=adbc_test_password
64+
65+
# OAuth test server configuration
66+
# OAuth token endpoint (oauthserver on port 8181)
67+
ADBC_OAUTH_TOKEN_URI=http://localhost:8181/token
68+
ADBC_OAUTH_CLIENT_ID=test-client
69+
ADBC_OAUTH_CLIENT_SECRET=test-secret
70+
ADBC_OAUTH_SUBJECT_TOKEN=test-subject-token
71+
ADBC_OAUTH_SKIP_VERIFY=true

.github/workflows/native-unix.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -734,7 +734,7 @@ jobs:
734734
docs/source/python/recipe/*.py
735735
- name: Test Recipes (Python)
736736
run: |
737-
docker compose up --detach --wait dremio flightsql-sqlite-test postgres-test gizmosql-test
737+
docker compose up --detach --wait dremio flightsql-sqlite-test postgres-test gizmosql-test oauth-server flightsql-test
738738
docker compose run --rm dremio-init
739739
export ADBC_CPP_RECIPE_BIN=~/local/bin
740740
# Needed for the combined C++/Python driver example

ci/docker/oauth-server.dockerfile

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
18+
# Simple OAuth 2.0 test server for ADBC FlightSQL OAuth testing
19+
ARG GO
20+
FROM golang:${GO}
21+
EXPOSE 8181

compose.yaml

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@ services:
279279
args:
280280
GO: ${GO}
281281
healthcheck:
282-
test: ["CMD", "curl", "--http2-prior-knowledge", "-XPOST", "-H", "content-type: application/grpc", "localhost:41414"]
282+
test: ["CMD", "curl", "-k", "--http2", "-XPOST", "-H", "content-type: application/grpc", "https://localhost:41414"]
283283
interval: 5s
284284
timeout: 30s
285285
retries: 3
@@ -288,8 +288,35 @@ services:
288288
- "41414:41414"
289289
volumes:
290290
- .:/adbc:delegated
291+
depends_on:
292+
oauth-server:
293+
condition: service_healthy
294+
command: >-
295+
/bin/bash -c "cd /adbc/go/adbc && go run ./driver/flightsql/cmd/testserver -host 0.0.0.0 -port 41414 -token-prefix oauth- -tls"
296+
297+
# OAuth test server for FlightSQL OAuth authentication testing
298+
oauth-server:
299+
container_name: adbc-oauth-server
300+
image: ${REPO}:adbc-oauth-server
301+
build:
302+
context: .
303+
cache_from:
304+
- ${REPO}:adbc-oauth-server
305+
dockerfile: ci/docker/oauth-server.dockerfile
306+
args:
307+
GO: ${GO}
308+
healthcheck:
309+
test: ["CMD", "curl", "--fail", "http://localhost:8181/health"]
310+
interval: 5s
311+
timeout: 10s
312+
retries: 3
313+
start_period: 30s
314+
ports:
315+
- "8181:8181"
316+
volumes:
317+
- .:/adbc:delegated
291318
command: >-
292-
/bin/bash -c "cd /adbc/go/adbc && go run ./driver/flightsql/cmd/testserver -host 0.0.0.0 -port 41414"
319+
/bin/bash -c "cd /adbc/go/adbc && go run ./driver/flightsql/cmd/oauthserver -host 0.0.0.0 -port 8181 -client-id test-client -client-secret test-secret"
293320
294321
flightsql-sqlite-test:
295322
image: ${REPO}:golang-${GO}-sqlite-flightsql

docs/source/driver/flight_sql.rst

Lines changed: 48 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -215,46 +215,76 @@ OAuth 2.0 Options
215215
Supported configurations to obtain tokens using OAuth 2.0 authentication flows.
216216

217217
``adbc.flight.sql.oauth.flow``
218-
Specifies the OAuth 2.0 flow type to use. Possible values: ``client_credentials``, ``token_exchange``
218+
Specifies the OAuth 2.0 flow type to use. Possible values: ``client_credentials``, ``token_exchange``
219+
220+
Python: :attr:`adbc_driver_flightsql.DatabaseOptions.OAUTH_FLOW`,
221+
:class:`adbc_driver_flightsql.OAuthFlowType`
219222

220223
``adbc.flight.sql.oauth.client_id``
221-
Unique identifier issued to the client application by the authorization server
224+
Unique identifier issued to the client application by the authorization server
225+
226+
Python: :attr:`adbc_driver_flightsql.DatabaseOptions.OAUTH_CLIENT_ID`
222227

223228
``adbc.flight.sql.oauth.client_secret``
224-
Secret associated to the client_id. Used to authenticate the client application to the authorization server
229+
Secret associated to the client_id. Used to authenticate the client application to the authorization server
230+
231+
Python: :attr:`adbc_driver_flightsql.DatabaseOptions.OAUTH_CLIENT_SECRET`
225232

226233
``adbc.flight.sql.oauth.token_uri``
227-
The endpoint URL where the client application requests tokens from the authorization server
234+
The endpoint URL where the client application requests tokens from the authorization server
235+
236+
Python: :attr:`adbc_driver_flightsql.DatabaseOptions.OAUTH_TOKEN_URI`
228237

229238
``adbc.flight.sql.oauth.scope``
230-
Space-separated list of permissions that the client is requesting access to (e.g ``"read.all offline_access"``)
239+
Space-separated list of permissions that the client is requesting access to (e.g ``"read.all offline_access"``)
240+
241+
Python: :attr:`adbc_driver_flightsql.DatabaseOptions.OAUTH_SCOPE`
231242

232243
``adbc.flight.sql.oauth.exchange.subject_token``
233-
The security token that the client application wants to exchange
244+
The security token that the client application wants to exchange
245+
246+
Python: :attr:`adbc_driver_flightsql.DatabaseOptions.OAUTH_EXCHANGE_SUBJECT_TOKEN`
234247

235248
``adbc.flight.sql.oauth.exchange.subject_token_type``
236-
Identifier for the type of the subject token.
237-
Check list below for supported token types.
249+
Identifier for the type of the subject token.
250+
Check list below for supported token types.
251+
252+
Python: :attr:`adbc_driver_flightsql.DatabaseOptions.OAUTH_EXCHANGE_SUBJECT_TOKEN_TYPE`,
253+
:class:`adbc_driver_flightsql.OAuthTokenType`
238254

239255
``adbc.flight.sql.oauth.exchange.actor_token``
240-
A security token that represents the identity of the acting party
256+
A security token that represents the identity of the acting party
257+
258+
Python: :attr:`adbc_driver_flightsql.DatabaseOptions.OAUTH_EXCHANGE_ACTOR_TOKEN`
241259

242260
``adbc.flight.sql.oauth.exchange.actor_token_type``
243-
Identifier for the type of the actor token.
244-
Check list below for supported token types.
261+
Identifier for the type of the actor token.
262+
Check list below for supported token types.
263+
264+
Python: :attr:`adbc_driver_flightsql.DatabaseOptions.OAUTH_EXCHANGE_ACTOR_TOKEN_TYPE`,
265+
:class:`adbc_driver_flightsql.OAuthTokenType`
266+
245267
``adbc.flight.sql.oauth.exchange.aud``
246-
The intended audience for the requested security token
268+
The intended audience for the requested security token
269+
270+
Python: :attr:`adbc_driver_flightsql.DatabaseOptions.OAUTH_EXCHANGE_AUD`
247271

248272
``adbc.flight.sql.oauth.exchange.resource``
249-
The resource server where the client intends to use the requested security token
273+
The resource server where the client intends to use the requested security token
274+
275+
Python: :attr:`adbc_driver_flightsql.DatabaseOptions.OAUTH_EXCHANGE_RESOURCE`
250276

251277
``adbc.flight.sql.oauth.exchange.scope``
252-
Specific permissions requested for the new token
278+
Specific permissions requested for the new token
279+
280+
Python: :attr:`adbc_driver_flightsql.DatabaseOptions.OAUTH_EXCHANGE_SCOPE`
253281

254282
``adbc.flight.sql.oauth.exchange.requested_token_type``
255-
The type of token the client wants to receive in exchange.
256-
Check list below for supported token types.
283+
The type of token the client wants to receive in exchange.
284+
Check list below for supported token types.
257285

286+
Python: :attr:`adbc_driver_flightsql.DatabaseOptions.OAUTH_EXCHANGE_REQUESTED_TOKEN_TYPE`,
287+
:class:`adbc_driver_flightsql.OAuthTokenType`
258288

259289
Supported token types:
260290
- ``urn:ietf:params:oauth:token-type:access_token``
@@ -264,6 +294,8 @@ Supported token types:
264294
- ``urn:ietf:params:oauth:token-type:saml2``
265295
- ``urn:ietf:params:oauth:token-type:jwt``
266296

297+
Python: :class:`adbc_driver_flightsql.OAuthTokenType`
298+
267299
Distributed Result Sets
268300
-----------------------
269301

docs/source/python/recipe/flight_sql.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,13 @@ Set the max gRPC message size
6161
-----------------------------
6262

6363
.. recipe:: flightsql_sqlite_max_msg_size.py
64+
65+
Connect with OAuth 2.0 Client Credentials
66+
-----------------------------------------
67+
68+
.. recipe:: flightsql_oauth_client_credentials.py
69+
70+
Connect with OAuth 2.0 Token Exchange
71+
-------------------------------------
72+
73+
.. recipe:: flightsql_oauth_token_exchange.py
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
18+
# RECIPE STARTS HERE
19+
20+
#: The Flight SQL driver supports OAuth 2.0 authentication. This example shows
21+
#: how to connect using the Client Credentials flow (RFC 6749), which is
22+
#: suitable for machine-to-machine authentication without user interaction.
23+
24+
import os
25+
26+
import adbc_driver_flightsql.dbapi
27+
from adbc_driver_flightsql import DatabaseOptions, OAuthFlowType
28+
29+
uri = os.environ["ADBC_TEST_FLIGHTSQL_URI"]
30+
token_uri = os.environ["ADBC_OAUTH_TOKEN_URI"]
31+
client_id = os.environ["ADBC_OAUTH_CLIENT_ID"]
32+
client_secret = os.environ["ADBC_OAUTH_CLIENT_SECRET"]
33+
34+
#: Connect using OAuth 2.0 Client Credentials flow.
35+
#: The driver will automatically obtain and refresh access tokens.
36+
37+
db_kwargs = {
38+
DatabaseOptions.OAUTH_FLOW.value: OAuthFlowType.CLIENT_CREDENTIALS.value,
39+
DatabaseOptions.OAUTH_TOKEN_URI.value: token_uri,
40+
DatabaseOptions.OAUTH_CLIENT_ID.value: client_id,
41+
DatabaseOptions.OAUTH_CLIENT_SECRET.value: client_secret,
42+
#: Optionally, request specific scopes
43+
# DatabaseOptions.OAUTH_SCOPE.value: "dremio.all",
44+
}
45+
46+
#: For testing with self-signed certificates, skip TLS verification.
47+
#: In production, you should provide proper TLS certificates.
48+
if os.environ.get("ADBC_OAUTH_SKIP_VERIFY", "true").lower() in ("1", "true"):
49+
db_kwargs[DatabaseOptions.TLS_SKIP_VERIFY.value] = "true"
50+
51+
conn = adbc_driver_flightsql.dbapi.connect(uri, db_kwargs=db_kwargs)
52+
53+
#: We can then execute queries as usual.
54+
55+
with conn.cursor() as cur:
56+
cur.execute("SELECT 1")
57+
58+
result = cur.fetchone()
59+
print(result)
60+
61+
conn.close()
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
18+
# RECIPE STARTS HERE
19+
20+
#: The Flight SQL driver supports OAuth 2.0 Token Exchange (RFC 8693). This
21+
#: allows exchanging an existing token (e.g., a JWT from an identity provider)
22+
#: for a new token that can be used to access the Flight SQL service.
23+
24+
import os
25+
26+
import adbc_driver_flightsql.dbapi
27+
from adbc_driver_flightsql import DatabaseOptions, OAuthFlowType, OAuthTokenType
28+
29+
uri = os.environ["ADBC_TEST_FLIGHTSQL_URI"]
30+
token_uri = os.environ["ADBC_OAUTH_TOKEN_URI"]
31+
#: This is typically a JWT or other token from your identity provider
32+
subject_token = os.environ["ADBC_OAUTH_SUBJECT_TOKEN"]
33+
34+
#: For testing with self-signed certificates, skip TLS verification.
35+
#: In production, you should provide proper TLS certificates.
36+
db_kwargs = {}
37+
if os.environ.get("ADBC_OAUTH_SKIP_VERIFY", "true").lower() in ("1", "true"):
38+
db_kwargs[DatabaseOptions.TLS_SKIP_VERIFY.value] = "true"
39+
40+
#: Connect using OAuth 2.0 Token Exchange flow.
41+
#: The driver will exchange the subject token for an access token.
42+
43+
db_kwargs.update(
44+
{
45+
DatabaseOptions.OAUTH_FLOW.value: OAuthFlowType.TOKEN_EXCHANGE.value,
46+
DatabaseOptions.OAUTH_TOKEN_URI.value: token_uri,
47+
DatabaseOptions.OAUTH_EXCHANGE_SUBJECT_TOKEN.value: subject_token,
48+
#: Specify the type of the subject token being exchanged
49+
DatabaseOptions.OAUTH_EXCHANGE_SUBJECT_TOKEN_TYPE.value: (
50+
OAuthTokenType.JWT.value
51+
),
52+
#: Optionally, specify the type of token you want to receive
53+
# DatabaseOptions.OAUTH_EXCHANGE_REQUESTED_TOKEN_TYPE.value:
54+
# OAuthTokenType.ACCESS_TOKEN.value,
55+
#: Optionally, specify the intended audience
56+
# DatabaseOptions.OAUTH_EXCHANGE_AUD.value: "my-service",
57+
}
58+
)
59+
60+
conn = adbc_driver_flightsql.dbapi.connect(uri, db_kwargs=db_kwargs)
61+
62+
#: We can then execute queries as usual.
63+
64+
with conn.cursor() as cur:
65+
cur.execute("SELECT 1")
66+
67+
result = cur.fetchone()
68+
print(result)
69+
70+
conn.close()

0 commit comments

Comments
 (0)