From c4212932257e3818dc35e686373af13d35f619d4 Mon Sep 17 00:00:00 2001
From: Swen Vermeul <swen@ethz.ch>
Date: Wed, 13 Jul 2016 11:11:30 +0200
Subject: [PATCH] SSDM-3867: class OpenbisCredentials and class
 OpenbisCredentialStore removed, because they make not much sense.
 delete_token and save_token re-implemented to make them compatible with
 jupyterhub login procedure.

---
 src/python/PyBis/pybis/pybis.py | 153 +++++++++++++-------------------
 1 file changed, 63 insertions(+), 90 deletions(-)

diff --git a/src/python/PyBis/pybis/pybis.py b/src/python/PyBis/pybis/pybis.py
index cde065f7e3a..84568e59eec 100644
--- a/src/python/PyBis/pybis/pybis.py
+++ b/src/python/PyBis/pybis/pybis.py
@@ -27,73 +27,11 @@ from threading import Thread
 from queue import Queue
 DROPBOX_PLUGIN = "jupyter-uploader-api"
 
-class OpenbisCredentials:
-    """Credentials for communicating with openBIS."""
-
-    def __init__(self, token=None, uname_and_pass=None):
-        """A connection can be authenticated either by a token or a username and password combination
-        :param token: An authentication token for openBIS, can be None.
-        :param uname_and_pass: A tuple with username and password, in that order.
-        """
-        self.token = token
-        self.uname_and_pass = uname_and_pass
-
-    def has_token(self):
-        return self.token is not None
-
-    def has_username_and_password(self):
-        return self.uname_and_pass is not None
-
-    @property
-    def username(self):
-        return self.uname_and_pass[0]
-
-    @property
-    def password(self):
-        return self.uname_and_pass[1]
-
-
-class OpenbisCredentialStore:
-    """Cache login tokens for reuse."""
-
-    def __init__(self, store_folder):
-        """Cache credentials on the file system at store_path.
-        If the store_folder does not exist, it will be created with the umask inherited from the shell.
-        :param store_folder: The folder to write the credentials to. It will be created if necessary.
-        """
-        self.store_folder = store_folder
-
-    @property
-    def store_path(self):
-        return os.path.join(self.store_folder, "bis_token.txt")
-
-    def read(self):
-        """Read the cached credentials and return a credentials object.
-        :return: A credentials object with a token, or an empty credentials object if no store was found.
-        """
-        if not os.path.exists(self.store_path):
-            return OpenbisCredentials()
-        with open(self.store_path, "r") as f:
-            token = f.read()
-        return OpenbisCredentials(token)
-
-    def write(self, credentials):
-        """Write a credentials object to the store, overwriting any previous information.
-        :param credentials: The credentials with a token to write. If it has no token, nothing is written.
-        """
-        if not credentials.has_token():
-            return
-        token = credentials.token
-        if not os.path.exists(self.store_folder):
-            os.makedirs(self.store_folder)
-        with open(self.store_path, "w") as f:
-            f.write(token)
-
 
 class Openbis:
     """Interface for communicating with openBIS."""
 
-    def __init__(self, url, verify_certificates=True, use_cached_token=False ):
+    def __init__(self, url='https://localhost:8443', verify_certificates=True, token=None):
         """Initialize an interface to openBIS with information necessary to connect to the server.
         :param host:
         """
@@ -110,44 +48,69 @@ class Openbis:
         self.as_v1 = '/openbis/openbis/rmi-general-information-v1.json'
         self.reg_v1 = '/openbis/openbis/rmi-query-v1.json'
         self.verify_certificates = verify_certificates
-        self.token = None
-        if use_cached_token:
-            self.initialize_token()
+        self.token = token
+        self.token_path = None
+
+        # use an existing token, if available
+        if self.token is None:
+            self.token = self.get_cached_token()
 
 
-    def initialize_token(self):
-        """Read the token from the cache, and set the token ivar to it, if there, otherwise None."""
-        token_path = self.token_path()
+    def get_cached_token(self):
+        """Read the token from the cache, and set the token ivar to it, if there, otherwise None.
+        If the token is not valid anymore, delete it. 
+        """
+        token_path = self.gen_token_path()
         if not os.path.exists(token_path):
-            self.token = None
-            return
+            return None
         try:
             with open(token_path) as f:
-                self.token = f.read()
-                if not self.is_token_valid():
-                    self.token = None
+                token = f.read()
+                if not self.is_token_valid(token):
                     os.remove(token_path)
+                    return None
+                else:
+                    return token
         except FileNotFoundError:
-            self.token = None
-
-    def token(self):
-        if self.token is None:
-            raise ValueError('no valid session available')
+            return None
 
 
-    def token_path(self, parent_folder=None):
-        """Return the path to the token file."""
+    def gen_token_path(self, parent_folder=None):
+        """generates a path to the token file.
+        The token is usually saved in a file called
+        ~/.pybis/hostname.token
+        """
         if parent_folder is None:
-            parent_folder = os.path.expanduser("~")
-        path = os.path.join(parent_folder, '.pybis', self.hostname + '.token')
+            # save token under ~/.pybis folder
+            parent_folder = os.path.join(
+                os.path.expanduser("~"),
+                '.pybis'
+            )
+        path = os.path.join(parent_folder, self.hostname + '.token')
         return path
 
 
-    def save_token(self, parent_folder=None):
-        token_path = self.token_path(parent_folder)
+    def save_token(self, token=None, parent_folder=None):
+        if token is None:
+            token = self.token
+
+        token_path = None;
+        if parent_folder is None:
+            token_path = self.gen_token_path()
+        else:
+            token_path = self.gen_token_path(parent_folder)
+
+        # create the necessary directories, if they don't exist yet
         os.makedirs(os.path.dirname(token_path), exist_ok=True)
         with open(token_path, 'w') as f:
-            f.write(self.token)
+            f.write(token)
+            self.token_path = token_path
+
+
+    def delete_token(self, token_path=None):
+        if token_path is None:
+            token_path = self.token_path
+        os.remove(token_path)
 
 
     def post_request(self, resource, data):
@@ -176,10 +139,13 @@ class Openbis:
             "jsonrpc":"2.0"
         }
         resp = self.post_request(self.as_v3, logout_request)
+        self.delete_token()
+        self.token = None
+        self.token_path = None
         return resp
 
 
-    def login(self, username=None, password=None, store_credentials=False):
+    def login(self, username=None, password=None, save_token=False):
         """Log into openBIS.
         Expects a username and a password and updates the token (session-ID).
         The token is then used for every request.
@@ -198,6 +164,9 @@ class Openbis:
             raise ValueError("login to openBIS failed")
         else:
             self.token = result
+            if save_token:
+                self.save_token()
+            return self.token
 
 
     def get_datastores(self):
@@ -240,6 +209,10 @@ class Openbis:
         return DataFrame()
         
 
+    def is_session_active(self):
+        return self.is_token_valid(self.token)
+
+
     def is_token_valid(self, token=None):
         """Check if the connection to openBIS is valid.
         This method is useful to check if a token is still valid or if it has timed out,
@@ -795,13 +768,13 @@ class DataSet():
         if resp.ok:
             data = resp.json()
             if 'error' in data:
-                raise ValueError('an error has occured: ' + data['error'] )
+                raise ValueError('Error from openBIS: ' + data['error'] )
             elif 'result' in data:
                 return data['result']
             else:
-                raise ValueError('request did not return either result nor error')
+                raise ValueError('request to openBIS did not return either result nor error')
         else:
-            raise ValueError('general error while performing post request')
+            raise ValueError('internal error while performing post request')
 
 
 class AttrDict(dict):
-- 
GitLab