From d344171fd88052293c11137d8d604e8e38289caa Mon Sep 17 00:00:00 2001 From: Robin Laugs Date: Thu, 30 May 2024 16:09:00 +0200 Subject: [PATCH 1/2] Allow password login --- src/igtcloud/client/core/auth.py | 15 ++++++++++----- src/igtcloud/client/tools/cli.py | 20 ++++++++++++-------- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/igtcloud/client/core/auth.py b/src/igtcloud/client/core/auth.py index 7f481a8..a50c104 100644 --- a/src/igtcloud/client/core/auth.py +++ b/src/igtcloud/client/core/auth.py @@ -27,7 +27,7 @@ @contextmanager -def smart_auth(domain, username=None, key=None, auto_refresh=True, auto_logout=True): +def smart_auth(domain, username=None, password=None, key=None, auto_refresh=True, auto_logout=True): url = urlparse(domain) if not url.scheme: url = urlparse('https://' + domain) @@ -43,6 +43,7 @@ def smart_auth(domain, username=None, key=None, auto_refresh=True, auto_logout=T if auth is None: with AuthRefresher(domain=domain, username=username, + password=password, key=key, auto_refresh=auto_refresh, auto_logout=auto_logout) as auth: @@ -272,7 +273,7 @@ def _extract_jwt_payload(jwt_token: str) -> dict: return json.loads(base64.b64decode(jwt_payload)) - def _login(self, username=None, key=None): + def _login(self, username=None, password=None, key=None): if self._token_data is None or self._token_data.get('refresh_token') is None: logger.info("Login to IGT Cloud ({})".format(self._host)) @@ -293,7 +294,11 @@ def _login(self, username=None, key=None): raise RuntimeError('Error during login') else: username = username or os.environ.get('CLOUD_USERNAME') or input("Username: ") - password = getpass('{}@{}\'s password: '.format(username, self._host)) + + if not password: + password = getpass('{}@{}\'s password: '.format(username, self._host)) + else: + password = base64.b64decode(password).decode('utf-8') try: self._oauth_login(username, password) @@ -367,7 +372,7 @@ def stop(self): self._running = False logger.info("Stopped IGT Cloud authentication handler") - def start(self, domain=None, username=None, key=None, blocking=True, auto_refresh=True, auto_logout=True): + def start(self, domain=None, username=None, password=None, key=None, blocking=True, auto_refresh=True, auto_logout=True): self._setup_mmap() try: logger.info("Starting IGT Cloud authentication handler...") @@ -383,7 +388,7 @@ def start(self, domain=None, username=None, key=None, blocking=True, auto_refres self._domain = url.geturl() self._host = url.netloc - self._login(username=username, key=key) + self._login(username=username, password=password, key=key) if auto_refresh: self._rt.start() diff --git a/src/igtcloud/client/tools/cli.py b/src/igtcloud/client/tools/cli.py index 25a1e11..a020602 100644 --- a/src/igtcloud/client/tools/cli.py +++ b/src/igtcloud/client/tools/cli.py @@ -26,6 +26,7 @@ def cli(): case_sensitive=False)) @click.option('--domain', default=None, help='Overwrites the environment setting') @click.option('--user', default=None, help='Username') +@click.option('--password', default=None, help='Base64 encoded password') @click.option('--ext', default=None, help='filter on specific file extension (i.e.: \'.txt\')') @click.option( '--start', @@ -60,7 +61,7 @@ def cli(): type=click.Choice(['flat', 'hierarchical'], case_sensitive=False), help='Folder structure of the data to be downloaded.' ) -def download(target_folder, project, institute, environment, domain, user, ext, start, end, category, +def download(target_folder, project, institute, environment, domain, user, password, ext, start, end, category, include_modified_date, project_files, debug, concurrent_studies, concurrent_files, folder_structure): """Download data from Philips Interventional Cloud. @@ -79,7 +80,7 @@ def download(target_folder, project, institute, environment, domain, user, ext, institute = None domain = _get_domain(domain, environment) - with smart_auth(domain, username=user) as auth: + with smart_auth(domain, username=user, password=password) as auth: set_auth(auth) logger.info(f"Using url: {auth.domain}") @@ -111,12 +112,13 @@ def download(target_folder, project, institute, environment, domain, user, ext, case_sensitive=False)) @click.option('--domain', default=None, help='Overwrites the environment setting') @click.option('--user', default=None, help='Username') +@click.option('--password', default=None, help='Base64 encoded password') @click.option('--ext', default=None, help='filter on specific file extension (i.e.: \'.txt\')') @click.option('--start', default=None, help='start date (YYYY-MM-DD) of date range filter. Default value is 1900-01-01.') @click.option('--end', default=None, help='end date (YYYY-MM-DD) of date range filter. Default value is 9999-12-31.') @click.option('--debug', flag_value=True, help='Enable debug logging') @click.option('--files', flag_value=True, help='Output all files under patient studies') -def csv(target_folder, project, institute, environment, domain, user, ext, start, end, debug, files): +def csv(target_folder, project, institute, environment, domain, user, password, ext, start, end, debug, files): """List data from Philips Interventional Cloud in CSV file. This tool will export all studies/files from INSTITUTE (or all institutes) in project PROJECT to @@ -134,7 +136,7 @@ def csv(target_folder, project, institute, environment, domain, user, ext, start institute = None domain = _get_domain(domain, environment) - with smart_auth(domain, username=user) as auth: + with smart_auth(domain, username=user, password=password) as auth: set_auth(auth) logger.info(f"Using url: {auth.domain}") list_project(project, target_folder, institute_name=institute, list_files=files, @@ -160,6 +162,7 @@ def _get_domain(domain, environment): case_sensitive=False)) @click.option('--domain', default=None, help='Overwrites the environment setting') @click.option('--user', default=None, help='Username') +@click.option('--password', default=None, help='Base64 encoded password') @click.option('--submit', flag_value=True, help='Set electronic record state to submitted state') @click.option('--debug', flag_value=True, help='Enable debug logging') @click.option('--concurrent-studies', type=int, default=None, help='Maximum number of concurrent studies upload') @@ -176,7 +179,7 @@ def _get_domain(domain, environment): type=click.Choice(['annotations'], case_sensitive=False), help='Uploads annotation files' ) -def upload(local_folder, project, institute, environment, domain, user, submit, debug, concurrent_studies, +def upload(local_folder, project, institute, environment, domain, user, password, submit, debug, concurrent_studies, concurrent_files, folder_structure, category): """Upload data to Philips Interventional Cloud. @@ -198,7 +201,7 @@ def upload(local_folder, project, institute, environment, domain, user, submit, if institute == "*": institute = None - with smart_auth(domain, username=user) as auth: + with smart_auth(domain, username=user, password=password) as auth: set_auth(auth) logger.info(f"Using url: {auth.domain}") upload_project(local_folder, project, institute, submit, concurrent_studies, concurrent_files, folder_structure, category) @@ -207,8 +210,9 @@ def upload(local_folder, project, institute, environment, domain, user, submit, @click.command(short_help="Login to Philips Interventional Cloud") @click.option('-d', '--domain', default=None, type=str) @click.option('-u', '--user', default=None, type=str) +@click.option('-p', '--password', default=None, type=str, help='Base64 encoded password') @click.option('-s', '--service-file', default=None, type=str, help='Use a service certificate to perform login') -def login(domain, user, service_file): +def login(domain, user, password, service_file): from igtcloud.client.core.auth import AuthRefresher auth_refresher = AuthRefresher() key = None @@ -216,7 +220,7 @@ def login(domain, user, service_file): user = user or os.path.splitext(os.path.basename(service_file))[0] with open(service_file) as fp: key = fp.read() - auth_refresher.start(domain=domain, username=user, key=key) + auth_refresher.start(domain=domain, username=user, password=password, key=key) @click.command('get-token', short_help="Get token for Philips Interventional Cloud") From 446dbcaacf825c3c07900c0d94e241528a1558dc Mon Sep 17 00:00:00 2001 From: Robin Laugs Date: Fri, 31 May 2024 10:22:02 +0200 Subject: [PATCH 2/2] Rename password to encoded_password --- src/igtcloud/client/core/auth.py | 14 +++++++------- src/igtcloud/client/tools/cli.py | 8 ++++---- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/igtcloud/client/core/auth.py b/src/igtcloud/client/core/auth.py index a50c104..007458b 100644 --- a/src/igtcloud/client/core/auth.py +++ b/src/igtcloud/client/core/auth.py @@ -27,7 +27,7 @@ @contextmanager -def smart_auth(domain, username=None, password=None, key=None, auto_refresh=True, auto_logout=True): +def smart_auth(domain, username=None, encoded_password=None, key=None, auto_refresh=True, auto_logout=True): url = urlparse(domain) if not url.scheme: url = urlparse('https://' + domain) @@ -43,7 +43,7 @@ def smart_auth(domain, username=None, password=None, key=None, auto_refresh=True if auth is None: with AuthRefresher(domain=domain, username=username, - password=password, + encoded_password=encoded_password, key=key, auto_refresh=auto_refresh, auto_logout=auto_logout) as auth: @@ -273,7 +273,7 @@ def _extract_jwt_payload(jwt_token: str) -> dict: return json.loads(base64.b64decode(jwt_payload)) - def _login(self, username=None, password=None, key=None): + def _login(self, username=None, encoded_password=None, key=None): if self._token_data is None or self._token_data.get('refresh_token') is None: logger.info("Login to IGT Cloud ({})".format(self._host)) @@ -295,10 +295,10 @@ def _login(self, username=None, password=None, key=None): else: username = username or os.environ.get('CLOUD_USERNAME') or input("Username: ") - if not password: + if not encoded_password: password = getpass('{}@{}\'s password: '.format(username, self._host)) else: - password = base64.b64decode(password).decode('utf-8') + password = base64.b64decode(encoded_password).decode('utf-8') try: self._oauth_login(username, password) @@ -372,7 +372,7 @@ def stop(self): self._running = False logger.info("Stopped IGT Cloud authentication handler") - def start(self, domain=None, username=None, password=None, key=None, blocking=True, auto_refresh=True, auto_logout=True): + def start(self, domain=None, username=None, encoded_password=None, key=None, blocking=True, auto_refresh=True, auto_logout=True): self._setup_mmap() try: logger.info("Starting IGT Cloud authentication handler...") @@ -388,7 +388,7 @@ def start(self, domain=None, username=None, password=None, key=None, blocking=Tr self._domain = url.geturl() self._host = url.netloc - self._login(username=username, password=password, key=key) + self._login(username=username, encoded_password=encoded_password, key=key) if auto_refresh: self._rt.start() diff --git a/src/igtcloud/client/tools/cli.py b/src/igtcloud/client/tools/cli.py index a020602..28e0303 100644 --- a/src/igtcloud/client/tools/cli.py +++ b/src/igtcloud/client/tools/cli.py @@ -80,7 +80,7 @@ def download(target_folder, project, institute, environment, domain, user, passw institute = None domain = _get_domain(domain, environment) - with smart_auth(domain, username=user, password=password) as auth: + with smart_auth(domain, username=user, encoded_password=password) as auth: set_auth(auth) logger.info(f"Using url: {auth.domain}") @@ -136,7 +136,7 @@ def csv(target_folder, project, institute, environment, domain, user, password, institute = None domain = _get_domain(domain, environment) - with smart_auth(domain, username=user, password=password) as auth: + with smart_auth(domain, username=user, encoded_password=password) as auth: set_auth(auth) logger.info(f"Using url: {auth.domain}") list_project(project, target_folder, institute_name=institute, list_files=files, @@ -201,7 +201,7 @@ def upload(local_folder, project, institute, environment, domain, user, password if institute == "*": institute = None - with smart_auth(domain, username=user, password=password) as auth: + with smart_auth(domain, username=user, encoded_password=password) as auth: set_auth(auth) logger.info(f"Using url: {auth.domain}") upload_project(local_folder, project, institute, submit, concurrent_studies, concurrent_files, folder_structure, category) @@ -220,7 +220,7 @@ def login(domain, user, password, service_file): user = user or os.path.splitext(os.path.basename(service_file))[0] with open(service_file) as fp: key = fp.read() - auth_refresher.start(domain=domain, username=user, password=password, key=key) + auth_refresher.start(domain=domain, username=user, encoded_password=password, key=key) @click.command('get-token', short_help="Get token for Philips Interventional Cloud")