Skip to content

Commit

Permalink
Issue ref #269: OAuth 2.0 Credential Format for Delta Sharing Python … (
Browse files Browse the repository at this point in the history
#309)

* Issue ref #269: OAuth 2.0 Credential Format for Delta Sharing Python Client Pull Request

Signed-off-by: Dima Alberg <[email protected]>

* Issue ref #269: OAuth 2.0 Credential Format for Delta Sharing Python Client Pull Request

Signed-off-by: Dima Alberg <[email protected]>

* Issue ref #269: OAuth 2.0 Credential Format for Delta Sharing Python Client Pull Request

Signed-off-by: Dima Alberg <[email protected]>

* Issue ref #269: OAuth 2.0 Credential Format for Delta Sharing Python Client Pull Request

Signed-off-by: Dima Alberg <[email protected]>

* Issue ref #269: OAuth 2.0 Credential Format for Delta Sharing Python Client Pull Request

Signed-off-by: Dima Alberg <[email protected]>

* Issue ref #269: OAuth 2.0 Credential Format for Delta Sharing Python Client Pull Request

Signed-off-by: Dima Alberg <[email protected]>

---------

Signed-off-by: Dima Alberg <[email protected]>
  • Loading branch information
dialberg authored Jun 5, 2023
1 parent 9140796 commit 60abca1
Show file tree
Hide file tree
Showing 7 changed files with 428 additions and 13 deletions.
13 changes: 13 additions & 0 deletions python/NOTICE.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
###############################################################################################
#
# Copyright © 2023, 2023, Oracle and/or its affiliates.
# Issue ref #269: OAuth 2.0 Credential Format for Delta Sharing Client
# Code update:
# - protocol.py
# - rest_client.py
# - test_protocol.py
# - test_profile_bearer.json
# - test_profile_oauth2.json
# - test_profile_basic.json
#
###############################################################################################
73 changes: 62 additions & 11 deletions python/delta_sharing/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,18 @@

@dataclass(frozen=True)
class DeltaSharingProfile:
CURRENT: ClassVar[int] = 1
CURRENT: ClassVar[int] = 2

share_credentials_version: int
endpoint: str
bearer_token: str
bearer_token: Optional[str] = None
expiration_time: Optional[str] = None
type: Optional[str] = None
token_endpoint: Optional[str] = None
client_id: Optional[str] = None
client_secret: Optional[str] = None
username: Optional[str] = None
password: Optional[str] = None

def __post_init__(self):
if self.share_credentials_version > DeltaSharingProfile.CURRENT:
Expand Down Expand Up @@ -56,16 +62,60 @@ def read_from_file(profile: Union[str, IO, Path]) -> "DeltaSharingProfile":
def from_json(json) -> "DeltaSharingProfile":
if isinstance(json, (str, bytes, bytearray)):
json = loads(json)

share_credentials_version = int(json["shareCredentialsVersion"])
endpoint = json["endpoint"]
if endpoint.endswith("/"):
if endpoint is not None and endpoint.endswith("/"):
endpoint = endpoint[:-1]
expiration_time = json.get("expirationTime")
return DeltaSharingProfile(
share_credentials_version=int(json["shareCredentialsVersion"]),
endpoint=endpoint,
bearer_token=json["bearerToken"],
expiration_time=expiration_time,
)

if share_credentials_version == 1:
return DeltaSharingProfile(
share_credentials_version=share_credentials_version,
endpoint=endpoint,
bearer_token=json["bearerToken"],
expiration_time=json.get("expirationTime"),
)
elif share_credentials_version == 2:
type = json["type"]
if type == "persistent_oauth2.0":
token_endpoint = json["tokenEndpoint"]
if token_endpoint is not None and token_endpoint.endswith("/"):
token_endpoint = token_endpoint[:-1]
return DeltaSharingProfile(
share_credentials_version=share_credentials_version,
type=type,
endpoint=endpoint,
token_endpoint=token_endpoint,
client_id=json["clientId"],
client_secret=json["clientSecret"],
)
elif type == "bearer_token":
return DeltaSharingProfile(
share_credentials_version=share_credentials_version,
type=type,
endpoint=endpoint,
bearer_token=json["bearerToken"],
expiration_time=json.get("expirationTime")
)
elif type == "basic":
return DeltaSharingProfile(
share_credentials_version=share_credentials_version,
type=type,
endpoint=endpoint,
username=json["username"],
password=json["password"],
)
else:
raise ValueError(
"The current release does not supports {type} type. "
"Please check type.")
else:
raise ValueError(
"'shareCredentialsVersion' in the profile is "
f"{share_credentials_version} which is too new. "
f"The current release supports version {DeltaSharingProfile.CURRENT} and below. "
"Please upgrade to a newer release."
)


@dataclass(frozen=True)
Expand Down Expand Up @@ -101,7 +151,8 @@ class Table:
def from_json(json) -> "Table":
if isinstance(json, (str, bytes, bytearray)):
json = loads(json)
return Table(name=json["name"], share=json["share"], schema=json["schema"])
return Table(name=json["name"], share=json["share"],
schema=json["schema"])


@dataclass(frozen=True)
Expand Down
53 changes: 51 additions & 2 deletions python/delta_sharing/rest_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,16 +143,65 @@ def __init__(self, profile: DeltaSharingProfile, num_retries=10):
self._profile = profile
self._num_retries = num_retries
self._sleeper = lambda sleep_ms: time.sleep(sleep_ms / 1000)
self.auth_session(profile)

def auth_session(self, profile):
self._session = requests.Session()
self.__auth_broker(profile)
if urlparse(profile.endpoint).hostname == "localhost":
self._session.verify = False

def __auth_broker(self, profile):
if profile.share_credentials_version == 2:
if profile.type == "persistent_oauth2.0":
self.__auth_persistent_oauth2(profile)
elif profile.type == "bearer_token":
self.__auth_bearer_token(profile)
elif profile.type == "basic":
self.__auth_basic(profile)
else:
self.__auth_bearer_token(profile)
else:
self.__auth_bearer_token(profile)

def __auth_bearer_token(self, profile):
self._session.headers.update(
{
"Authorization": f"Bearer {profile.bearer_token}",
"User-Agent": DataSharingRestClient.USER_AGENT,
}
)
if urlparse(profile.endpoint).hostname == "localhost":
self._session.verify = False

def __auth_persistent_oauth2(self, profile):
headers = {"Content-Type": "application/x-www-form-urlencoded",
"Accept": "application/json"}

response = requests.post(profile.token_endpoint,
data={"grant_type": "client_credentials"},
headers=headers,
auth=(profile.client_id,
profile.client_secret),)

bearer_token = "{}".format(response.json()["access_token"])

self._session.headers.update(
{
"Authorization": f"Bearer {bearer_token}",
"User-Agent": DataSharingRestClient.USER_AGENT,
}
)

def __auth_basic(self, profile):
self._session.auth = (profile.username, profile.password)

response = self._session.post(profile.endpoint,
data={"grant_type": "client_credentials"},)

self._session.headers.update(
{
"User-Agent": DataSharingRestClient.USER_AGENT,
}
)

@retry_with_exponential_backoff
def list_shares(
Expand Down
7 changes: 7 additions & 0 deletions python/delta_sharing/tests/test_profile_basic.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"shareCredentialsVersion": 2,
"type": "basic",
"endpoint": "https://localhost/delta-sharing/",
"username": "username",
"password": "password"
}
7 changes: 7 additions & 0 deletions python/delta_sharing/tests/test_profile_bearer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"shareCredentialsVersion": 2,
"type": "bearer_token",
"endpoint": "https://localhost:12345/delta-sharing/",
"bearerToken": "dapi5e3574ec767ca1548ae5bbed1a2dc04d",
"expirationTime": "2021-11-12T00:12:29.0Z"
}
8 changes: 8 additions & 0 deletions python/delta_sharing/tests/test_profile_oauth2.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"shareCredentialsVersion": 2,
"type": "persistent_oauth2.0",
"endpoint": "https://localhost/delta-sharing/",
"tokenEndpoint": "tokenEndpoint",
"clientId": "clientId",
"clientSecret": "clientSecret"
}
Loading

0 comments on commit 60abca1

Please sign in to comment.