fix bug 823: make user-show backward compatible providing 'projects' with the list...
[osm/NBI.git] / osm_nbi / nbi.py
index 705979b..33147b7 100644 (file)
@@ -24,7 +24,7 @@ import logging.handlers
 import getopt
 import sys
 
-from authconn import AuthException
+from authconn import AuthException, AuthconnException
 from auth import Authenticator
 from engine import Engine, EngineException
 from subscriptions import SubscriptionThread
@@ -40,7 +40,7 @@ __author__ = "Alfonso Tierno <alfonso.tiernosepulveda@telefonica.com>"
 
 __version__ = "0.1.3"
 version_date = "Jan 2019"
-database_version = '1.0'
+database_version = '1.2'
 auth_database_version = '1.0'
 nbi_server = None           # instance of Server class
 subscription_thread = None  # instance of SubscriptionThread class
@@ -158,6 +158,12 @@ query string:
         exclude_default and include=<list>     … all attributes except those complex attributes with a minimum cardinality
         of zero that are not conditionally mandatory and that are part of the "default exclude set" defined in the
         present specification for the particular resource, but that are not part of <list>
+    Additionally it admits some administrator values:
+        FORCE: To force operations skipping dependency checkings
+        ADMIN: To act as an administrator or a different project
+        PUBLIC: To get public descriptors or set a descriptor as public
+        SET_PROJECT: To make a descriptor available for other project
+        
 Header field name      Reference       Example Descriptions
     Accept     IETF RFC 7231 [19]      application/json        Content-Types that are acceptable for the response.
     This header field shall be present if the response is expected to have a non-empty message body.
@@ -184,6 +190,264 @@ Header field name Reference       Example Descriptions
     Retry-After        IETF RFC 7231 [19]      Fri, 31 Dec 1999 23:59:59 GMT
 """
 
+valid_query_string = ("ADMIN", "SET_PROJECT", "FORCE", "PUBLIC")
+# ^ Contains possible administrative query string words:
+#     ADMIN=True(by default)|Project|Project-list:  See all elements, or elements of a project
+#           (not owned by my session project).
+#     PUBLIC=True(by default)|False: See/hide public elements. Set/Unset a topic to be public
+#     FORCE=True(by default)|False: Force edition/deletion operations
+#     SET_PROJECT=Project|Project-list: Add/Delete the topic to the projects portfolio
+
+valid_url_methods = {
+    # contains allowed URL and methods, and the role_permission name
+    "admin": {
+        "v1": {
+            "tokens": {"METHODS": ("GET", "POST", "DELETE"),
+                       "ROLE_PERMISSION": "tokens:",
+                       "<ID>": {"METHODS": ("GET", "DELETE"),
+                                "ROLE_PERMISSION": "tokens:id:"
+                                }
+                       },
+            "users": {"METHODS": ("GET", "POST"),
+                      "ROLE_PERMISSION": "users:",
+                      "<ID>": {"METHODS": ("GET", "POST", "DELETE", "PATCH", "PUT"),
+                               "ROLE_PERMISSION": "users:id:"
+                               }
+                      },
+            "projects": {"METHODS": ("GET", "POST"),
+                         "ROLE_PERMISSION": "projects:",
+                         "<ID>": {"METHODS": ("GET", "DELETE", "PUT"),
+                                  "ROLE_PERMISSION": "projects:id:"}
+                         },
+            "roles": {"METHODS": ("GET", "POST"),
+                      "ROLE_PERMISSION": "roles:",
+                      "<ID>": {"METHODS": ("GET", "POST", "DELETE", "PUT"),
+                               "ROLE_PERMISSION": "roles:id:"
+                               }
+                      },
+            "vims": {"METHODS": ("GET", "POST"),
+                     "ROLE_PERMISSION": "vims:",
+                     "<ID>": {"METHODS": ("GET", "DELETE", "PATCH", "PUT"),
+                              "ROLE_PERMISSION": "vims:id:"
+                              }
+                     },
+            "vim_accounts": {"METHODS": ("GET", "POST"),
+                             "ROLE_PERMISSION": "vim_accounts:",
+                             "<ID>": {"METHODS": ("GET", "DELETE", "PATCH", "PUT"),
+                                      "ROLE_PERMISSION": "vim_accounts:id:"
+                                      }
+                             },
+            "wim_accounts": {"METHODS": ("GET", "POST"),
+                             "ROLE_PERMISSION": "wim_accounts:",
+                             "<ID>": {"METHODS": ("GET", "DELETE", "PATCH", "PUT"),
+                                      "ROLE_PERMISSION": "wim_accounts:id:"
+                                      }
+                             },
+            "sdns": {"METHODS": ("GET", "POST"),
+                     "ROLE_PERMISSION": "sdn_controllers:",
+                     "<ID>": {"METHODS": ("GET", "DELETE", "PATCH", "PUT"),
+                              "ROLE_PERMISSION": "sdn_controllers:id:"
+                              }
+                     },
+        }
+    },
+    "pdu": {
+        "v1": {
+            "pdu_descriptors": {"METHODS": ("GET", "POST"),
+                                "ROLE_PERMISSION": "pduds:",
+                                "<ID>": {"METHODS": ("GET", "POST", "DELETE", "PATCH", "PUT"),
+                                         "ROLE_PERMISSION": "pduds:id:"
+                                         }
+                                },
+        }
+    },
+    "nsd": {
+        "v1": {
+            "ns_descriptors_content": {"METHODS": ("GET", "POST"),
+                                       "ROLE_PERMISSION": "nsds:",
+                                       "<ID>": {"METHODS": ("GET", "PUT", "DELETE"),
+                                                "ROLE_PERMISSION": "nsds:id:"
+                                                }
+                                       },
+            "ns_descriptors": {"METHODS": ("GET", "POST"),
+                               "ROLE_PERMISSION": "nsds:",
+                               "<ID>": {"METHODS": ("GET", "DELETE", "PATCH"),
+                                        "ROLE_PERMISSION": "nsds:id:",
+                                        "nsd_content": {"METHODS": ("GET", "PUT"),
+                                                        "ROLE_PERMISSION": "nsds:id:content:",
+                                                        },
+                                        "nsd": {"METHODS": ("GET",),  # descriptor inside package
+                                                "ROLE_PERMISSION": "nsds:id:content:"
+                                                },
+                                        "artifacts": {"*": {"METHODS": ("GET",),
+                                                            "ROLE_PERMISSION": "nsds:id:nsd_artifact:"
+                                                            }
+                                                      }
+                                        }
+                               },
+            "pnf_descriptors": {"TODO": ("GET", "POST"),
+                                "<ID>": {"TODO": ("GET", "DELETE", "PATCH"),
+                                         "pnfd_content": {"TODO": ("GET", "PUT")}
+                                         }
+                                },
+            "subscriptions": {"TODO": ("GET", "POST"),
+                              "<ID>": {"TODO": ("GET", "DELETE")}
+                              },
+        }
+    },
+    "vnfpkgm": {
+        "v1": {
+            "vnf_packages_content": {"METHODS": ("GET", "POST"),
+                                     "ROLE_PERMISSION": "vnfds:",
+                                     "<ID>": {"METHODS": ("GET", "PUT", "DELETE"),
+                                              "ROLE_PERMISSION": "vnfds:id:"}
+                                     },
+            "vnf_packages": {"METHODS": ("GET", "POST"),
+                             "ROLE_PERMISSION": "vnfds:",
+                             "<ID>": {"METHODS": ("GET", "DELETE", "PATCH"),  # GET: vnfPkgInfo
+                                      "ROLE_PERMISSION": "vnfds:id:",
+                                      "package_content": {"METHODS": ("GET", "PUT"),  # package
+                                                          "ROLE_PERMISSION": "vnfds:id:",
+                                                          "upload_from_uri": {"METHODS": (),
+                                                                              "TODO": ("POST", ),
+                                                                              "ROLE_PERMISSION": "vnfds:id:upload:"
+                                                                              }
+                                                          },
+                                      "vnfd": {"METHODS": ("GET", ),  # descriptor inside package
+                                               "ROLE_PERMISSION": "vnfds:id:content:"
+                                               },
+                                      "artifacts": {"*": {"METHODS": ("GET", ),
+                                                          "ROLE_PERMISSION": "vnfds:id:vnfd_artifact:"
+                                                          }
+                                                    }
+                                      }
+                             },
+            "subscriptions": {"TODO": ("GET", "POST"),
+                              "<ID>": {"TODO": ("GET", "DELETE")}
+                              },
+        }
+    },
+    "nslcm": {
+        "v1": {
+            "ns_instances_content": {"METHODS": ("GET", "POST"),
+                                     "ROLE_PERMISSION": "ns_instances:",
+                                     "<ID>": {"METHODS": ("GET", "DELETE"),
+                                              "ROLE_PERMISSION": "ns_instances:id:"
+                                              }
+                                     },
+            "ns_instances": {"METHODS": ("GET", "POST"),
+                             "ROLE_PERMISSION": "ns_instances:",
+                             "<ID>": {"METHODS": ("GET", "DELETE"),
+                                      "ROLE_PERMISSION": "ns_instances:id:",
+                                      "scale": {"METHODS": ("POST",),
+                                                "ROLE_PERMISSION": "ns_instances:id:scale:"
+                                                },
+                                      "terminate": {"METHODS": ("POST",),
+                                                    "ROLE_PERMISSION": "ns_instances:id:terminate:"
+                                                    },
+                                      "instantiate": {"METHODS": ("POST",),
+                                                      "ROLE_PERMISSION": "ns_instances:id:instantiate:"
+                                                      },
+                                      "action": {"METHODS": ("POST",),
+                                                 "ROLE_PERMISSION": "ns_instances:id:action:"
+                                                 },
+                                      }
+                             },
+            "ns_lcm_op_occs": {"METHODS": ("GET",),
+                               "ROLE_PERMISSION": "ns_instances:opps:",
+                               "<ID>": {"METHODS": ("GET",),
+                                        "ROLE_PERMISSION": "ns_instances:opps:id:"
+                                        },
+                               },
+            "vnfrs": {"METHODS": ("GET",),
+                      "ROLE_PERMISSION": "vnf_instances:",
+                      "<ID>": {"METHODS": ("GET",),
+                               "ROLE_PERMISSION": "vnf_instances:id:"
+                               }
+                      },
+            "vnf_instances": {"METHODS": ("GET",),
+                              "ROLE_PERMISSION": "vnf_instances:",
+                              "<ID>": {"METHODS": ("GET",),
+                                       "ROLE_PERMISSION": "vnf_instances:id:"
+                                       }
+                              },
+        }
+    },
+    "nst": {
+        "v1": {
+            "netslice_templates_content": {"METHODS": ("GET", "POST"),
+                                           "ROLE_PERMISSION": "slice_templates:",
+                                           "<ID>": {"METHODS": ("GET", "PUT", "DELETE"),
+                                                    "ROLE_PERMISSION": "slice_templates:id:", }
+                                           },
+            "netslice_templates": {"METHODS": ("GET", "POST"),
+                                   "ROLE_PERMISSION": "slice_templates:",
+                                   "<ID>": {"METHODS": ("GET", "DELETE"),
+                                            "TODO": ("PATCH",),
+                                            "ROLE_PERMISSION": "slice_templates:id:",
+                                            "nst_content": {"METHODS": ("GET", "PUT"),
+                                                            "ROLE_PERMISSION": "slice_templates:id:content:"
+                                                            },
+                                            "nst": {"METHODS": ("GET",),  # descriptor inside package
+                                                    "ROLE_PERMISSION": "slice_templates:id:content:"
+                                                    },
+                                            "artifacts": {"*": {"METHODS": ("GET",),
+                                                                "ROLE_PERMISSION": "slice_templates:id:content:"
+                                                                }
+                                                          }
+                                            }
+                                   },
+            "subscriptions": {"TODO": ("GET", "POST"),
+                              "<ID>": {"TODO": ("GET", "DELETE")}
+                              },
+        }
+    },
+    "nsilcm": {
+        "v1": {
+            "netslice_instances_content": {"METHODS": ("GET", "POST"),
+                                           "ROLE_PERMISSION": "slice_instances:",
+                                           "<ID>": {"METHODS": ("GET", "DELETE"),
+                                                    "ROLE_PERMISSION": "slice_instances:id:"
+                                                    }
+                                           },
+            "netslice_instances": {"METHODS": ("GET", "POST"),
+                                   "ROLE_PERMISSION": "slice_instances:",
+                                   "<ID>": {"METHODS": ("GET", "DELETE"),
+                                            "ROLE_PERMISSION": "slice_instances:id:",
+                                            "terminate": {"METHODS": ("POST",),
+                                                          "ROLE_PERMISSION": "slice_instances:id:terminate:"
+                                                          },
+                                            "instantiate": {"METHODS": ("POST",),
+                                                            "ROLE_PERMISSION": "slice_instances:id:instantiate:"
+                                                            },
+                                            "action": {"METHODS": ("POST",),
+                                                       "ROLE_PERMISSION": "slice_instances:id:action:"
+                                                       },
+                                            }
+                                   },
+            "nsi_lcm_op_occs": {"METHODS": ("GET",),
+                                "ROLE_PERMISSION": "slice_instances:opps:",
+                                "<ID>": {"METHODS": ("GET",),
+                                         "ROLE_PERMISSION": "slice_instances:opps:id:",
+                                         },
+                                },
+        }
+    },
+    "nspm": {
+        "v1": {
+            "pm_jobs": {
+                "<ID>": {
+                    "reports": {
+                        "<ID>": {"METHODS": ("GET",),
+                                 "ROLE_PERMISSION": "reports:id:",
+                                 }
+                    }
+                },
+            },
+        },
+    },
+}
+
 
 class NbiException(Exception):
 
@@ -200,155 +464,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": {
-                    "tokens": {"METHODS": ("GET", "POST", "DELETE"),
-                               "<ID>": {"METHODS": ("GET", "DELETE")}
-                               },
-                    "users": {"METHODS": ("GET", "POST"),
-                              "<ID>": {"METHODS": ("GET", "POST", "DELETE", "PATCH", "PUT")}
-                              },
-                    "projects": {"METHODS": ("GET", "POST"),
-                                 # Added PUT to allow Project Name modification
-                                 "<ID>": {"METHODS": ("GET", "DELETE", "PUT")}
-                                 },
-                    "roles": {"METHODS": ("GET", "POST"),
-                              "<ID>": {"METHODS": ("GET", "POST", "DELETE")}
-                              },
-                    "vims": {"METHODS": ("GET", "POST"),
-                             "<ID>": {"METHODS": ("GET", "DELETE", "PATCH", "PUT")}
-                             },
-                    "vim_accounts": {"METHODS": ("GET", "POST"),
-                                     "<ID>": {"METHODS": ("GET", "DELETE", "PATCH", "PUT")}
-                                     },
-                    "wim_accounts": {"METHODS": ("GET", "POST"),
-                                     "<ID>": {"METHODS": ("GET", "DELETE", "PATCH", "PUT")}
-                                     },
-                    "sdns": {"METHODS": ("GET", "POST"),
-                             "<ID>": {"METHODS": ("GET", "DELETE", "PATCH", "PUT")}
-                             },
-                }
-            },
-            "pdu": {
-                "v1": {
-                    "pdu_descriptors": {"METHODS": ("GET", "POST"),
-                                        "<ID>": {"METHODS": ("GET", "POST", "DELETE", "PATCH", "PUT")}
-                                        },
-                }
-            },
-            "nsd": {
-                "v1": {
-                    "ns_descriptors_content": {"METHODS": ("GET", "POST"),
-                                               "<ID>": {"METHODS": ("GET", "PUT", "DELETE")}
-                                               },
-                    "ns_descriptors": {"METHODS": ("GET", "POST"),
-                                       "<ID>": {"METHODS": ("GET", "DELETE", "PATCH"),
-                                                "nsd_content": {"METHODS": ("GET", "PUT")},
-                                                "nsd": {"METHODS": "GET"},  # descriptor inside package
-                                                "artifacts": {"*": {"METHODS": "GET"}}
-                                                }
-                                       },
-                    "pnf_descriptors": {"TODO": ("GET", "POST"),
-                                        "<ID>": {"TODO": ("GET", "DELETE", "PATCH"),
-                                                 "pnfd_content": {"TODO": ("GET", "PUT")}
-                                                 }
-                                        },
-                    "subscriptions": {"TODO": ("GET", "POST"),
-                                      "<ID>": {"TODO": ("GET", "DELETE")}
-                                      },
-                }
-            },
-            "vnfpkgm": {
-                "v1": {
-                    "vnf_packages_content": {"METHODS": ("GET", "POST"),
-                                             "<ID>": {"METHODS": ("GET", "PUT", "DELETE")}
-                                             },
-                    "vnf_packages": {"METHODS": ("GET", "POST"),
-                                     "<ID>": {"METHODS": ("GET", "DELETE", "PATCH"),  # GET: vnfPkgInfo
-                                              "package_content": {"METHODS": ("GET", "PUT"),         # package
-                                                                  "upload_from_uri": {"TODO": "POST"}
-                                                                  },
-                                              "vnfd": {"METHODS": "GET"},                    # descriptor inside package
-                                              "artifacts": {"*": {"METHODS": "GET"}}
-                                              }
-                                     },
-                    "subscriptions": {"TODO": ("GET", "POST"),
-                                      "<ID>": {"TODO": ("GET", "DELETE")}
-                                      },
-                }
-            },
-            "nslcm": {
-                "v1": {
-                    "ns_instances_content": {"METHODS": ("GET", "POST"),
-                                             "<ID>": {"METHODS": ("GET", "DELETE")}
-                                             },
-                    "ns_instances": {"METHODS": ("GET", "POST"),
-                                     "<ID>": {"METHODS": ("GET", "DELETE"),
-                                              "scale": {"METHODS": "POST"},
-                                              "terminate": {"METHODS": "POST"},
-                                              "instantiate": {"METHODS": "POST"},
-                                              "action": {"METHODS": "POST"},
-                                              }
-                                     },
-                    "ns_lcm_op_occs": {"METHODS": "GET",
-                                       "<ID>": {"METHODS": "GET"},
-                                       },
-                    "vnfrs": {"METHODS": ("GET"),
-                              "<ID>": {"METHODS": ("GET")}
-                              },
-                    "vnf_instances": {"METHODS": ("GET"),
-                                      "<ID>": {"METHODS": ("GET")}
-                                      },
-                }
-            },
-            "nst": {
-                "v1": {
-                    "netslice_templates_content": {"METHODS": ("GET", "POST"),
-                                                   "<ID>": {"METHODS": ("GET", "PUT", "DELETE")}
-                                                   },
-                    "netslice_templates": {"METHODS": ("GET", "POST"),
-                                           "<ID>": {"METHODS": ("GET", "DELETE"), "TODO": "PATCH",
-                                                    "nst_content": {"METHODS": ("GET", "PUT")},
-                                                    "nst": {"METHODS": "GET"},  # descriptor inside package
-                                                    "artifacts": {"*": {"METHODS": "GET"}}
-                                                    }
-                                           },
-                    "subscriptions": {"TODO": ("GET", "POST"),
-                                      "<ID>": {"TODO": ("GET", "DELETE")}
-                                      },
-                }
-            },
-            "nsilcm": {
-                "v1": {
-                    "netslice_instances_content": {"METHODS": ("GET", "POST"),
-                                                   "<ID>": {"METHODS": ("GET", "DELETE")}
-                                                   },
-                    "netslice_instances": {"METHODS": ("GET", "POST"),
-                                           "<ID>": {"METHODS": ("GET", "DELETE"),
-                                                    "terminate": {"METHODS": "POST"},
-                                                    "instantiate": {"METHODS": "POST"},
-                                                    "action": {"METHODS": "POST"},
-                                                    }
-                                           },
-                    "nsi_lcm_op_occs": {"METHODS": "GET",
-                                        "<ID>": {"METHODS": "GET"},
-                                        },
-                }
-            },
-            "nspm": {
-                "v1": {
-                    "pm_jobs": {
-                        "<ID>": {
-                            "reports": {
-                                "<ID>": {"METHODS": ("GET")}
-                            }
-                        },
-                    },
-                },
-            },
-        }
+        self.authenticator = Authenticator(valid_url_methods, valid_query_string)
 
     def _format_in(self, kwargs):
         try:
@@ -434,18 +550,18 @@ class Server(object):
             raise NbiException(error_text + str(exc), HTTPStatus.BAD_REQUEST)
 
     @staticmethod
-    def _format_out(data, session=None, _format=None):
+    def _format_out(data, token_info=None, _format=None):
         """
         return string of dictionary data according to requested json, yaml, xml. By default json
         :param data: response to be sent. Can be a dict, text or file
-        :param session:
+        :param token_info: Contains among other username and project
         :param _format: The format to be set as Content-Type ir data is a file
         :return: None
         """
         accept = cherrypy.request.headers.get("Accept")
         if data is None:
             if accept and "text/html" in accept:
-                return html.format(data, cherrypy.request, cherrypy.response, session)
+                return html.format(data, cherrypy.request, cherrypy.response, token_info)
             # cherrypy.response.status = HTTPStatus.NO_CONTENT.value
             return
         elif hasattr(data, "read"):  # file object
@@ -463,7 +579,7 @@ class Server(object):
                 a = json.dumps(data, indent=4) + "\n"
                 return a.encode("utf8")
             elif "text/html" in accept:
-                return html.format(data, cherrypy.request, cherrypy.response, session)
+                return html.format(data, cherrypy.request, cherrypy.response, token_info)
 
             elif "application/yaml" in accept or "*/*" in accept or "text/plain" in accept:
                 pass
@@ -478,21 +594,21 @@ class Server(object):
 
     @cherrypy.expose
     def index(self, *args, **kwargs):
-        session = None
+        token_info = None
         try:
             if cherrypy.request.method == "GET":
-                session = self.authenticator.authorize()
-                outdata = "Index page"
+                token_info = self.authenticator.authorize()
+                outdata = token_info   # Home page
             else:
                 raise cherrypy.HTTPError(HTTPStatus.METHOD_NOT_ALLOWED.value,
                                          "Method {} not allowed for tokens".format(cherrypy.request.method))
 
-            return self._format_out(outdata, session)
+            return self._format_out(outdata, token_info)
 
         except (EngineException, AuthException) as e:
-            cherrypy.log("index Exception {}".format(e))
+            cherrypy.log("index Exception {}".format(e))
             cherrypy.response.status = e.http_code.value
-            return self._format_out("Welcome to OSM!", session)
+            return self._format_out("Welcome to OSM!", token_info)
 
     @cherrypy.expose
     def version(self, *args, **kwargs):
@@ -513,56 +629,69 @@ class Server(object):
             }
             return self._format_out(problem_details, None)
 
+    @staticmethod
+    def _format_login(token_info):
+        """
+        Changes cherrypy.request.login to include username/project_name;session so that cherrypy access log will
+        log this information
+        :param token_info: Dictionary with token content
+        :return: None
+        """
+        cherrypy.request.login = token_info.get("username", "-")
+        if token_info.get("project_name"):
+            cherrypy.request.login += "/" + token_info["project_name"]
+        if token_info.get("id"):
+            cherrypy.request.login += ";session=" + token_info["id"][0:12]
+
     @cherrypy.expose
     def token(self, method, token_id=None, kwargs=None):
-        session = None
+        token_info = None
         # self.engine.load_dbase(cherrypy.request.app.config)
         indata = self._format_in(kwargs)
         if not isinstance(indata, dict):
             raise NbiException("Expected application/yaml or application/json Content-Type", HTTPStatus.BAD_REQUEST)
-        try:
-            if method == "GET":
-                session = self.authenticator.authorize()
-                if token_id:
-                    outdata = self.authenticator.get_token(session, token_id)
-                else:
-                    outdata = self.authenticator.get_token_list(session)
-            elif method == "POST":
-                try:
-                    session = self.authenticator.authorize()
-                except Exception:
-                    session = None
-                if kwargs:
-                    indata.update(kwargs)
-                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"])
-                # cherrypy.response.cookie["Authorization"] = outdata["id"]
-                # cherrypy.response.cookie["Authorization"]['expires'] = 3600
-            elif method == "DELETE":
-                if not token_id and "id" in kwargs:
-                    token_id = kwargs["id"]
-                elif not token_id:
-                    session = self.authenticator.authorize()
-                    token_id = session["_id"]
-                outdata = self.authenticator.del_token(token_id)
-                session = None
-                cherrypy.session['Authorization'] = "logout"
-                # cherrypy.response.cookie["Authorization"] = token_id
-                # cherrypy.response.cookie["Authorization"]['expires'] = 0
+
+        if method == "GET":
+            token_info = self.authenticator.authorize()
+            # for logging
+            self._format_login(token_info)
+            if token_id:
+                outdata = self.authenticator.get_token(token_info, token_id)
             else:
-                raise NbiException("Method {} not allowed for token".format(method), HTTPStatus.METHOD_NOT_ALLOWED)
-            return self._format_out(outdata, session)
-        except (NbiException, EngineException, DbException, AuthException) as e:
-            cherrypy.log("tokens Exception {}".format(e))
-            cherrypy.response.status = e.http_code.value
-            problem_details = {
-                "code": e.http_code.name,
-                "status": e.http_code.value,
-                "detail": str(e),
-            }
-            return self._format_out(problem_details, session)
+                outdata = self.authenticator.get_token_list(token_info)
+        elif method == "POST":
+            try:
+                token_info = self.authenticator.authorize()
+            except Exception:
+                token_info = None
+            if kwargs:
+                indata.update(kwargs)
+            # This is needed to log the user when authentication fails
+            cherrypy.request.login = "{}".format(indata.get("username", "-"))
+            outdata = token_info = self.authenticator.new_token(token_info, indata, cherrypy.request.remote)
+            cherrypy.session['Authorization'] = outdata["_id"]
+            self._set_location_header("admin", "v1", "tokens", outdata["_id"])
+            # for logging
+            self._format_login(token_info)
+
+            # cherrypy.response.cookie["Authorization"] = outdata["id"]
+            # cherrypy.response.cookie["Authorization"]['expires'] = 3600
+        elif method == "DELETE":
+            if not token_id and "id" in kwargs:
+                token_id = kwargs["id"]
+            elif not token_id:
+                token_info = self.authenticator.authorize()
+                # for logging
+                self._format_login(token_info)
+                token_id = token_info["_id"]
+            outdata = self.authenticator.del_token(token_id)
+            token_info = None
+            cherrypy.session['Authorization'] = "logout"
+            # cherrypy.response.cookie["Authorization"] = token_id
+            # cherrypy.response.cookie["Authorization"]['expires'] = 0
+        else:
+            raise NbiException("Method {} not allowed for token".format(method), HTTPStatus.METHOD_NOT_ALLOWED)
+        return self._format_out(outdata, token_info)
 
     @cherrypy.expose
     def test(self, *args, **kwargs):
@@ -656,11 +785,12 @@ class Server(object):
         return_text += "</pre></html>"
         return return_text
 
-    def _check_valid_url_method(self, method, *args):
+    @staticmethod
+    def _check_valid_url_method(method, *args):
         if len(args) < 3:
             raise NbiException("URL must contain at least 'main_topic/version/topic'", HTTPStatus.METHOD_NOT_ALLOWED)
 
-        reference = self.valid_methods
+        reference = valid_url_methods
         for arg in args:
             if arg is None:
                 break
@@ -681,7 +811,7 @@ class Server(object):
             raise NbiException("Method {} not supported yet for this URL".format(method), HTTPStatus.NOT_IMPLEMENTED)
         elif "METHODS" in reference and method not in reference["METHODS"]:
             raise NbiException("Method {} not supported for this URL".format(method), HTTPStatus.METHOD_NOT_ALLOWED)
-        return
+        return reference["ROLE_PERMISSION"] + method.lower()
 
     @staticmethod
     def _set_location_header(main_topic, version, topic, id):
@@ -697,15 +827,101 @@ class Server(object):
         cherrypy.response.headers["Location"] = "/osm/{}/{}/{}/{}".format(main_topic, version, topic, id)
         return
 
+    @staticmethod
+    def _extract_query_string_operations(kwargs, method):
+        """
+
+        :param kwargs:
+        :return:
+        """
+        query_string_operations = []
+        if kwargs:
+            for qs in ("FORCE", "PUBLIC", "ADMIN", "SET_PROJECT"):
+                if qs in kwargs and kwargs[qs].lower() != "false":
+                    query_string_operations.append(qs.lower() + ":" + method.lower())
+        return query_string_operations
+
+    @staticmethod
+    def _manage_admin_query(token_info, kwargs, method, _id):
+        """
+        Processes the administrator query inputs (if any) of FORCE, ADMIN, PUBLIC, SET_PROJECT
+        Check that users has rights to use them and returs the admin_query
+        :param token_info: token_info rights obtained by token
+        :param kwargs: query string input.
+        :param method: http method: GET, POSST, PUT, ...
+        :param _id:
+        :return: admin_query dictionary with keys:
+            public: True, False or None
+            force: True or False
+            project_id: tuple with projects used for accessing an element
+            set_project: tuple with projects that a created element will belong to
+            method: show, list, delete, write
+        """
+        admin_query = {"force": False, "project_id": (token_info["project_id"], ), "username": token_info["username"],
+                       "admin": token_info["admin"], "public": None}
+        if kwargs:
+            # FORCE
+            if "FORCE" in kwargs:
+                if kwargs["FORCE"].lower() != "false":  # if None or True set force to True
+                    admin_query["force"] = True
+                del kwargs["FORCE"]
+            # PUBLIC
+            if "PUBLIC" in kwargs:
+                if kwargs["PUBLIC"].lower() != "false":  # if None or True set public to True
+                    admin_query["public"] = True
+                else:
+                    admin_query["public"] = False
+                del kwargs["PUBLIC"]
+            # ADMIN
+            if "ADMIN" in kwargs:
+                behave_as = kwargs.pop("ADMIN")
+                if behave_as.lower() != "false":
+                    if not token_info["admin"]:
+                        raise NbiException("Only admin projects can use 'ADMIN' query string", HTTPStatus.UNAUTHORIZED)
+                    if not behave_as or behave_as.lower() == "true":  # convert True, None to empty list
+                        admin_query["project_id"] = ()
+                    elif isinstance(behave_as, (list, tuple)):
+                        admin_query["project_id"] = behave_as
+                    else:   # isinstance(behave_as, str)
+                        admin_query["project_id"] = (behave_as, )
+            if "SET_PROJECT" in kwargs:
+                set_project = kwargs.pop("SET_PROJECT")
+                if not set_project:
+                    admin_query["set_project"] = list(admin_query["project_id"])
+                else:
+                    if isinstance(set_project, str):
+                        set_project = (set_project, )
+                    if admin_query["project_id"]:
+                        for p in set_project:
+                            if p not in admin_query["project_id"]:
+                                raise NbiException("Unauthorized for 'SET_PROJECT={p}'. Try with 'ADMIN=True' or "
+                                                   "'ADMIN='{p}'".format(p=p), HTTPStatus.UNAUTHORIZED)
+                    admin_query["set_project"] = set_project
+
+            # PROJECT_READ
+            # if "PROJECT_READ" in kwargs:
+            #     admin_query["project"] = kwargs.pop("project")
+            #     if admin_query["project"] == token_info["project_id"]:
+        if method == "GET":
+            if _id:
+                admin_query["method"] = "show"
+            else:
+                admin_query["method"] = "list"
+        elif method == "DELETE":
+            admin_query["method"] = "delete"
+        else:
+            admin_query["method"] = "write"
+        return admin_query
+
     @cherrypy.expose
     def default(self, main_topic=None, version=None, topic=None, _id=None, item=None, *args, **kwargs):
-        session = None
+        token_info = None
         outdata = None
         _format = None
         method = "DONE"
         engine_topic = None
         rollback = []
-        session = None
+        engine_session = None
         try:
             if not main_topic or not version or not topic:
                 raise NbiException("URL must contain at least 'main_topic/version/topic'",
@@ -720,16 +936,14 @@ class Server(object):
                 method = kwargs.pop("METHOD")
             else:
                 method = cherrypy.request.method
-            if kwargs and "FORCE" in kwargs:
-                force = kwargs.pop("FORCE")
-            else:
-                force = False
-            self._check_valid_url_method(method, main_topic, version, topic, _id, item, *args)
+
+            role_permission = self._check_valid_url_method(method, main_topic, version, topic, _id, item, *args)
+            query_string_operations = self._extract_query_string_operations(kwargs, method)
             if main_topic == "admin" and topic == "tokens":
                 return self.token(method, _id, kwargs)
 
-            # self.engine.load_dbase(cherrypy.request.app.config)
-            session = self.authenticator.authorize()
+            token_info = self.authenticator.authorize(role_permission, query_string_operations)
+            engine_session = self._manage_admin_query(token_info, kwargs, method, _id)
             indata = self._format_in(kwargs)
             engine_topic = topic
             if topic == "subscriptions":
@@ -755,7 +969,7 @@ class Server(object):
                     engine_topic = "nsilcmops"
             elif main_topic == "pdu":
                 engine_topic = "pdus"
-            if engine_topic == "vims":   # TODO this is for backward compatibility, it will remove in the future
+            if engine_topic == "vims":   # TODO this is for backward compatibility, it will be removed in the future
                 engine_topic = "vim_accounts"
 
             if method == "GET":
@@ -768,24 +982,25 @@ class Server(object):
                         path = ()
                     else:
                         path = None
-                    file, _format = self.engine.get_file(session, engine_topic, _id, path,
+                    file, _format = self.engine.get_file(engine_session, engine_topic, _id, path,
                                                          cherrypy.request.headers.get("Accept"))
                     outdata = file
                 elif not _id:
-                    outdata = self.engine.get_item_list(session, engine_topic, kwargs)
+                    outdata = self.engine.get_item_list(engine_session, engine_topic, kwargs)
                 else:
                     if item == "reports":
                         # TODO check that project_id (_id in this context) has permissions
                         _id = args[0]
-                    outdata = self.engine.get_item(session, engine_topic, _id)
+                    outdata = self.engine.get_item(engine_session, engine_topic, _id)
             elif method == "POST":
+                cherrypy.response.status = HTTPStatus.CREATED.value
                 if topic in ("ns_descriptors_content", "vnf_packages_content", "netslice_templates_content"):
                     _id = cherrypy.request.headers.get("Transaction-Id")
                     if not _id:
-                        _id = self.engine.new_item(rollback, session, engine_topic, {}, None, cherrypy.request.headers,
-                                                   force=force)
-                    completed = self.engine.upload_content(session, engine_topic, _id, indata, kwargs,
-                                                           cherrypy.request.headers, force=force)
+                        _id, _ = self.engine.new_item(rollback, engine_session, engine_topic, {}, None,
+                                                      cherrypy.request.headers)
+                    completed = self.engine.upload_content(engine_session, engine_topic, _id, indata, kwargs,
+                                                           cherrypy.request.headers)
                     if completed:
                         self._set_location_header(main_topic, version, topic, _id)
                     else:
@@ -793,97 +1008,109 @@ class Server(object):
                     outdata = {"id": _id}
                 elif topic == "ns_instances_content":
                     # creates NSR
-                    _id = self.engine.new_item(rollback, session, engine_topic, indata, kwargs, force=force)
+                    _id, _ = self.engine.new_item(rollback, engine_session, engine_topic, indata, kwargs)
                     # creates nslcmop
                     indata["lcmOperationType"] = "instantiate"
                     indata["nsInstanceId"] = _id
-                    self.engine.new_item(rollback, session, "nslcmops", indata, None)
+                    nslcmop_id, _ = self.engine.new_item(rollback, engine_session, "nslcmops", indata, None)
                     self._set_location_header(main_topic, version, topic, _id)
-                    outdata = {"id": _id}
+                    outdata = {"id": _id, "nslcmop_id": nslcmop_id}
                 elif topic == "ns_instances" and item:
                     indata["lcmOperationType"] = item
                     indata["nsInstanceId"] = _id
-                    _id = self.engine.new_item(rollback, session, "nslcmops", indata, kwargs)
+                    _id, _ = self.engine.new_item(rollback, engine_session, "nslcmops", indata, kwargs)
                     self._set_location_header(main_topic, version, "ns_lcm_op_occs", _id)
                     outdata = {"id": _id}
                     cherrypy.response.status = HTTPStatus.ACCEPTED.value
                 elif topic == "netslice_instances_content":
                     # creates NetSlice_Instance_record (NSIR)
-                    _id = self.engine.new_item(rollback, session, engine_topic, indata, kwargs, force=force)
+                    _id, _ = self.engine.new_item(rollback, engine_session, engine_topic, indata, kwargs)
                     self._set_location_header(main_topic, version, topic, _id)
                     indata["lcmOperationType"] = "instantiate"
-                    indata["nsiInstanceId"] = _id
-                    self.engine.new_item(rollback, session, "nsilcmops", indata, kwargs)
-                    outdata = {"id": _id}
+                    indata["netsliceInstanceId"] = _id
+                    nsilcmop_id, _ = self.engine.new_item(rollback, engine_session, "nsilcmops", indata, kwargs)
+                    outdata = {"id": _id, "nsilcmop_id": nsilcmop_id}
 
                 elif topic == "netslice_instances" and item:
                     indata["lcmOperationType"] = item
-                    indata["nsiInstanceId"] = _id
-                    _id = self.engine.new_item(rollback, session, "nsilcmops", indata, kwargs)
+                    indata["netsliceInstanceId"] = _id
+                    _id, _ = self.engine.new_item(rollback, engine_session, "nsilcmops", indata, kwargs)
                     self._set_location_header(main_topic, version, "nsi_lcm_op_occs", _id)
                     outdata = {"id": _id}
                     cherrypy.response.status = HTTPStatus.ACCEPTED.value
                 else:
-                    _id = self.engine.new_item(rollback, session, engine_topic, indata, kwargs,
-                                               cherrypy.request.headers, force=force)
+                    _id, op_id = self.engine.new_item(rollback, engine_session, engine_topic, indata, kwargs,
+                                                      cherrypy.request.headers)
                     self._set_location_header(main_topic, version, topic, _id)
                     outdata = {"id": _id}
+                    if op_id:
+                        outdata["op_id"] = op_id
+                        cherrypy.response.status = HTTPStatus.ACCEPTED.value
                     # TODO form NsdInfo when topic in ("ns_descriptors", "vnf_packages")
-                cherrypy.response.status = HTTPStatus.CREATED.value
 
             elif method == "DELETE":
                 if not _id:
-                    outdata = self.engine.del_item_list(session, engine_topic, kwargs)
+                    outdata = self.engine.del_item_list(engine_session, engine_topic, kwargs)
                     cherrypy.response.status = HTTPStatus.OK.value
                 else:  # len(args) > 1
                     delete_in_process = False
-                    if topic == "ns_instances_content" and not force:
+                    if topic == "ns_instances_content" and not engine_session["force"]:
                         nslcmop_desc = {
                             "lcmOperationType": "terminate",
                             "nsInstanceId": _id,
                             "autoremove": True
                         }
-                        opp_id = self.engine.new_item(rollback, session, "nslcmops", nslcmop_desc, None)
+                        opp_id, _ = self.engine.new_item(rollback, engine_session, "nslcmops", nslcmop_desc, None)
                         if opp_id:
                             delete_in_process = True
                             outdata = {"_id": opp_id}
                             cherrypy.response.status = HTTPStatus.ACCEPTED.value
-                    elif topic == "netslice_instances_content" and not force:
+                    elif topic == "netslice_instances_content" and not engine_session["force"]:
                         nsilcmop_desc = {
                             "lcmOperationType": "terminate",
-                            "nsiInstanceId": _id,
+                            "netsliceInstanceId": _id,
                             "autoremove": True
                         }
-                        opp_id = self.engine.new_item(rollback, session, "nsilcmops", nsilcmop_desc, None)
+                        opp_id, _ = self.engine.new_item(rollback, engine_session, "nsilcmops", nsilcmop_desc, None)
                         if opp_id:
                             delete_in_process = True
                             outdata = {"_id": opp_id}
                             cherrypy.response.status = HTTPStatus.ACCEPTED.value
                     if not delete_in_process:
-                        self.engine.del_item(session, engine_topic, _id, force)
+                        self.engine.del_item(engine_session, engine_topic, _id)
                         cherrypy.response.status = HTTPStatus.NO_CONTENT.value
                 if engine_topic in ("vim_accounts", "wim_accounts", "sdns"):
                     cherrypy.response.status = HTTPStatus.ACCEPTED.value
 
             elif method in ("PUT", "PATCH"):
-                outdata = None
-                if not indata and not kwargs:
+                op_id = None
+                if not indata and not kwargs and not engine_session.get("set_project"):
                     raise NbiException("Nothing to update. Provide payload and/or query string",
                                        HTTPStatus.BAD_REQUEST)
                 if item in ("nsd_content", "package_content", "nst_content") and method == "PUT":
-                    completed = self.engine.upload_content(session, engine_topic, _id, indata, kwargs,
-                                                           cherrypy.request.headers, force=force)
+                    completed = self.engine.upload_content(engine_session, engine_topic, _id, indata, kwargs,
+                                                           cherrypy.request.headers)
                     if not completed:
                         cherrypy.response.headers["Transaction-Id"] = id
                 else:
-                    self.engine.edit_item(session, engine_topic, _id, indata, kwargs, force=force)
-                cherrypy.response.status = HTTPStatus.NO_CONTENT.value
+                    op_id = self.engine.edit_item(engine_session, engine_topic, _id, indata, kwargs)
+
+                if op_id:
+                    cherrypy.response.status = HTTPStatus.ACCEPTED.value
+                    outdata = {"op_id": op_id}
+                else:
+                    cherrypy.response.status = HTTPStatus.NO_CONTENT.value
+                    outdata = None
             else:
                 raise NbiException("Method {} not allowed".format(method), HTTPStatus.METHOD_NOT_ALLOWED)
-            return self._format_out(outdata, session, _format)
+
+            # 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, token_info, _format)
         except Exception as e:
             if isinstance(e, (NbiException, EngineException, DbException, FsException, MsgException, AuthException,
-                              ValidationError)):
+                              ValidationError, AuthconnException)):
                 http_code_value = cherrypy.response.status = e.http_code.value
                 http_code_name = e.http_code.name
                 cherrypy.log("Exception {}".format(e))
@@ -915,8 +1142,15 @@ class Server(object):
                 "status": http_code_value,
                 "detail": error_text,
             }
-            return self._format_out(problem_details, session)
+            return self._format_out(problem_details, token_info)
             # raise cherrypy.HTTPError(e.http_code.value, str(e))
+        finally:
+            if token_info:
+                self._format_login(token_info)
+                if method in ("PUT", "PATCH", "POST") and isinstance(outdata, dict):
+                    for logging_id in ("id", "op_id", "nsilcmop_id", "nslcmop_id"):
+                        if outdata.get(logging_id):
+                            cherrypy.request.login += ";{}={}".format(logging_id, outdata[logging_id][:36])
 
 
 def _start_service():
@@ -1021,7 +1255,10 @@ def _start_service():
     try:
         with open("{}/version".format(engine_config["/static"]['tools.staticdir.dir'])) as version_file:
             version_data = version_file.read()
-            cherrypy.log.error("Starting OSM NBI Version: {}".format(version_data.replace("\n", " ")))
+            version = version_data.replace("\n", " ")
+            backend = engine_config["authentication"]["backend"]
+            cherrypy.log.error("Starting OSM NBI Version {} with {} authentication backend"
+                               .format(version, backend))
     except Exception:
         pass
 
@@ -1032,7 +1269,8 @@ def _stop_service():
     TODO: Ending database connections.
     """
     global subscription_thread
-    subscription_thread.terminate()
+    if subscription_thread:
+        subscription_thread.terminate()
     subscription_thread = None
     cherrypy.tree.apps['/osm'].root.engine.stop()
     cherrypy.log.error("Stopping osm_nbi")