X-Git-Url: https://osm.etsi.org/gitweb/?p=osm%2FNBI.git;a=blobdiff_plain;f=osm_nbi%2Fnbi.py;h=28fee6a217b61ac3febbd3980cc92f3a50447ede;hp=a0229c8aee7fe0587172fdb0d92a2f59a1dd25e6;hb=refs%2Fchanges%2F56%2F6356%2F8;hpb=7ae1011101bd2c136680170c7d8f05717f204c8d diff --git a/osm_nbi/nbi.py b/osm_nbi/nbi.py index a0229c8..28fee6a 100644 --- a/osm_nbi/nbi.py +++ b/osm_nbi/nbi.py @@ -10,11 +10,13 @@ import logging import logging.handlers import getopt import sys + +from authconn import AuthException +from auth import Authenticator from engine import Engine, EngineException from osm_common.dbbase import DbException from osm_common.fsbase import FsException from osm_common.msgbase import MsgException -from base64 import standard_b64decode from http import HTTPStatus from codecs import getreader from os import environ, path @@ -25,11 +27,12 @@ __author__ = "Alfonso Tierno " __version__ = "0.1.3" version_date = "Apr 2018" database_version = '1.0' +auth_database_version = '1.0' """ North Bound Interface (O: OSM specific; 5,X: SOL005 not implemented yet; O5: SOL005 implemented) URL: /osm GET POST PUT DELETE PATCH - /nsd/v1 O O + /nsd/v1 /ns_descriptors_content O O / O O O O /ns_descriptors O5 O5 @@ -59,7 +62,7 @@ URL: /osm GET POST /ns_instances_content O O / O O /ns_instances 5 5 - / 5 5 + / O5 O5 instantiate O5 terminate O5 action O @@ -68,15 +71,20 @@ URL: /osm GET POST /ns_lcm_op_occs 5 5 / 5 5 5 TO BE COMPLETED 5 5 - /vnfrs O - / O + /vnf_instances (also vnfrs for compatibility) O + / O /subscriptions 5 5 / 5 X + + /pdu/v1 + /pdu_descriptor O O + / O O O O + /admin/v1 /tokens O O / O O /users O O - / O O + / O O O O /projects O O / O O /vims_accounts (also vims for compatibility) O O @@ -84,6 +92,30 @@ URL: /osm GET POST /sdns O O / O O O + /nst/v1 O O + /netslice_templates_content O O + / O O O O + /netslice_templates O O + / O O O + /nst_content O O + /nst O + /artifacts[/] O + /subscriptions X X + / X X + + /nsilcm/v1 + /netslice_instances_content O O + / O O + /netslice_instances O O + / O O + instantiate O + terminate O + action O + /nsi_lcm_op_occs O O + / O O O + /subscriptions X X + / X X + query string: Follows SOL005 section 4.3.2 It contains extra METHOD to override http method, FORCE to force. For filtering inside array, it must select the element of the array, or add ANYINDEX to apply the filtering over any @@ -145,6 +177,7 @@ class Server(object): def __init__(self): self.instance += 1 self.engine = Engine() + self.authenticator = Authenticator() self.valid_methods = { # contains allowed URL and methods "admin": { "v1": { @@ -152,7 +185,7 @@ class Server(object): "": {"METHODS": ("GET", "DELETE")} }, "users": {"METHODS": ("GET", "POST"), - "": {"METHODS": ("GET", "POST", "DELETE")} + "": {"METHODS": ("GET", "POST", "DELETE", "PATCH", "PUT")} }, "projects": {"METHODS": ("GET", "POST"), "": {"METHODS": ("GET", "DELETE")} @@ -168,6 +201,13 @@ class Server(object): }, } }, + "pdu": { + "v1": { + "pdu_descriptors": {"METHODS": ("GET", "POST"), + "": {"METHODS": ("GET", "POST", "DELETE", "PATCH", "PUT")} + }, + } + }, "nsd": { "v1": { "ns_descriptors_content": {"METHODS": ("GET", "POST"), @@ -196,7 +236,7 @@ class Server(object): "": {"METHODS": ("GET", "PUT", "DELETE")} }, "vnf_packages": {"METHODS": ("GET", "POST"), - "": {"METHODS": ("GET", "DELETE"), "TODO": "PATCH", # GET: vnfPkgInfo + "": {"METHODS": ("GET", "DELETE", "PATCH"), # GET: vnfPkgInfo "package_content": {"METHODS": ("GET", "PUT"), # package "upload_from_uri": {"TODO": "POST"} }, @@ -215,8 +255,8 @@ class Server(object): "": {"METHODS": ("GET", "DELETE")} }, "ns_instances": {"METHODS": ("GET", "POST"), - "": {"TODO": ("GET", "DELETE"), - "scale": {"TODO": "POST"}, + "": {"METHODS": ("GET", "DELETE"), + "scale": {"METHODS": "POST"}, "terminate": {"METHODS": "POST"}, "instantiate": {"METHODS": "POST"}, "action": {"METHODS": "POST"}, @@ -228,52 +268,47 @@ class Server(object): "vnfrs": {"METHODS": ("GET"), "": {"METHODS": ("GET")} }, + "vnf_instances": {"METHODS": ("GET"), + "": {"METHODS": ("GET")} + }, + } + }, + "nst": { + "v1": { + "netslice_templates_content": {"METHODS": ("GET", "POST"), + "": {"METHODS": ("GET", "PUT", "DELETE")} + }, + "netslice_templates": {"METHODS": ("GET", "POST"), + "": {"METHODS": ("GET", "DELETE"), "TODO": "PATCH", + "nst_content": {"METHODS": ("GET", "PUT")}, + "nst": {"METHODS": "GET"}, # descriptor inside package + "artifacts": {"*": {"METHODS": "GET"}} + } + }, + "subscriptions": {"TODO": ("GET", "POST"), + "": {"TODO": ("GET", "DELETE")} + }, + } + }, + "nsilcm": { + "v1": { + "netslice_instances_content": {"METHODS": ("GET", "POST"), + "": {"METHODS": ("GET", "DELETE")} + }, + "netslice_instances": {"METHODS": ("GET", "POST"), + "": {"METHODS": ("GET", "DELETE"), + "terminate": {"METHODS": "POST"}, + "instantiate": {"METHODS": "POST"}, + "action": {"METHODS": "POST"}, + } + }, + "nsi_lcm_op_occs": {"METHODS": "GET", + "": {"METHODS": "GET"}, + }, } }, } - def _authorization(self): - token = None - user_passwd64 = None - try: - # 1. Get token Authorization bearer - auth = cherrypy.request.headers.get("Authorization") - if auth: - auth_list = auth.split(" ") - if auth_list[0].lower() == "bearer": - token = auth_list[-1] - elif auth_list[0].lower() == "basic": - user_passwd64 = auth_list[-1] - if not token: - if cherrypy.session.get("Authorization"): - # 2. Try using session before request a new token. If not, basic authentication will generate - token = cherrypy.session.get("Authorization") - if token == "logout": - token = None # force Unauthorized response to insert user pasword again - elif user_passwd64 and cherrypy.request.config.get("auth.allow_basic_authentication"): - # 3. Get new token from user password - user = None - passwd = None - try: - user_passwd = standard_b64decode(user_passwd64).decode() - user, _, passwd = user_passwd.partition(":") - except: - pass - outdata = self.engine.new_token(None, {"username": user, "password": passwd}) - token = outdata["id"] - cherrypy.session['Authorization'] = token - # 4. Get token from cookie - # if not token: - # auth_cookie = cherrypy.request.cookie.get("Authorization") - # if auth_cookie: - # token = auth_cookie.value - return self.engine.authorize(token) - except EngineException as e: - if cherrypy.session.get('Authorization'): - del cherrypy.session['Authorization'] - cherrypy.response.headers["WWW-Authenticate"] = 'Bearer realm="{}"'.format(e) - raise - def _format_in(self, kwargs): try: indata = None @@ -323,15 +358,15 @@ class Server(object): elif format_yaml: try: kwargs[k] = yaml.load(v) - except: + except Exception: pass elif k.endswith(".gt") or k.endswith(".lt") or k.endswith(".gte") or k.endswith(".lte"): try: kwargs[k] = int(v) - except: + except Exception: try: kwargs[k] = float(v) - except: + except Exception: pass elif v.find(",") > 0: kwargs[k] = v.split(",") @@ -342,7 +377,7 @@ class Server(object): elif format_yaml: try: v[index] = yaml.load(v[index]) - except: + except Exception: pass return indata @@ -400,7 +435,7 @@ class Server(object): session = None try: if cherrypy.request.method == "GET": - session = self._authorization() + session = self.authenticator.authorize() outdata = "Index page" else: raise cherrypy.HTTPError(HTTPStatus.METHOD_NOT_ALLOWED.value, @@ -408,7 +443,7 @@ class Server(object): return self._format_out(outdata, session) - except EngineException as e: + except (EngineException, AuthException) as e: cherrypy.log("index Exception {}".format(e)) cherrypy.response.status = e.http_code.value return self._format_out("Welcome to OSM!", session) @@ -441,19 +476,19 @@ class Server(object): raise NbiException("Expected application/yaml or application/json Content-Type", HTTPStatus.BAD_REQUEST) try: if method == "GET": - session = self._authorization() + session = self.authenticator.authorize() if token_id: - outdata = self.engine.get_token(session, token_id) + outdata = self.authenticator.get_token(session, token_id) else: - outdata = self.engine.get_token_list(session) + outdata = self.authenticator.get_token_list(session) elif method == "POST": try: - session = self._authorization() - except: + session = self.authenticator.authorize() + except Exception: session = None if kwargs: indata.update(kwargs) - outdata = self.engine.new_token(session, indata, cherrypy.request.remote) + outdata = self.authenticator.new_token(session, indata, cherrypy.request.remote) session = outdata cherrypy.session['Authorization'] = outdata["_id"] self._set_location_header("admin", "v1", "tokens", outdata["_id"]) @@ -463,9 +498,9 @@ class Server(object): if not token_id and "id" in kwargs: token_id = kwargs["id"] elif not token_id: - session = self._authorization() + session = self.authenticator.authorize() token_id = session["_id"] - outdata = self.engine.del_token(token_id) + outdata = self.authenticator.del_token(token_id) session = None cherrypy.session['Authorization'] = "logout" # cherrypy.response.cookie["Authorization"] = token_id @@ -473,7 +508,7 @@ class Server(object): else: raise NbiException("Method {} not allowed for token".format(method), HTTPStatus.METHOD_NOT_ALLOWED) return self._format_out(outdata, session) - except (NbiException, EngineException, DbException) as e: + except (NbiException, EngineException, DbException, AuthException) as e: cherrypy.log("tokens Exception {}".format(e)) cherrypy.response.status = e.http_code.value problem_details = { @@ -488,7 +523,7 @@ class Server(object): thread_info = None if args and args[0] == "help": return "
\ninit\nfile/  download file\ndb-clear/table\nprune\nlogin\nlogin2\n"\
-                    "sleep/
" + "sleep/