From 1546f2a46d99a4741b23857e6ceb4b813223e297 Mon Sep 17 00:00:00 2001 From: tierno Date: Tue, 20 Aug 2019 15:38:11 +0000 Subject: [PATCH] fix bug 823: make user-show backward compatible providing 'projects' with the list of project names 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 --- osm_nbi/admin_topics.py | 28 +++++++++++----- osm_nbi/auth.py | 2 ++ osm_nbi/authconn_internal.py | 62 +++++++++++++++++++++++------------- osm_nbi/authconn_keystone.py | 28 ++++++++-------- osm_nbi/tests/clear-all.sh | 2 +- osm_nbi/tests/test.py | 7 ++-- 6 files changed, 80 insertions(+), 49 deletions(-) diff --git a/osm_nbi/admin_topics.py b/osm_nbi/admin_topics.py index efcb0f1..7d1e85d 100644 --- a/osm_nbi/admin_topics.py +++ b/osm_nbi/admin_topics.py @@ -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 ? diff --git a/osm_nbi/auth.py b/osm_nbi/auth.py index 94eb1e9..fde7455 100644 --- a/osm_nbi/auth.py +++ b/osm_nbi/auth.py @@ -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") diff --git a/osm_nbi/authconn_internal.py b/osm_nbi/authconn_internal.py index 02b5890..40d3215 100644 --- a/osm_nbi/authconn_internal.py +++ b/osm_nbi/authconn_internal.py @@ -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={}): diff --git a/osm_nbi/authconn_keystone.py b/osm_nbi/authconn_keystone.py index 3aedfab..115de7c 100644 --- a/osm_nbi/authconn_keystone.py +++ b/osm_nbi/authconn_keystone.py @@ -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: diff --git a/osm_nbi/tests/clear-all.sh b/osm_nbi/tests/clear-all.sh index 7e4e1d6..23a87ac 100755 --- a/osm_nbi/tests/clear-all.sh +++ b/osm_nbi/tests/clear-all.sh @@ -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 diff --git a/osm_nbi/tests/test.py b/osm_nbi/tests/test.py index 6c308aa..c70e384 100755 --- a/osm_nbi/tests/test.py +++ b/osm_nbi/tests/test.py @@ -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") -- 2.25.1