Moving internal authentication methods to auth.py 23/6623/5
authorEduardo Sousa <esousa@whitestack.com>
Thu, 4 Oct 2018 03:24:18 +0000 (04:24 +0100)
committerEduardo Sousa <esousa@whitestack.com>
Mon, 8 Oct 2018 10:37:23 +0000 (11:37 +0100)
Change-Id: I64ed5b92f0b5d30429e92e6d8fe419e1178f90d7
Signed-off-by: Eduardo Sousa <esousa@whitestack.com>
osm_nbi/auth.py
osm_nbi/engine.py
osm_nbi/html_public/version
osm_nbi/nbi.py

index 4ee9ce2..90fc1e2 100644 (file)
@@ -9,18 +9,21 @@ list inside the projects that they are inserted
 __author__ = "Eduardo Sousa <esousa@whitestack.com>"
 __date__ = "$27-jul-2018 23:59:59$"
 
 __author__ = "Eduardo Sousa <esousa@whitestack.com>"
 __date__ = "$27-jul-2018 23:59:59$"
 
+import cherrypy
 import logging
 from base64 import standard_b64decode
 from copy import deepcopy
 from functools import reduce
 import logging
 from base64 import standard_b64decode
 from copy import deepcopy
 from functools import reduce
+from hashlib import sha256
 from http import HTTPStatus
 from http import HTTPStatus
+from random import choice as random_choice
 from time import time
 
 from time import time
 
-import cherrypy
-
 from authconn import AuthException
 from authconn_keystone import AuthconnKeystone
 from authconn import AuthException
 from authconn_keystone import AuthconnKeystone
-from engine import EngineException
+from osm_common import dbmongo
+from osm_common import dbmemory
+from osm_common.dbbase import DbException
 
 
 class Authenticator:
 
 
 class Authenticator:
@@ -31,19 +34,11 @@ class Authenticator:
     RBAC model to manage permissions on operations.
     """
 
     RBAC model to manage permissions on operations.
     """
 
-    def __init__(self, engine):
+    def __init__(self):
         """
         Authenticator initializer. Setup the initial state of the object,
         while it waits for the config dictionary and database initialization.
         """
         Authenticator initializer. Setup the initial state of the object,
         while it waits for the config dictionary and database initialization.
-
-        Note: engine is only here until all the calls can to it can be replaced.
-
-        :param engine: reference to engine object used.
         """
         """
-        super().__init__()
-
-        self.engine = engine
-
         self.backend = None
         self.config = None
         self.db = None
         self.backend = None
         self.config = None
         self.db = None
@@ -67,14 +62,28 @@ class Authenticator:
                 elif config["authentication"]["backend"] == "internal":
                     pass
                 else:
                 elif config["authentication"]["backend"] == "internal":
                     pass
                 else:
-                    raise Exception("No authentication backend defined")
+                    raise AuthException("Unknown authentication backend: {}"
+                                        .format(config["authentication"]["backend"]))
             if not self.db:
             if not self.db:
-                pass
-                # TODO: Implement database initialization
-                # NOTE: Database needed to store the mappings
+                if config["database"]["driver"] == "mongo":
+                    self.db = dbmongo.DbMongo()
+                    self.db.db_connect(config["database"])
+                elif config["database"]["driver"] == "memory":
+                    self.db = dbmemory.DbMemory()
+                    self.db.db_connect(config["database"])
+                else:
+                    raise AuthException("Invalid configuration param '{}' at '[database]':'driver'"
+                                        .format(config["database"]["driver"]))
         except Exception as e:
             raise AuthException(str(e))
 
         except Exception as e:
             raise AuthException(str(e))
 
+    def stop(self):
+        try:
+            if self.db:
+                self.db.db_disconnect()
+        except DbException as e:
+            raise AuthException(str(e), http_code=e.http_code)
+
     def init_db(self, target_version='1.0'):
         """
         Check if the database has been initialized. If not, create the required tables
     def init_db(self, target_version='1.0'):
         """
         Check if the database has been initialized. If not, create the required tables
@@ -116,7 +125,7 @@ class Authenticator:
                     token = outdata["id"]
                     cherrypy.session['Authorization'] = token
             if self.config["authentication"]["backend"] == "internal":
                     token = outdata["id"]
                     cherrypy.session['Authorization'] = token
             if self.config["authentication"]["backend"] == "internal":
-                return self.engine.authorize(token)
+                return self._internal_authorize(token)
             else:
                 try:
                     self.backend.validate_token(token)
             else:
                 try:
                     self.backend.validate_token(token)
@@ -124,7 +133,7 @@ class Authenticator:
                 except AuthException:
                     self.del_token(token)
                     raise
                 except AuthException:
                     self.del_token(token)
                     raise
-        except EngineException as e:
+        except AuthException as e:
             if cherrypy.session.get('Authorization'):
                 del cherrypy.session['Authorization']
             cherrypy.response.headers["WWW-Authenticate"] = 'Bearer realm="{}"'.format(e)
             if cherrypy.session.get('Authorization'):
                 del cherrypy.session['Authorization']
             cherrypy.response.headers["WWW-Authenticate"] = 'Bearer realm="{}"'.format(e)
@@ -132,7 +141,7 @@ class Authenticator:
 
     def new_token(self, session, indata, remote):
         if self.config["authentication"]["backend"] == "internal":
 
     def new_token(self, session, indata, remote):
         if self.config["authentication"]["backend"] == "internal":
-            return self.engine.new_token(session, indata, remote)
+            return self._internal_new_token(session, indata, remote)
         else:
             if indata.get("username"):
                 token, projects = self.backend.authenticate_with_user_password(
         else:
             if indata.get("username"):
                 token, projects = self.backend.authenticate_with_user_password(
@@ -163,7 +172,7 @@ class Authenticator:
                 "_id": token,
                 "id": token,
                 "issued_at": now,
                 "_id": token,
                 "id": token,
                 "issued_at": now,
-                "expires": now+3600,
+                "expires": now + 3600,
                 "project_id": project_id,
                 "username": indata.get("username") if not session else session.get("username"),
                 "remote_port": remote.port,
                 "project_id": project_id,
                 "username": indata.get("username") if not session else session.get("username"),
                 "remote_port": remote.port,
@@ -181,29 +190,122 @@ class Authenticator:
 
     def get_token_list(self, session):
         if self.config["authentication"]["backend"] == "internal":
 
     def get_token_list(self, session):
         if self.config["authentication"]["backend"] == "internal":
-            return self.engine.get_token_list(session)
+            return self._internal_get_token_list(session)
         else:
             return [deepcopy(token) for token in self.tokens.values()
                     if token["username"] == session["username"]]
 
     def get_token(self, session, token):
         if self.config["authentication"]["backend"] == "internal":
         else:
             return [deepcopy(token) for token in self.tokens.values()
                     if token["username"] == session["username"]]
 
     def get_token(self, session, token):
         if self.config["authentication"]["backend"] == "internal":
-            return self.engine.get_token(session, token)
+            return self._internal_get_token(session, token)
         else:
             token_value = self.tokens.get(token)
             if not token_value:
         else:
             token_value = self.tokens.get(token)
             if not token_value:
-                raise EngineException("token not found", http_code=HTTPStatus.NOT_FOUND)
+                raise AuthException("token not found", http_code=HTTPStatus.NOT_FOUND)
             if token_value["username"] != session["username"] and not session["admin"]:
             if token_value["username"] != session["username"] and not session["admin"]:
-                raise EngineException("needed admin privileges", http_code=HTTPStatus.UNAUTHORIZED)
+                raise AuthException("needed admin privileges", http_code=HTTPStatus.UNAUTHORIZED)
             return token_value
 
     def del_token(self, token):
         if self.config["authentication"]["backend"] == "internal":
             return token_value
 
     def del_token(self, token):
         if self.config["authentication"]["backend"] == "internal":
-            return self.engine.del_token(token)
+            return self._internal_del_token(token)
         else:
             try:
                 self.backend.revoke_token(token)
                 del self.tokens[token]
                 return "token '{}' deleted".format(token)
             except KeyError:
         else:
             try:
                 self.backend.revoke_token(token)
                 del self.tokens[token]
                 return "token '{}' deleted".format(token)
             except KeyError:
-                raise EngineException("Token '{}' not found".format(token), http_code=HTTPStatus.NOT_FOUND)
+                raise AuthException("Token '{}' not found".format(token), http_code=HTTPStatus.NOT_FOUND)
+
+    def _internal_authorize(self, token):
+        try:
+            if not token:
+                raise AuthException("Needed a token or Authorization http header", http_code=HTTPStatus.UNAUTHORIZED)
+            if token not in self.tokens:
+                raise AuthException("Invalid token or Authorization http header", http_code=HTTPStatus.UNAUTHORIZED)
+            session = self.tokens[token]
+            now = time()
+            if session["expires"] < now:
+                del self.tokens[token]
+                raise AuthException("Expired Token or Authorization http header", http_code=HTTPStatus.UNAUTHORIZED)
+            return session
+        except AuthException:
+            if self.config["global"].get("test.user_not_authorized"):
+                return {"id": "fake-token-id-for-test",
+                        "project_id": self.config["global"].get("test.project_not_authorized", "admin"),
+                        "username": self.config["global"]["test.user_not_authorized"]}
+            else:
+                raise
+
+    def _internal_new_token(self, session, indata, remote):
+        now = time()
+        user_content = None
+
+        # Try using username/password
+        if indata.get("username"):
+            user_rows = self.db.get_list("users", {"username": indata.get("username")})
+            user_content = None
+            if user_rows:
+                user_content = user_rows[0]
+                salt = user_content["_admin"]["salt"]
+                shadow_password = sha256(indata.get("password", "").encode('utf-8') + salt.encode('utf-8')).hexdigest()
+                if shadow_password != user_content["password"]:
+                    user_content = None
+            if not user_content:
+                raise AuthException("Invalid username/password", http_code=HTTPStatus.UNAUTHORIZED)
+        elif session:
+            user_rows = self.db.get_list("users", {"username": session["username"]})
+            if user_rows:
+                user_content = user_rows[0]
+            else:
+                raise AuthException("Invalid token", http_code=HTTPStatus.UNAUTHORIZED)
+        else:
+            raise AuthException("Provide credentials: username/password or Authorization Bearer token",
+                                http_code=HTTPStatus.UNAUTHORIZED)
+
+        token_id = ''.join(random_choice('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789')
+                           for _ in range(0, 32))
+        if indata.get("project_id"):
+            project_id = indata.get("project_id")
+            if project_id not in user_content["projects"]:
+                raise AuthException("project {} not allowed for this user"
+                                    .format(project_id), http_code=HTTPStatus.UNAUTHORIZED)
+        else:
+            project_id = user_content["projects"][0]
+        if project_id == "admin":
+            session_admin = True
+        else:
+            project = self.db.get_one("projects", {"_id": project_id})
+            session_admin = project.get("admin", False)
+        new_session = {"issued_at": now, "expires": now + 3600,
+                       "_id": token_id, "id": token_id, "project_id": project_id, "username": user_content["username"],
+                       "remote_port": remote.port, "admin": session_admin}
+        if remote.name:
+            new_session["remote_host"] = remote.name
+        elif remote.ip:
+            new_session["remote_host"] = remote.ip
+
+        self.tokens[token_id] = new_session
+        return deepcopy(new_session)
+
+    def _internal_get_token_list(self, session):
+        token_list = []
+        for token_id, token_value in self.tokens.items():
+            if token_value["username"] == session["username"]:
+                token_list.append(deepcopy(token_value))
+        return token_list
+
+    def _internal_get_token(self, session, token_id):
+        token_value = self.tokens.get(token_id)
+        if not token_value:
+            raise AuthException("token not found", http_code=HTTPStatus.NOT_FOUND)
+        if token_value["username"] != session["username"] and not session["admin"]:
+            raise AuthException("needed admin privileges", http_code=HTTPStatus.UNAUTHORIZED)
+        return token_value
+
+    def _internal_del_token(self, token_id):
+        try:
+            del self.tokens[token_id]
+            return "token '{}' deleted".format(token_id)
+        except KeyError:
+            raise AuthException("Token '{}' not found".format(token_id), http_code=HTTPStatus.NOT_FOUND)
index 29bd4f1..53bf219 100644 (file)
@@ -9,7 +9,6 @@ import tarfile
 import yaml
 import json
 import logging
 import yaml
 import json
 import logging
-from random import choice as random_choice
 from uuid import uuid4
 from hashlib import sha256, md5
 from osm_common.dbbase import DbException, deep_update
 from uuid import uuid4
 from hashlib import sha256, md5
 from osm_common.dbbase import DbException, deep_update
@@ -17,7 +16,7 @@ from osm_common.fsbase import FsException
 from osm_common.msgbase import MsgException
 from http import HTTPStatus
 from time import time
 from osm_common.msgbase import MsgException
 from http import HTTPStatus
 from time import time
-from copy import deepcopy, copy
+from copy import copy
 from validation import validate_input, ValidationError
 
 __author__ = "Alfonso Tierno <alfonso.tiernosepulveda@telefonica.com>"
 from validation import validate_input, ValidationError
 
 __author__ = "Alfonso Tierno <alfonso.tiernosepulveda@telefonica.com>"
@@ -100,102 +99,6 @@ class Engine(object):
         except (DbException, FsException, MsgException) as e:
             raise EngineException(str(e), http_code=e.http_code)
 
         except (DbException, FsException, MsgException) as e:
             raise EngineException(str(e), http_code=e.http_code)
 
-    def authorize(self, token):
-        try:
-            if not token:
-                raise EngineException("Needed a token or Authorization http header",
-                                      http_code=HTTPStatus.UNAUTHORIZED)
-            if token not in self.tokens:
-                raise EngineException("Invalid token or Authorization http header",
-                                      http_code=HTTPStatus.UNAUTHORIZED)
-            session = self.tokens[token]
-            now = time()
-            if session["expires"] < now:
-                del self.tokens[token]
-                raise EngineException("Expired Token or Authorization http header",
-                                      http_code=HTTPStatus.UNAUTHORIZED)
-            return session
-        except EngineException:
-            if self.config["global"].get("test.user_not_authorized"):
-                return {"id": "fake-token-id-for-test",
-                        "project_id": self.config["global"].get("test.project_not_authorized", "admin"),
-                        "username": self.config["global"]["test.user_not_authorized"]}
-            else:
-                raise
-
-    def new_token(self, session, indata, remote):
-        now = time()
-        user_content = None
-
-        # Try using username/password
-        if indata.get("username"):
-            user_rows = self.db.get_list("users", {"username": indata.get("username")})
-            user_content = None
-            if user_rows:
-                user_content = user_rows[0]
-                salt = user_content["_admin"]["salt"]
-                shadow_password = sha256(indata.get("password", "").encode('utf-8') + salt.encode('utf-8')).hexdigest()
-                if shadow_password != user_content["password"]:
-                    user_content = None
-            if not user_content:
-                raise EngineException("Invalid username/password", http_code=HTTPStatus.UNAUTHORIZED)
-        elif session:
-            user_rows = self.db.get_list("users", {"username": session["username"]})
-            if user_rows:
-                user_content = user_rows[0]
-            else:
-                raise EngineException("Invalid token", http_code=HTTPStatus.UNAUTHORIZED)
-        else:
-            raise EngineException("Provide credentials: username/password or Authorization Bearer token",
-                                  http_code=HTTPStatus.UNAUTHORIZED)
-
-        token_id = ''.join(random_choice('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789')
-                           for _ in range(0, 32))
-        if indata.get("project_id"):
-            project_id = indata.get("project_id")
-            if project_id not in user_content["projects"]:
-                raise EngineException("project {} not allowed for this user".format(project_id),
-                                      http_code=HTTPStatus.UNAUTHORIZED)
-        else:
-            project_id = user_content["projects"][0]
-        if project_id == "admin":
-            session_admin = True
-        else:
-            project = self.db.get_one("projects", {"_id": project_id})
-            session_admin = project.get("admin", False)
-        new_session = {"issued_at": now, "expires": now+3600,
-                       "_id": token_id, "id": token_id, "project_id": project_id, "username": user_content["username"],
-                       "remote_port": remote.port, "admin": session_admin}
-        if remote.name:
-            new_session["remote_host"] = remote.name
-        elif remote.ip:
-            new_session["remote_host"] = remote.ip
-
-        self.tokens[token_id] = new_session
-        return deepcopy(new_session)
-
-    def get_token_list(self, session):
-        token_list = []
-        for token_id, token_value in self.tokens.items():
-            if token_value["username"] == session["username"]:
-                token_list.append(deepcopy(token_value))
-        return token_list
-
-    def get_token(self, session, token_id):
-        token_value = self.tokens.get(token_id)
-        if not token_value:
-            raise EngineException("token not found", http_code=HTTPStatus.NOT_FOUND)
-        if token_value["username"] != session["username"] and not session["admin"]:
-            raise EngineException("needed admin privileges", http_code=HTTPStatus.UNAUTHORIZED)
-        return token_value
-
-    def del_token(self, token_id):
-        try:
-            del self.tokens[token_id]
-            return "token '{}' deleted".format(token_id)
-        except KeyError:
-            raise EngineException("Token '{}' not found".format(token_id), http_code=HTTPStatus.NOT_FOUND)
-
     @staticmethod
     def _remove_envelop(item, indata=None):
         """
     @staticmethod
     def _remove_envelop(item, indata=None):
         """
index 96460cd..85cfc0d 100644 (file)
@@ -1,3 +1,3 @@
-0.1.18
-2018-09-26
+0.1.19
+2018-10-04
 
 
index e42284f..fa2f043 100644 (file)
@@ -148,7 +148,7 @@ class Server(object):
     def __init__(self):
         self.instance += 1
         self.engine = Engine()
     def __init__(self):
         self.instance += 1
         self.engine = Engine()
-        self.authenticator = Authenticator(self.engine)
+        self.authenticator = Authenticator()
         self.valid_methods = {   # contains allowed URL and methods
             "admin": {
                 "v1": {
         self.valid_methods = {   # contains allowed URL and methods
             "admin": {
                 "v1": {