fix bug 823: make user-show backward compatible providing 'projects' with the list... 51/7851/2
authortierno <alfonso.tiernosepulveda@telefonica.com>
Tue, 20 Aug 2019 15:38:11 +0000 (15:38 +0000)
committertierno <alfonso.tiernosepulveda@telefonica.com>
Wed, 21 Aug 2019 08:06:07 +0000 (08:06 +0000)
fix returned format of authconn get_user_list
Allow partially the old format for adding/removing projects to an user with the array edition

Change-Id: If8741fd8c73fd16222b1236d30fb92f70b673d49
Signed-off-by: tierno <alfonso.tiernosepulveda@telefonica.com>
osm_nbi/admin_topics.py
osm_nbi/auth.py
osm_nbi/authconn_internal.py
osm_nbi/authconn_keystone.py
osm_nbi/tests/clear-all.sh
osm_nbi/tests/test.py

index efcb0f1..7d1e85d 100644 (file)
@@ -547,7 +547,7 @@ class UserTopicAuth(UserTopic):
         users = self.auth.get_user_list(filter_q)
 
         if len(users) == 1:
-            return self.format_on_show(users[0])
+            return users[0]
         elif len(users) > 1:
             raise EngineException("Too many users found", HTTPStatus.CONFLICT)
         else:
@@ -596,6 +596,16 @@ class UserTopicAuth(UserTopic):
                 rid = role[0]["_id"]
                 if "add_project_role_mappings" not in indata:
                     indata["add_project_role_mappings"] = []
+                if "remove_project_role_mappings" not in indata:
+                    indata["remove_project_role_mappings"] = []
+                if isinstance(indata.get("projects"), dict):
+                    # backward compatible
+                    for k, v in indata["projects"].items():
+                        if k.startswith("$") and v is None:
+                            indata["remove_project_role_mappings"].append({"project": k[1:]})
+                        elif k.startswith("$+"):
+                            indata["add_project_role_mappings"].append({"project": v, "role": rid})
+                    del indata["projects"]
                 for proj in indata.get("projects", []) + indata.get("add_projects", []):
                     indata["add_project_role_mappings"].append({"project": proj, "role": rid})
 
@@ -665,7 +675,7 @@ class UserTopicAuth(UserTopic):
         :param filter_q: filter of data to be applied
         :return: The list, it can be empty if no one match the filter.
         """
-        users = [self.format_on_show(user) for user in self.auth.get_user_list(filter_q)]
+        users = self.auth.get_user_list(filter_q)
 
         return users
 
@@ -766,9 +776,10 @@ class ProjectTopicAuth(ProjectTopic):
         # If any user is using this project, raise CONFLICT exception
         if not session["force"]:
             for user in self.auth.get_user_list():
-                if _id in [proj["_id"] for proj in user.get("projects", [])]:
-                    raise EngineException("Project '{}' ({}) is being used by user '{}'"
-                                          .format(db_content["name"], _id, user["username"]), HTTPStatus.CONFLICT)
+                for prm in user.get("project_role_mappings"):
+                    if prm["project"] == _id:
+                        raise EngineException("Project '{}' ({}) is being used by user '{}'"
+                                              .format(db_content["name"], _id, user["username"]), HTTPStatus.CONFLICT)
 
         # If any VNFD, NSD, NST, PDU, etc. is using this project, raise CONFLICT exception
         if not session["force"]:
@@ -1000,9 +1011,10 @@ class RoleTopicAuth(BaseTopic):
 
         # If any user is using this role, raise CONFLICT exception
         for user in self.auth.get_user_list():
-            if _id in [prl["_id"] for proj in user.get("projects", []) for prl in proj.get("roles", [])]:
-                raise EngineException("Role '{}' ({}) is being used by user '{}'"
-                                      .format(role["name"], _id, user["username"]), HTTPStatus.CONFLICT)
+            for prm in user.get("project_role_mappings"):
+                if prm["role"] == _id:
+                    raise EngineException("Role '{}' ({}) is being used by user '{}'"
+                                          .format(role["name"], _id, user["username"]), HTTPStatus.CONFLICT)
 
     @staticmethod
     def format_on_new(content, project_id=None, make_public=False):   # TO BE REMOVED ?
index 94eb1e9..fde7455 100644 (file)
@@ -259,6 +259,8 @@ class Authenticator:
         pid = self.create_admin_project()
         self.create_admin_user(pid)
 
+        # self.backend.update_user({"_id": "admin",
+        #                           "add_project_role_mappings": {"project": "admin", "role": "system_admin"}})
         if self.config["authentication"]["backend"] == "keystone":
             try:
                 self.backend.assign_role_to_user("admin", "admin", "system_admin")
index 02b5890..40d3215 100644 (file)
@@ -348,31 +348,47 @@ class AuthconnInternal(Authconn):
             filt["username"] = filt["name"]
             del filt["name"]
         users = self.db.get_list("users", filt)
+        project_id_name = {}
+        role_id_name = {}
         for user in users:
-            projects = []
-            projs_with_roles = []
-            prms = user.get("project_role_mappings", [])
-            for prm in prms:
-                if prm["project"] not in projects:
-                    projects.append(prm["project"])
-            for project in projects:
-                roles = []
-                roles_for_proj = []
+            prms = user.get("project_role_mappings")
+            projects = user.get("projects")
+            if prms:
+                projects = []
+                # add project_name and role_name. Generate projects for backward compatibility
                 for prm in prms:
-                    if prm["project"] == project and prm["role"] not in roles:
-                        role = prm["role"]
-                        roles.append(role)
-                        rl = self.db.get_one("roles", {BaseTopic.id_field("roles", role): role})
-                        roles_for_proj.append({"name": rl["name"], "_id": rl["_id"], "id": rl["_id"]})
-                try:
-                    pr = self.db.get_one("projects", {BaseTopic.id_field("projects", project): project})
-                    projs_with_roles.append({"name": pr["name"], "_id": pr["_id"], "id": pr["_id"],
-                                             "roles": roles_for_proj})
-                except Exception as e:
-                    self.logger.exception("Error during user listing using internal backend: {}".format(e))
-            user["projects"] = projs_with_roles
-            if "project_role_mappings" in user:
-                del user["project_role_mappings"]
+                    project_id = prm["project"]
+                    if project_id not in project_id_name:
+                        pr = self.db.get_one("projects", {BaseTopic.id_field("projects", project_id): project_id},
+                                             fail_on_empty=False)
+                        project_id_name[project_id] = pr["name"] if pr else None
+                    prm["project_name"] = project_id_name[project_id]
+                    if prm["project_name"] not in projects:
+                        projects.append(prm["project_name"])
+
+                    role_id = prm["role"]
+                    if role_id not in role_id_name:
+                        role = self.db.get_one("roles", {BaseTopic.id_field("roles", role_id): role_id},
+                                               fail_on_empty=False)
+                        role_id_name[role_id] = role["name"] if role else None
+                    prm["role_name"] = role_id_name[role_id]
+                user["projects"] = projects  # for backward compatibility
+            elif projects:
+                # user created with an old version. Create a project_role mapping with role project_admin
+                user["project_role_mappings"] = []
+                role = self.db.get_one("roles", {BaseTopic.id_field("roles", "project_admin"): "project_admin"})
+                for p_id_name in projects:
+                    pr = self.db.get_one("projects", {BaseTopic.id_field("projects", p_id_name): p_id_name})
+                    prm = {"project": pr["_id"],
+                           "project_name": pr["name"],
+                           "role_name": "project_admin",
+                           "role": role["_id"]
+                           }
+                    user["project_role_mappings"].append(prm)
+            else:
+                user["projects"] = []
+                user["project_role_mappings"] = []
+
         return users
 
     def get_project_list(self, filter_q={}):
index 3aedfab..115de7c 100644 (file)
@@ -299,23 +299,21 @@ class AuthconnKeystone(Authconn):
                 users = [user for user in users if filter_q["_id"] == user["_id"]]
 
             for user in users:
+                user["project_role_mappings"] = []
+                user["projects"] = []
                 projects = self.keystone.projects.list(user=user["_id"])
-                projects = [{
-                    "name": project.name,
-                    "_id": project.id,
-                    "id": project.id
-                } for project in projects]
-
                 for project in projects:
-                    roles = self.keystone.roles.list(user=user["_id"], project=project["_id"])
-                    roles = [{
-                        "name": role.name,
-                        "_id": role.id,
-                        "id": role.id
-                    } for role in roles]
-                    project["roles"] = roles
-
-                user["projects"] = projects
+                    user["projects"].append(project.name)
+
+                    roles = self.keystone.roles.list(user=user["_id"], project=project.id)
+                    for role in roles:
+                        prm = {
+                            "project": project.id,
+                            "project_name": project.name,
+                            "role_name": role.name,
+                            "role": role.id,
+                        }
+                        user["project_role_mappings"].append(prm)
 
             return users
         except ClientException as e:
index 7e4e1d6..23a87ac 100755 (executable)
@@ -81,7 +81,7 @@ then
     done
 fi
 
-for item in vim_accounts wim_accounts sdns nsrs vnfrs nslcmops nsds vnfds projects pdus nsts nsis nsilcmops # vims
+for item in vim_accounts wim_accounts sdns nsrs vnfrs nslcmops nsds vnfds pdus nsts nsis nsilcmops # vims
 do
     curl --insecure ${OSMNBI_URL}/test/db-clear/${item}
 done
index 6c308aa..c70e384 100755 (executable)
@@ -620,8 +620,11 @@ class TestProjectsDescriptors:
                     (201, 204), {"Location": "/admin/v1/projects/", "Content-Type": "application/json"}, "json")
 
         engine.test("Create user U1", "POST", "/admin/v1/users", headers_json,
-                    {"username": "U1", "projects": ["Padmin", "P2", "P3"], "password": "pw1"}, 201,
-                    {"Location": "/admin/v1/users/", "Content-Type": "application/json"}, "json")
+                    {"username": "U1", "password": "pw1",
+                     "project_role_mappings": [{"project": "Padmin", "role": "system_admin"},
+                                               {"project": "P2", "role": "project_admin"},
+                                               {"project": "P3", "role": "project_admin"}],
+                     }, 201, {"Location": "/admin/v1/users/", "Content-Type": "application/json"}, "json")
 
         engine.test("Onboard VNFD id1", "POST", "/vnfpkgm/v1/vnf_packages_content?id=id1", headers_yaml,
                     TestDescriptors.vnfd_empty, 201, r_headers_yaml_location_vnfd, "yaml")