aboutsummaryrefslogtreecommitdiffstats
path: root/moon_engine/moon_engine/server.py
diff options
context:
space:
mode:
Diffstat (limited to 'moon_engine/moon_engine/server.py')
-rw-r--r--moon_engine/moon_engine/server.py288
1 files changed, 288 insertions, 0 deletions
diff --git a/moon_engine/moon_engine/server.py b/moon_engine/moon_engine/server.py
new file mode 100644
index 00000000..0b0f28b2
--- /dev/null
+++ b/moon_engine/moon_engine/server.py
@@ -0,0 +1,288 @@
+# Software Name: MOON
+
+# Version: 5.4
+
+# SPDX-FileCopyrightText: Copyright (c) 2018-2020 Orange and its contributors
+# SPDX-License-Identifier: Apache-2.0
+
+# This software is distributed under the 'Apache License 2.0',
+# the text of which is available at 'http://www.apache.org/licenses/LICENSE-2.0.txt'
+# or see the "LICENSE" file for more details.
+
+
+
+from falcon.http_error import HTTPError
+import hug
+import logging.config
+import json
+import re
+import requests
+import sys
+from uuid import uuid4
+from moon_engine.api import ERROR_CODE
+from moon_engine.api import status, logs, import_json, configuration
+from moon_utilities import exceptions
+from moon_utilities import auth_functions
+from moon_utilities import json_utils
+from moon_cache import cache
+from moon_engine import authz_driver
+
+LOGGER = logging.getLogger("moon.engine.server")
+CACHE = None
+
+
+@hug.directive()
+def server_uuid(default="", **kwargs):
+ """
+ Hug directive allowing to get the UUID of the component everywhere
+ :param default:
+ :param kwargs:
+ :return: UUID of the component
+ """
+ return configuration.get_configuration("uuid")
+
+
+def get_updates_from_manager():
+ """
+ Request the Manager to get all data from the database
+ :return: None
+ """
+ LOGGER.info("Retrieving all data from Manager")
+ for attribute in (
+ "pdp",
+ "models",
+ "policies",
+ "subjects",
+ "objects",
+ "actions",
+ "subject_categories",
+ "object_categories",
+ "action_categories",
+ "subject_assignments",
+ "object_assignments",
+ "action_assignments",
+ "meta_rules",
+ # "rules",
+ ):
+ # Note: force updates by getting attributes
+ LOGGER.info("Retrieving {} from manager {}".format(
+ attribute, configuration.get_configuration("manager_url")))
+ getattr(CACHE, attribute)
+
+
+def get_updates_from_local_conf():
+ """
+ Read the local data file and update the cache
+ :return: None
+ """
+ filename = configuration.get_configuration("data")
+ LOGGER.info("Retrieving all data from configuration ({})".format(filename))
+ data = json.load(open(filename))
+ LOGGER.debug("keys={}".format(list(data.keys())))
+ tool = json_utils.JsonImport(driver_name="cache", driver=CACHE)
+ tool.import_json(body=data)
+
+
+def get_attributes_from_config(filename):
+ """
+ Retrieve the configuration from the file given in the command line
+ :param filename: filename of the configuration file
+ :return: None
+ """
+ # TODO: manage the case if the filename attribute doesn't contain a true filename
+ # => case if it doesn't start with Gunicorn
+ # => generate a temporary RAM file and point to the moon.yaml in the source code
+ for line in open(filename):
+ _match_conf = re.match(r"moon\s*=\s*\"(.+)\"", line)
+ if _match_conf:
+ yaml_filename = _match_conf.groups()[0]
+ configuration.CONF_FILE = yaml_filename
+ _conf = configuration.search_config_file(yaml_filename)
+ break
+ else:
+ LOGGER.warning("Cannot find Moon configuration filename in {}".format(filename))
+ _conf = configuration.search_config_file("moon.yaml")
+
+ configuration.set_configuration(_conf)
+
+
+def get_bind_from_configfile(filename):
+ """
+ Retrieve the binding configuration from the file given in the command line
+ :param filename: filename of the configuration file
+ :return: URL
+ """
+ # TODO: manage the case if the filename attribute doesn't contain a true filename
+ # => case if it doesn't start with Gunicorn
+ # => case during tests
+ # => generate a temporary RAM file and point to the moon.yaml in the source code
+ for line in open(filename):
+ _match_conf = re.match(r"bind\s*=\s*\"(.+)\"", line)
+ if _match_conf:
+ return "http://" + _match_conf.groups()[0].replace("0.0.0.0", "127.0.0.1") # nosec
+ else:
+ LOGGER.warning("Cannot find binding configuration in {}".format(filename))
+
+
+def init_logging_system():
+ """
+ Initialize the logging system
+ either by the configuration given in the configuration file
+ either by the configuration in the Manager
+ :return: None
+ """
+ logging_conf = configuration.get_configuration("logging")
+ manager_url = configuration.get_configuration("manager_url")
+ if logging_conf:
+ configuration.init_logging()
+ elif manager_url:
+ req = requests.get("{}/config".format(manager_url))
+ if req.status_code != 200:
+ raise Exception("Error getting configurationĀ data "
+ "from manager (code={})".format(req.status_code))
+ logging.config.dictConfig(req.json().get("logging", {}))
+
+
+def get_policy_configuration_from_manager():
+ """
+ Retrieve all data from the Manager
+ :return: None
+ """
+ pdp_id = CACHE.get_pdp_from_vim_project(configuration.get_configuration("uuid"))
+ CACHE.update(pdp_id=pdp_id)
+
+
+def init_pipeline():
+ """
+ Initialize the pipeline configuration
+ :return: None
+ """
+ if configuration.get_configuration("management").get("url"):
+ get_policy_configuration_from_manager()
+
+
+def initialize():
+ """Adds initial data to the api on startup"""
+ global CACHE
+ LOGGER.warning("Starting the server and initializing data")
+ filename = sys.argv[-1]
+ try:
+ get_attributes_from_config(filename)
+ except FileNotFoundError:
+ LOGGER.warning("{} file not found".format(filename))
+ except IsADirectoryError:
+ LOGGER.warning("{} file is a directory.".format(filename))
+
+ init_logging_system()
+
+ LOGGER.info("management={}".format(configuration.get_configuration("management")))
+ auth_functions.init_db(configuration.get_configuration("management").get("token_file"))
+ CACHE = cache.Cache.getInstance(
+ manager_url=configuration.get_configuration("management").get('url'),
+ incremental=configuration.get_configuration("incremental_updates"),
+ manager_api_key=configuration.get_configuration("api_token"))
+
+ if configuration.get_configuration("type") == "pipeline":
+ init_pipeline()
+
+ if not configuration.get_configuration("incremental_updates"):
+ if configuration.get_configuration("manager_url"):
+ get_updates_from_manager()
+ elif configuration.get_configuration("data"):
+ get_updates_from_local_conf()
+ auth_functions.add_user("admin", uuid4().hex)
+ # NOTE: the password is not saved anywhere but
+ # the API key is printed in the log
+ # and is xor-ed with the Manager API key
+ api_key = auth_functions.get_api_key_for_user("admin")
+ LOGGER.info(f"api_key={api_key}")
+ LOGGER.info(f"configuration.get_configuration('api_token')={configuration.get_configuration('api_token')}")
+ try:
+ encrypt_key = auth_functions.xor_encode(api_key,
+ configuration.get_configuration("api_token"))
+ except exceptions.EncryptError:
+ encrypt_key = ""
+ try:
+ local_server = get_bind_from_configfile(filename)
+ CACHE.set_current_server(url=local_server, api_key=api_key)
+ except (FileNotFoundError, IsADirectoryError):
+ LOGGER.warning("Cannot find configuration file {}".format(filename))
+ LOGGER.critical("APIKEY={}".format(encrypt_key))
+ authz_driver.init()
+
+
+def __get_status_code(exception):
+ """
+ Return the status code to send depending on the exception thrown
+ :param exception: the exception that will be sent
+ :return:
+ """
+ if isinstance(exception, HTTPError):
+ return exception.status
+ status_code = getattr(exception, "code", 500)
+ if status_code in ERROR_CODE:
+ status_code = ERROR_CODE[status_code]
+ else:
+ status_code = hug.HTTP_500
+ return status_code
+
+
+@hug.exception(exceptions.MoonError)
+def handle_custom_exceptions(exception, response):
+ """
+ Handle Moon exceptions
+ :param exception: the exception that has been raised
+ :param response: the response to send to the client
+ :return: JSON data to send to the client
+ """
+ response.status = __get_status_code(exception)
+ error_message = {"result": False,
+ 'message': str(exception),
+ "code": getattr(exception, "code", 500)}
+ LOGGER.exception(exception)
+ return error_message
+
+
+@hug.exception(Exception)
+def handle_exception(exception, response):
+ """
+ Handle general exceptions
+ :param exception: the exception that has been raised
+ :param response: the response to send to the client
+ :return: JSON data to send to the client
+ """
+ response.status = __get_status_code(exception)
+ LOGGER.exception(exception)
+ return {"result": False, 'message': str(exception), "code": getattr(exception, "code", 500)}
+
+
+def get_api_from_plugins(api_type):
+ return configuration.get_plugins_by_type(api_type)
+
+
+@hug.extend_api()
+def with_other_apis():
+ """
+ Give to Hug all available APIs
+ :return: list of APIs
+ """
+ initialize()
+ _type = configuration.get_configuration("type")
+ if _type == "wrapper":
+ from moon_engine.api.wrapper.api import pipeline
+ from moon_engine.api.wrapper.api import update as wrapper_update
+ from moon_engine.api.wrapper.api import authz as wrapper_authz
+ LOGGER.info("Starting the Wrapper API interfaces")
+ return [status, logs, import_json, wrapper_update, pipeline, wrapper_authz] + \
+ list(configuration.get_plugins_by_type("wrapper_api"))
+ elif _type == "pipeline":
+ from moon_engine.api.pipeline import update as pipeline_update
+ from moon_engine.api.pipeline import authz as pipeline_authz
+ LOGGER.info("Starting the Pipeline API interfaces")
+ return [status, logs, import_json, pipeline_update, pipeline_authz] + \
+ list(configuration.get_plugins_by_type("engine_api"))
+ raise Exception("The type of component must be 'wrapper' or 'pipeline' (got {} instead)".format(
+ _type
+ ))
+
+