From f28f884b86ad6a01afb3ebe7959a86aed0701539 Mon Sep 17 00:00:00 2001 From: asteroide Date: Wed, 4 Oct 2017 09:04:38 +0200 Subject: Update code to the latest working version: version 4.2 Change-Id: I25d222ce9c8f4af8755886b79852d99b6870d515 --- moonv4/moon_wrapper/moon_wrapper/api/__init__.py | 0 moonv4/moon_wrapper/moon_wrapper/api/generic.py | 151 +++++++++++++++++ moonv4/moon_wrapper/moon_wrapper/api/wrapper.py | 101 ++++++++++++ moonv4/moon_wrapper/moon_wrapper/http_server.py | 197 +++++++++++++++-------- moonv4/moon_wrapper/requirements.txt | 1 - 5 files changed, 383 insertions(+), 67 deletions(-) create mode 100644 moonv4/moon_wrapper/moon_wrapper/api/__init__.py create mode 100644 moonv4/moon_wrapper/moon_wrapper/api/generic.py create mode 100644 moonv4/moon_wrapper/moon_wrapper/api/wrapper.py (limited to 'moonv4/moon_wrapper') diff --git a/moonv4/moon_wrapper/moon_wrapper/api/__init__.py b/moonv4/moon_wrapper/moon_wrapper/api/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/moonv4/moon_wrapper/moon_wrapper/api/generic.py b/moonv4/moon_wrapper/moon_wrapper/api/generic.py new file mode 100644 index 00000000..80e8abff --- /dev/null +++ b/moonv4/moon_wrapper/moon_wrapper/api/generic.py @@ -0,0 +1,151 @@ +# Copyright 2015 Open Platform for NFV Project, Inc. and its contributors +# This software is distributed under the terms and conditions of the 'Apache-2.0' +# license which can be found in the file 'LICENSE' in this package distribution +# or at 'http://www.apache.org/licenses/LICENSE-2.0'. +""" +Those API are helping API used to manage the Moon platform. +""" + +from flask_restful import Resource, request +from oslo_log import log as logging +from moon_utilities.security_functions import call +import moon_interface.api +from moon_utilities.security_functions import check_auth + +__version__ = "0.1.0" + +LOG = logging.getLogger("moon.interface.api." + __name__) + + +class Status(Resource): + """ + Endpoint for status requests + """ + + __urls__ = ("/status", "/status/", "/status/") + + def get(self, component_id=None): + """Retrieve status of all components + + :return: { + "orchestrator": { + "status": "Running" + }, + "security_router": { + "status": "Running" + } + } + """ + return call("security_router", method="get_status", ctx={"component_id": component_id}) + + +class Logs(Resource): + """ + Endpoint for logs requests + """ + + __urls__ = ("/logs", "/logs/", "/logs/") + + def get(self, component_id=None): + """Get logs from the Moon platform + + :param component_id: the ID of the component your are looking for (optional) + :return: [ + "2015-04-15-13:45:20 + "2015-04-15-13:45:21 + "2015-04-15-13:45:22 + "2015-04-15-13:45:23 + ] + """ + filter_str = request.args.get('filter', '') + from_str = request.args.get('from', '') + to_str = request.args.get('to', '') + event_number = request.args.get('event_number', '') + try: + event_number = int(event_number) + except ValueError: + event_number = None + args = dict() + args["filter"] = filter_str + args["from"] = from_str + args["to"] = to_str + args["event_number"] = event_number + + return call("security_router", method="get_logs", ctx={"component_id": component_id}, args=args) + + +class API(Resource): + """ + Endpoint for API requests + """ + + __urls__ = ( + "/api", + "/api/", + "/api/", + "/api//", + "/api//") + + @check_auth + def get(self, group_id="", endpoint_id="", user_id=""): + """Retrieve all API endpoints or a specific endpoint if endpoint_id is given + + :param group_id: the name of one existing group (ie generic, ...) + :param endpoint_id: the name of one existing component (ie Logs, Status, ...) + :return: { + "group_name": { + "endpoint_name": { + "description": "a description", + "methods": { + "get": "description of the HTTP method" + }, + "urls": ('/api', '/api/', '/api/') + } + } + """ + __methods = ("get", "post", "put", "delete", "options", "patch") + api_list = filter(lambda x: "__" not in x, dir(moon_interface.api)) + api_desc = dict() + for api_name in api_list: + api_desc[api_name] = {} + group_api_obj = eval("moon_interface.api.{}".format(api_name)) + api_desc[api_name]["description"] = group_api_obj.__doc__ + if "__version__" in dir(group_api_obj): + api_desc[api_name]["version"] = group_api_obj.__version__ + object_list = list(filter(lambda x: "__" not in x, dir(group_api_obj))) + for obj in map(lambda x: eval("moon_interface.api.{}.{}".format(api_name, x)), object_list): + if "__urls__" in dir(obj): + api_desc[api_name][obj.__name__] = dict() + api_desc[api_name][obj.__name__]["urls"] = obj.__urls__ + api_desc[api_name][obj.__name__]["methods"] = dict() + for _method in filter(lambda x: x in __methods, dir(obj)): + docstring = eval("moon_interface.api.{}.{}.{}.__doc__".format(api_name, obj.__name__, _method)) + api_desc[api_name][obj.__name__]["methods"][_method] = docstring + api_desc[api_name][obj.__name__]["description"] = str(obj.__doc__) + if group_id in api_desc: + if endpoint_id in api_desc[group_id]: + return {group_id: {endpoint_id: api_desc[group_id][endpoint_id]}} + elif len(endpoint_id) > 0: + LOG.error("Unknown endpoint_id {}".format(endpoint_id)) + return {"error": "Unknown endpoint_id {}".format(endpoint_id)} + return {group_id: api_desc[group_id]} + return api_desc + + +class InternalAPI(Resource): + """ + Endpoint for status requests + """ + + __urls__ = ("/internal_api", "/internal_api/", "/internal_api/") + + def get(self, component_id=None, user_id=""): + api_list = ("orchestrator", "security_router") + if not component_id: + return {"api": api_list} + if component_id in api_list: + api_desc = dict() + api_desc["name"] = component_id + api_desc["endpoints"] = call("security_router", component_id, {}, "list_api") + return api_desc + diff --git a/moonv4/moon_wrapper/moon_wrapper/api/wrapper.py b/moonv4/moon_wrapper/moon_wrapper/api/wrapper.py new file mode 100644 index 00000000..832909c5 --- /dev/null +++ b/moonv4/moon_wrapper/moon_wrapper/api/wrapper.py @@ -0,0 +1,101 @@ +# Copyright 2015 Open Platform for NFV Project, Inc. and its contributors +# This software is distributed under the terms and conditions of the 'Apache-2.0' +# license which can be found in the file 'LICENSE' in this package distribution +# or at 'http://www.apache.org/licenses/LICENSE-2.0'. +""" +Authz is the endpoint to get authorization response +""" + +import flask +from flask import request +from flask_restful import Resource +import logging +import json +import requests +import time +from uuid import uuid4 + +from moon_interface.api.authz import pdp_in_cache, pdp_in_manager, container_exist, \ + create_containers, create_authz_request +from moon_interface.authz_requests import AuthzRequest +from moon_utilities import configuration + +__version__ = "0.1.0" + +LOG = logging.getLogger("moon.wrapper.api." + __name__) + + +class Wrapper(Resource): + """ + Endpoint for authz requests + """ + + __urls__ = ( + "/", + ) + + def __init__(self, **kwargs): + self.port = kwargs.get("port") + self.CACHE = kwargs.get("cache", {}) + self.TIMEOUT = 5 + + # def get(self): + # LOG.info("GET") + # return self.manage_data() + + def post(self): + LOG.info("POST {}".format(request.form)) + response = flask.make_response("False") + if self.manage_data(): + response = flask.make_response("True") + response.headers['content-type'] = 'application/octet-stream' + return response + + @staticmethod + def __get_subject(target, credentials): + _subject = target.get("user_id", "") + if not _subject: + _subject = credentials.get("user_id", "none") + return _subject + + @staticmethod + def __get_object(target, credentials): + try: + # note: case of Glance + return target['target']['name'] + except KeyError: + pass + + # note: default case + return target.get("project_id", "none") + + @staticmethod + def __get_project_id(target, credentials): + return target.get("project_id", "none") + + def get_interface_url(self, project_id): + for container in self.CACHE.containers.values(): + if container.get("keystone_project_id") == project_id: + return "http://{}:{}".format(container['hostname'], + container['port'][0]["PublicPort"]) + + def manage_data(self): + target = json.loads(request.form.get('target', {})) + credentials = json.loads(request.form.get('credentials', {})) + rule = request.form.get('rule', "") + _subject = self.__get_subject(target, credentials) + _object = self.__get_object(target, credentials) + _project_id = self.__get_project_id(target, credentials) + LOG.info("POST with args project={} / " + "subject={} - object={} - action={}".format( + _project_id, _subject, _object, rule)) + interface_url = self.get_interface_url(_project_id) + req = requests.get("{}/{}/{}/{}".format( + interface_url, + _subject, + _object, + rule + )) + if req.status_code == 200: + if req.json().get("result", False): + return True diff --git a/moonv4/moon_wrapper/moon_wrapper/http_server.py b/moonv4/moon_wrapper/moon_wrapper/http_server.py index f50213c7..d93d6966 100644 --- a/moonv4/moon_wrapper/moon_wrapper/http_server.py +++ b/moonv4/moon_wrapper/moon_wrapper/http_server.py @@ -3,74 +3,139 @@ # license which can be found in the file 'LICENSE' in this package distribution # or at 'http://www.apache.org/licenses/LICENSE-2.0'. -import requests -import json -from flask import Flask, request +from flask import Flask, jsonify +from flask_cors import CORS, cross_origin +from flask_restful import Resource, Api import logging -from moon_utilities import configuration +from moon_wrapper import __version__ +from moon_wrapper.api.generic import Status, Logs, API +from moon_wrapper.api.wrapper import Wrapper +from moon_utilities.cache import Cache +from moon_utilities import configuration, exceptions logger = logging.getLogger("moon.wrapper.http") -def __get_subject(target, credentials): - _subject = target.get("user_id", "") - if not _subject: - _subject = credentials.get("user_id", "") - return _subject - - -def __get_object(target, credentials): - try: - # note: case of Glance - return target['target']['name'] - except KeyError: - pass - - # note: default case - return target.get("project_id", "") - - -def __get_project_id(target, credentials): - return target.get("project_id", "") - - -def HTTPServer(host, port): - app = Flask(__name__) - conf = configuration.get_configuration("components/wrapper") - timeout = conf["components/wrapper"].get("timeout", 5) - conf = configuration.get_configuration("components/interface") - interface_hostname = conf["components/interface"].get("hostname", "interface") - interface_port = conf["components/interface"].get("port", 80) - conf = configuration.get_configuration("logging") - try: - debug = conf["logging"]["loggers"]['moon']['level'] == "DEBUG" - except KeyError: - debug = False - - @app.route("/", methods=['POST', 'GET']) - def wrapper(): - try: - target = json.loads(request.form.get('target', {})) - credentials = json.loads(request.form.get('credentials', {})) - rule = request.form.get('rule', "") - _subject = __get_subject(target, credentials) - _object = __get_object(target, credentials) - _project_id = __get_project_id(target, credentials) - logger.info("GET with args {} / {} - {} - {}".format(_project_id, _subject, _object, rule)) - _url = "http://{}:{}/authz/{}/{}/{}/{}".format( - interface_hostname, - interface_port, - _project_id, - _subject, - _object, - rule - ) - req = requests.get(url=_url, timeout=timeout) - logger.info("req txt={}".format(req.text)) - if req.json()["result"] == True: - return "True" - except Exception as e: - logger.exception("An exception occurred: {}".format(e)) - return "False" - - app.run(debug=debug, host=host, port=port) +CACHE = Cache() + + +class Server: + """Base class for HTTP server""" + + def __init__(self, host="localhost", port=80, api=None, **kwargs): + """Run a server + + :param host: hostname of the server + :param port: port for the running server + :param kwargs: optional parameters + :return: a running server + """ + self._host = host + self._port = port + self._api = api + self._extra = kwargs + + @property + def host(self): + return self._host + + @host.setter + def host(self, name): + self._host = name + + @host.deleter + def host(self): + self._host = "" + + @property + def port(self): + return self._port + + @port.setter + def port(self, number): + self._port = number + + @port.deleter + def port(self): + self._port = 80 + + def run(self): + raise NotImplementedError() + +__API__ = ( + Status, Logs, API + ) + + +class Root(Resource): + """ + The root of the web service + """ + __urls__ = ("/", ) + __methods = ("get", "post", "put", "delete", "options") + + def get(self): + tree = {"/": {"methods": ("get",), + "description": "List all methods for that service."}} + for item in __API__: + tree[item.__name__] = {"urls": item.__urls__} + _methods = [] + for _method in self.__methods: + if _method in dir(item): + _methods.append(_method) + tree[item.__name__]["methods"] = _methods + tree[item.__name__]["description"] = item.__doc__.strip() + return { + "version": __version__, + "tree": tree + } + + +class HTTPServer(Server): + + def __init__(self, host="localhost", port=80, **kwargs): + super(HTTPServer, self).__init__(host=host, port=port, **kwargs) + self.app = Flask(__name__) + self.port = port + conf = configuration.get_configuration("components/orchestrator") + _hostname = conf["components/orchestrator"].get("hostname", + "orchestrator") + _port = conf["components/manager"].get("port", 80) + _protocol = conf["components/manager"].get("protocol", "http") + self.orchestrator_url = "{}://{}:{}".format( + _protocol, _hostname, _port) + # Todo : specify only few urls instead of * + # CORS(self.app) + self.api = Api(self.app) + self.__set_route() + self.__hook_errors() + + def __hook_errors(self): + + def get_404_json(e): + return jsonify({"result": False, "code": 404, + "description": str(e)}), 404 + self.app.register_error_handler(404, get_404_json) + + def get_400_json(e): + return jsonify({"result": False, "code": 400, + "description": str(e)}), 400 + self.app.register_error_handler(400, lambda e: get_400_json) + self.app.register_error_handler(403, exceptions.AuthException) + + def __set_route(self): + self.api.add_resource(Root, '/') + + for api in __API__: + self.api.add_resource(api, *api.__urls__) + self.api.add_resource(Wrapper, *Wrapper.__urls__, + resource_class_kwargs={ + "orchestrator_url": self.orchestrator_url, + "cache": CACHE, + } + ) + + def run(self): + self.app.run(host=self._host, port=self._port) # nosec + # self.app.run(debug=True, host=self._host, port=self._port) # nosec + diff --git a/moonv4/moon_wrapper/requirements.txt b/moonv4/moon_wrapper/requirements.txt index 51b57277..399ee15b 100644 --- a/moonv4/moon_wrapper/requirements.txt +++ b/moonv4/moon_wrapper/requirements.txt @@ -1,4 +1,3 @@ flask flask_restful -babel moon_utilities \ No newline at end of file -- cgit 1.2.3-korg