diff options
39 files changed, 2327 insertions, 2064 deletions
diff --git a/moonv4/bin/bootstrap.py b/moonv4/bin/bootstrap.py index 98b8f9a3..6f2a5e03 100644 --- a/moonv4/bin/bootstrap.py +++ b/moonv4/bin/bootstrap.py @@ -213,6 +213,7 @@ def start_moon(data_config): output = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + time.sleep(3) if output.returncode != 0: log.info(" ".join(cmd)) log.info(output.returncode) diff --git a/moonv4/moon_authz/Dockerfile b/moonv4/moon_authz/Dockerfile index 6ecc8f2d..579ddd16 100644 --- a/moonv4/moon_authz/Dockerfile +++ b/moonv4/moon_authz/Dockerfile @@ -3,11 +3,12 @@ FROM ubuntu:latest ENV UUID=null RUN apt update && apt install python3.5 python3-pip -y -RUN pip3 install moon_utilities moon_db pip --upgrade +RUN pip3 install pip --upgrade ADD . /root WORKDIR /root/ -RUN pip3 install -r requirements.txt +RUN pip3 install -r requirements.txt --upgrade +RUN pip3 install dist/moon_utilities-1.3.0-py3-none-any.whl --upgrade RUN pip3 install . CMD ["python3", "-m", "moon_authz"]
\ No newline at end of file diff --git a/moonv4/moon_authz/moon_authz/api/authorization.py b/moonv4/moon_authz/moon_authz/api/authorization.py index 94f1e13d..caa41082 100644 --- a/moonv4/moon_authz/moon_authz/api/authorization.py +++ b/moonv4/moon_authz/moon_authz/api/authorization.py @@ -3,17 +3,15 @@ # license which can be found in the file 'LICENSE' in this package distribution # or at 'http://www.apache.org/licenses/LICENSE-2.0'. -import hashlib +import binascii import itertools -from oslo_log import log as logging -from oslo_config import cfg -import oslo_messaging -from moon_utilities.security_functions import call, Context, notify -from moon_utilities.misc import get_uuid_from_name +import pickle +from uuid import uuid4 +import logging from moon_utilities import exceptions -from moon_db.core import PDPManager -from moon_db.core import ModelManager -from moon_db.core import PolicyManager +import flask +from flask import request +from flask_restful import Resource # TODO (asteroide): # - end the dev of the context @@ -21,43 +19,85 @@ from moon_db.core import PolicyManager # - call the next security function # - call the master if an element is absent -LOG = logging.getLogger(__name__) -CONF = cfg.CONF +LOG = logging.getLogger("moon.api." + __name__) -class Authorization(object): +class Authz(Resource): """ - Retrieve the current status of all components. + Endpoint for authz requests """ + __urls__ = ( + "/authz", + "/authz/", + "/authz/<string:uuid>/<string:subject_name>/<string:object_name>/<string:action_name>", + ) __version__ = "0.1.0" pdp_id = None meta_rule_id = None keystone_project_id = None payload = None - def __init__(self, component_desc): - self.component_id = component_desc - LOG.info("ext={}".format(component_desc)) - self.filter_rule = oslo_messaging.NotificationFilter( - event_type='^authz$', - context={'container_id': "\w+_"+hashlib.sha224(component_desc.encode("utf-8")).hexdigest()} - ) + def __init__(self, **kwargs): + component_data = kwargs.get("component_data", {}) + self.component_id = component_data['component_id'] + self.pdp_id = component_data['pdp_id'] + self.meta_rule_id = component_data['meta_rule_id'] + self.keystone_project_id = component_data['keystone_project_id'] + self.cache = kwargs.get("cache") + self.context = None - for _id_value in component_desc.split("_"): - _type, _id = _id_value.split(":") - if _type == "pdp": - self.pdp_id = _id - elif _type == "metarule": - self.meta_rule_id = _id - elif _type == "project": - self.keystone_project_id = _id + def post(self, uuid=None, subject_name=None, object_name=None, action_name=None): + """Get a response on an authorization request - def __check_rules(self, context): + :param uuid: uuid of a tenant or an intra_extension + :param subject_name: name of the subject or the request + :param object_name: name of the object + :param action_name: name of the action + :return: { + "args": {}, + "ctx": { + "action_name": "4567", + "id": "123456", + "method": "authz", + "object_name": "234567", + "subject_name": "123456", + "user_id": "admin" + }, + "error": { + "code": 500, + "description": "", + "title": "Moon Error" + }, + "intra_extension_id": "123456", + "result": false + } + :internal_api: authz + """ + self.context = pickle.loads(request.data) + self.context.set_cache(self.cache) + self.context.increment_index() + self.run() + self.context.delete_cache() + response = flask.make_response(pickle.dumps(self.context)) + response.headers['content-type'] = 'application/octet-stream' + return response + + def run(self): + LOG.info("self.context.pdp_set={}".format(self.context.pdp_set)) + result, message = self.__check_rules() + if result: + return self.__exec_instructions(result) + else: + self.context.current_state = "deny" + # self.__exec_next_state(result) + return + + def __check_rules(self): scopes_list = list() - current_header_id = context['headers'][context['index']] - Context.update_target(context) - current_pdp = context['pdp_set'][current_header_id] + current_header_id = self.context.headers[self.context.index] + # Context.update_target(context) + current_pdp = self.context.pdp_set[current_header_id] category_list = list() category_list.extend(current_pdp["meta_rules"]["subject_categories"]) category_list.extend(current_pdp["meta_rules"]["object_categories"]) @@ -65,13 +105,12 @@ class Authorization(object): for category in category_list: scope = list(current_pdp['target'][category]) scopes_list.append(scope) - policy_id = PolicyManager.get_policy_from_meta_rules("admin", current_header_id) - rules = PolicyManager.get_rules(user_id="admin", - policy_id=policy_id, - meta_rule_id=current_header_id) + # policy_id = self.cache.get_policy_from_meta_rules("admin", current_header_id) + for item in itertools.product(*scopes_list): req = list(item) - for rule in rules['rules']: + for rule in self.cache.rules[self.context.current_policy_id]["rules"]: + LOG.info("rule={}".format(rule)) if req == rule['rule']: return rule['instructions'], "" LOG.warning("No rule match the request...") @@ -84,13 +123,12 @@ class Authorization(object): except ValueError: LOG.error("Cannot understand value in instruction ({})".format(target)) return False - pdp_set = self.payload["authz_context"]['pdp_set'] - for meta_rule_id in self.payload["authz_context"]['pdp_set']: - policy_id = PolicyManager.get_policy_from_meta_rules("admin", meta_rule_id) + # pdp_set = self.payload["authz_context"]['pdp_set'] + for meta_rule_id in self.context.pdp_set: if meta_rule_id == "effect": continue - if pdp_set[meta_rule_id]["meta_rules"]["name"] == policy_name: - for category_id, category_value in ModelManager.get_subject_categories("admin").items(): + if self.context.pdp_set[meta_rule_id]["meta_rules"]["name"] == policy_name: + for category_id, category_value in self.cache.subject_categories.items(): if category_value["name"] == "role": subject_category_id = category_id break @@ -131,95 +169,96 @@ class Authorization(object): return self.payload["container_chaining"][index] def __update_headers(self, name): - context = self.payload["authz_context"] - for meta_rule_id, meta_rule_value in context["pdp_set"].items(): + # context = self.payload["authz_context"] + for meta_rule_id, meta_rule_value in self.context.pdp_set.items(): if meta_rule_id == "effect": continue if meta_rule_value["meta_rules"]["name"] == name: - self.payload["authz_context"]['headers'].append(meta_rule_id) + self.context.headers.append(meta_rule_id) return True return False - def __exec_next_state(self, rule_found): - index = self.payload["authz_context"]['index'] - current_meta_rule = self.payload["authz_context"]['headers'][index] - current_container = self.__get_container_from_meta_rule(current_meta_rule) - current_container_genre = current_container["genre"] - try: - next_meta_rule = self.payload["authz_context"]['headers'][index+1] - except IndexError: - next_meta_rule = None - if current_container_genre == "authz": - if rule_found: - return self.__return_to_router() - pass - if next_meta_rule: - # next will be session if current is deny and session is unset - if self.payload["authz_context"]['pdp_set'][next_meta_rule]['effect'] == "unset": - return notify( - request_id=self.payload["authz_context"]["request_id"], - container_id=self.__get_container_from_meta_rule(next_meta_rule)['container_id'], - payload=self.payload) - # next will be delegation if current is deny and session is passed or deny and delegation is unset - else: - LOG.error("Delegation is not developed!") + # def __exec_next_state(self, rule_found): + # index = self.context.index + # current_meta_rule = self.context.headers[index] + # current_container = self.__get_container_from_meta_rule(current_meta_rule) + # current_container_genre = current_container["genre"] + # try: + # next_meta_rule = self.context.headers[index + 1] + # except IndexError: + # next_meta_rule = None + # if current_container_genre == "authz": + # if rule_found: + # return True + # pass + # if next_meta_rule: + # # next will be session if current is deny and session is unset + # if self.payload["authz_context"]['pdp_set'][next_meta_rule]['effect'] == "unset": + # return notify( + # request_id=self.payload["authz_context"]["request_id"], + # container_id=self.__get_container_from_meta_rule(next_meta_rule)['container_id'], + # payload=self.payload) + # # next will be delegation if current is deny and session is passed or deny and delegation is unset + # else: + # LOG.error("Delegation is not developed!") + # + # else: + # # else next will be None and the request is sent to router + # return self.__return_to_router() + # elif current_container_genre == "session": + # pass + # # next will be next container in headers if current is passed + # if self.payload["authz_context"]['pdp_set'][current_meta_rule]['effect'] == "passed": + # return notify( + # request_id=self.payload["authz_context"]["request_id"], + # container_id=self.__get_container_from_meta_rule(next_meta_rule)['container_id'], + # payload=self.payload) + # # next will be None if current is grant and the request is sent to router + # else: + # return self.__return_to_router() + # elif current_container_genre == "delegation": + # LOG.error("Delegation is not developed!") + # # next will be authz if current is deny + # # next will be None if current is grant and the request is sent to router - else: - # else next will be None and the request is sent to router - return self.__return_to_router() - elif current_container_genre == "session": - pass - # next will be next container in headers if current is passed - if self.payload["authz_context"]['pdp_set'][current_meta_rule]['effect'] == "passed": - return notify( - request_id=self.payload["authz_context"]["request_id"], - container_id=self.__get_container_from_meta_rule(next_meta_rule)['container_id'], - payload=self.payload) - # next will be None if current is grant and the request is sent to router - else: - return self.__return_to_router() - elif current_container_genre == "delegation": - LOG.error("Delegation is not developed!") - # next will be authz if current is deny - # next will be None if current is grant and the request is sent to router - - def __return_to_router(self): - call(endpoint="security_router", - ctx={"id": self.component_id, - "call_master": False, - "method": "return_authz", - "request_id": self.payload["authz_context"]["request_id"]}, - method="route", - args=self.payload["authz_context"]) + # def __return_to_router(self): + # call(endpoint="security_router", + # ctx={"id": self.component_id, + # "call_master": False, + # "method": "return_authz", + # "request_id": self.payload["authz_context"]["request_id"]}, + # method="route", + # args=self.payload["authz_context"]) def __exec_instructions(self, instructions): - current_header_id = self.payload["authz_context"]['headers'][self.payload["authz_context"]['index']] for instruction in instructions: for key in instruction: if key == "decision": if instruction["decision"] == "grant": - self.payload["authz_context"]['pdp_set'][current_header_id]["effect"] = "grant" - self.__return_to_router() + self.context.current_state = "grant" + LOG.info("__exec_instructions True {}".format( + self.context.current_state)) + return True else: - self.payload["authz_context"]['pdp_set'][current_header_id]["effect"] = instruction["decision"] + self.context.current_state = instruction["decision"].lower() elif key == "chain": result = self.__update_headers(**instruction["chain"]) if not result: - self.payload["authz_context"]['pdp_set'][current_header_id]["effect"] = "deny" + self.context.current_state = "deny" else: - self.payload["authz_context"]['pdp_set'][current_header_id]["effect"] = "passed" + self.context.current_state = "passed" elif key == "update": result = self.__update_subject_category_in_policy(**instruction["update"]) if not result: - self.payload["authz_context"]['pdp_set'][current_header_id]["effect"] = "deny" + self.context.current_state = "deny" else: - self.payload["authz_context"]['pdp_set'][current_header_id]["effect"] = "passed" - # LOG.info("__exec_instructions {}".format(self.payload["authz_context"])) + self.context.current_state = "passed" + LOG.info("__exec_instructions False {}".format(self.context.current_state)) def __update_current_request(self): index = self.payload["authz_context"]["index"] current_header_id = self.payload["authz_context"]['headers'][index] - previous_header_id = self.payload["authz_context"]['headers'][index-1] + previous_header_id = self.payload["authz_context"]['headers'][index - 1] current_policy_id = PolicyManager.get_policy_from_meta_rules("admin", current_header_id) previous_policy_id = PolicyManager.get_policy_from_meta_rules("admin", previous_header_id) # FIXME (asteroide): must change those lines to be ubiquitous against any type of policy @@ -233,11 +272,11 @@ class Authorization(object): break for assignment_id, assignment_value in PolicyManager.get_subject_assignments( "admin", previous_policy_id, subject, subject_category_id).items(): - for data_id in assignment_value["assignments"]: - data = PolicyManager.get_subject_data("admin", previous_policy_id, data_id, subject_category_id) - for _data in data: - for key, value in _data["data"].items(): - role_names.append(value["name"]) + for data_id in assignment_value["assignments"]: + data = PolicyManager.get_subject_data("admin", previous_policy_id, data_id, subject_category_id) + for _data in data: + for key, value in _data["data"].items(): + role_names.append(value["name"]) new_role_ids = [] for perimeter_id, perimeter_value in PolicyManager.get_objects("admin", current_policy_id).items(): if perimeter_value["name"] in role_names: @@ -251,72 +290,65 @@ class Authorization(object): self.payload["authz_context"]['current_request']['object'] = new_role_ids[0] self.payload["authz_context"]['current_request']['action'] = perimeter_id elif self.payload["authz_context"]['pdp_set'][current_header_id]['meta_rules']['name'] == "rbac": - self.payload["authz_context"]['current_request']['subject'] = self.payload["authz_context"]['initial_request']['subject'] - self.payload["authz_context"]['current_request']['object'] = self.payload["authz_context"]['initial_request']['object'] - self.payload["authz_context"]['current_request']['action'] = self.payload["authz_context"]['initial_request']['action'] + self.payload["authz_context"]['current_request']['subject'] = \ + self.payload["authz_context"]['initial_request']['subject'] + self.payload["authz_context"]['current_request']['object'] = \ + self.payload["authz_context"]['initial_request']['object'] + self.payload["authz_context"]['current_request']['action'] = \ + self.payload["authz_context"]['initial_request']['action'] - def critical(self, ctxt, publisher_id, event_type, payload, metadata): - """This is the authz endpoint - but due to the oslo_messaging notification architecture, we must call it "critical" - - :param ctxt: context of the request - :param publisher_id: ID of the publisher - :param event_type: type of event ("authz" here) - :param payload: content of the authz request - :param metadata: metadata of the notification - :return: result of the authorization for the current component - """ - LOG.info("calling authz {} {}".format(ctxt, payload)) - self.keystone_project_id = payload["id"] - self.payload = payload + def get_authz(self): + # self.keystone_project_id = payload["id"] + # LOG.info("get_authz {}".format(payload)) + # self.payload = payload try: - if "authz_context" not in payload: - try: - self.payload["authz_context"] = Context(self.keystone_project_id, - self.payload["subject_name"], - self.payload["object_name"], - self.payload["action_name"], - self.payload["request_id"]).to_dict() - except exceptions.SubjectUnknown: - ctx = { - "subject_name": self.payload["subject_name"], - "object_name": self.payload["object_name"], - "action_name": self.payload["action_name"], - } - call("moon_manager", method="update_from_master", ctx=ctx, args={}) - self.payload["authz_context"] = Context(self.keystone_project_id, - self.payload["subject_name"], - self.payload["object_name"], - self.payload["action_name"], - self.payload["request_id"]).to_dict() - except exceptions.ObjectUnknown: - ctx = { - "subject_name": self.payload["subject_name"], - "object_name": self.payload["object_name"], - "action_name": self.payload["action_name"], - } - call("moon_manager", method="update_from_master", ctx=ctx, args={}) - self.payload["authz_context"] = Context(self.keystone_project_id, - self.payload["subject_name"], - self.payload["object_name"], - self.payload["action_name"], - self.payload["request_id"]).to_dict() - except exceptions.ActionUnknown: - ctx = { - "subject_name": self.payload["subject_name"], - "object_name": self.payload["object_name"], - "action_name": self.payload["action_name"], - } - call("moon_manager", method="update_from_master", ctx=ctx, args={}) - self.payload["authz_context"] = Context(self.keystone_project_id, - self.payload["subject_name"], - self.payload["object_name"], - self.payload["action_name"], - self.payload["request_id"]).to_dict() - self.__update_container_chaining() - else: - self.payload["authz_context"]["index"] += 1 - self.__update_current_request() + # if "authz_context" not in payload: + # try: + # self.payload["authz_context"] = Context(self.keystone_project_id, + # self.payload["subject_name"], + # self.payload["object_name"], + # self.payload["action_name"], + # self.payload["request_id"]).to_dict() + # except exceptions.SubjectUnknown: + # ctx = { + # "subject_name": self.payload["subject_name"], + # "object_name": self.payload["object_name"], + # "action_name": self.payload["action_name"], + # } + # call("moon_manager", method="update_from_master", ctx=ctx, args={}) + # self.payload["authz_context"] = Context(self.keystone_project_id, + # self.payload["subject_name"], + # self.payload["object_name"], + # self.payload["action_name"], + # self.payload["request_id"]).to_dict() + # except exceptions.ObjectUnknown: + # ctx = { + # "subject_name": self.payload["subject_name"], + # "object_name": self.payload["object_name"], + # "action_name": self.payload["action_name"], + # } + # call("moon_manager", method="update_from_master", ctx=ctx, args={}) + # self.payload["authz_context"] = Context(self.keystone_project_id, + # self.payload["subject_name"], + # self.payload["object_name"], + # self.payload["action_name"], + # self.payload["request_id"]).to_dict() + # except exceptions.ActionUnknown: + # ctx = { + # "subject_name": self.payload["subject_name"], + # "object_name": self.payload["object_name"], + # "action_name": self.payload["action_name"], + # } + # call("moon_manager", method="update_from_master", ctx=ctx, args={}) + # self.payload["authz_context"] = Context(self.keystone_project_id, + # self.payload["subject_name"], + # self.payload["object_name"], + # self.payload["action_name"], + # self.payload["request_id"]).to_dict() + # self.__update_container_chaining() + # else: + # self.payload["authz_context"]["index"] += 1 + # self.__update_current_request() result, message = self.__check_rules(self.payload["authz_context"]) current_header_id = self.payload["authz_context"]['headers'][self.payload["authz_context"]['index']] if result: @@ -327,7 +359,7 @@ class Authorization(object): return {"authz": result, "error": message, "pdp_id": self.pdp_id, - "ctx": ctxt, "args": self.payload} + "args": self.payload} except Exception as e: try: LOG.error(self.payload["authz_context"]) @@ -337,5 +369,8 @@ class Authorization(object): return {"authz": False, "error": str(e), "pdp_id": self.pdp_id, - "ctx": ctxt, "args": self.payload} + "args": self.payload} + def head(self, uuid=None, subject_name=None, object_name=None, action_name=None): + LOG.info("HEAD request") + return "", 200
\ No newline at end of file diff --git a/moonv4/moon_authz/moon_authz/http_server.py b/moonv4/moon_authz/moon_authz/http_server.py new file mode 100644 index 00000000..b6818a9f --- /dev/null +++ b/moonv4/moon_authz/moon_authz/http_server.py @@ -0,0 +1,140 @@ +# 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'. + +from flask import Flask, request +# from flask_cors import CORS, cross_origin +from flask_restful import Resource, Api, reqparse +import logging +from moon_utilities import exceptions +from moon_authz import __version__ +from moon_authz.api.authorization import Authz +from moon_utilities.cache import Cache + +logger = logging.getLogger("moon." + __name__) + +CACHE = Cache() +CACHE.update() + + +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__ = ( + Authz, + ) + + +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 + } + + def head(self): + return "", 201 + + +class HTTPServer(Server): + + def __init__(self, host="0.0.0.0", port=38001, **kwargs): + super(HTTPServer, self).__init__(host=host, port=port, **kwargs) + self.component_data = kwargs.get("component_data", {}) + logger.info("HTTPServer port={} {}".format(port, kwargs)) + self.app = Flask(__name__) + self._port = port + self._host = host + # Todo : specify only few urls instead of * + # CORS(self.app) + self.component_id = kwargs.get("component_id") + self.keystone_project_id = kwargs.get("keystone_project_id") + self.container_chaining = kwargs.get("container_chaining") + self.api = Api(self.app) + self.__set_route() + # self.__hook_errors() + + @self.app.errorhandler(exceptions.AuthException) + def _auth_exception(error): + return {"error": "Unauthorized"}, 401 + + def __hook_errors(self): + # FIXME (dthom): it doesn't work + def get_404_json(e): + return {"error": "Error", "code": 404, "description": e} + self.app.register_error_handler(404, get_404_json) + + def get_400_json(e): + return {"error": "Error", "code": 400, "description": e} + 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__, + resource_class_kwargs={ + "component_data": self.component_data, + "cache": CACHE + } + ) + + def run(self): + self.app.run(host=self._host, port=self._port) # nosec diff --git a/moonv4/moon_authz/moon_authz/messenger.py b/moonv4/moon_authz/moon_authz/messenger.py deleted file mode 100644 index 6fa34770..00000000 --- a/moonv4/moon_authz/moon_authz/messenger.py +++ /dev/null @@ -1,50 +0,0 @@ -# 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'. - -from oslo_config import cfg -import oslo_messaging -import time -from oslo_log import log as logging -from moon_authz.api.generic import Status, Logs -from moon_authz.api.authorization import Authorization -from moon_utilities.api import APIList - -LOG = logging.getLogger(__name__) -CONF = cfg.CONF - - -class Server: - - def __init__(self, component_id, keystone_project_id): - self.TOPIC = "authz-workers" - transport = oslo_messaging.get_notification_transport(cfg.CONF) - targets = [ - oslo_messaging.Target(topic=self.TOPIC), - ] - self.endpoints = [ - APIList((Status, Logs)), - Status(), - Logs(), - Authorization(component_id) - ] - pool = "authz-workers" - self.server = oslo_messaging.get_notification_listener(transport, targets, - self.endpoints, executor='threading', - pool=pool) - LOG.info("Starting MQ notification server with topic: {}".format(self.TOPIC)) - - def run(self): - try: - self.server.start() - while True: - time.sleep(0.1) - except KeyboardInterrupt: - print("Stopping server by crtl+c") - except SystemExit: - print("Stopping server") - - self.server.stop() - - diff --git a/moonv4/moon_authz/moon_authz/server.py b/moonv4/moon_authz/moon_authz/server.py index 0c2a36ff..5ca96e6f 100644 --- a/moonv4/moon_authz/moon_authz/server.py +++ b/moonv4/moon_authz/moon_authz/server.py @@ -4,31 +4,43 @@ # or at 'http://www.apache.org/licenses/LICENSE-2.0'. import os -from oslo_config import cfg from oslo_log import log as logging -# cfg.CONF.register_cli_opt(cfg.StrOpt('function_type', positional=True, -# help="The type of function managed by this component (example 'authz').")) -cfg.CONF.register_cli_opt(cfg.StrOpt('uuid', positional=True, - help="The ID of the component managed here.")) -cfg.CONF.register_cli_opt(cfg.StrOpt('keystone_project_id', positional=True, - help="The ID of the component managed here.")) -from moon_utilities import options # noqa -from moon_authz.messenger import Server - -LOG = logging.getLogger(__name__) -CONF = cfg.CONF +from moon_authz.http_server import HTTPServer as Server +from moon_utilities import configuration + +LOG = logging.getLogger("moon.server") DOMAIN = "moon_authz" __CWD__ = os.path.dirname(os.path.abspath(__file__)) def main(): - component_id = CONF.uuid - keystone_project_id = CONF.keystone_project_id - # function_type = CONF.intra_extension_id.replace(component_id, "").strip('_') - LOG.info("Starting server with IP {} on component {}".format( - CONF.security_router.host, component_id)) - server = Server(component_id=component_id, keystone_project_id=keystone_project_id) + component_id = os.getenv("UUID") + component_type = os.getenv("TYPE") + tcp_port = os.getenv("PORT") + pdp_id = os.getenv("PDP_ID") + meta_rule_id = os.getenv("META_RULE_ID") + keystone_project_id = os.getenv("KEYSTONE_PROJECT_ID") + configuration.init_logging() + LOG.info("component_type={}".format(component_type)) + conf = configuration.get_configuration("plugins/{}".format(component_type)) + conf["plugins/{}".format(component_type)]['id'] = component_id + hostname = conf["plugins/{}".format(component_type)].get('hostname', component_id) + port = conf["plugins/{}".format(component_type)].get('port', tcp_port) + bind = conf["plugins/{}".format(component_type)].get('bind', "0.0.0.0") + + LOG.info("Starting server with IP {} on port {} bind to {}".format(hostname, port, bind)) + server = Server( + host=bind, + port=int(port), + component_data={ + 'component_id': component_id, + 'component_type': component_type, + 'pdp_id': pdp_id, + 'meta_rule_id': meta_rule_id, + 'keystone_project_id': keystone_project_id, + } + ) server.run() diff --git a/moonv4/moon_authz/requirements.txt b/moonv4/moon_authz/requirements.txt index 8faf9439..344bec52 100644 --- a/moonv4/moon_authz/requirements.txt +++ b/moonv4/moon_authz/requirements.txt @@ -1 +1,8 @@ -kombu !=4.0.1,!=4.0.0
\ No newline at end of file +kombu !=4.0.1,!=4.0.0 +oslo.log +flask +oslo.config +flask_restful +flask_cors +moon_db +moon_utilities diff --git a/moonv4/moon_interface/moon_interface/api/assignments.py b/moonv4/moon_interface/moon_interface/api/assignments.py deleted file mode 100644 index 855a9049..00000000 --- a/moonv4/moon_interface/moon_interface/api/assignments.py +++ /dev/null @@ -1,264 +0,0 @@ -# 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'. -""" -Assignments allow to connect data with elements of perimeter - -""" - -from flask import request -from flask_restful import Resource -from oslo_log import log as logging -from moon_utilities.security_functions import call -from moon_utilities.security_functions import check_auth - -__version__ = "0.2.0" - -LOG = logging.getLogger("moon.interface.api." + __name__) - - -class SubjectAssignments(Resource): - """ - Endpoint for subject assignment requests - """ - - __urls__ = ( - "/policies/<string:uuid>/subject_assignments", - "/policies/<string:uuid>/subject_assignments/", - "/policies/<string:uuid>/subject_assignments/<string:perimeter_id>", - "/policies/<string:uuid>/subject_assignments/<string:perimeter_id>/<string:category_id>", - "/policies/<string:uuid>/subject_assignments/<string:perimeter_id>/<string:category_id>/<string:data_id>", - ) - - @check_auth - def get(self, uuid=None, perimeter_id=None, category_id=None, data_id=None, user_id=None): - """Retrieve all subject assignments or a specific one for a given policy - - :param uuid: uuid of the policy - :param perimeter_id: uuid of the subject - :param category_id: uuid of the subject category - :param data_id: uuid of the subject scope - :param user_id: user ID who do the request - :return: { - "subject_data_id": { - "policy_id": "ID of the policy", - "subject_id": "ID of the subject", - "category_id": "ID of the category", - "assignments": "Assignments list (list of data_id)", - } - } - :internal_api: get_subject_assignments - """ - return call(ctx={"id": uuid, "method": "get_subject_assignments", "perimeter_id": perimeter_id, "category_id": category_id, "user_id": user_id}, - args={"data_id": data_id}) - - @check_auth - def post(self, uuid=None, perimeter_id=None, category_id=None, data_id=None, user_id=None): - """Create a subject assignment. - - :param uuid: uuid of the policy - :param perimeter_id: uuid of the subject (not used here) - :param category_id: uuid of the subject category (not used here) - :param data_id: uuid of the subject scope (not used here) - :param user_id: user ID who do the request - :request body: { - "id": "UUID of the subject", - "category_id": "UUID of the category" - "data_id": "UUID of the scope" - } - :return: { - "subject_data_id": { - "policy_id": "ID of the policy", - "subject_id": "ID of the subject", - "category_id": "ID of the category", - "assignments": "Assignments list (list of data_id)", - } - } - :internal_api: update_subject_assignment - """ - return call("security_router", - ctx={"id": uuid, "method": "update_subject_assignment", "user_id": user_id}, args=request.json) - - @check_auth - def delete(self, uuid=None, perimeter_id=None, category_id=None, data_id=None, user_id=None): - """Delete a subject assignment for a given policy - - :param uuid: uuid of the policy - :param perimeter_id: uuid of the subject - :param category_id: uuid of the subject category - :param data_id: uuid of the subject scope - :param user_id: user ID who do the request - :return: { - "result": "True or False", - "message": "optional message" - } - :internal_api: delete_subject_assignment - """ - return call("security_router", - ctx={"id": uuid, "method": "delete_subject_assignment", "perimeter_id": perimeter_id, "category_id": category_id, "user_id": user_id}, - args={"data_id": data_id}) - - -class ObjectAssignments(Resource): - """ - Endpoint for object assignment requests - """ - - __urls__ = ( - "/policies/<string:uuid>/object_assignments", - "/policies/<string:uuid>/object_assignments/", - "/policies/<string:uuid>/object_assignments/<string:perimeter_id>", - "/policies/<string:uuid>/object_assignments/<string:perimeter_id>/<string:category_id>", - "/policies/<string:uuid>/object_assignments/<string:perimeter_id>/<string:category_id>/<string:data_id>", - ) - - @check_auth - def get(self, uuid=None, perimeter_id=None, category_id=None, data_id=None, user_id=None): - """Retrieve all object assignment or a specific one for a given policy - - :param uuid: uuid of the policy - :param perimeter_id: uuid of the object - :param category_id: uuid of the object category - :param data_id: uuid of the object scope - :param user_id: user ID who do the request - :return: { - "object_data_id": { - "policy_id": "ID of the policy", - "object_id": "ID of the object", - "category_id": "ID of the category", - "assignments": "Assignments list (list of data_id)", - } - } - :internal_api: get_object_assignments - """ - return call("security_router", - ctx={"id": uuid, "method": "get_object_assignments", "perimeter_id": perimeter_id, "category_id": category_id, "user_id": user_id}, - args={"data_id": data_id}) - - @check_auth - def post(self, uuid=None, perimeter_id=None, category_id=None, data_id=None, user_id=None): - """Create an object assignment. - - :param uuid: uuid of the policy - :param perimeter_id: uuid of the object (not used here) - :param category_id: uuid of the object category (not used here) - :param data_id: uuid of the object scope (not used here) - :param user_id: user ID who do the request - :request body: { - "id": "UUID of the action", - "category_id": "UUID of the category" - "data_id": "UUID of the scope" - } - :return: { - "object_data_id": { - "policy_id": "ID of the policy", - "object_id": "ID of the object", - "category_id": "ID of the category", - "assignments": "Assignments list (list of data_id)", - } - } - :internal_api: update_object_assignment - """ - return call("security_router", - ctx={"id": uuid, "method": "update_object_assignment", "user_id": user_id}, args=request.json) - - @check_auth - def delete(self, uuid=None, perimeter_id=None, category_id=None, data_id=None, user_id=None): - """Delete a object assignment for a given policy - - :param uuid: uuid of the policy - :param perimeter_id: uuid of the object - :param category_id: uuid of the object category - :param data_id: uuid of the object scope - :param user_id: user ID who do the request - :return: { - "result": "True or False", - "message": "optional message" - } - :internal_api: delete_object_assignment - """ - return call("security_router", - ctx={"id": uuid, "method": "delete_object_assignment", "perimeter_id": perimeter_id, "category_id": category_id, "user_id": user_id}, - args={"data_id": data_id}) - - -class ActionAssignments(Resource): - """ - Endpoint for action assignment requests - """ - - __urls__ = ( - "/policies/<string:uuid>/action_assignments", - "/policies/<string:uuid>/action_assignments/", - "/policies/<string:uuid>/action_assignments/<string:perimeter_id>", - "/policies/<string:uuid>/action_assignments/<string:perimeter_id>/<string:category_id>", - "/policies/<string:uuid>/action_assignments/<string:perimeter_id>/<string:category_id>/<string:data_id>", - ) - - @check_auth - def get(self, uuid=None, perimeter_id=None, category_id=None, data_id=None, user_id=None): - """Retrieve all action assignment or a specific one for a given policy - - :param uuid: uuid of the policy - :param perimeter_id: uuid of the action - :param category_id: uuid of the action category - :param data_id: uuid of the action scope - :param user_id: user ID who do the request - :return: { - "action_data_id": { - "policy_id": "ID of the policy", - "object_id": "ID of the action", - "category_id": "ID of the category", - "assignments": "Assignments list (list of data_id)", - } - } - :internal_api: get_action_assignments - """ - return call("security_router", ctx={"id": uuid, "method": "get_action_assignments", "perimeter_id": perimeter_id, "category_id": category_id, "user_id": user_id}, - args={"data_id": data_id}) - - @check_auth - def post(self, uuid=None, perimeter_id=None, category_id=None, data_id=None, user_id=None): - """Create an action assignment. - - :param uuid: uuid of the policy - :param perimeter_id: uuid of the action (not used here) - :param category_id: uuid of the action category (not used here) - :param data_id: uuid of the action scope (not used here) - :param user_id: user ID who do the request - :request body: { - "id": "UUID of the action", - "category_id": "UUID of the category", - "data_id": "UUID of the scope" - } - :return: { - "action_data_id": { - "policy_id": "ID of the policy", - "object_id": "ID of the action", - "category_id": "ID of the category", - "assignments": "Assignments list (list of data_id)", - } - } - :internal_api: update_action_assignment - """ - return call("security_router", ctx={"id": uuid, "method": "update_action_assignment", "user_id": user_id}, - args=request.json) - - @check_auth - def delete(self, uuid=None, perimeter_id=None, category_id=None, data_id=None, user_id=None): - """Delete a action assignment for a given policy - - :param uuid: uuid of the policy - :param perimeter_id: uuid of the action - :param category_id: uuid of the action category - :param data_id: uuid of the action scope - :param user_id: user ID who do the request - :return: { - "result": "True or False", - "message": "optional message" - } - :internal_api: delete_action_assignment - """ - return call("security_router", ctx={"id": uuid, "method": "delete_action_assignment", "perimeter_id": perimeter_id, "category_id": category_id, "user_id": user_id}, - args={"data_id": data_id}) diff --git a/moonv4/moon_interface/moon_interface/api/authz.py b/moonv4/moon_interface/moon_interface/api/authz.py index 69de0f80..3847cc73 100644 --- a/moonv4/moon_interface/moon_interface/api/authz.py +++ b/moonv4/moon_interface/moon_interface/api/authz.py @@ -6,23 +6,194 @@ Authz is the endpoint to get authorization response """ -from uuid import uuid4 -import time +from flask import request from flask_restful import Resource -from oslo_log import log as logging -from moon_utilities.security_functions import call +import logging +import pickle +import requests +import time +from uuid import uuid4 + +from moon_interface.containers import DockerManager +from moon_interface.authz_requests import AuthzRequest +from moon_utilities import configuration __version__ = "0.1.0" LOG = logging.getLogger("moon.interface.api." + __name__) +def pdp_in_cache(cache, uuid): + """Check if a PDP exist with this Keystone Project ID in the cache of this component + + :param cache: Cache to use + :param uuid: Keystone Project ID + :return: True or False + """ + for item_uuid, item_value in cache.pdp.items(): + if uuid == item_value['keystone_project_id']: + return item_uuid, item_value + return None, None + + +def pdp_in_manager(cache, uuid): + """Check if a PDP exist with this Keystone Project ID in the Manager component + + :param cache: Cache to use + :param uuid: Keystone Project ID + :return: True or False + """ + cache.update() + return pdp_in_cache(cache, uuid) + + +def container_exist(cache, uuid): + """Check if a PDP exist with this Keystone Project ID in the Manager component + + :param cache: Cache to use + :param uuid: Keystone Project ID + :return: True or False + """ + for key, value in cache.containers.items(): + if "keystone_project_id" not in value: + continue + if value["keystone_project_id"] == uuid: + try: + req = requests.head("http://{}:{}/".format( + value.get("hostname"), + value.get("port")[0].get("PublicPort"))) + LOG.info("container_exist {}".format(req.status_code)) + if req.status_code in (200, 201): + return value + return + except requests.exceptions.ConnectionError: + pass + # maybe hostname is not working so trying with IP address + try: + req = requests.head("http://{}:{}/".format( + value.get("ip"), + value.get("port")[0].get("PublicPort"))) + if req.status_code in (200, 201): + return value + return + except requests.exceptions.ConnectionError: + return + + +def build_container(cache, manager_url, uuid, meta_rule_id, plugin_name="authz"): + """Create the container and update the cache with the given perimeter elements + + :param cache: Cache to use + :param manager_url: URL of the manager + :param uuid: Keystone Project ID + :param meta_rule_id: UUID of the meta_rule + :param plugin_name: name of the plugin to use + :return: True or False + """ + LOG.info("Building a new container for {}".format(plugin_name)) + manager = DockerManager() + tcp_port = configuration.increment_port() + container_name = configuration.get_plugins()[plugin_name]['container'] + name = "{}_{}".format(plugin_name, uuid4().hex) + policy_id = cache.get_policy_from_meta_rules(meta_rule_id) + container_data = { + "name": name, + "hostname": name, + "port": { + "PrivatePort": tcp_port, + "Type": "tcp", + "IP": "0.0.0.0", + "PublicPort": tcp_port + }, + "keystone_project_id": uuid, + "pdp_id": cache.get_pdp_from_keystone_project(uuid), + "meta_rule_id": meta_rule_id, + "policy_id": policy_id, + "container_name": container_name, + "plugin_name": plugin_name + } + container = manager.create_container(container_data) + container_data['container_id'] = container.id + container_data['port']["IP"] = container.ip + container_data['start_time'] = time.time() + req = requests.post("{}/containers".format(manager_url), + json=container_data) + if req.status_code == 200: + cache.add_container(container_data) + return True + + +def create_containers(cache, manager_url, uuid, plugin_name="authz"): + """Create the container and update the cache with the given perimeter elements + + :param cache: Cache to use + :param manager_url: URL of the manager + :param uuid: Keystone Project ID + :param plugin_name: name of the plugin to use + :return: True or False + """ + LOG.info("Need to create some containers for {}".format(uuid)) + for pdp_id, pdp_value in cache.pdp.items(): + LOG.info("pdp {}".format(pdp_value)) + if uuid == pdp_value.get("keystone_project_id", ""): + LOG.info("uuid {}".format(uuid)) + for policy_id in pdp_value.get("security_pipeline", []): + LOG.info("policy {}".format(policy_id)) + model_id = cache.policies[policy_id]["model_id"] + model_value = cache.models[model_id] + for meta_rule_id in model_value["meta_rules"]: + LOG.info("meta_rule {}".format(meta_rule_id)) + build_container( + cache=cache, + uuid=uuid, + manager_url=manager_url, + meta_rule_id=meta_rule_id, + plugin_name=plugin_name) + return + + +def create_authz_request(cache, interface_name, manager_url, uuid, subject_name, object_name, action_name): + """Create the authorization request and make the first call to the Authz function + + :param cache: Cache to use + :param interface_name: hostname of the interface + :param manager_url: URL of the manager + :param uuid: Keystone Project ID + :param subject_name: name of the subject + :param object_name: name of the object + :param action_name: name of the action + :return: Authorisation request + """ + req_id = uuid4().hex + ctx = { + "project_id": uuid, + "subject_name": subject_name, + "object_name": object_name, + "action_name": action_name, + "request_id": req_id, + "interface_name": interface_name, + "manager_url": manager_url, + "cookie": uuid4().hex + } + cache.authz_requests[req_id] = AuthzRequest(ctx) + return cache.authz_requests[req_id] + + class Authz(Resource): """ Endpoint for authz requests """ - __urls__ = ("/authz/<string:uuid>/<string:subject_name>/<string:object_name>/<string:action_name>", ) + __urls__ = ( + "/authz/<string:uuid>", + "/authz/<string:uuid>/<string:subject_name>/<string:object_name>/<string:action_name>", + ) + + def __init__(self, **kwargs): + self.CACHE = kwargs.get("cache") + self.INTERFACE_NAME = kwargs.get("interface_name", "interface") + self.MANAGER_URL = kwargs.get("manager_url", "http://manager:8080") + self.TIMEOUT = 5 def get(self, uuid=None, subject_name=None, object_name=None, action_name=None): """Get a response on an authorization request @@ -51,17 +222,52 @@ class Authz(Resource): } :internal_api: authz """ - # Note (asteroide): user_id default to admin to be able to read the database - # it would be better to have a read-only user. - start_time = time.time() - result = call("security_router", ctx={"id": uuid, - "call_master": False, - "method": "authz", - "subject_name": subject_name, - "object_name": object_name, - "action_name": action_name, - "user_id": "admin", - "request_id": uuid4().hex}, args={}) - end_time = time.time() - result['time'] = {"start": start_time, "end": end_time} - return result + pdp_id, pdp_value = pdp_in_cache(self.CACHE, uuid) + if not pdp_id: + pdp_id, pdp_value = pdp_in_manager(self.CACHE, uuid) + if not pdp_id: + return { + "result": False, + "message": "Unknown Project ID or " + "Project ID is not bind to a PDP."}, 403 + if not container_exist(self.CACHE, uuid): + create_containers( + cache=self.CACHE, + uuid=uuid, + manager_url=self.MANAGER_URL, + plugin_name="authz") + authz_request = create_authz_request( + cache=self.CACHE, + uuid=uuid, + interface_name=self.INTERFACE_NAME, + manager_url=self.MANAGER_URL, + subject_name=subject_name, + object_name=object_name, + action_name=action_name) + cpt = 0 + while True: + if cpt > self.TIMEOUT*10: + return {"result": False, + "message": "Authz request had timed out."}, 500 + if authz_request.is_authz(): + if authz_request.final_result == "Grant": + return {"result": True, "message": ""}, 200 + return {"result": False, "message": ""}, 401 + cpt += 1 + time.sleep(0.1) + + def patch(self, uuid=None, subject_name=None, object_name=None, action_name=None): + """Get a response on an authorization request + + :param uuid: uuid of the authorization request + :param subject_name: not used + :param object_name: not used + :param action_name: not used + :request body: a Context object + :return: {} + :internal_api: authz + """ + if uuid in self.CACHE.authz_requests: + self.CACHE.authz_requests[uuid].set_result(pickle.loads(request.data)) + return "", 201 + return {"result": False, "message": "The request ID is unknown"}, 500 diff --git a/moonv4/moon_interface/moon_interface/api/data.py b/moonv4/moon_interface/moon_interface/api/data.py deleted file mode 100644 index 6d959095..00000000 --- a/moonv4/moon_interface/moon_interface/api/data.py +++ /dev/null @@ -1,259 +0,0 @@ -# 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'. -""" -Data are elements used to create rules - -""" - -from flask import request -from flask_restful import Resource -from oslo_log import log as logging -from moon_utilities.security_functions import call -from moon_utilities.security_functions import check_auth - -__version__ = "0.2.0" - -LOG = logging.getLogger("moon.interface.api." + __name__) - - -class SubjectData(Resource): - """ - Endpoint for subject data requests - """ - - __urls__ = ( - "/policies/<string:uuid>/subject_data", - "/policies/<string:uuid>/subject_data/", - "/policies/<string:uuid>/subject_data/<string:category_id>", - "/policies/<string:uuid>/subject_data/<string:category_id>/<string:data_id>", - ) - - @check_auth - def get(self, uuid=None, category_id=None, data_id=None, user_id=None): - """Retrieve all subject categories or a specific one if sid is given for a given policy - - :param uuid: uuid of the policy - :param category_id: uuid of the subject category - :param data_id: uuid of the subject data - :param user_id: user ID who do the request - :return: [{ - "policy_id": "policy_id1", - "category_id": "category_id1", - "data": { - "subject_data_id": { - "name": "name of the data", - "description": "description of the data" - } - } - }] - :internal_api: get_subject_data - """ - return call("security_router", ctx={"id": uuid, "method": "get_subject_data", "category_id": category_id, "user_id": user_id}, - args={"data_id": data_id}) - - @check_auth - def post(self, uuid=None, category_id=None, data_id=None, user_id=None): - """Create or update a subject. - - :param uuid: uuid of the policy - :param category_id: uuid of the subject category - :param data_id: uuid of the subject data - :param user_id: user ID who do the request - :request body: { - "name": "name of the data", - "description": "description of the data" - } - :return: { - "policy_id": "policy_id1", - "category_id": "category_id1", - "data": { - "subject_data_id": { - "name": "name of the data", - "description": "description of the data" - } - } - } - :internal_api: add_subject_data - """ - return call("security_router", ctx={"id": uuid, "method": "add_subject_data", "category_id": category_id, "user_id": user_id}, - args=request.json) - - @check_auth - def delete(self, uuid=None, category_id=None, data_id=None, user_id=None): - """Delete a subject for a given policy - - :param uuid: uuid of the policy - :param category_id: uuid of the subject category - :param data_id: uuid of the subject data - :param user_id: user ID who do the request - :return: [{ - "result": "True or False", - "message": "optional message" - }] - :internal_api: delete_subject_data - """ - return call("security_router", ctx={"id": uuid, "method": "delete_subject_data", "category_id": category_id, "user_id": user_id}, - args={"data_id": data_id}) - - -class ObjectData(Resource): - """ - Endpoint for object data requests - """ - - __urls__ = ( - "/policies/<string:uuid>/object_data", - "/policies/<string:uuid>/object_data/", - "/policies/<string:uuid>/object_data/<string:category_id>", - "/policies/<string:uuid>/object_data/<string:category_id>/<string:data_id>", - ) - - @check_auth - def get(self, uuid=None, category_id=None, data_id=None, user_id=None): - """Retrieve all object categories or a specific one if sid is given for a given policy - - :param uuid: uuid of the policy - :param category_id: uuid of the object category - :param data_id: uuid of the object data - :param user_id: user ID who do the request - :return: [{ - "policy_id": "policy_id1", - "category_id": "category_id1", - "data": { - "object_data_id": { - "name": "name of the data", - "description": "description of the data" - } - } - }] - :internal_api: get_object_data - """ - return call("security_router", ctx={"id": uuid, "method": "get_object_data", "category_id": category_id, "user_id": user_id}, - args={"data_id": data_id}) - - @check_auth - def post(self, uuid=None, category_id=None, data_id=None, user_id=None): - """Create or update a object. - - :param uuid: uuid of the policy - :param category_id: uuid of the object category - :param data_id: uuid of the object data - :param user_id: user ID who do the request - :request body: { - "name": "name of the data", - "description": "description of the data" - } - :return: { - "policy_id": "policy_id1", - "category_id": "category_id1", - "data": { - "object_data_id": { - "name": "name of the data", - "description": "description of the data" - } - } - } - :internal_api: add_object_data - """ - return call("security_router", ctx={"id": uuid, "method": "add_object_data", "category_id": category_id, "user_id": user_id}, args=request.json) - - @check_auth - def delete(self, uuid=None, category_id=None, data_id=None, user_id=None): - """Delete a object for a given policy - - :param uuid: uuid of the policy - :param category_id: uuid of the object category - :param data_id: uuid of the object data - :param user_id: user ID who do the request - :return: { - "result": "True or False", - "message": "optional message" - } - :internal_api: delete_object_data - """ - return call("security_router", ctx={"id": uuid, "method": "delete_object_data", "category_id": category_id, "user_id": user_id}, - args={"data_id": data_id}) - - -class ActionData(Resource): - """ - Endpoint for action data requests - """ - - __urls__ = ( - "/policies/<string:uuid>/action_data", - "/policies/<string:uuid>/action_data/", - "/policies/<string:uuid>/action_data/<string:category_id>", - "/policies/<string:uuid>/action_data/<string:category_id>/<string:data_id>", - ) - - @check_auth - def get(self, uuid=None, category_id=None, data_id=None, user_id=None): - """Retrieve all action categories or a specific one if sid is given for a given policy - - :param uuid: uuid of the policy - :param category_id: uuid of the action category - :param data_id: uuid of the action data - :param user_id: user ID who do the request - :return: [{ - "policy_id": "policy_id1", - "category_id": "category_id1", - "data": { - "action_data_id": { - "name": "name of the data", - "description": "description of the data" - } - } - }] - :internal_api: get_action_data - """ - return call("security_router", ctx={"id": uuid, "method": "get_action_data", "category_id": category_id, "user_id": user_id}, - args={"data_id": data_id}) - - @check_auth - def post(self, uuid=None, category_id=None, data_id=None, user_id=None): - """Create or update a action. - - :param uuid: uuid of the policy - :param category_id: uuid of the action category - :param data_id: uuid of the action data - :param user_id: user ID who do the request - :request body: { - "name": "name of the data", - "description": "description of the data" - } - :return: { - "policy_id": "policy_id1", - "category_id": "category_id1", - "data": { - "action_data_id": { - "name": "name of the data", - "description": "description of the data" - } - } - } - :internal_api: add_action_data - """ - return call("security_router", ctx={"id": uuid, "method": "add_action_data", "category_id": category_id, "user_id": user_id}, - args=request.json) - - @check_auth - def delete(self, uuid=None, category_id=None, data_id=None, user_id=None): - """Delete a action for a given policy - - :param uuid: uuid of the policy - :param category_id: uuid of the action category - :param data_id: uuid of the action data - :param user_id: user ID who do the request - :return: { - "result": "True or False", - "message": "optional message" - } - :internal_api: delete_action_data - """ - return call("security_router", ctx={"id": uuid, "method": "delete_action_data", "category_id": category_id, "user_id": user_id}, - args={"data_id": data_id}) - - diff --git a/moonv4/moon_interface/moon_interface/api/meta_data.py b/moonv4/moon_interface/moon_interface/api/meta_data.py deleted file mode 100644 index 3c933759..00000000 --- a/moonv4/moon_interface/moon_interface/api/meta_data.py +++ /dev/null @@ -1,204 +0,0 @@ -# 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'. -""" -Meta Data are elements used to create Meta data (skeleton of security policies) - -""" - -from flask import request -from flask_restful import Resource -from oslo_log import log as logging -from moon_utilities.security_functions import call -from moon_utilities.security_functions import check_auth - -__version__ = "0.2.0" - -LOG = logging.getLogger("moon.interface.api." + __name__) - - -class SubjectCategories(Resource): - """ - Endpoint for subject categories requests - """ - - __urls__ = ( - "/subject_categories", - "/subject_categories/", - "/subject_categories/<string:category_id>", - ) - - @check_auth - def get(self, category_id=None, user_id=None): - """Retrieve all subject categories or a specific one - - :param category_id: uuid of the subject category - :param user_id: user ID who do the request - :return: { - "subject_category_id": { - "name": "name of the category", - "description": "description of the category" - } - } - :internal_api: get_subject_categories - """ - return call("security_router", ctx={"method": "get_subject_categories", "user_id": user_id}, args={"category_id": category_id}) - - @check_auth - def post(self, category_id=None, user_id=None): - """Create or update a subject category. - - :param category_id: must not be used here - :param user_id: user ID who do the request - :request body: { - "name": "name of the category", - "description": "description of the category" - } - :return: { - "subject_category_id": { - "name": "name of the category", - "description": "description of the category" - } - } - :internal_api: add_subject_category - """ - return call("security_router", ctx={"method": "set_subject_category", "user_id": user_id}, args=request.json) - - @check_auth - def delete(self, category_id=None, user_id=None): - """Delete a subject category - - :param category_id: uuid of the subject category to delete - :param user_id: user ID who do the request - :return: { - "result": "True or False", - "message": "optional message" - } - :internal_api: delete_subject_category - """ - return call("security_router", ctx={"method": "delete_subject_category", "user_id": user_id}, args={"category_id": category_id}) - - -class ObjectCategories(Resource): - """ - Endpoint for object categories requests - """ - - __urls__ = ( - "/object_categories", - "/object_categories/", - "/object_categories/<string:category_id>", - ) - - @check_auth - def get(self, category_id=None, user_id=None): - """Retrieve all object categories or a specific one - - :param category_id: uuid of the object category - :param user_id: user ID who do the request - :return: { - "object_category_id": { - "name": "name of the category", - "description": "description of the category" - } - } - :internal_api: get_object_categories - """ - return call("security_router", ctx={"method": "get_object_categories", "user_id": user_id}, args={"category_id": category_id}) - - @check_auth - def post(self, category_id=None, user_id=None): - """Create or update a object category. - - :param category_id: must not be used here - :param user_id: user ID who do the request - :request body: { - "name": "name of the category", - "description": "description of the category" - } - :return: { - "object_category_id": { - "name": "name of the category", - "description": "description of the category" - } - } - :internal_api: add_object_category - """ - return call("security_router", ctx={"method": "set_object_category", "user_id": user_id}, args=request.json) - - @check_auth - def delete(self, category_id=None, user_id=None): - """Delete an object category - - :param category_id: uuid of the object category to delete - :param user_id: user ID who do the request - :return: { - "result": "True or False", - "message": "optional message" - } - :internal_api: delete_object_category - """ - return call("security_router", ctx={"method": "delete_object_category", "user_id": user_id}, args={"category_id": category_id}) - - -class ActionCategories(Resource): - """ - Endpoint for action categories requests - """ - - __urls__ = ( - "/action_categories", - "/action_categories/", - "/action_categories/<string:category_id>", - ) - - @check_auth - def get(self, category_id=None, user_id=None): - """Retrieve all action categories or a specific one - - :param category_id: uuid of the action category - :param user_id: user ID who do the request - :return: { - "action_category_id": { - "name": "name of the category", - "description": "description of the category" - } - } - :internal_api: get_action_categories - """ - return call("security_router", ctx={"method": "get_action_categories", "user_id": user_id}, args={"category_id": category_id}) - - @check_auth - def post(self, category_id=None, user_id=None): - """Create or update an action category. - - :param category_id: must not be used here - :param user_id: user ID who do the request - :request body: { - "name": "name of the category", - "description": "description of the category" - } - :return: { - "action_category_id": { - "name": "name of the category", - "description": "description of the category" - } - } - :internal_api: add_action_category - """ - return call("security_router", ctx={"method": "set_action_category", "user_id": user_id}, args=request.json) - - @check_auth - def delete(self, category_id=None, user_id=None): - """Delete an action - - :param category_id: uuid of the action category to delete - :param user_id: user ID who do the request - :return: { - "result": "True or False", - "message": "optional message" - } - :internal_api: delete_action_category - """ - return call("security_router", ctx={"method": "delete_action_category", "user_id": user_id}, args={"category_id": category_id}) diff --git a/moonv4/moon_interface/moon_interface/api/meta_rules.py b/moonv4/moon_interface/moon_interface/api/meta_rules.py deleted file mode 100644 index 85072243..00000000 --- a/moonv4/moon_interface/moon_interface/api/meta_rules.py +++ /dev/null @@ -1,138 +0,0 @@ -# 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'. -""" -Meta rules are skeleton for security policies - -""" - -from flask import request -from flask_restful import Resource -from oslo_log import log as logging -from moon_utilities.security_functions import call -from moon_utilities.security_functions import check_auth - -__version__ = "0.1.0" - -LOG = logging.getLogger("moon.interface.api." + __name__) - - -class MetaRules(Resource): - """ - Endpoint for meta rules requests - """ - - __urls__ = ("/meta_rules", - "/meta_rules/", - "/meta_rules/<string:meta_rule_id>", - "/meta_rules/<string:meta_rule_id>/") - - @check_auth - def get(self, meta_rule_id=None, user_id=None): - """Retrieve all sub meta rules - - :param meta_rule_id: Meta rule algorithm ID - :param user_id: user ID who do the request - :return: { - "meta_rules": { - "meta_rule_id1": { - "name": "name of the meta rule", - "algorithm": "name of the meta rule algorithm", - "subject_categories": ["subject_category_id1", "subject_category_id2"], - "object_categories": ["object_category_id1"], - "action_categories": ["action_category_id1"] - }, - } - } - :internal_api: get_meta_rules - """ - return call("security_router", ctx={"method": "get_meta_rules", - "user_id": user_id, - "meta_rule_id": meta_rule_id}, args={}) - - @check_auth - def post(self, meta_rule_id=None, user_id=None): - """Add a meta rule - - :param meta_rule_id: Meta rule ID - :param user_id: user ID who do the request - :request body: post = { - "name": "name of the meta rule", - "subject_categories": ["subject_category_id1", "subject_category_id2"], - "object_categories": ["object_category_id1"], - "action_categories": ["action_category_id1"] - } - :return: { - "meta_rules": { - "meta_rule_id1": { - "name": "name of the meta rule", - "subject_categories": ["subject_category_id1", "subject_category_id2"], - "object_categories": ["object_category_id1"], - "action_categories": ["action_category_id1"] - }, - } - } - :internal_api: add_meta_rules - """ - return call("security_router", ctx={"method": "add_meta_rules", - "user_id": user_id, - "meta_rule_id": meta_rule_id}, args=request.json) - - @check_auth - def patch(self, meta_rule_id=None, user_id=None): - """Update a meta rule - - :param meta_rule_id: Meta rule ID - :param user_id: user ID who do the request - :request body: patch = { - "name": "name of the meta rule", - "subject_categories": ["subject_category_id1", "subject_category_id2"], - "object_categories": ["object_category_id1"], - "action_categories": ["action_category_id1"] - } - :return: { - "meta_rules": { - "meta_rule_id1": { - "name": "name of the meta rule", - "subject_categories": ["subject_category_id1", "subject_category_id2"], - "object_categories": ["object_category_id1"], - "action_categories": ["action_category_id1"] - }, - } - } - :internal_api: set_meta_rules - """ - return call("security_router", ctx={"method": "set_meta_rules", - "user_id": user_id, - "meta_rule_id": meta_rule_id}, args=request.json) - - @check_auth - def delete(self, meta_rule_id=None, user_id=None): - """Delete a meta rule - - :param meta_rule_id: Meta rule ID - :param user_id: user ID who do the request - :request body: delete = { - "name": "name of the meta rule", - "subject_categories": ["subject_category_id1", "subject_category_id2"], - "object_categories": ["object_category_id1"], - "action_categories": ["action_category_id1"] - } - :return: { - "meta_rules": { - "meta_rule_id1": { - "name": "name of the meta rule", - "subject_categories": ["subject_category_id1", "subject_category_id2"], - "object_categories": ["object_category_id1"], - "action_categories": ["action_category_id1"] - }, - } - } - :internal_api: delete_meta_rules - """ - return call("security_router", ctx={"method": "delete_meta_rules", - "user_id": user_id, - "meta_rule_id": meta_rule_id}, args=request.json) - - diff --git a/moonv4/moon_interface/moon_interface/api/models.py b/moonv4/moon_interface/moon_interface/api/models.py deleted file mode 100644 index f905db63..00000000 --- a/moonv4/moon_interface/moon_interface/api/models.py +++ /dev/null @@ -1,101 +0,0 @@ -# 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'. -""" -Models aggregate multiple meta rules -""" - -from flask import request -from flask_restful import Resource -from oslo_log import log as logging -from moon_utilities.security_functions import call -from moon_utilities.security_functions import check_auth - -__version__ = "0.1.0" - -LOG = logging.getLogger("moon.interface.api." + __name__) - - -class Models(Resource): - """ - Endpoint for model requests - """ - - __urls__ = ( - "/models", - "/models/", - "/models/<string:uuid>", - "/models/<string:uuid>/", - ) - - @check_auth - def get(self, uuid=None, user_id=None): - """Retrieve all models - - :param uuid: uuid of the model - :param user_id: user ID who do the request - :return: { - "model_id1": { - "name": "...", - "description": "...", - "meta_rules": ["meta_rule_id1", ] - } - } - :internal_api: get_models - """ - return call("security_router", ctx={"id": uuid, "method": "get_models", "user_id": user_id}, args={}) - - @check_auth - def post(self, uuid=None, user_id=None): - """Create model. - - :param uuid: uuid of the model (not used here) - :param user_id: user ID who do the request - :request body: { - "name": "...", - "description": "...", - "meta_rules": ["meta_rule_id1", ] - } - :return: { - "model_id1": { - "name": "...", - "description": "...", - "meta_rules": ["meta_rule_id1", ] - } - } - :internal_api: add_model - """ - return call("security_router", ctx={"id": uuid, "method": "add_model", "user_id": user_id}, args=request.json) - - @check_auth - def delete(self, uuid=None, user_id=None): - """Delete a model - - :param uuid: uuid of the model to delete - :param user_id: user ID who do the request - :return: { - "result": "True or False", - "message": "optional message" - } - :internal_api: delete_model - """ - return call("security_router", ctx={"id": uuid, "method": "delete_model", "user_id": user_id}, args={}) - - @check_auth - def patch(self, uuid=None, user_id=None): - """Update a model - - :param uuid: uuid of the model to update - :param user_id: user ID who do the request - :return: { - "model_id1": { - "name": "...", - "description": "...", - "meta_rules": ["meta_rule_id1", ] - } - } - :internal_api: update_model - """ - return call("security_router", ctx={"id": uuid, "method": "update_model", "user_id": user_id}, args=request.json) - diff --git a/moonv4/moon_interface/moon_interface/api/pdp.py b/moonv4/moon_interface/moon_interface/api/pdp.py deleted file mode 100644 index 5316227b..00000000 --- a/moonv4/moon_interface/moon_interface/api/pdp.py +++ /dev/null @@ -1,106 +0,0 @@ -# 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'. -""" -PDP are Policy Decision Point. - -""" - -from flask import request -from flask_restful import Resource -from oslo_log import log as logging -from moon_utilities.security_functions import call -from moon_utilities.security_functions import check_auth - -__version__ = "0.1.0" - -LOG = logging.getLogger("moon.interface.api." + __name__) - - -class PDP(Resource): - """ - Endpoint for pdp requests - """ - - __urls__ = ( - "/pdp", - "/pdp/", - "/pdp/<string:uuid>", - "/pdp/<string:uuid>/", - ) - - @check_auth - def get(self, uuid=None, user_id=None): - """Retrieve all pdp - - :param uuid: uuid of the pdp - :param user_id: user ID who do the request - :return: { - "pdp_id1": { - "name": "...", - "security_pipeline": [...], - "keystone_project_id": "keystone_project_id1", - "description": "...", - } - } - :internal_api: get_pdp - """ - return call("security_router", ctx={"id": uuid, "method": "get_pdp", "user_id": user_id}, args={}) - - @check_auth - def post(self, uuid=None, user_id=None): - """Create pdp. - - :param uuid: uuid of the pdp (not used here) - :param user_id: user ID who do the request - :request body: { - "name": "...", - "security_pipeline": [...], - "keystone_project_id": "keystone_project_id1", - "description": "...", - } - :return: { - "pdp_id1": { - "name": "...", - "security_pipeline": [...], - "keystone_project_id": "keystone_project_id1", - "description": "...", - } - } - :internal_api: add_pdp - """ - return call("security_router", ctx={"id": uuid, "method": "add_pdp", "user_id": user_id}, args=request.json) - - @check_auth - def delete(self, uuid=None, user_id=None): - """Delete a pdp - - :param uuid: uuid of the pdp to delete - :param user_id: user ID who do the request - :return: { - "result": "True or False", - "message": "optional message" - } - :internal_api: delete_pdp - """ - return call("security_router", ctx={"id": uuid, "method": "delete_pdp", "user_id": user_id}, args={}) - - @check_auth - def patch(self, uuid=None, user_id=None): - """Update a pdp - - :param uuid: uuid of the pdp to update - :param user_id: user ID who do the request - :return: { - "pdp_id1": { - "name": "...", - "security_pipeline": [...], - "keystone_project_id": "keystone_project_id1", - "description": "...", - } - } - :internal_api: update_pdp - """ - return call("security_router", ctx={"id": uuid, "method": "update_pdp", "user_id": user_id}, args=request.json) - diff --git a/moonv4/moon_interface/moon_interface/api/perimeter.py b/moonv4/moon_interface/moon_interface/api/perimeter.py deleted file mode 100644 index 177161f6..00000000 --- a/moonv4/moon_interface/moon_interface/api/perimeter.py +++ /dev/null @@ -1,312 +0,0 @@ -# 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'. -""" -* Subjects are the source of an action on an object (examples : users, virtual machines) -* Objects are the destination of an action (examples virtual machines, virtual Routers) -* Actions are what subject wants to do on an object -""" - -from flask import request -from flask_restful import Resource -from oslo_log import log as logging -from moon_utilities.security_functions import call -from moon_utilities.security_functions import check_auth - -__version__ = "0.2.0" - -LOG = logging.getLogger("moon.interface.api." + __name__) - - -class Subjects(Resource): - """ - Endpoint for subjects requests - """ - - __urls__ = ( - "/subjects", - "/subjects/", - "/subjects/<string:perimeter_id>", - "/policies/<string:uuid>/subjects", - "/policies/<string:uuid>/subjects/", - "/policies/<string:uuid>/subjects/<string:perimeter_id>", - ) - - @check_auth - def get(self, uuid=None, perimeter_id=None, user_id=None): - """Retrieve all subjects or a specific one if perimeter_id is given for a given policy - - :param uuid: uuid of the policy - :param perimeter_id: uuid of the subject - :param user_id: user ID who do the request - :return: { - "subject_id": { - "name": "name of the subject", - "keystone_id": "keystone id of the subject", - "description": "a description" - } - } - :internal_api: get_subjects - """ - return call("security_router", ctx={"id": uuid, "method": "get_subjects", "user_id": user_id}, args={"perimeter_id": perimeter_id}) - - @check_auth - def post(self, uuid=None, perimeter_id=None, user_id=None): - """Create or update a subject. - - :param uuid: uuid of the policy - :param perimeter_id: must not be used here - :param user_id: user ID who do the request - :request body: { - "name": "name of the subject", - "description": "description of the subject", - "password": "password for the subject", - "email": "email address of the subject" - } - :return: { - "subject_id": { - "name": "name of the subject", - "keystone_id": "keystone id of the subject", - "description": "description of the subject", - "password": "password for the subject", - "email": "email address of the subject" - } - } - :internal_api: set_subject - """ - return call("security_router", ctx={"id": uuid, "method": "set_subject", "user_id": user_id, "perimeter_id": None}, - args=request.json) - - @check_auth - def patch(self, uuid=None, perimeter_id=None, user_id=None): - """Create or update a subject. - - :param uuid: uuid of the policy - :param perimeter_id: must not be used here - :param user_id: user ID who do the request - :request body: { - "name": "name of the subject", - "description": "description of the subject", - "password": "password for the subject", - "email": "email address of the subject" - } - :return: { - "subject_id": { - "name": "name of the subject", - "keystone_id": "keystone id of the subject", - "description": "description of the subject", - "password": "password for the subject", - "email": "email address of the subject" - } - } - :internal_api: set_subject - """ - return call("security_router", ctx={"id": uuid, "method": "set_subject", "user_id": user_id, "perimeter_id": perimeter_id}, - args=request.json) - - @check_auth - def delete(self, uuid=None, perimeter_id=None, user_id=None): - """Delete a subject for a given policy - - :param uuid: uuid of the policy - :param perimeter_id: uuid of the subject - :param user_id: user ID who do the request - :return: { - "subject_id": { - "name": "name of the subject", - "keystone_id": "keystone id of the subject", - "description": "description of the subject", - "password": "password for the subject", - "email": "email address of the subject" - } - } - :internal_api: delete_subject - """ - return call("security_router", ctx={"id": uuid, "method": "delete_subject", "user_id": user_id}, args={"perimeter_id": perimeter_id}) - - -class Objects(Resource): - """ - Endpoint for objects requests - """ - - __urls__ = ( - "/objects", - "/objects/", - "/objects/<string:perimeter_id>", - "/policies/<string:uuid>/objects", - "/policies/<string:uuid>/objects/", - "/policies/<string:uuid>/objects/<string:perimeter_id>", - ) - - @check_auth - def get(self, uuid=None, perimeter_id=None, user_id=None): - """Retrieve all objects or a specific one if perimeter_id is given for a given policy - - :param uuid: uuid of the policy - :param perimeter_id: uuid of the object - :param user_id: user ID who do the request - :return: { - "object_id": { - "name": "name of the object", - "description": "description of the object" - } - } - :internal_api: get_objects - """ - return call("security_router", ctx={"id": uuid, "method": "get_objects", "user_id": user_id}, args={"perimeter_id": perimeter_id}) - - @check_auth - def post(self, uuid=None, perimeter_id=None, user_id=None): - """Create or update a object. - - :param uuid: uuid of the policy - :param perimeter_id: must not be used here - :param user_id: user ID who do the request - :request body: { - "object_name": "name of the object", - "object_description": "description of the object" - } - :return: { - "object_id": { - "name": "name of the object", - "description": "description of the object" - } - } - :internal_api: set_object - """ - return call("security_router", ctx={"id": uuid, "method": "set_object", "user_id": user_id, "perimeter_id": None}, - args=request.json) - - @check_auth - def patch(self, uuid=None, perimeter_id=None, user_id=None): - """Create or update a object. - - :param uuid: uuid of the policy - :param perimeter_id: must not be used here - :param user_id: user ID who do the request - :request body: { - "object_name": "name of the object", - "object_description": "description of the object" - } - :return: { - "object_id": { - "name": "name of the object", - "description": "description of the object" - } - } - :internal_api: set_object - """ - return call("security_router", ctx={"id": uuid, "method": "set_object", "user_id": user_id, "perimeter_id": perimeter_id}, - args=request.json) - - @check_auth - def delete(self, uuid=None, perimeter_id=None, user_id=None): - """Delete a object for a given policy - - :param uuid: uuid of the policy - :param perimeter_id: uuid of the object - :param user_id: user ID who do the request - :return: { - "object_id": { - "name": "name of the object", - "description": "description of the object" - } - } - :internal_api: delete_object - """ - return call("security_router", ctx={"id": uuid, "method": "delete_object", "user_id": user_id}, args={"perimeter_id": perimeter_id}) - - -class Actions(Resource): - """ - Endpoint for actions requests - """ - - __urls__ = ( - "/actions", - "/actions/", - "/actions/<string:perimeter_id>", - "/policies/<string:uuid>/actions", - "/policies/<string:uuid>/actions/", - "/policies/<string:uuid>/actions/<string:perimeter_id>", - ) - - @check_auth - def get(self, uuid=None, perimeter_id=None, user_id=None): - """Retrieve all actions or a specific one if perimeter_id is given for a given policy - - :param uuid: uuid of the policy - :param perimeter_id: uuid of the action - :param user_id: user ID who do the request - :return: { - "action_id": { - "name": "name of the action", - "description": "description of the action" - } - } - :internal_api: get_actions - """ - return call("security_router", ctx={"id": uuid, "method": "get_actions", "user_id": user_id}, args={"perimeter_id": perimeter_id}) - - @check_auth - def post(self, uuid=None, perimeter_id=None, user_id=None): - """Create or update a action. - - :param uuid: uuid of the policy - :param perimeter_id: must not be used here - :param user_id: user ID who do the request - :request body: { - "name": "name of the action", - "description": "description of the action" - } - :return: { - "action_id": { - "name": "name of the action", - "description": "description of the action" - } - } - :internal_api: set_action - """ - return call("security_router", ctx={"id": uuid, "method": "set_action", "user_id": user_id, "perimeter_id": None}, - args=request.json) - - @check_auth - def patch(self, uuid=None, perimeter_id=None, user_id=None): - """Create or update a action. - - :param uuid: uuid of the policy - :param perimeter_id: must not be used here - :param user_id: user ID who do the request - :request body: { - "name": "name of the action", - "description": "description of the action" - } - :return: { - "action_id": { - "name": "name of the action", - "description": "description of the action" - } - } - :internal_api: set_action - """ - return call("security_router", ctx={"id": uuid, "method": "set_action", "user_id": user_id, "perimeter_id": perimeter_id}, - args=request.json) - - @check_auth - def delete(self, uuid=None, perimeter_id=None, user_id=None): - """Delete a action for a given policy - - :param uuid: uuid of the policy - :param perimeter_id: uuid of the action - :param user_id: user ID who do the request - :return: { - "action_id": { - "name": "name of the action", - "description": "description of the action" - } - } - :internal_api: delete_action - """ - return call("security_router", ctx={"id": uuid, "method": "delete_action", "user_id": user_id}, args={"perimeter_id": perimeter_id}) diff --git a/moonv4/moon_interface/moon_interface/api/policies.py b/moonv4/moon_interface/moon_interface/api/policies.py deleted file mode 100644 index 5a84b612..00000000 --- a/moonv4/moon_interface/moon_interface/api/policies.py +++ /dev/null @@ -1,106 +0,0 @@ -# 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'. -""" -Policies are instances of security models and implement security policies - -""" - -from flask import request -from flask_restful import Resource -from oslo_log import log as logging -from moon_utilities.security_functions import call -from moon_utilities.security_functions import check_auth - -__version__ = "0.1.0" - -LOG = logging.getLogger("moon.interface.api." + __name__) - - -class Policies(Resource): - """ - Endpoint for policy requests - """ - - __urls__ = ( - "/policies", - "/policies/", - "/policies/<string:uuid>", - "/policies/<string:uuid>/", - ) - - @check_auth - def get(self, uuid=None, user_id=None): - """Retrieve all policies - - :param uuid: uuid of the policy - :param user_id: user ID who do the request - :return: { - "policy_id1": { - "name": "...", - "model_id": "...", - "genre": "...", - "description": "...", - } - } - :internal_api: get_policies - """ - return call("security_router", ctx={"id": uuid, "method": "get_policies", "user_id": user_id}, args={}) - - @check_auth - def post(self, uuid=None, user_id=None): - """Create policy. - - :param uuid: uuid of the policy (not used here) - :param user_id: user ID who do the request - :request body: { - "name": "...", - "model_id": "...", - "genre": "...", - "description": "...", - } - :return: { - "policy_id1": { - "name": "...", - "model_id": "...", - "genre": "...", - "description": "...", - } - } - :internal_api: add_policy - """ - return call("security_router", ctx={"id": uuid, "method": "add_policy", "user_id": user_id}, args=request.json) - - @check_auth - def delete(self, uuid=None, user_id=None): - """Delete a policy - - :param uuid: uuid of the policy to delete - :param user_id: user ID who do the request - :return: { - "result": "True or False", - "message": "optional message" - } - :internal_api: delete_policy - """ - return call("security_router", ctx={"id": uuid, "method": "delete_policy", "user_id": user_id}, args={}) - - @check_auth - def patch(self, uuid=None, user_id=None): - """Update a policy - - :param uuid: uuid of the policy to update - :param user_id: user ID who do the request - :return: { - "policy_id1": { - "name": "...", - "model_id": "...", - "genre": "...", - "description": "...", - } - } - :internal_api: update_policy - """ - return call("security_router", ctx={"id": uuid, "method": "update_policy", "user_id": user_id}, args=request.json) - diff --git a/moonv4/moon_interface/moon_interface/api/rules.py b/moonv4/moon_interface/moon_interface/api/rules.py deleted file mode 100644 index 1111729c..00000000 --- a/moonv4/moon_interface/moon_interface/api/rules.py +++ /dev/null @@ -1,114 +0,0 @@ -# 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'. -""" -Rules (TODO) -""" - -from flask import request -from flask_restful import Resource -from oslo_log import log as logging -from moon_utilities.security_functions import call -from moon_utilities.security_functions import check_auth - -__version__ = "0.1.0" - -LOG = logging.getLogger("moon.interface.api." + __name__) - - -class Rules(Resource): - """ - Endpoint for rules requests - """ - - __urls__ = ("/policies/<string:uuid>/rules", - "/policies/<string:uuid>/rules/", - "/policies/<string:uuid>/rules/<string:rule_id>", - "/policies/<string:uuid>/rules/<string:rule_id>/", - ) - - @check_auth - def get(self, uuid=None, rule_id=None, user_id=None): - """Retrieve all rules or a specific one - - :param uuid: policy ID - :param rule_id: rule ID - :param user_id: user ID who do the request - :return: { - "rules": [ - "policy_id": "policy_id1", - "meta_rule_id": "meta_rule_id1", - "rule_id1": ["subject_data_id1", "object_data_id1", "action_data_id1"], - "rule_id2": ["subject_data_id2", "object_data_id2", "action_data_id2"], - ] - } - :internal_api: get_rules - """ - return call("security_router", ctx={"id": uuid, - "method": "get_rules", - "user_id": user_id, - "rule_id": rule_id}, args={}) - - @check_auth - def post(self, uuid=None, rule_id=None, user_id=None): - """Add a rule to a meta rule - - :param uuid: policy ID - :param rule_id: rule ID - :param user_id: user ID who do the request - :request body: post = { - "meta_rule_id": "meta_rule_id1", - "rule": ["subject_data_id2", "object_data_id2", "action_data_id2"], - "instructions": ( - {"decision": "grant"}, - ) - "enabled": True - } - :return: { - "rules": [ - "meta_rule_id": "meta_rule_id1", - "rule_id1": { - "rule": ["subject_data_id1", "object_data_id1", "action_data_id1"], - "instructions": ( - {"decision": "grant"}, # "grant" to immediately exit, - # "continue" to wait for the result of next policy - # "deny" to deny the request - ) - } - "rule_id2": { - "rule": ["subject_data_id2", "object_data_id2", "action_data_id2"], - "instructions": ( - { - "update": { - "operation": "add", # operations may be "add" or "delete" - "target": "rbac:role:admin" # add the role admin to the current user - } - }, - {"chain": {"name": "rbac"}} # chain with the policy named rbac - ) - } - ] - } - :internal_api: add_rule - """ - return call("security_router", ctx={"id": uuid, - "method": "add_rule", - "user_id": user_id, - "rule_id": rule_id}, args=request.json) - - @check_auth - def delete(self, uuid=None, rule_id=None, user_id=None): - """Delete one rule linked to a specific sub meta rule - - :param uuid: policy ID - :param rule_id: rule ID - :param user_id: user ID who do the request - :return: { "result": true } - :internal_api: delete_rule - """ - return call("security_router", ctx={"id": uuid, - "method": "delete_rule", - "user_id": user_id, - "rule_id": rule_id}, args={}) - diff --git a/moonv4/moon_interface/moon_interface/api/wrapper.py b/moonv4/moon_interface/moon_interface/api/wrapper.py new file mode 100644 index 00000000..5ba5779f --- /dev/null +++ b/moonv4/moon_interface/moon_interface/api/wrapper.py @@ -0,0 +1,120 @@ +# 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.interface.api." + __name__) + + +class Wrapper(Resource): + """ + Endpoint for authz requests + """ + + __urls__ = ( + "/authz/wrapper", + "/authz/wrapper/", + ) + + def __init__(self, **kwargs): + self.port = kwargs.get("port") + self.CACHE = kwargs.get("cache", {}) + self.INTERFACE_NAME = kwargs.get("interface_name", "interface") + self.MANAGER_URL = kwargs.get("manager_url", "http://manager:8080") + 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 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("GET with args project={} / " + "subject={} - object={} - action={}".format( + _project_id, _subject, _object, rule)) + pdp_id, pdp_value = pdp_in_cache(self.CACHE, _project_id) + if not pdp_id: + pdp_id, pdp_value = pdp_in_manager(self.CACHE, _project_id) + if not pdp_id: + LOG.error("Unknown Project ID or " + "Project ID is not bind to a PDP.") + return False + if not container_exist(self.CACHE, _project_id): + create_containers(self.CACHE, _project_id, self.MANAGER_URL, + plugin_name="authz") + authz_request = create_authz_request( + cache=self.CACHE, + uuid=_project_id, + interface_name=self.INTERFACE_NAME, + manager_url=self.MANAGER_URL, + subject_name=_subject, + object_name=_object, + action_name=rule) + cpt = 0 + while True: + LOG.info("Wait") + if cpt > self.TIMEOUT*10: + LOG.error("Authz request had timed out.") + return False + if authz_request.is_authz(): + if authz_request.final_result == "Grant": + LOG.info("Grant") + return True + LOG.info("Deny") + return False + cpt += 1 + time.sleep(0.1) diff --git a/moonv4/moon_interface/moon_interface/authz_requests.py b/moonv4/moon_interface/moon_interface/authz_requests.py new file mode 100644 index 00000000..2eb5fd19 --- /dev/null +++ b/moonv4/moon_interface/moon_interface/authz_requests.py @@ -0,0 +1,159 @@ +# 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'. + +import logging +import itertools +import pickle +import requests +from moon_utilities import configuration, exceptions +from moon_utilities.security_functions import Context +from moon_utilities.cache import Cache + +LOG = logging.getLogger("moon.interface.authz_requests") + + +CACHE = Cache() +CACHE.update() + + +class AuthzRequest: + + result = None + final_result = "Deny" + req_max_delay = 2 + + def __init__(self, ctx, args=None): + self.context = Context(ctx, CACHE) + self.args = args + self.request_id = ctx["request_id"] + if ctx['project_id'] not in CACHE.container_chaining: + raise exceptions.KeystoneProjectError("Unknown Project ID {}".format(ctx['project_id'])) + self.container_chaining = CACHE.container_chaining[ctx['project_id']] + if len(self.container_chaining) == 0: + raise exceptions.MoonError('Void container chaining') + self.pdp_container = self.container_chaining[0]["container_id"] + self.run() + + def run(self): + self.context.delete_cache() + try: + req = requests.post("http://{}:{}/authz".format( + self.container_chaining[0]["hostname"], + self.container_chaining[0]["port"], + ), data=pickle.dumps(self.context)) + if req.status_code != 200: + # LOG.error("Cannot connect to {}".format( + # "http://{}:{}/authz".format( + # self.container_chaining[0]["hostname"], + # self.container_chaining[0]["port"] + # ))) + raise exceptions.AuthzException( + "Receive bad response from Authz function " + "(with hostname - {})".format( + req.status_code + )) + except requests.exceptions.ConnectionError: + try: + req = requests.post("http://{}:{}/authz".format( + self.container_chaining[0]["hostip"], + self.container_chaining[0]["port"], + ), data=pickle.dumps(self.context)) + if req.status_code != 200: + # LOG.error("req={}".format(req)) + raise exceptions.AuthzException( + "Receive bad response from Authz function " + "(with IP address - {})".format( + req.status_code + )) + except requests.exceptions.ConnectionError: + LOG.error("Cannot connect to {}".format( + "http://{}:{}/authz".format( + self.container_chaining[0]["hostip"], + self.container_chaining[0]["port"] + ))) + raise exceptions.AuthzException( + "Cannot connect to Authz function with IP address") + self.context.set_cache(CACHE) + if len(self.container_chaining) == 1: + # req.raw.decode_content = True + self.result = pickle.loads(req.content) + + def __exec_next_state(self, rule_found): + index = self.context.index + current_meta_rule = self.context.headers[index] + current_container = self.__get_container_from_meta_rule(current_meta_rule) + current_container_genre = current_container["genre"] + try: + next_meta_rule = self.context.headers[index + 1] + except IndexError: + next_meta_rule = None + if current_container_genre == "authz": + if rule_found: + return True + pass + if next_meta_rule: + # next will be session if current is deny and session is unset + if self.payload["authz_context"]['pdp_set'][next_meta_rule]['effect'] == "unset": + return notify( + request_id=self.payload["authz_context"]["request_id"], + container_id=self.__get_container_from_meta_rule(next_meta_rule)['container_id'], + payload=self.payload) + # next will be delegation if current is deny and session is passed or deny and delegation is unset + else: + LOG.error("Delegation is not developed!") + + else: + # else next will be None and the request is sent to router + return self.__return_to_router() + elif current_container_genre == "session": + pass + # next will be next container in headers if current is passed + if self.payload["authz_context"]['pdp_set'][current_meta_rule]['effect'] == "passed": + return notify( + request_id=self.payload["authz_context"]["request_id"], + container_id=self.__get_container_from_meta_rule(next_meta_rule)['container_id'], + payload=self.payload) + # next will be None if current is grant and the request is sent to router + else: + return self.__return_to_router() + elif current_container_genre == "delegation": + LOG.error("Delegation is not developed!") + # next will be authz if current is deny + # next will be None if current is grant and the request is sent to router + + def set_result(self, result): + self.result = result + + def is_authz(self): + if not self.result: + return False + authz_results = [] + for key in self.result.pdp_set: + if "effect" in self.result.pdp_set[key]: + if self.result.pdp_set[key]["effect"] == "grant": + # the pdp is a authorization PDP and grant the request + authz_results.append(True) + elif self.result.pdp_set[key]["effect"] == "passed": + # the pdp is not a authorization PDP (session or delegation) and had run normally + authz_results.append(True) + elif self.result.pdp_set[key]["effect"] == "unset": + # the pdp is not a authorization PDP (session or delegation) and had not yep run + authz_results.append(True) + else: + # the pdp is (or not) a authorization PDP and had run badly + authz_results.append(False) + if list(itertools.accumulate(authz_results, lambda x, y: x & y))[-1]: + self.result.pdp_set["effect"] = "grant" + if self.result: + if self.result.pdp_set["effect"] == "grant": + self.final_result = "Grant" + return True + self.final_result = "Deny" + return True + + # def notify(self, request_id, container_id, payload): + # LOG.info("notify {} {} {}".format(request_id, container_id, payload)) + # # TODO: send the notification and wait for the result + # # req = requests.get() diff --git a/moonv4/moon_interface/moon_interface/containers.py b/moonv4/moon_interface/moon_interface/containers.py new file mode 100644 index 00000000..1ca76a2d --- /dev/null +++ b/moonv4/moon_interface/moon_interface/containers.py @@ -0,0 +1,102 @@ +# Copyright 2017 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'. + +import docker +import logging +import re +import requests +import time +from moon_utilities import configuration, exceptions + +__version__ = "0.1.0" + +LOG = logging.getLogger("moon.interface.container") + + +class DockerManager: + + def __init__(self): + docker_conf = configuration.get_configuration("docker")['docker'] + self.docker = docker.DockerClient(base_url=docker_conf['url']) + + def create_container(self, data): + """Create the container through the docker client + + :param data: { + "name": "authz", + "hostname": "authz123456789", + "port": { + "PrivatePort": 8090, + "Type": "tcp", + "IP": "0.0.0.0", + "PublicPort": 8090 + }, + "keystone_project_id": "keystone_project_id1", + "pdp_id": "123456789", + "container_name": "wukongsun/moon_authz:v4.1" + } + :return: container output + """ + output = self.docker.containers.run( + image=data.get("container_name"), + hostname=data.get("hostname", data.get("name"))[:63], + name=data.get("name"), + network='moon', + ports={'{}/{}'.format( + data.get("port").get("PrivatePort"), + data.get("port").get("Type") + ): int(data.get("port").get("PrivatePort"))}, + environment={ + "UUID": data.get("hostname"), + "BIND": data.get("port").get("IP"), + "TYPE": data.get("plugin_name"), + "PORT": data.get("port").get("PrivatePort"), + "PDP_ID": data.get("pdp_id"), + "META_RULE_ID": data.get("meta_rule_id"), + "KEYSTONE_PROJECT_ID": data.get("keystone_project_id"), + }, + detach=True + ) + try: + req = requests.head("http://{}:{}/".format(data.get("hostname"), data.get("port").get("PublicPort"))) + except requests.exceptions.ConnectionError: + pass + else: + if req.status_code != 200: + raise exceptions.DockerError("Container {} is not running!".format(data.get("hostname"))) + output.ip = "0.0.0.0" + return output + + # Note: host is not reachable through hostname so trying to find th IP address + res = output.exec_run("ip addr") + find = re.findall("inet (\d+\.\d+\.\d+\.\d+)", res.decode("utf-8")) + ip = "127.0.0.1" + for ip in find: + if ip.startswith("127"): + continue + break + cpt = 0 + while True: + try: + req = requests.head("http://{}:{}/".format(ip, data.get("port").get("PublicPort"))) + except requests.exceptions.ConnectionError: + pass + else: + if req.status_code not in (200, 201): + LOG.error("url={}".format("http://{}:{}/".format(ip, data.get("port").get("PublicPort")))) + LOG.error("req={}".format(req)) + raise exceptions.DockerError("Container {} is not running!".format(data.get("hostname"))) + output.ip = ip + return output + finally: + cpt += 1 + time.sleep(0.1) + if cpt > 20: + break + output.ip = ip + return output + + def delete_container(self, uuid): + raise NotImplementedError diff --git a/moonv4/moon_interface/moon_interface/http_server.py b/moonv4/moon_interface/moon_interface/http_server.py index 046337a2..d7f8469c 100644 --- a/moonv4/moon_interface/moon_interface/http_server.py +++ b/moonv4/moon_interface/moon_interface/http_server.py @@ -3,23 +3,16 @@ # license which can be found in the file 'LICENSE' in this package distribution # or at 'http://www.apache.org/licenses/LICENSE-2.0'. -from flask import Flask +from flask import Flask, jsonify from flask_cors import CORS, cross_origin from flask_restful import Resource, Api import logging from moon_interface import __version__ from moon_interface.api.generic import Status, Logs, API -from moon_interface.api.models import Models -from moon_interface.api.policies import Policies -from moon_interface.api.pdp import PDP -from moon_interface.api.meta_rules import MetaRules -from moon_interface.api.meta_data import SubjectCategories, ObjectCategories, ActionCategories -from moon_interface.api.perimeter import Subjects, Objects, Actions -from moon_interface.api.data import SubjectData, ObjectData, ActionData -from moon_interface.api.assignments import SubjectAssignments, ObjectAssignments, ActionAssignments -from moon_interface.api.rules import Rules from moon_interface.api.authz import Authz -from moon_utilities import exceptions +from moon_interface.api.wrapper import Wrapper +from moon_interface.authz_requests import CACHE +from moon_utilities import configuration, exceptions logger = logging.getLogger("moon.interface.http") @@ -68,13 +61,7 @@ class Server: raise NotImplementedError() __API__ = ( - Status, Logs, API, - MetaRules, SubjectCategories, ObjectCategories, ActionCategories, - Subjects, Objects, Actions, - SubjectAssignments, ObjectAssignments, ActionAssignments, - SubjectData, ObjectData, ActionData, - Rules, Authz, - Models, Policies, PDP + Status, Logs, API ) @@ -106,24 +93,28 @@ 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/manager") + self.manager_hostname = conf["components/manager"].get("hostname", "manager") + self.manager_port = conf["components/manager"].get("port", 80) #Todo : specify only few urls instead of * CORS(self.app) self.api = Api(self.app) self.__set_route() - # self.__hook_errors() + self.__hook_errors() - @self.app.errorhandler(exceptions.AuthException) - def _auth_exception(error): - return {"error": "Unauthorized"}, 401 + # @self.app.errorhandler(exceptions.AuthException) + # def _auth_exception(error): + # return {"error": "Unauthorized"}, 401 def __hook_errors(self): - # FIXME (dthom): it doesn't work + def get_404_json(e): - return {"error": "Error", "code": 404, "description": 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 {"error": "Error", "code": 400, "description": 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) @@ -132,7 +123,23 @@ class HTTPServer(Server): for api in __API__: self.api.add_resource(api, *api.__urls__) + self.api.add_resource(Wrapper, *Wrapper.__urls__, + resource_class_kwargs={ + "port": self.port, + "cache": CACHE, + "interface_name": self.host, + "manager_url": "http://{}:{}".format(self.manager_hostname, self.manager_port), + } + ) + self.api.add_resource(Authz, *Authz.__urls__, + resource_class_kwargs={ + "cache": CACHE, + "interface_name": self.host, + "manager_url": "http://{}:{}".format(self.manager_hostname, self.manager_port), + } + ) def run(self): - self.app.run(debug=True, host=self._host, port=self._port) # nosec + 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_interface/moon_interface/server.py b/moonv4/moon_interface/moon_interface/server.py index 711aa00a..86274ec5 100644 --- a/moonv4/moon_interface/moon_interface/server.py +++ b/moonv4/moon_interface/moon_interface/server.py @@ -14,7 +14,6 @@ def main(): configuration.init_logging() try: conf = configuration.get_configuration("components/interface") - LOG.debug("interface.conf={}".format(conf)) hostname = conf["components/interface"].get("hostname", "interface") port = conf["components/interface"].get("port", 80) bind = conf["components/interface"].get("bind", "127.0.0.1") @@ -25,6 +24,8 @@ def main(): configuration.add_component(uuid="interface", name=hostname, port=port, bind=bind) LOG.info("Starting server with IP {} on port {} bind to {}".format(hostname, port, bind)) server = HTTPServer(host=bind, port=port) + # LOG.info("Starting server") + # server = HTTPServer(host="0.0.0.0", port=8081) server.run() diff --git a/moonv4/moon_manager/moon_manager/api/containers.py b/moonv4/moon_manager/moon_manager/api/containers.py new file mode 100644 index 00000000..44e7baac --- /dev/null +++ b/moonv4/moon_manager/moon_manager/api/containers.py @@ -0,0 +1,179 @@ +# 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'. +""" +PDP are Policy Decision Point. + +""" + +import copy +from docker import Client +from flask import request +from flask_restful import Resource +from oslo_log import log as logging +from moon_utilities.security_functions import check_auth +from moon_db.core import PDPManager +from moon_utilities import configuration, exceptions + +docker_conf = configuration.get_configuration("docker")['docker'] +docker = Client(base_url=docker_conf['url']) + +__version__ = "0.1.0" + +LOG = logging.getLogger("moon.manager.api." + __name__) + + +class Container(Resource): + """ + Endpoint for container requests + """ + + __urls__ = ( + "/containers", + "/containers/", + "/containers/<string:uuid>", + "/containers/<string:uuid>/", + ) + + def __init__(self): + self.containers = {} + self.update() + + def update(self): + for _container in docker.containers(): + if _container['Id'] not in self.containers: + self.containers[_container['Id']] = { + "name": _container["Names"], + "port": _container["Ports"], + } + + @check_auth + def get(self, uuid=None, user_id=None): + """Retrieve all containers + + :param uuid: uuid of the container + :param user_id: user ID who do the request + :return: { + "containers": { + "da0fd80fc1dc146e1b...a2e07d240cde09f0a": { + "name": [ + "/wrapper" + ], + "port": [ + { + "PrivatePort": 8080, + "Type": "tcp", + "IP": "0.0.0.0", + "PublicPort": 8080 + } + ] + }, + } + } + :internal_api: get_containers + """ + # try: + # data = [{"name": item["Names"], "port": item["Ports"], } for item in docker.containers()] + # except Exception as e: + # LOG.error(e, exc_info=True) + # return {"result": False, + # "error": str(e)} + return {"containers": self.containers} + + @check_auth + def post(self, uuid=None, user_id=None): + """Add a new container. + + :param uuid: uuid of the pdp (not used here) + :param user_id: user ID who do the request + :request body: { + "id": "id of the new container", + "name": "name of the new container", + "hostname": "hostname of the new container", + "port": { + "PrivatePort": 8080, + "Type": "tcp", + "IP": "0.0.0.0", + "PublicPort": 8080 + }, + "keystone_project_id": "keystone_project_id1", + "pdp_id": "PDP UUID", + "container_name": "wukongsun/moon_authz:v4.1" + } + :return: { + "containers": { + "da0fd80fc1dc146e1b...a2e07d240cde09f0a": { + "name": [ + "/wrapper" + ], + "port": [ + { + "PrivatePort": 8080, + "Type": "tcp", + "IP": "0.0.0.0", + "PublicPort": 8080 + } + ] + }, + } + } + :internal_api: add_container + """ + try: + self.update() + self.containers[request.json.get('id')] = copy.deepcopy(request.json) + LOG.info("Added a new container {}".format(request.json.get('name'))) + except Exception as e: + LOG.error(e, exc_info=True) + return {"result": False, + "error": str(e)}, 500 + return {"containers": self.containers} + + @check_auth + def delete(self, uuid=None, user_id=None): + """Delete a pdp + + :param uuid: uuid of the pdp to delete + :param user_id: user ID who do the request + :return: { + "result": "True or False", + "message": "optional message" + } + :internal_api: delete_pdp + """ + # try: + # data = PDPManager.delete_pdp(user_id=user_id, pdp_id=uuid) + # except Exception as e: + # LOG.error(e, exc_info=True) + # return {"result": False, + # "error": str(e)} + # return {"result": True} + raise NotImplementedError + + @check_auth + def patch(self, uuid=None, user_id=None): + """Update a pdp + + :param uuid: uuid of the pdp to update + :param user_id: user ID who do the request + :return: { + "pdp_id1": { + "name": "...", + "security_pipeline": [...], + "keystone_project_id": "keystone_project_id1", + "description": "...", + } + } + :internal_api: update_pdp + """ + # try: + # data = PDPManager.update_pdp(user_id=user_id, pdp_id=uuid, value=request.json) + # add_container(uuid=uuid, pipeline=data[uuid]['security_pipeline']) + # except Exception as e: + # LOG.error(e, exc_info=True) + # return {"result": False, + # "error": str(e)} + # return {"pdps": data} + raise NotImplementedError + diff --git a/moonv4/moon_manager/moon_manager/http_server.py b/moonv4/moon_manager/moon_manager/http_server.py index bdd429e4..a59921f0 100644 --- a/moonv4/moon_manager/moon_manager/http_server.py +++ b/moonv4/moon_manager/moon_manager/http_server.py @@ -18,6 +18,7 @@ from moon_manager.api.perimeter import Subjects, Objects, Actions from moon_manager.api.data import SubjectData, ObjectData, ActionData from moon_manager.api.assignments import SubjectAssignments, ObjectAssignments, ActionAssignments from moon_manager.api.rules import Rules +from moon_manager.api.containers import Container from moon_utilities import configuration, exceptions logger = logging.getLogger("moon.manager.http") @@ -72,7 +73,7 @@ __API__ = ( Subjects, Objects, Actions, SubjectAssignments, ObjectAssignments, ActionAssignments, SubjectData, ObjectData, ActionData, - Rules, + Rules, Container, Models, Policies, PDP ) diff --git a/moonv4/moon_manager/requirements.txt b/moonv4/moon_manager/requirements.txt index e7be3d0e..6b2af70b 100644 --- a/moonv4/moon_manager/requirements.txt +++ b/moonv4/moon_manager/requirements.txt @@ -2,4 +2,5 @@ flask flask_restful flask_cors moon_utilities -moon_db
\ No newline at end of file +moon_db +docker-py diff --git a/moonv4/moon_utilities/Changelog b/moonv4/moon_utilities/Changelog index e39a8b53..51a007c2 100644 --- a/moonv4/moon_utilities/Changelog +++ b/moonv4/moon_utilities/Changelog @@ -35,3 +35,7 @@ CHANGES ----- - Add authentication features for interface +1.3.0 +----- +- Add cache functionality + diff --git a/moonv4/moon_utilities/build.sh b/moonv4/moon_utilities/build.sh new file mode 100644 index 00000000..4c7db18d --- /dev/null +++ b/moonv4/moon_utilities/build.sh @@ -0,0 +1,21 @@ + + +VERSION=moon_utilities-1.2.0 + +python3 setup.py sdist bdist_wheel + +rm dist/*.asc + +gpg --detach-sign -u "A0A96E75" -a dist/${VERSION}-py3-none-any.whl +gpg --detach-sign -u "A0A96E75" -a dist/${VERSION}.linux-x86_64.tar.gz + +if [ "$1" = "upload" ]; then + twine upload dist/${VERSION}-py3-none-any.whl dist/${VERSION}-py3-none-any.whl.asc + twine upload dist/${VERSION}.linux-x86_64.tar.gz dist/${VERSION}.linux-x86_64.tar.gz.asc +fi + +cp dist/${VERSION}-py3-none-any.whl ../moon_orchestrator/dist/ +cp dist/${VERSION}-py3-none-any.whl ../moon_router/dist/ +cp dist/${VERSION}-py3-none-any.whl ../moon_interface/dist/ +cp dist/${VERSION}-py3-none-any.whl ../moon_manager/dist/ +cp dist/${VERSION}-py3-none-any.whl ../moon_authz/dist/ diff --git a/moonv4/moon_utilities/moon_utilities/__init__.py b/moonv4/moon_utilities/moon_utilities/__init__.py index 2c7f8f5c..6e5782ce 100644 --- a/moonv4/moon_utilities/moon_utilities/__init__.py +++ b/moonv4/moon_utilities/moon_utilities/__init__.py @@ -3,4 +3,4 @@ # license which can be found in the file 'LICENSE' in this package distribution # or at 'http://www.apache.org/licenses/LICENSE-2.0'. -__version__ = "1.2.0" +__version__ = "1.3.0" diff --git a/moonv4/moon_utilities/moon_utilities/cache.py b/moonv4/moon_utilities/moon_utilities/cache.py new file mode 100644 index 00000000..7c938b39 --- /dev/null +++ b/moonv4/moon_utilities/moon_utilities/cache.py @@ -0,0 +1,520 @@ +import logging +import time +import requests +from uuid import uuid4 +from moon_utilities import configuration, exceptions + +LOG = logging.getLogger("moon.utilities.cache") + + +class Cache(object): + # TODO (asteroide): set cache integer in CONF file + __UPDATE_INTERVAL = 10 + + __CONTAINERS = {} + __CONTAINERS_UPDATE = 0 + + __CONTAINER_CHAINING_UPDATE = 0 + __CONTAINER_CHAINING = {} + + __PDP = {} + __PDP_UPDATE = 0 + + __POLICIES = {} + __POLICIES_UPDATE = 0 + + __MODELS = {} + __MODELS_UPDATE = 0 + + __SUBJECTS = {} + __OBJECTS = {} + __ACTIONS = {} + + __SUBJECT_ASSIGNMENTS = {} + __OBJECT_ASSIGNMENTS = {} + __ACTION_ASSIGNMENTS = {} + + __SUBJECT_CATEGORIES = {} + __SUBJECT_CATEGORIES_UPDATE = 0 + __OBJECT_CATEGORIES = {} + __OBJECT_CATEGORIES_UPDATE = 0 + __ACTION_CATEGORIES = {} + __ACTION_CATEGORIES_UPDATE = 0 + + __META_RULES = {} + __META_RULES_UPDATE = 0 + + __RULES = {} + __RULES_UPDATE = 0 + + __AUTHZ_REQUESTS = {} + + + def __init__(self): + self.manager_url = "{}://{}:{}".format( + configuration.get_components()['manager'].get('protocol', 'http'), + configuration.get_components()['manager']['hostname'], + configuration.get_components()['manager']['port'] + ) + self.orchestrator_url = "{}://{}:{}".format( + configuration.get_components()['orchestrator'].get('protocol', 'http'), + configuration.get_components()['orchestrator']['hostname'], + configuration.get_components()['orchestrator']['port'] + ) + + def update(self): + self.__update_container() + self.__update_pdp() + self.__update_policies() + self.__update_models() + for key, value in self.__PDP.items(): + # LOG.info("Updating container_chaining with {}".format(value["keystone_project_id"])) + self.__update_container_chaining(value["keystone_project_id"]) + + @property + def authz_requests(self): + return self.__AUTHZ_REQUESTS + + # perimeter functions + + @property + def subjects(self): + return self.__SUBJECTS + + def update_subjects(self, policy_id=None): + req = requests.get("{}/policies/{}/subjects".format(self.manager_url, policy_id)) + self.__SUBJECTS[policy_id] = req.json()['subjects'] + + def get_subject(self, policy_id, name): + try: + for _subject_id, _subject_dict in self.__SUBJECTS[policy_id].items(): + if _subject_dict["name"] == name: + return _subject_id + except KeyError: + pass + self.update_subjects(policy_id) + for _subject_id, _subject_dict in self.__SUBJECTS[policy_id].items(): + if _subject_dict["name"] == name: + return _subject_id + raise exceptions.SubjectUnknown("Cannot find subject {}".format(name)) + + @property + def objects(self): + return self.__OBJECTS + + def update_objects(self, policy_id=None): + req = requests.get("{}/policies/{}/objects".format(self.manager_url, policy_id)) + self.__OBJECTS[policy_id] = req.json()['objects'] + + def get_object(self, policy_id, name): + try: + for _object_id, _object_dict in self.__OBJECTS[policy_id].items(): + if _object_dict["name"] == name: + return _object_id + except KeyError: + pass + self.update_objects(policy_id) + for _object_id, _object_dict in self.__OBJECTS[policy_id].items(): + if _object_dict["name"] == name: + return _object_id + raise exceptions.SubjectUnknown("Cannot find object {}".format(name)) + + @property + def actions(self): + return self.__ACTIONS + + def update_actions(self, policy_id=None): + req = requests.get("{}/policies/{}/actions".format(self.manager_url, policy_id)) + self.__ACTIONS[policy_id] = req.json()['actions'] + + def get_action(self, policy_id, name): + try: + for _action_id, _action_dict in self.__ACTIONS[policy_id].items(): + if _action_dict["name"] == name: + return _action_id + except KeyError: + pass + self.update_actions(policy_id) + for _action_id, _action_dict in self.__ACTIONS[policy_id].items(): + if _action_dict["name"] == name: + return _action_id + raise exceptions.SubjectUnknown("Cannot find action {}".format(name)) + + # meta_rule functions + + @property + def meta_rules(self): + current_time = time.time() + if self.__META_RULES_UPDATE + self.__UPDATE_INTERVAL < current_time: + self.__update_meta_rules() + self.__META_RULES_UPDATE = current_time + return self.__META_RULES + + def __update_meta_rules(self): + req = requests.get("{}/meta_rules".format(self.manager_url)) + self.__META_RULES = req.json()['meta_rules'] + + # rule functions + + @property + def rules(self): + current_time = time.time() + if self.__RULES_UPDATE + self.__UPDATE_INTERVAL < current_time: + self.__update_rules() + self.__RULES_UPDATE = current_time + return self.__RULES + + def __update_rules(self): + for policy_id in self.__POLICIES: + LOG.info("Get {}".format("{}/policies/{}/rules".format( + self.manager_url, policy_id))) + req = requests.get("{}/policies/{}/rules".format( + self.manager_url, policy_id)) + self.__RULES[policy_id] = req.json()['rules'] + LOG.info("UPDATE RULES {}".format(self.__RULES)) + + # assignment functions + + @property + def subject_assignments(self): + return self.__SUBJECT_ASSIGNMENTS + + def update_subject_assignments(self, policy_id=None, perimeter_id=None): + if perimeter_id: + req = requests.get("{}/policies/{}/subject_assignments/{}".format( + self.manager_url, policy_id, perimeter_id)) + else: + req = requests.get("{}/policies/{}/subject_assignments".format(self.manager_url, policy_id)) + if policy_id not in self.__SUBJECT_ASSIGNMENTS: + self.__SUBJECT_ASSIGNMENTS[policy_id] = {} + self.__SUBJECT_ASSIGNMENTS[policy_id].update(req.json()['subject_assignments']) + + def get_subject_assignments(self, policy_id, perimeter_id, category_id): + if policy_id not in self.subject_assignments: + self.update_subject_assignments(policy_id, perimeter_id) + if policy_id not in self.subject_assignments: + raise Exception("Cannot found the policy {}".format(policy_id)) + for key, value in self.subject_assignments[policy_id].items(): + if perimeter_id == value['subject_id'] and category_id == value['category_id']: + return value['assignments'] + return [] + + @property + def object_assignments(self): + return self.__OBJECT_ASSIGNMENTS + + def update_object_assignments(self, policy_id=None, perimeter_id=None): + if perimeter_id: + req = requests.get("{}/policies/{}/object_assignments/{}".format( + self.manager_url, policy_id, perimeter_id)) + else: + req = requests.get("{}/policies/{}/object_assignments".format(self.manager_url, policy_id)) + if policy_id not in self.__OBJECT_ASSIGNMENTS: + self.__OBJECT_ASSIGNMENTS[policy_id] = {} + self.__OBJECT_ASSIGNMENTS[policy_id].update(req.json()['object_assignments']) + + def get_object_assignments(self, policy_id, perimeter_id, category_id): + if policy_id not in self.object_assignments: + self.update_object_assignments(policy_id, perimeter_id) + if policy_id not in self.object_assignments: + raise Exception("Cannot found the policy {}".format(policy_id)) + for key, value in self.object_assignments[policy_id].items(): + if perimeter_id == value['object_id'] and category_id == value['category_id']: + return value['assignments'] + return [] + + @property + def action_assignments(self): + return self.__ACTION_ASSIGNMENTS + + def update_action_assignments(self, policy_id=None, perimeter_id=None): + if perimeter_id: + req = requests.get("{}/policies/{}/action_assignments/{}".format( + self.manager_url, policy_id, perimeter_id)) + else: + req = requests.get("{}/policies/{}/action_assignments".format(self.manager_url, policy_id)) + if policy_id not in self.__ACTION_ASSIGNMENTS: + self.__ACTION_ASSIGNMENTS[policy_id] = {} + self.__ACTION_ASSIGNMENTS[policy_id].update(req.json()['action_assignments']) + + def get_action_assignments(self, policy_id, perimeter_id, category_id): + if policy_id not in self.action_assignments: + self.update_action_assignments(policy_id, perimeter_id) + if policy_id not in self.action_assignments: + raise Exception("Cannot found the policy {}".format(policy_id)) + for key, value in self.action_assignments[policy_id].items(): + if perimeter_id == value['action_id'] and category_id == value['category_id']: + return value['assignments'] + return [] + + # category functions + + @property + def subject_categories(self): + current_time = time.time() + if self.__SUBJECT_CATEGORIES_UPDATE + self.__UPDATE_INTERVAL < current_time: + self.__update_subject_categories() + self.__SUBJECT_CATEGORIES_UPDATE = current_time + return self.__SUBJECT_CATEGORIES + + def __update_subject_categories(self): + req = requests.get("{}/policies/subject_categories".format( + self.manager_url)) + self.__SUBJECT_CATEGORIES.update(req.json()['subject_categories']) + + @property + def object_categories(self): + current_time = time.time() + if self.__OBJECT_CATEGORIES_UPDATE + self.__UPDATE_INTERVAL < current_time: + self.__update_object_categories() + self.__OBJECT_CATEGORIES_UPDATE = current_time + return self.__OBJECT_CATEGORIES + + def __update_object_categories(self): + req = requests.get("{}/policies/object_categories".format( + self.manager_url)) + self.__OBJECT_CATEGORIES.update(req.json()['object_categories']) + + @property + def action_categories(self): + current_time = time.time() + if self.__ACTION_CATEGORIES_UPDATE + self.__UPDATE_INTERVAL < current_time: + self.__update_action_categories() + self.__ACTION_CATEGORIES_UPDATE = current_time + return self.__ACTION_CATEGORIES + + def __update_action_categories(self): + req = requests.get("{}/policies/action_categories".format( + self.manager_url)) + self.__ACTION_CATEGORIES.update(req.json()['action_categories']) + + # PDP functions + + def __update_pdp(self): + req = requests.get("{}/pdp".format(self.manager_url)) + pdp = req.json() + for _pdp in pdp["pdps"].values(): + if _pdp['keystone_project_id'] not in self.__CONTAINER_CHAINING: + self.__CONTAINER_CHAINING[_pdp['keystone_project_id']] = {} + # Note (asteroide): force update of chaining + self.__update_container_chaining(_pdp['keystone_project_id']) + for key, value in pdp["pdps"].items(): + self.__PDP[key] = value + + @property + def pdp(self): + """Policy Decision Point + Example of content: + { + "pdp_id": { + "keystone_project_id": "keystone_project_id", + "name": "pdp1", + "description": "test", + "security_pipeline": [ + "policy_id" + ] + } + } + + :return: + """ + current_time = time.time() + if self.__PDP_UPDATE + self.__UPDATE_INTERVAL < current_time: + self.__update_pdp() + self.__PDP_UPDATE = current_time + return self.__PDP + + # policy functions + def __update_policies(self): + req = requests.get("{}/policies".format(self.manager_url)) + policies = req.json() + for key, value in policies["policies"].items(): + self.__POLICIES[key] = value + + @property + def policies(self): + current_time = time.time() + if self.__POLICIES_UPDATE + self.__UPDATE_INTERVAL < current_time: + self.__update_policies() + self.__POLICIES_UPDATE = current_time + return self.__POLICIES + + # model functions + + def __update_models(self): + req = requests.get("{}/models".format(self.manager_url)) + models = req.json() + for key, value in models["models"].items(): + self.__MODELS[key] = value + + @property + def models(self): + current_time = time.time() + if self.__MODELS_UPDATE + self.__UPDATE_INTERVAL < current_time: + self.__update_models() + self.__MODELS_UPDATE = current_time + return self.__MODELS + + # helper functions + + def get_policy_from_meta_rules(self, meta_rule_id): + for pdp_key, pdp_value in self.pdp.items(): + for policy_id in pdp_value["security_pipeline"]: + model_id = self.policies[policy_id]["model_id"] + if meta_rule_id in self.models[model_id]["meta_rules"]: + return policy_id + + def get_pdp_from_keystone_project(self, keystone_project_id): + for pdp_key, pdp_value in self.pdp.items(): + if keystone_project_id == pdp_value["keystone_project_id"]: + return pdp_key + + def get_keystone_project_id_from_policy_id(self, policy_id): + for pdp_key, pdp_value in self.pdp.items(): + if policy_id in pdp_value["security_pipeline"]: + return pdp_value["keystone_project_id"] + # for policy_id in pdp_value["security_pipeline"]: + # model_id = self.policies[policy_id]["model_id"] + # if meta_rule_id in self.models[model_id]["meta_rules"]: + # return pdp_value["keystone_project_id"] + + def get_containers_from_keystone_project_id(self, keystone_project_id, meta_rule_id=None): + for container_id, container_value in self.containers.items(): + if 'keystone_project_id' not in container_value: + continue + if container_value['keystone_project_id'] == keystone_project_id: + if not meta_rule_id: + yield container_id, container_value + elif container_value['meta_rule_id'] == meta_rule_id: + yield container_id, container_value + break + + # containers functions + + def __update_container(self): + req = requests.get("{}/containers".format(self.manager_url)) + containers = req.json() + for key, value in containers["containers"].items(): + if key not in self.__CONTAINERS: + self.__CONTAINERS[key] = value + else: + self.__CONTAINERS[key].update(value) + + def add_container(self, container_data): + """Add a new container in the cache + + :param container_data: dictionary with information for the container + Example: + { + "name": name, + "hostname": name, + "port": { + "PrivatePort": tcp_port, + "Type": "tcp", + "IP": "0.0.0.0", + "PublicPort": tcp_port + }, + "keystone_project_id": uuid, + "pdp_id": self.CACHE.get_pdp_from_keystone_project(uuid), + "meta_rule_id": meta_rule_id, + "container_name": container_name, + "plugin_name": plugin_name + "container_id": "container_id" + } + + :return: + """ + self.__CONTAINERS[uuid4().hex] = { + "keystone_project_id": container_data['keystone_project_id'], + "name": container_data['name'], + "container_id": container_data['container_id'], + "hostname": container_data['name'], + "policy_id": container_data['policy_id'], + "meta_rule_id": container_data['meta_rule_id'], + "port": [ + { + "PublicPort": container_data['port']["PublicPort"], + "Type": container_data['port']["Type"], + "IP": container_data['port']["IP"], + "PrivatePort": container_data['port']["PrivatePort"] + } + ], + "genre": container_data['plugin_name'] + } + self.__update_container_chaining(self.get_keystone_project_id_from_policy_id(container_data['policy_id'])) + + @property + def containers(self): + """Containers cache + example of content : + { + "policy_uuid1": "component_hostname1", + "policy_uuid2": "component_hostname2", + } + :return: + """ + current_time = time.time() + if self.__CONTAINERS_UPDATE + self.__UPDATE_INTERVAL < current_time: + self.__update_container() + self.__CONTAINERS_UPDATE = current_time + return self.__CONTAINERS + + @property + def container_chaining(self): + """Cache for mapping Keystone Project ID with meta_rule ID and container ID + Example of content: + { + "keystone_project_id": [ + { + "container_id": "container_id", + "genre": "genre", + "policy_id": "policy_id", + "meta_rule_id": "meta_rule_id", + } + ] + } + + :return: + """ + current_time = time.time() + if self.__CONTAINER_CHAINING_UPDATE + self.__UPDATE_INTERVAL < current_time: + for key, value in self.pdp.items(): + self.__update_container_chaining(value["keystone_project_id"]) + self.__CONTAINER_CHAINING_UPDATE = current_time + LOG.info(self.__CONTAINER_CHAINING_UPDATE) + return self.__CONTAINER_CHAINING + + def __update_container_chaining(self, keystone_project_id): + container_ids = [] + for pdp_id, pdp_value, in self.__PDP.items(): + # LOG.info("pdp_id, pdp_value = {}, {}".format(pdp_id, pdp_value)) + # LOG.info("__POLICIES = {}".format(self.__POLICIES)) + if pdp_value: + if pdp_value["keystone_project_id"] == keystone_project_id: + for policy_id in pdp_value["security_pipeline"]: + model_id = self.__POLICIES[policy_id]['model_id'] + # LOG.info("model_id = {}".format(model_id)) + # LOG.info("CACHE = {}".format(self.__MODELS[model_id])) + # LOG.info("CACHE.containers = {}".format(self.__CONTAINERS)) + # LOG.info("CACHE.models = {}".format(self.__MODELS)) + for meta_rule_id in self.__MODELS[model_id]["meta_rules"]: + # LOG.info("meta_rule = {}".format(self.__MODELS[model_id]["meta_rules"])) + for container_id, container_value in self.get_containers_from_keystone_project_id( + keystone_project_id, + meta_rule_id + ): + # LOG.info("CONTAINER: {} {}".format(container_id, container_value)) + container_ids.append( + { + "container_id": self.__CONTAINERS[container_id]["container_id"], + "genre": self.__CONTAINERS[container_id]["genre"], + "policy_id": policy_id, + "meta_rule_id": meta_rule_id, + "hostname": self.__CONTAINERS[container_id]["hostname"], + "hostip": self.__CONTAINERS[container_id]["port"][0]["IP"], + "port": self.__CONTAINERS[container_id]["port"][0]["PublicPort"], + } + ) + # LOG.info("__update_container_chaining={}".format(container_ids)) + self.__CONTAINER_CHAINING[keystone_project_id] = container_ids + diff --git a/moonv4/moon_utilities/moon_utilities/configuration.py b/moonv4/moon_utilities/moon_utilities/configuration.py index 32eeff13..d1c5545f 100644 --- a/moonv4/moon_utilities/moon_utilities/configuration.py +++ b/moonv4/moon_utilities/moon_utilities/configuration.py @@ -53,8 +53,8 @@ def get_configuration(key): url = "http://{}:{}/v1/kv/{}".format(CONSUL_HOST, CONSUL_PORT, key) req = requests.get(url) if req.status_code != 200: - LOG.info("url={}".format(url)) - raise exceptions.ConsulComponentNotFound + LOG.error("url={}".format(url)) + raise exceptions.ConsulComponentNotFound("error={}: {}".format(req.status_code, req.text)) data = req.json() if len(data) == 1: data = data[0] diff --git a/moonv4/moon_utilities/moon_utilities/get_os_apis.py b/moonv4/moon_utilities/moon_utilities/get_os_apis.py index dea2a878..d019c1cd 100644 --- a/moonv4/moon_utilities/moon_utilities/get_os_apis.py +++ b/moonv4/moon_utilities/moon_utilities/get_os_apis.py @@ -1,4 +1,5 @@ import json +import yaml import logging import requests import argparse @@ -26,6 +27,7 @@ def init(): parser.add_argument("--debug", "-d", action='store_true', help="debug mode") parser.add_argument("--format", "-f", help="Output format (txt, json)", default="json") parser.add_argument("--output", "-o", help="Output filename") + parser.add_argument("--from-policies", "-p", help="Get API from policy.{json,yaml}", target="policies") parser.add_argument("--credentials", "-c", help="Github credential filename (inside format user:pass)") args = parser.parse_args() @@ -98,6 +100,13 @@ def to_str(results): return output +def get_data_from_policies(policies): + for filename in policies.split(","): + try: + obj = json.loads(open(filename.strip()).read()) + + + def save(results, args): if args.output: if args.format == 'json': @@ -114,8 +123,11 @@ def save(results, args): def main(): args = init() results = {} - for key in URLS: - results[key] = get_content(key, args) + if not args.policies: + for key in URLS: + results[key] = get_content(key, args) + else: + get_data_from_policies(args.policies) save(results, args) if __name__ == "__main__": diff --git a/moonv4/moon_utilities/moon_utilities/security_functions.py b/moonv4/moon_utilities/moon_utilities/security_functions.py index ad1a44fa..98935996 100644 --- a/moonv4/moon_utilities/moon_utilities/security_functions.py +++ b/moonv4/moon_utilities/moon_utilities/security_functions.py @@ -209,59 +209,90 @@ def call(endpoint="security_router", ctx=None, method="route", **kwargs): class Context: - def __init__(self, _keystone_project_id, _subject, _object, _action, _request_id): - from moon_db.core import PDPManager, ModelManager, PolicyManager - self.PolicyManager = PolicyManager - self.ModelManager = ModelManager - self.PDPManager = PDPManager - self.__keystone_project_id = _keystone_project_id + def __init__(self, init_context, cache): + self.cache = cache + self.__keystone_project_id = init_context.get("project_id") self.__pdp_id = None self.__pdp_value = None - for _pdp_key, _pdp_value in PDPManager.get_pdp("admin").items(): - if _pdp_value["keystone_project_id"] == _keystone_project_id: + for _pdp_key, _pdp_value in self.cache.pdp.items(): + if _pdp_value["keystone_project_id"] == self.__keystone_project_id: self.__pdp_id = _pdp_key self.__pdp_value = copy.deepcopy(_pdp_value) break - self.__subject = _subject - self.__object = _object - self.__action = _action + if not self.__pdp_value: + raise exceptions.AuthzException( + "Cannot create context for authz " + "with Keystone project ID {}".format( + self.__keystone_project_id + )) + self.__subject = init_context.get("subject_name") + self.__object = init_context.get("object_name") + self.__action = init_context.get("action_name") self.__current_request = None - self.__request_id = _request_id - self.__index = 0 - self.__init_initial_request() + self.__request_id = init_context.get("req_id") + self.__cookie = init_context.get("cookie") + self.__manager_url = init_context.get("manager_url") + self.__interface_name = init_context.get("interface_name") + self.__index = -1 + # self.__init_initial_request() self.__headers = [] - policies = PolicyManager.get_policies("admin") - models = ModelManager.get_models("admin") + policies = self.cache.policies + models = self.cache.models for policy_id in self.__pdp_value["security_pipeline"]: model_id = policies[policy_id]["model_id"] for meta_rule in models[model_id]["meta_rules"]: self.__headers.append(meta_rule) - self.__meta_rules = ModelManager.get_meta_rules("admin") + self.__meta_rules = self.cache.meta_rules self.__pdp_set = {} + # self.__init_pdp_set() + + def delete_cache(self): + self.cache = {} + + def set_cache(self, cache): + self.cache = cache + + def increment_index(self): + self.__index += 1 + self.__init_current_request() self.__init_pdp_set() - def __init_initial_request(self): - subjects = self.PolicyManager.get_subjects("admin", policy_id=None) - for _subject_id, _subject_dict in subjects.items(): - if _subject_dict["name"] == self.__subject: - self.__subject = _subject_id - break - else: - raise exceptions.SubjectUnknown("Cannot find subject {}".format(self.__subject)) - objects = self.PolicyManager.get_objects("admin", policy_id=None) - for _object_id, _object_dict in objects.items(): - if _object_dict["name"] == self.__object: - self.__object = _object_id - break - else: - raise exceptions.ObjectUnknown("Cannot find object {}".format(self.__object)) - actions = self.PolicyManager.get_actions("admin", policy_id=None) - for _action_id, _action_dict in actions.items(): - if _action_dict["name"] == self.__action: - self.__action = _action_id - break - else: - raise exceptions.ActionUnknown("Cannot find action {}".format(self.__action)) + @property + def current_state(self): + return self.__pdp_set[self.__headers[self.__index]]['effect'] + + @current_state.setter + def current_state(self, state): + if state not in ("grant", "deny", "passed"): + state = "passed" + self.__pdp_set[self.__headers[self.__index]]['effect'] = state + + @current_state.deleter + def current_state(self): + self.__pdp_set[self.__headers[self.__index]]['effect'] = "unset" + + @property + def current_policy_id(self): + return self.__pdp_value["security_pipeline"][self.__index] + + @current_policy_id.setter + def current_policy_id(self, value): + pass + + @current_policy_id.deleter + def current_policy_id(self): + pass + + def __init_current_request(self): + self.__subject = self.cache.get_subject( + self.__pdp_value["security_pipeline"][self.__index], + self.__subject) + self.__object = self.cache.get_object( + self.__pdp_value["security_pipeline"][self.__index], + self.__object) + self.__action = self.cache.get_action( + self.__pdp_value["security_pipeline"][self.__index], + self.__action) self.__current_request = dict(self.initial_request) def __init_pdp_set(self): @@ -269,67 +300,64 @@ class Context: self.__pdp_set[header] = dict() self.__pdp_set[header]["meta_rules"] = self.__meta_rules[header] self.__pdp_set[header]["target"] = self.__add_target(header) - # TODO (asteroide): the following information must be retrieve somewhere self.__pdp_set[header]["effect"] = "unset" self.__pdp_set["effect"] = "deny" - @staticmethod - def update_target(context): - from moon_db.core import PDPManager, ModelManager, PolicyManager - # result = dict() - current_request = context['current_request'] - _subject = current_request.get("subject") - _object = current_request.get("object") - _action = current_request.get("action") - meta_rule_id = context['headers'][context['index']] - policy_id = PolicyManager.get_policy_from_meta_rules("admin", meta_rule_id) - meta_rules = ModelManager.get_meta_rules("admin") - # for meta_rule_id in meta_rules: - for sub_cat in meta_rules[meta_rule_id]['subject_categories']: - if sub_cat not in context["pdp_set"][meta_rule_id]["target"]: - context["pdp_set"][meta_rule_id]["target"][sub_cat] = [] - for assign in PolicyManager.get_subject_assignments("admin", policy_id, _subject, sub_cat).values(): - for assign in assign["assignments"]: - if assign not in context["pdp_set"][meta_rule_id]["target"][sub_cat]: - context["pdp_set"][meta_rule_id]["target"][sub_cat].append(assign) - for obj_cat in meta_rules[meta_rule_id]['object_categories']: - if obj_cat not in context["pdp_set"][meta_rule_id]["target"]: - context["pdp_set"][meta_rule_id]["target"][obj_cat] = [] - for assign in PolicyManager.get_object_assignments("admin", policy_id, _object, obj_cat).values(): - for assign in assign["assignments"]: - if assign not in context["pdp_set"][meta_rule_id]["target"][obj_cat]: - context["pdp_set"][meta_rule_id]["target"][obj_cat].append(assign) - for act_cat in meta_rules[meta_rule_id]['action_categories']: - if act_cat not in context["pdp_set"][meta_rule_id]["target"]: - context["pdp_set"][meta_rule_id]["target"][act_cat] = [] - for assign in PolicyManager.get_action_assignments("admin", policy_id, _action, act_cat).values(): - for assign in assign["assignments"]: - if assign not in context["pdp_set"][meta_rule_id]["target"][act_cat]: - context["pdp_set"][meta_rule_id]["target"][act_cat].append(assign) - # context["pdp_set"][meta_rule_id]["target"].update(result) + # def update_target(self, context): + # # result = dict() + # current_request = context['current_request'] + # _subject = current_request.get("subject") + # _object = current_request.get("object") + # _action = current_request.get("action") + # meta_rule_id = context['headers'][context['index']] + # policy_id = self.cache.get_policy_from_meta_rules(meta_rule_id) + # meta_rules = self.cache.meta_rules() + # # for meta_rule_id in meta_rules: + # for sub_cat in meta_rules[meta_rule_id]['subject_categories']: + # if sub_cat not in context["pdp_set"][meta_rule_id]["target"]: + # context["pdp_set"][meta_rule_id]["target"][sub_cat] = [] + # for assign in self.cache.get_subject_assignments(policy_id, _subject, sub_cat).values(): + # for assign in assign["assignments"]: + # if assign not in context["pdp_set"][meta_rule_id]["target"][sub_cat]: + # context["pdp_set"][meta_rule_id]["target"][sub_cat].append(assign) + # for obj_cat in meta_rules[meta_rule_id]['object_categories']: + # if obj_cat not in context["pdp_set"][meta_rule_id]["target"]: + # context["pdp_set"][meta_rule_id]["target"][obj_cat] = [] + # for assign in self.cache.get_object_assignments(policy_id, _object, obj_cat).values(): + # for assign in assign["assignments"]: + # if assign not in context["pdp_set"][meta_rule_id]["target"][obj_cat]: + # context["pdp_set"][meta_rule_id]["target"][obj_cat].append(assign) + # for act_cat in meta_rules[meta_rule_id]['action_categories']: + # if act_cat not in context["pdp_set"][meta_rule_id]["target"]: + # context["pdp_set"][meta_rule_id]["target"][act_cat] = [] + # for assign in self.cache.get_action_assignments(policy_id, _action, act_cat).values(): + # for assign in assign["assignments"]: + # if assign not in context["pdp_set"][meta_rule_id]["target"][act_cat]: + # context["pdp_set"][meta_rule_id]["target"][act_cat].append(assign) + # # context["pdp_set"][meta_rule_id]["target"].update(result) def __add_target(self, meta_rule_id): result = dict() _subject = self.__current_request["subject"] _object = self.__current_request["object"] _action = self.__current_request["action"] - meta_rules = self.ModelManager.get_meta_rules("admin") - policy_id = self.PolicyManager.get_policy_from_meta_rules("admin", meta_rule_id) + meta_rules = self.cache.meta_rules + policy_id = self.cache.get_policy_from_meta_rules(meta_rule_id) for sub_cat in meta_rules[meta_rule_id]['subject_categories']: if sub_cat not in result: result[sub_cat] = [] - for assign in self.PolicyManager.get_subject_assignments("admin", policy_id, _subject, sub_cat).values(): - result[sub_cat].extend(assign["assignments"]) + result[sub_cat].extend( + self.cache.get_subject_assignments(policy_id, _subject, sub_cat)) for obj_cat in meta_rules[meta_rule_id]['object_categories']: if obj_cat not in result: result[obj_cat] = [] - for assign in self.PolicyManager.get_object_assignments("admin", policy_id, _object, obj_cat).values(): - result[obj_cat].extend(assign["assignments"]) + result[obj_cat].extend( + self.cache.get_object_assignments(policy_id, _object, obj_cat)) for act_cat in meta_rules[meta_rule_id]['action_categories']: if act_cat not in result: result[act_cat] = [] - for assign in self.PolicyManager.get_action_assignments("admin", policy_id, _action, act_cat).values(): - result[act_cat].extend(assign["assignments"]) + result[act_cat].extend( + self.cache.get_action_assignments(policy_id, _action, act_cat)) return result def __repr__(self): @@ -356,6 +384,8 @@ pdp_set: {pdp_set} "index": copy.deepcopy(self.__index), "pdp_set": copy.deepcopy(self.__pdp_set), "request_id": copy.deepcopy(self.__request_id), + "manager_url": copy.deepcopy(self.__manager_url), + "interface_name": copy.deepcopy(self.__interface_name), } @property @@ -371,6 +401,42 @@ pdp_set: {pdp_set} raise Exception("You cannot update the request_id") @property + def manager_url(self): + return self.__manager_url + + @manager_url.setter + def manager_url(self, value): + raise Exception("You cannot update the manager_url") + + @manager_url.deleter + def manager_url(self): + raise Exception("You cannot update the manager_url") + + @property + def interface_name(self): + return self.__interface_name + + @interface_name.setter + def interface_name(self, value): + raise Exception("You cannot update the interface_name") + + @interface_name.deleter + def interface_name(self): + raise Exception("You cannot update the interface_name") + + @property + def cookie(self): + return self.__cookie + + @cookie.setter + def cookie(self, value): + raise Exception("You cannot update the cookie") + + @cookie.deleter + def cookie(self): + raise Exception("You cannot delete the cookie") + + @property def initial_request(self): return { "subject": self.__subject, @@ -425,7 +491,7 @@ pdp_set: {pdp_set} @index.deleter def index(self): - self.__index = 0 + self.__index = -1 @property def pdp_set(self): 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 --- /dev/null +++ b/moonv4/moon_wrapper/moon_wrapper/api/__init__.py 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/<string:component_id>") + + 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/<string:component_id>") + + 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/<string:group_id>", + "/api/<string:group_id>/", + "/api/<string:group_id>/<string:endpoint_id>") + + @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/<string:endpoint_id>') + } + } + """ + __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/<string:component_id>") + + 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 diff --git a/moonv4/tests/populate_default_values.py b/moonv4/tests/populate_default_values.py index 740ad8ed..cd06ab1a 100644 --- a/moonv4/tests/populate_default_values.py +++ b/moonv4/tests/populate_default_values.py @@ -7,8 +7,11 @@ from utils.policies import * parser = argparse.ArgumentParser() parser.add_argument('filename', help='scenario filename', nargs=1) -parser.add_argument("--verbose", "-v", action='store_true', help="verbose mode") +parser.add_argument("--verbose", "-v", action='store_true', + help="verbose mode") parser.add_argument("--debug", "-d", action='store_true', help="debug mode") +parser.add_argument("--keystone-pid", "-k", dest="keystone_pid", default="", + help="Force a particular Keystone Project ID") args = parser.parse_args() FORMAT = '%(asctime)-15s %(levelname)s %(message)s' @@ -202,11 +205,12 @@ def create_policy(model_id, meta_rule_list): def create_pdp(policy_id=None): logger.info("Creating PDP {}".format(scenario.pdp_name)) projects = get_keystone_projects() - admin_project_id = None - for _project in projects['projects']: - if _project['name'] == "admin": - admin_project_id = _project['id'] - assert admin_project_id + project_id = args.keystone_pid + if not project_id: + for _project in projects['projects']: + if _project['name'] == "admin": + project_id = _project['id'] + assert project_id pdps = check_pdp()["pdps"] for pdp_id, pdp_value in pdps.items(): if scenario.pdp_name == pdp_value["name"]: @@ -214,7 +218,7 @@ def create_pdp(policy_id=None): logger.debug("Found existing PDP named {} (will add policy {})".format(scenario.pdp_name, policy_id)) return pdp_id _pdp_id = add_pdp(name=scenario.pdp_name, policy_id=policy_id) - map_to_keystone(pdp_id=_pdp_id, keystone_project_id=admin_project_id) + map_to_keystone(pdp_id=_pdp_id, keystone_project_id=project_id) return _pdp_id if __name__ == "__main__": diff --git a/moonv4/tests/send_authz.py b/moonv4/tests/send_authz.py index a6ad7339..c21c8bca 100644 --- a/moonv4/tests/send_authz.py +++ b/moonv4/tests/send_authz.py @@ -136,7 +136,9 @@ def _send(url, stress_test=False): return {} if not stress_test: if res.status_code == 200: - logger.info("\033[1m{}\033[m {}".format(url, res.status_code)) + logger.warning("\033[1m{}\033[m \033[32mGrant\033[m".format(url)) + elif res.status_code == 401: + logger.warning("\033[1m{}\033[m \033[31mDeny\033[m".format(url)) else: logger.error("\033[1m{}\033[m {} {}".format(url, res.status_code, res.text)) try: @@ -146,11 +148,11 @@ def _send(url, stress_test=False): logger.error(res.text) else: if j.get("result"): - logger.warning("{} \033[32m{}\033[m".format(url, j.get("result"))) + # logger.warning("{} \033[32m{}\033[m".format(url, j.get("result"))) logger.debug("{}".format(j.get("error", ""))) current_request['result'] = "Grant" else: - logger.warning("{} \033[31m{}\033[m".format(url, "Deny")) + # logger.warning("{} \033[31m{}\033[m".format(url, "Deny")) logger.debug("{}".format(j)) current_request['result'] = "Deny" return current_request |