Skip to content

Commit

Permalink
609 create internal client (#610)
Browse files Browse the repository at this point in the history
* add internal client

* get the correct client type; add a test

* entry in changelog

* fix the test

* switch create_service_json_entry to static method

* combine 2 clients

* rewrite test

* hashing it properly
  • Loading branch information
longshuicy authored Sep 19, 2024
1 parent 13f3fda commit ebaa993
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 20 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

### Added
- Apply Black formatter [#589](https://github.com/IN-CORE/pyincore/issues/589)
- Internal client connecting to the IN-CORE services [#609](https://github.com/IN-CORE/pyincore/issues/609)


## [1.19.0] - 2024-06-12
Expand Down
83 changes: 67 additions & 16 deletions pyincore/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,9 @@ def __init__(
self,
service_url: str = None,
token_file_name: str = None,
username: str = None,
usergroups: list = None,
internal: bool = False,
offline: bool = False,
):
"""
Expand All @@ -179,10 +182,14 @@ def __init__(
"""
super().__init__()
self.offline = offline
self.internal = internal

if not offline:
if service_url is None or len(service_url.strip()) == 0:
service_url = pyglobals.INCORE_API_PROD_URL
if internal:
service_url = pyglobals.INCORE_INTERNAL_API_URL

self.service_url = service_url
self.token_url = urllib.parse.urljoin(
self.service_url, pyglobals.KEYCLOAK_AUTH_PATH
Expand All @@ -204,29 +211,38 @@ def __init__(
if not os.path.exists(self.hashed_svc_data_dir):
os.makedirs(self.hashed_svc_data_dir)

# store the token file in the respective repository's directory
if token_file_name is None or len(token_file_name.strip()) == 0:
token_file_name = "." + self.hashed_service_url + "_token"
self.token_file = os.path.join(
pyglobals.PYINCORE_USER_CACHE, token_file_name
)

authorization = self.retrieve_token_from_file()
if authorization is not None:
self.session.headers["Authorization"] = authorization
print(
"Connection successful to IN-CORE services.",
"pyIncore version detected:",
pyglobals.PACKAGE_VERSION,
if internal:
# Constructing the headers
self.session.headers["x-auth-userinfo"] = json.dumps(
{"preferred_username": username}
)
self.session.headers["x-auth-usergroup"] = json.dumps(
{"groups": usergroups}
)

else:
if self.login():
# store the token file in the respective repository's directory
if token_file_name is None or len(token_file_name.strip()) == 0:
token_file_name = "." + self.hashed_service_url + "_token"
self.token_file = os.path.join(
pyglobals.PYINCORE_USER_CACHE, token_file_name
)

authorization = self.retrieve_token_from_file()
if authorization is not None:
self.session.headers["Authorization"] = authorization
print(
"Connection successful to IN-CORE services.",
"pyIncore version detected:",
pyglobals.PACKAGE_VERSION,
)
else:
if self.login():
print(
"Connection successful to IN-CORE services.",
"pyIncore version detected:",
pyglobals.PACKAGE_VERSION,
)

else:
self.service_url = ""
self.token_url = ""
Expand All @@ -240,6 +256,13 @@ def __init__(
)

def login(self):
if self.offline is True:
logger.warning("Offline mode does not have login method.")
return False
if self.internal is True:
logger.warning("Internal mode does not have login method.")
return False

for attempt in range(pyglobals.MAX_LOGIN_ATTEMPTS):
try:
username = input("Enter username: ")
Expand Down Expand Up @@ -279,6 +302,16 @@ def store_authorization_in_file(self, authorization: str):
authorization (str): An authorization in the format "bearer access_token".
"""
if self.offline is True:
logger.warning(
"Offline mode does not have store_authorization_in_file method."
)
return
if self.internal is True:
logger.warning(
"Internal mode does not have store_authorization_in_file method."
)
return
try:
with open(self.token_file, "w") as f:
f.write(authorization)
Expand All @@ -291,6 +324,13 @@ def is_token_expired(self, token):
Returns:
True if the token has expired, False otherwise
"""
if self.offline is True:
logger.warning("Offline mode does not have is_token_expired method.")
return
if self.internal is True:
logger.warning("Internal mode does not have is_token_expired method.")
return

# Split the token to get payload
_, payload_encoded, _ = token.split(".")
# Decode the payload
Expand All @@ -311,6 +351,17 @@ def retrieve_token_from_file(self):
dict: Dictionary containing authorization in the format "bearer access_token" if file exists, None otherwise
"""
if self.offline is True:
logger.warning(
"Offline mode does not have retrieve_token_from_file method."
)
return
if self.internal is True:
logger.warning(
"Internal mode does not have retrieve_token_from_file method."
)
return

if not os.path.isfile(self.token_file):
return None
else:
Expand Down
2 changes: 2 additions & 0 deletions pyincore/globals.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
INCORE_API_PROD_URL = "https://incore.ncsa.illinois.edu"
INCORE_API_DEV_URL = "https://incore-dev.ncsa.illinois.edu"

INCORE_INTERNAL_API_URL = "http://localhost:8080"

KEYCLOAK_AUTH_PATH = "/auth/realms/In-core/protocol/openid-connect/token"
KEYCLOAK_USERINFO_PATH = "/auth/realms/In-core/protocol/openid-connect/userinfo"
CLIENT_ID = "react-auth"
Expand Down
2 changes: 0 additions & 2 deletions pyincore/restorationservice.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
# This program and the accompanying materials are made available under the
# terms of the Mozilla Public License v2.0 which accompanies this distribution,
# and is available at https://www.mozilla.org/en-US/MPL/2.0/


from urllib.parse import urljoin

from pyincore import IncoreClient
Expand Down
1 change: 0 additions & 1 deletion pyincore/spaceservice.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
# This program and the accompanying materials are made available under the
# terms of the Mozilla Public License v2.0 which accompanies this distribution,
# and is available at https://www.mozilla.org/en-US/MPL/2.0/

from urllib.parse import urljoin
from pyincore import IncoreClient
import pyincore.globals as pyglobals
Expand Down
2 changes: 1 addition & 1 deletion tests/pyincore/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from pyincore import IncoreClient


def test_client_success(monkeypatch):
def test_client_success():
"""
testing successful login
"""
Expand Down
42 changes: 42 additions & 0 deletions tests/pyincore/test_internal_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Copyright (c) 2019 University of Illinois and others. All rights reserved.
#
# This program and the accompanying materials are made available under the
# terms of the Mozilla Public License v2.0 which accompanies this distribution,
# and is available at https://www.mozilla.org/en-US/MPL/2.0/
import os.path


from pyincore import IncoreClient

# from pyincore import DataService

user = {
"service_url": "http://localhost:8080",
"internal": True,
"username": "incrtest",
"usergroups": ["incore_ncsa"],
}


def test_client_success():
"""
testing successful login
"""
client = IncoreClient(**user)
assert "incrtest" in str(client.session.headers["x-auth-userinfo"])
assert "incore_ncsa" in str(client.session.headers["x-auth-usergroup"])


def test_delete_repo_cache():
client = IncoreClient(**user)
hashed_repo_dir_path = client.hashed_svc_data_dir
client.clear_cache()
assert os.path.exists(hashed_repo_dir_path) is False


# def test_get_dataset_metadata():
# client = IncoreClient(**user)
# datasvc_internal = DataService(client)
# dataset_id = "5a284f09c7d30d13bc0819a6"
# metadata = datasvc_internal.get_dataset_metadata(dataset_id)
# assert metadata["id"] == dataset_id

0 comments on commit ebaa993

Please sign in to comment.