bug 739 reload roles info when there is a change at roles 68/7668/1
authortierno <alfonso.tiernosepulveda@telefonica.com>
Fri, 14 Jun 2019 09:45:39 +0000 (09:45 +0000)
committertierno <alfonso.tiernosepulveda@telefonica.com>
Fri, 14 Jun 2019 09:52:05 +0000 (09:52 +0000)
Adding 'admin' operation to track query string ADMIN

Change-Id: I2af27018643fa5e84dce3c606249d66893178a82
Signed-off-by: tierno <alfonso.tiernosepulveda@telefonica.com>
osm_nbi/auth.py
osm_nbi/authconn_keystone.py
osm_nbi/nbi.py
osm_nbi/resources_to_operations.yml

index e9a6b4e..6c44499 100644 (file)
@@ -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
+        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")
 
-        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()
@@ -253,8 +254,7 @@ class Authenticator:
             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
@@ -293,11 +293,12 @@ class Authenticator:
                     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,
+                    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)
-                    # TODO: check if this can be avoided. Backend may provide enough information
-                    return deepcopy(self.tokens_cache[token])
+                    return token_info
                 except AuthException:
                     self.del_token(token)
                     raise
@@ -412,7 +413,14 @@ class Authenticator:
 
         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
index 23b07e3..9fff792 100644 (file)
@@ -188,16 +188,30 @@ class AuthconnKeystone(Authconn):
         Check if the token is valid.
 
         :param token: token to validate
-        :return: dictionary with information associated with the token. If the
-        token is not valid, returns None.
+        :return: dictionary with information associated with the token:
+             "expires":
+             "_id": token_id,
+             "project_id": project_id,
+             "username": ,
+             "roles": list with dict containing {name, id}
+         If the token is not valid an exception is raised.
         """
         if not token:
             return
 
         try:
             token_info = self.keystone.tokens.validate(token=token)
+            ses = {
+                "_id": token_info["auth_token"],
+                "project_id": token_info["project"]["id"],
+                "project_name": token_info["project"]["name"],
+                "user_id": token_info["user"]["id"],
+                "username": token_info["user"]["name"],
+                "roles": token_info["roles"],
+                "expires": token_info.expires.timestamp()
+            }
 
-            return token_info
+            return ses
         except ClientException as e:
             self.logger.exception("Error during token validation using keystone: {}".format(e))
             raise AuthException("Error during token validation using Keystone: {}".format(e),
index 9d22df7..afc65c0 100644 (file)
@@ -955,6 +955,10 @@ class Server(object):
                 cherrypy.response.status = HTTPStatus.NO_CONTENT.value
             else:
                 raise NbiException("Method {} not allowed".format(method), HTTPStatus.METHOD_NOT_ALLOWED)
+
+            # if Role information changes, it is needed to reload the information of roles
+            if topic == "roles" and method != "GET":
+                self.authenticator.load_operation_to_allowed_roles()
             return self._format_out(outdata, session, _format)
         except Exception as e:
             if isinstance(e, (NbiException, EngineException, DbException, FsException, MsgException, AuthException,
index fa5f1a3..ff659d4 100644 (file)
@@ -358,3 +358,9 @@ resources_to_operations:
   "GET /nsilcm/v1/netslice_instances/<SliceInstanceId>/nsi_lcm_op_occs": "slice_instances:id:opps:get"
 
   "GET /nsilcm/v1/netslice_instances/<SliceInstanceId>/nsi_lcm_op_occs/<nsiLcmOpOccId>": "slice_instances:id:opps:id:get"
+
+################################################################################
+############################ Admin          ####################################
+################################################################################
+
+  "GET ADMIN": "admin"