Fix Bug 774 - NBI+Keystone: Trying to obtain a token with user+password+project gives...
[osm/NBI.git] / osm_nbi / auth.py
index e9a6b4e..576ae4d 100644 (file)
@@ -42,7 +42,7 @@ from time import time
 from os import path
 from base_topic import BaseTopic    # To allow project names in project_id
 
 from os import path
 from base_topic import BaseTopic    # To allow project names in project_id
 
-from authconn import AuthException
+from authconn import AuthException, AuthExceptionUnauthorized
 from authconn_keystone import AuthconnKeystone
 from osm_common import dbmongo
 from osm_common import dbmemory
 from authconn_keystone import AuthconnKeystone
 from osm_common import dbmongo
 from osm_common import dbmemory
@@ -229,13 +229,14 @@ class Authenticator:
     def load_operation_to_allowed_roles(self):
         """
         Fills the internal self.operation_to_allowed_roles based on database role content and self.operations
     def load_operation_to_allowed_roles(self):
         """
         Fills the internal self.operation_to_allowed_roles based on database role content and self.operations
+        It works in a shadow copy and replace at the end to allow other threads working with the old copy
         :return: None
         """
 
         permissions = {oper: [] for oper in self.operations}
         records = self.db.get_list("roles_operations")
 
         :return: None
         """
 
         permissions = {oper: [] for oper in self.operations}
         records = self.db.get_list("roles_operations")
 
-        ignore_fields = ["_id", "_admin", "name", "default", "admin"]
+        ignore_fields = ["_id", "_admin", "name", "default"]
         for record in records:
             record_permissions = {oper: record["permissions"].get("default", False) for oper in self.operations}
             operations_joined = [(oper, value) for oper, value in record["permissions"].items()
         for record in records:
             record_permissions = {oper: record["permissions"].get("default", False) for oper in self.operations}
             operations_joined = [(oper, value) for oper, value in record["permissions"].items()
@@ -253,8 +254,7 @@ class Authenticator:
             for allowed_op in allowed_operations:
                 permissions[allowed_op].append(record["name"])
 
             for allowed_op in allowed_operations:
                 permissions[allowed_op].append(record["name"])
 
-        for oper, role_list in permissions.items():
-            self.operation_to_allowed_roles[oper] = role_list
+        self.operation_to_allowed_roles = permissions
 
     def authorize(self):
         token = None
 
     def authorize(self):
         token = None
@@ -292,20 +292,18 @@ class Authenticator:
                 if not token:
                     raise AuthException("Needed a token or Authorization http header",
                                         http_code=HTTPStatus.UNAUTHORIZED)
                 if not token:
                     raise AuthException("Needed a token or Authorization http header",
                                         http_code=HTTPStatus.UNAUTHORIZED)
-                try:
-                    self.backend.validate_token(token)
-                    self.check_permissions(self.tokens_cache[token], cherrypy.request.path_info,
-                                           cherrypy.request.method)
-                    # TODO: check if this can be avoided. Backend may provide enough information
-                    return deepcopy(self.tokens_cache[token])
-                except AuthException:
-                    self.del_token(token)
-                    raise
+                token_info = self.backend.validate_token(token)
+                # TODO add to token info remote host, port
+
+                self.check_permissions(token_info, cherrypy.request.path_info,
+                                       cherrypy.request.method)
+                return token_info
         except AuthException as e:
         except AuthException as e:
-            if cherrypy.session.get('Authorization'):
-                del cherrypy.session['Authorization']
-            cherrypy.response.headers["WWW-Authenticate"] = 'Bearer realm="{}"'.format(e)
-            raise AuthException(str(e))
+            if not isinstance(e, AuthExceptionUnauthorized):
+                if cherrypy.session.get('Authorization'):
+                    del cherrypy.session['Authorization']
+                cherrypy.response.headers["WWW-Authenticate"] = 'Bearer realm="{}"'.format(e)
+            raise
 
     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":
@@ -316,7 +314,7 @@ class Authenticator:
                 current_token = session.get("token")
             token_info = self.backend.authenticate(
                 user=indata.get("username"),
                 current_token = session.get("token")
             token_info = self.backend.authenticate(
                 user=indata.get("username"),
-                password=indata.get("username"),
+                password=indata.get("password"),
                 token=current_token,
                 project=indata.get("project_id")
             )
                 token=current_token,
                 project=indata.get("project_id")
             )
@@ -412,7 +410,14 @@ class Authenticator:
 
         operation = self.resources_to_operations_mapping[key]
         roles_required = self.operation_to_allowed_roles[operation]
 
         operation = self.resources_to_operations_mapping[key]
         roles_required = self.operation_to_allowed_roles[operation]
-        roles_allowed = self.backend.get_user_role_list(session["_id"])
+        roles_allowed = [role["name"] for role in session["roles"]]
+
+        # fills session["admin"] if some roles allows it
+        session["admin"] = False
+        for role in roles_allowed:
+            if role in self.operation_to_allowed_roles["admin"]:
+                session["admin"] = True
+                break
 
         if "anonymous" in roles_required:
             return
 
         if "anonymous" in roles_required:
             return
@@ -421,7 +426,7 @@ class Authenticator:
             if role in roles_required:
                 return
 
             if role in roles_required:
                 return
 
-        raise AuthException("Access denied: lack of permissions.")
+        raise AuthExceptionUnauthorized("Access denied: lack of permissions.")
 
     def get_user_list(self):
         return self.backend.get_user_list()
 
     def get_user_list(self):
         return self.backend.get_user_list()