From a13df9c658b6daf5e94c9ff82110e78446118f99 Mon Sep 17 00:00:00 2001 From: Stephen Knox Date: Thu, 12 May 2022 21:29:21 +0100 Subject: [PATCH] 1: Add ability for a remote client to accept a session ID from a differnt service (aka HWI) and to use that to log in to the API. 2: Add ability for a uuser's password to be saved in a file, but avoiding storing it in plain text. (cherry picked from commit 838f9656cf66ac7d175a88519e8f5ab3ef943f13) --- hydra_client/config.py | 3 + hydra_client/connection/base_connection.py | 72 ++++++++++++++++--- .../connection/remote_json_connection.py | 16 ++++- 3 files changed, 77 insertions(+), 14 deletions(-) diff --git a/hydra_client/config.py b/hydra_client/config.py index 17c738a..4da119e 100644 --- a/hydra_client/config.py +++ b/hydra_client/config.py @@ -1,3 +1,6 @@ port = 8080 domain = '127.0.0.1' json_path = 'json' +seed = 'changeme' +cache_password = True +cipherkey=b'M0EppyrL8yf9cTE7z5UywMBl1202aSTCntQ4ZFXLiiE=' diff --git a/hydra_client/connection/base_connection.py b/hydra_client/connection/base_connection.py index 7bf3070..46469ae 100644 --- a/hydra_client/connection/base_connection.py +++ b/hydra_client/connection/base_connection.py @@ -1,15 +1,16 @@ -from .. import config - +import os +import logging import collections import six - -import os +import tempfile +import getpass +import random +from cryptography.fernet import Fernet import hydra_base -import getpass +from .. import config -import logging log = logging.getLogger(__name__) DEFAULT_DATETIME_FORMAT = "%Y-%m-%dT%H:%M:%S.%f000Z" @@ -48,10 +49,6 @@ def get_url(self, url, path): """ - #Remove trailing slashes - if len(url) > 0 and url[-1] == '/': - url = url[0:-1] - if url is None: port = config.port domain = config.domain @@ -62,6 +59,11 @@ def get_url(self, url, path): ret_url = "%s:%s/%s" % (domain, port, path) else: log.info("Using user-defined URL: %s", url) + + #Remove trailing slashes + if len(url) > 0 and url[-1] == '/': + url = url[0:-1] + port = self.get_port(url) hostname = self.get_hostname(url) url_path = self.get_path(url) @@ -183,6 +185,14 @@ def get_username_and_password(self, username, password): else: ret_username = username + #Check if the password is in a cache + password_cached = False + if password is None: + password = self.get_cached_password() + if password is not None: + password_cached = True + + if password is None: log.info("No password specified. Defaulting looking at 'HYDRA_PASSWORD'") @@ -191,6 +201,46 @@ def get_username_and_password(self, username, password): if ret_password is None: ret_password = getpass.getpass() else: - ret_password=password + ret_password = password + + if config.cache_password is True and password_cached is False: + self.cache_password(ret_password) return ret_username, ret_password + + def cache_password(self, password): + """ + Save password to a cached file in /tmp. + """ + encrypter = Fernet(config.cipherkey) + encoded_text = encrypter.encrypt(password.encode('utf-8')) + tmp = tempfile.gettempdir() + random.seed(config.seed) + i = int(random.random() * 10e16) + filename = f'.{i}' + pwdfile = os.path.join(tmp, filename) + with open(pwdfile, 'w') as f: + f.write(encoded_text.decode('utf-8')) + + def get_cached_password(self): + """ + Save password to a cached file in /tmp. + """ + + encrypter = Fernet(config.cipherkey) + tmp = tempfile.gettempdir() + random.seed(config.seed) + i = int(random.random() * 10e16) + filename = f'.{i}' + pwdfile = os.path.join(tmp, filename) + + if not os.path.exists(pwdfile): + return None + log.info("Using cached password") + + with open(pwdfile, 'r') as f: + encoded_text = f.read() + + password = encrypter.decrypt(encoded_text.encode('utf-8')) + + return password.decode('utf-8') diff --git a/hydra_client/connection/remote_json_connection.py b/hydra_client/connection/remote_json_connection.py index b328043..0d4bcc0 100644 --- a/hydra_client/connection/remote_json_connection.py +++ b/hydra_client/connection/remote_json_connection.py @@ -34,7 +34,7 @@ class RemoteJSONConnection(BaseConnection): """ Remote connection to a Hydra server. """ - def __init__(self, url=None, session_id=None, app_name=None, test_server=None): + def __init__(self, url=None, session_id=None, app_name=None, test_server=None, **kwargs): """ args: url: The url of the hydra platform server @@ -46,7 +46,7 @@ def __init__(self, url=None, session_id=None, app_name=None, test_server=None): """ super(RemoteJSONConnection, self).__init__(app_name=app_name) - self.user_id = None + self.user_id = None self.url = self.get_url(url, 'json') self.app_name = app_name if app_name else '' self.session_id = session_id @@ -134,6 +134,7 @@ def call(self, func, *args, **kwargs): } if func != 'login': log.info("Args %s", call) + cookie = {'beaker.session.id':self.session_id, 'user_id': str(self.user_id), 'appname': self.app_name.replace(' ', '_')#for some reason, beaker fails when the appname cookie has a space in it @@ -163,7 +164,7 @@ def call(self, func, *args, **kwargs): if self.session_id is None: - self.session_id = r.cookies['beaker.session.id'] + self.session_id = r.cookies.get('beaker.session.id') log.info(self.session_id) json_ret = json.loads(r.content) @@ -199,6 +200,15 @@ def login(self, username=None, password=None): return self.user_id, self.session_id + def get_remote_session(self, session_id): + resp = self.call('get_remote_session', {'session_id': session_id}) + if resp.get('user_id') is not None: + log.info("Session found for user: %s", self.user_id) + else: + log.warning("No session found with ID %s", session_id) + self.login() + + class JsonConnection(RemoteJSONConnection): def __init__(self, *args, **kwargs):