Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion .env
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,15 @@ ADBC_JDBC_POSTGRESQL_PASSWORD=password
ADBC_JDBC_POSTGRESQL_DATABASE=postgres
ADBC_POSTGRESQL_TEST_URI="postgresql://localhost:5432/postgres?user=postgres&password=password"
ADBC_SQLITE_FLIGHTSQL_URI=grpc+tcp://localhost:8080
ADBC_TEST_FLIGHTSQL_URI=grpc+tcp://localhost:41414
ADBC_TEST_FLIGHTSQL_URI=grpc+tls://localhost:41414
ADBC_GIZMOSQL_URI=grpc+tcp://localhost:31337
ADBC_GIZMOSQL_USER=adbc_test_user
ADBC_GIZMOSQL_PASSWORD=adbc_test_password

# OAuth test server configuration
# OAuth token endpoint (oauthserver on port 8181)
ADBC_OAUTH_TOKEN_URI=http://localhost:8181/token
ADBC_OAUTH_CLIENT_ID=test-client
ADBC_OAUTH_CLIENT_SECRET=test-secret
ADBC_OAUTH_SUBJECT_TOKEN=test-subject-token
ADBC_OAUTH_SKIP_VERIFY=true
2 changes: 1 addition & 1 deletion .github/workflows/native-unix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -728,7 +728,7 @@ jobs:
docs/source/python/recipe/*.py
- name: Test Recipes (Python)
run: |
docker compose up --detach --wait dremio flightsql-sqlite-test postgres-test gizmosql-test
docker compose up --detach --wait dremio flightsql-sqlite-test postgres-test gizmosql-test oauth-server flightsql-test
docker compose run --rm dremio-init
export ADBC_CPP_RECIPE_BIN=~/local/bin
# Needed for the combined C++/Python driver example
Expand Down
21 changes: 21 additions & 0 deletions ci/docker/oauth-server.dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.

# Simple OAuth 2.0 test server for ADBC FlightSQL OAuth testing
ARG GO
FROM golang:${GO}
EXPOSE 8181
31 changes: 29 additions & 2 deletions compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ services:
args:
GO: ${GO}
healthcheck:
test: ["CMD", "curl", "--http2-prior-knowledge", "-XPOST", "-H", "content-type: application/grpc", "localhost:41414"]
test: ["CMD", "curl", "-k", "--http2", "-XPOST", "-H", "content-type: application/grpc", "https://localhost:41414"]
interval: 5s
timeout: 30s
retries: 3
Expand All @@ -288,8 +288,35 @@ services:
- "41414:41414"
volumes:
- .:/adbc:delegated
depends_on:
oauth-server:
condition: service_healthy
command: >-
/bin/bash -c "cd /adbc/go/adbc && go run ./driver/flightsql/cmd/testserver -host 0.0.0.0 -port 41414 -token-prefix oauth- -tls"

# OAuth test server for FlightSQL OAuth authentication testing
oauth-server:
container_name: adbc-oauth-server
image: ${REPO}:adbc-oauth-server
build:
context: .
cache_from:
- ${REPO}:adbc-oauth-server
dockerfile: ci/docker/oauth-server.dockerfile
args:
GO: ${GO}
healthcheck:
test: ["CMD", "curl", "--fail", "http://localhost:8181/health"]
interval: 5s
timeout: 10s
retries: 3
start_period: 30s
ports:
- "8181:8181"
volumes:
- .:/adbc:delegated
command: >-
/bin/bash -c "cd /adbc/go/adbc && go run ./driver/flightsql/cmd/testserver -host 0.0.0.0 -port 41414"
/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"

flightsql-sqlite-test:
image: ${REPO}:golang-${GO}-sqlite-flightsql
Expand Down
64 changes: 48 additions & 16 deletions docs/source/driver/flight_sql.rst
Original file line number Diff line number Diff line change
Expand Up @@ -215,46 +215,76 @@ OAuth 2.0 Options
Supported configurations to obtain tokens using OAuth 2.0 authentication flows.

``adbc.flight.sql.oauth.flow``
Specifies the OAuth 2.0 flow type to use. Possible values: ``client_credentials``, ``token_exchange``
Specifies the OAuth 2.0 flow type to use. Possible values: ``client_credentials``, ``token_exchange``

Python: :attr:`adbc_driver_flightsql.DatabaseOptions.OAUTH_FLOW`,
:class:`adbc_driver_flightsql.OAuthFlowType`

``adbc.flight.sql.oauth.client_id``
Unique identifier issued to the client application by the authorization server
Unique identifier issued to the client application by the authorization server

Python: :attr:`adbc_driver_flightsql.DatabaseOptions.OAUTH_CLIENT_ID`

``adbc.flight.sql.oauth.client_secret``
Secret associated to the client_id. Used to authenticate the client application to the authorization server
Secret associated to the client_id. Used to authenticate the client application to the authorization server

Python: :attr:`adbc_driver_flightsql.DatabaseOptions.OAUTH_CLIENT_SECRET`

``adbc.flight.sql.oauth.token_uri``
The endpoint URL where the client application requests tokens from the authorization server
The endpoint URL where the client application requests tokens from the authorization server

Python: :attr:`adbc_driver_flightsql.DatabaseOptions.OAUTH_TOKEN_URI`

``adbc.flight.sql.oauth.scope``
Space-separated list of permissions that the client is requesting access to (e.g ``"read.all offline_access"``)
Space-separated list of permissions that the client is requesting access to (e.g ``"read.all offline_access"``)

Python: :attr:`adbc_driver_flightsql.DatabaseOptions.OAUTH_SCOPE`

``adbc.flight.sql.oauth.exchange.subject_token``
The security token that the client application wants to exchange
The security token that the client application wants to exchange

Python: :attr:`adbc_driver_flightsql.DatabaseOptions.OAUTH_EXCHANGE_SUBJECT_TOKEN`

``adbc.flight.sql.oauth.exchange.subject_token_type``
Identifier for the type of the subject token.
Check list below for supported token types.
Identifier for the type of the subject token.
Check list below for supported token types.

Python: :attr:`adbc_driver_flightsql.DatabaseOptions.OAUTH_EXCHANGE_SUBJECT_TOKEN_TYPE`,
:class:`adbc_driver_flightsql.OAuthTokenType`

``adbc.flight.sql.oauth.exchange.actor_token``
A security token that represents the identity of the acting party
A security token that represents the identity of the acting party

Python: :attr:`adbc_driver_flightsql.DatabaseOptions.OAUTH_EXCHANGE_ACTOR_TOKEN`

``adbc.flight.sql.oauth.exchange.actor_token_type``
Identifier for the type of the actor token.
Check list below for supported token types.
Identifier for the type of the actor token.
Check list below for supported token types.

Python: :attr:`adbc_driver_flightsql.DatabaseOptions.OAUTH_EXCHANGE_ACTOR_TOKEN_TYPE`,
:class:`adbc_driver_flightsql.OAuthTokenType`

``adbc.flight.sql.oauth.exchange.aud``
The intended audience for the requested security token
The intended audience for the requested security token

Python: :attr:`adbc_driver_flightsql.DatabaseOptions.OAUTH_EXCHANGE_AUD`

``adbc.flight.sql.oauth.exchange.resource``
The resource server where the client intends to use the requested security token
The resource server where the client intends to use the requested security token

Python: :attr:`adbc_driver_flightsql.DatabaseOptions.OAUTH_EXCHANGE_RESOURCE`

``adbc.flight.sql.oauth.exchange.scope``
Specific permissions requested for the new token
Specific permissions requested for the new token

Python: :attr:`adbc_driver_flightsql.DatabaseOptions.OAUTH_EXCHANGE_SCOPE`

``adbc.flight.sql.oauth.exchange.requested_token_type``
The type of token the client wants to receive in exchange.
Check list below for supported token types.
The type of token the client wants to receive in exchange.
Check list below for supported token types.

Python: :attr:`adbc_driver_flightsql.DatabaseOptions.OAUTH_EXCHANGE_REQUESTED_TOKEN_TYPE`,
:class:`adbc_driver_flightsql.OAuthTokenType`

Supported token types:
- ``urn:ietf:params:oauth:token-type:access_token``
Expand All @@ -264,6 +294,8 @@ Supported token types:
- ``urn:ietf:params:oauth:token-type:saml2``
- ``urn:ietf:params:oauth:token-type:jwt``

Python: :class:`adbc_driver_flightsql.OAuthTokenType`

Distributed Result Sets
-----------------------

Expand Down
10 changes: 10 additions & 0 deletions docs/source/python/recipe/flight_sql.rst
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,13 @@ Set the max gRPC message size
-----------------------------

.. recipe:: flightsql_sqlite_max_msg_size.py

Connect with OAuth 2.0 Client Credentials
-----------------------------------------

.. recipe:: flightsql_oauth_client_credentials.py

Connect with OAuth 2.0 Token Exchange
-------------------------------------

.. recipe:: flightsql_oauth_token_exchange.py
61 changes: 61 additions & 0 deletions docs/source/python/recipe/flightsql_oauth_client_credentials.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.

# RECIPE STARTS HERE

#: The Flight SQL driver supports OAuth 2.0 authentication. This example shows
#: how to connect using the Client Credentials flow (RFC 6749), which is
#: suitable for machine-to-machine authentication without user interaction.

import os

import adbc_driver_flightsql.dbapi
from adbc_driver_flightsql import DatabaseOptions, OAuthFlowType

uri = os.environ["ADBC_TEST_FLIGHTSQL_URI"]
token_uri = os.environ["ADBC_OAUTH_TOKEN_URI"]
client_id = os.environ["ADBC_OAUTH_CLIENT_ID"]
client_secret = os.environ["ADBC_OAUTH_CLIENT_SECRET"]

#: Connect using OAuth 2.0 Client Credentials flow.
#: The driver will automatically obtain and refresh access tokens.

db_kwargs = {
DatabaseOptions.OAUTH_FLOW.value: OAuthFlowType.CLIENT_CREDENTIALS.value,
DatabaseOptions.OAUTH_TOKEN_URI.value: token_uri,
DatabaseOptions.OAUTH_CLIENT_ID.value: client_id,
DatabaseOptions.OAUTH_CLIENT_SECRET.value: client_secret,
#: Optionally, request specific scopes
# DatabaseOptions.OAUTH_SCOPE.value: "dremio.all",
}

#: For testing with self-signed certificates, skip TLS verification.
#: In production, you should provide proper TLS certificates.
if os.environ.get("ADBC_OAUTH_SKIP_VERIFY", "true").lower() in ("1", "true"):
db_kwargs[DatabaseOptions.TLS_SKIP_VERIFY.value] = "true"

conn = adbc_driver_flightsql.dbapi.connect(uri, db_kwargs=db_kwargs)

#: We can then execute queries as usual.

with conn.cursor() as cur:
cur.execute("SELECT 1")

result = cur.fetchone()
print(result)

conn.close()
70 changes: 70 additions & 0 deletions docs/source/python/recipe/flightsql_oauth_token_exchange.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.

# RECIPE STARTS HERE

#: The Flight SQL driver supports OAuth 2.0 Token Exchange (RFC 8693). This
#: allows exchanging an existing token (e.g., a JWT from an identity provider)
#: for a new token that can be used to access the Flight SQL service.

import os

import adbc_driver_flightsql.dbapi
from adbc_driver_flightsql import DatabaseOptions, OAuthFlowType, OAuthTokenType

uri = os.environ["ADBC_TEST_FLIGHTSQL_URI"]
token_uri = os.environ["ADBC_OAUTH_TOKEN_URI"]
#: This is typically a JWT or other token from your identity provider
subject_token = os.environ["ADBC_OAUTH_SUBJECT_TOKEN"]

#: For testing with self-signed certificates, skip TLS verification.
#: In production, you should provide proper TLS certificates.
db_kwargs = {}
if os.environ.get("ADBC_OAUTH_SKIP_VERIFY", "true").lower() in ("1", "true"):
db_kwargs[DatabaseOptions.TLS_SKIP_VERIFY.value] = "true"

#: Connect using OAuth 2.0 Token Exchange flow.
#: The driver will exchange the subject token for an access token.

db_kwargs.update(
{
DatabaseOptions.OAUTH_FLOW.value: OAuthFlowType.TOKEN_EXCHANGE.value,
DatabaseOptions.OAUTH_TOKEN_URI.value: token_uri,
DatabaseOptions.OAUTH_EXCHANGE_SUBJECT_TOKEN.value: subject_token,
#: Specify the type of the subject token being exchanged
DatabaseOptions.OAUTH_EXCHANGE_SUBJECT_TOKEN_TYPE.value: (
OAuthTokenType.JWT.value
),
#: Optionally, specify the type of token you want to receive
# DatabaseOptions.OAUTH_EXCHANGE_REQUESTED_TOKEN_TYPE.value:
# OAuthTokenType.ACCESS_TOKEN.value,
#: Optionally, specify the intended audience
# DatabaseOptions.OAUTH_EXCHANGE_AUD.value: "my-service",
}
)

conn = adbc_driver_flightsql.dbapi.connect(uri, db_kwargs=db_kwargs)

#: We can then execute queries as usual.

with conn.cursor() as cur:
cur.execute("SELECT 1")

result = cur.fetchone()
print(result)

conn.close()
Loading
Loading