X-Git-Url: https://osm.etsi.org/gitweb/?p=osm%2FNBI.git;a=blobdiff_plain;f=osm_nbi%2Fauth.py;h=bb65738cf4a3688b05540cbf246da173d44df1ad;hp=751dd90e5cca6a2aace81e66373470f28a061c91;hb=f269fa5cd7c46515aab49ad8af6da7e918afa592;hpb=5c01e193a17fcf730406e39fe7d019e1dee5c64d diff --git a/osm_nbi/auth.py b/osm_nbi/auth.py index 751dd90..bb65738 100644 --- a/osm_nbi/auth.py +++ b/osm_nbi/auth.py @@ -40,6 +40,7 @@ from http import HTTPStatus from random import choice as random_choice from time import time from os import path +from base_topic import BaseTopic # To allow project names in project_id from authconn import AuthException from authconn_keystone import AuthconnKeystone @@ -54,6 +55,7 @@ class Authenticator: Authorization. Initially it should support Openstack Keystone as a backend through a plugin model where more backends can be added and a RBAC model to manage permissions on operations. + This class must be threading safe """ periodin_db_pruning = 60 * 30 # for the internal backend only. every 30 minutes expired tokens will be pruned @@ -154,7 +156,7 @@ class Authenticator: # Note: it is faster to rewrite the value than to check if it is already there or not if self.config["authentication"]["backend"] == "internal": return - + operations = [] with open(self.resources_to_operations_file, "r") as stream: resources_to_operations_yaml = yaml.load(stream) @@ -229,7 +231,12 @@ class Authenticator: if self.config["authentication"]["backend"] != "internal" and \ role_with_operations["role"] != "anonymous": - keystone_id = self.backend.create_role(role_with_operations["role"]) + keystone_id = [role for role in self.backend.get_role_list() + if role["name"] == role_with_operations["role"]] + if keystone_id: + keystone_id = keystone_id[0] + else: + keystone_id = self.backend.create_role(role_with_operations["role"]) operation_to_roles_item["_id"] = keystone_id["_id"] self.db.create("roles_operations", operation_to_roles_item) @@ -479,7 +486,8 @@ class Authenticator: now = time() session = self.tokens_cache.get(token_id) if session and session["expires"] < now: - del self.tokens_cache[token_id] + # delete token. MUST be done with care, as another thread maybe already delete it. Do not use del + self.tokens_cache.pop(token_id, None) session = None if session: return session @@ -500,7 +508,7 @@ class Authenticator: 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"]} + "username": self.config["global"]["test.user_not_authorized"], "admin": True} else: raise @@ -531,17 +539,21 @@ class Authenticator: 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) + project_id = indata.get("project_id") + if project_id: + if project_id != "admin": + # To allow project names in project_id + proj = self.db.get_one("projects", {BaseTopic.id_field("projects", project_id): project_id}) + if proj["_id"] not in user_content["projects"] and proj["name"] 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}) + # To allow project names in project_id + project = self.db.get_one("projects", {BaseTopic.id_field("projects", project_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"],