From 94aa78ca23c4db13a0752fbdd0df96730b1e7288 Mon Sep 17 00:00:00 2001 From: asteroide Date: Fri, 5 May 2017 11:16:38 +0200 Subject: Code update for chaining policies. Change-Id: If9f6c2640492f69d0f3af2118fade72700df47e6 --- moonv4/moon_authz/moon_authz/api/authorization.py | 437 ++++----------------- moonv4/moon_authz/moon_authz/messenger.py | 37 +- moonv4/moon_db/moon_db/api/policy.py | 9 + moonv4/moon_interface/moon_interface/tools.py | 1 - .../moon_interface/tests/apitests/scenario/mls.py | 1 + .../moon_interface/tests/apitests/scenario/rbac.py | 7 +- moonv4/moon_interface/tests/apitests/set_authz.py | 9 +- moonv4/moon_orchestrator/conf/plugins/authz.py | 17 +- moonv4/moon_secrouter/moon_secrouter/api/route.py | 192 +++++++-- .../moon_utilities/security_functions.py | 120 +++--- 10 files changed, 328 insertions(+), 502 deletions(-) diff --git a/moonv4/moon_authz/moon_authz/api/authorization.py b/moonv4/moon_authz/moon_authz/api/authorization.py index 248a9565..5968178e 100644 --- a/moonv4/moon_authz/moon_authz/api/authorization.py +++ b/moonv4/moon_authz/moon_authz/api/authorization.py @@ -3,11 +3,12 @@ # license which can be found in the file 'LICENSE' in this package distribution # or at 'http://www.apache.org/licenses/LICENSE-2.0'. -import copy +import hashlib import itertools from oslo_log import log as logging from oslo_config import cfg -from moon_utilities.security_functions import call, Context +import oslo_messaging +from moon_utilities.security_functions import call, Context, notify from moon_utilities.misc import get_uuid_from_name from moon_utilities import exceptions from moon_db.core import PDPManager @@ -40,11 +41,15 @@ class Authorization(object): meta_rule_id = None keystone_project_id = None - def __init__(self, component_id): - self.component_id = component_id - LOG.info("ext={}".format(component_id)) - for _id_value in component_id.split("_"): - LOG.info("_id_value={}".format(_id_value.split(":"))) + 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': "authz_"+hashlib.sha224(component_desc.encode("utf-8")).hexdigest()} + ) + + for _id_value in component_desc.split("_"): _type, _id = _id_value.split(":") if _type == "pdp": self.pdp_id = _id @@ -52,388 +57,80 @@ class Authorization(object): self.meta_rule_id = _id elif _type == "project": self.keystone_project_id = _id - # self.manager = IntraExtensionAdminManager - # self.context = {"id": self.component_id, "user_id": "admin"} - # self.aggregation_algorithm_dict = ConfigurationManager.driver.get_aggregation_algorithms_dict() - # self.__subjects = None - # self.__objects = None - # self.__actions = None - # self.__subject_scopes = None - # self.__object_scopes = None - # self.__action_scopes = None - # self.__subject_categories = None - # self.__object_categories = None - # self.__action_categories = None - # self.__subject_assignments = None - # self.__object_assignments = None - # self.__action_assignments = None - # self.__sub_meta_rules = None - # self.__rules = None - # self.aggregation_algorithm_id = None - - # @property - # def subjects(self): - # if not self.__subjects: - # self.__subjects = call("moon_secpolicy_{}".format(self.intra_extension_id), ctx=self.context, - # method="get_subjects", args={}) - # if "subjects" in self.__subjects: - # return self.__subjects - # else: - # LOG.error("An error occurred {}".format(self.__subjects)) - # return self.__subjects - # - # @property - # def objects(self): - # if not self.__objects: - # self.__objects = call("moon_secpolicy_{}".format(self.intra_extension_id), ctx=self.context, - # method="get_objects", args={}) - # if "objects" in self.__objects: - # return self.__objects - # else: - # LOG.error("An error occurred {}".format(self.__objects)) - # return self.__objects - # - # @property - # def actions(self): - # if not self.__actions: - # self.__actions = call("moon_secpolicy_{}".format(self.intra_extension_id), ctx=self.context, - # method="get_actions", args={}) - # if "actions" in self.__actions: - # return self.__actions - # else: - # LOG.error("An error occurred {}".format(self.__actions)) - # return self.__actions - # - # @property - # def subject_scopes(self): - # if not self.__subject_scopes: - # self.__subject_scopes = call("moon_secpolicy_{}".format(self.intra_extension_id), ctx=self.context, - # method="get_subject_scopes", args={}) - # if "subject_scopes" in self.__subject_scopes: - # return self.__subject_scopes - # else: - # LOG.error("An error occurred {}".format(self.__subject_scopes)) - # return self.__subject_scopes - # - # @property - # def object_scopes(self): - # if not self.__object_scopes: - # self.__object_scopes = call("moon_secpolicy_{}".format(self.intra_extension_id), ctx=self.context, - # method="get_object_scopes", args={}) - # if "object_scopes" in self.__object_scopes: - # return self.__object_scopes - # else: - # LOG.error("An error occurred {}".format(self.__object_scopes)) - # return self.__object_scopes - # - # @property - # def action_scopes(self): - # if not self.__action_scopes: - # self.__action_scopes = call("moon_secpolicy_{}".format(self.intra_extension_id), ctx=self.context, - # method="get_action_scopes", args={}) - # if "action_scopes" in self.__action_scopes: - # return self.__action_scopes - # else: - # LOG.error("An error occurred {}".format(self.__action_scopes)) - # return self.__action_scopes - # - # @property - # def subject_categories(self): - # if not self.__subject_categories: - # self.__subject_categories = call("moon_secpolicy_{}".format(self.intra_extension_id), ctx=self.context, - # method="get_subject_categories", args={}) - # if "subject_categories" in self.__subject_categories: - # return self.__subject_categories - # else: - # LOG.error("An error occurred {}".format(self.__subject_categories)) - # return self.__subject_categories - # - # @property - # def object_categories(self): - # if not self.__object_categories: - # self.__object_categories = call("moon_secpolicy_{}".format(self.intra_extension_id), ctx=self.context, - # method="get_object_categories", args={}) - # if "object_categories" in self.__object_categories: - # return self.__object_categories - # else: - # LOG.error("An error occurred {}".format(self.__object_categories)) - # return self.__object_categories - # - # @property - # def action_categories(self): - # if not self.__action_categories: - # self.__action_categories = call("moon_secpolicy_{}".format(self.intra_extension_id), ctx=self.context, - # method="get_action_categories", args={}) - # if "action_categories" in self.__action_categories: - # return self.__action_categories - # else: - # LOG.error("An error occurred {}".format(self.__action_categories)) - # return self.__action_categories - # - # @property - # def subject_assignments(self): - # if not self.__subject_assignments: - # context = copy.deepcopy(self.context) - # context['sid'] = None - # context['scid'] = None - # args = {'ssid': None} - # self.__subject_assignments = call("moon_secpolicy_{}".format(self.intra_extension_id), ctx=context, - # method="get_subject_assignments", args=args) - # if "subject_assignments" in self.__subject_assignments: - # return self.__subject_assignments - # else: - # LOG.error("An error occurred {}".format(self.__subject_assignments)) - # return self.__subject_assignments - # - # @property - # def object_assignments(self): - # if not self.__object_assignments: - # context = copy.deepcopy(self.context) - # context['sid'] = None - # context['scid'] = None - # args = {'ssid': None} - # self.__object_assignments = call("moon_secpolicy_{}".format(self.intra_extension_id), ctx=context, - # method="get_object_assignments", args=args) - # if "object_assignments" in self.__object_assignments: - # return self.__object_assignments - # else: - # LOG.error("An error occurred {}".format(self.__object_assignments)) - # return self.__object_assignments - # - # @property - # def action_assignments(self): - # if not self.__action_assignments: - # context = copy.deepcopy(self.context) - # context['sid'] = None - # context['scid'] = None - # args = {'ssid': None} - # self.__action_assignments = call("moon_secpolicy_{}".format(self.intra_extension_id), ctx=context, - # method="get_action_assignments", args=args) - # if "action_assignments" in self.__action_assignments: - # return self.__action_assignments - # else: - # LOG.error("An error occurred {}".format(self.__action_assignments)) - # return self.__action_assignments - # - # @property - # def sub_meta_rules(self): - # if not self.__sub_meta_rules: - # self.__sub_meta_rules = call("moon_secfunction_{}".format(self.intra_extension_id), ctx=self.context, - # method="get_sub_meta_rules", args={}) - # if "sub_meta_rules" in self.__sub_meta_rules: - # return self.__sub_meta_rules - # else: - # LOG.error("An error occurred {}".format(self.__sub_meta_rules)) - # return self.__sub_meta_rules - # - # @property - # def rules(self): - # if not self.__rules: - # self.__rules = dict() - # for _id, _value in self.sub_meta_rules["sub_meta_rules"].items(): - # context = copy.deepcopy(self.context) - # context["sub_meta_rule_id"] = _id - # __elements = call("moon_secfunction_{}".format(self.intra_extension_id), ctx=context, - # method="get_rules", args={}) - # if "rules" in __elements: - # self.__rules[_id] = __elements - # else: - # LOG.error("An error occurred {}".format(__elements)) - # return self.__rules - - # def __get_authz_buffer(self, subject_id, object_id, action_id): - # """ - # :param intra_extension_id: - # :param subject_id: - # :param object_id: - # :param action_id: - # :return: authz_buffer = { - # 'subject_id': xxx, - # 'object_id': yyy, - # 'action_id': zzz, - # 'subject_assignments': { - # 'subject_category1': [], - # 'subject_category2': [], - # ... - # }, - # 'object_assignments': {}, - # 'action_assignments': {}, - # } - # """ - # authz_buffer = dict() - # # Sometimes it is not the subject ID but the User Keystone ID, so, we have to check - # subjects_dict = copy.deepcopy(self.subjects) - # if subject_id not in subjects_dict["subjects"].keys(): - # for _subject_id in subjects_dict["subjects"]: - # if subjects_dict["subjects"][_subject_id]['keystone_id']: - # subject_id = _subject_id - # break - # authz_buffer['subject_id'] = subject_id - # authz_buffer['object_id'] = object_id - # authz_buffer['action_id'] = action_id - # meta_data_dict = dict() - # meta_data_dict["subject_categories"] = copy.deepcopy(self.subject_categories["subject_categories"]) - # meta_data_dict["object_categories"] = copy.deepcopy(self.object_categories["object_categories"]) - # meta_data_dict["action_categories"] = copy.deepcopy(self.action_categories["action_categories"]) - # subject_assignment_dict = copy.deepcopy(self.subject_assignments['subject_assignments'][subject_id]) - # LOG.info("__get_authz_buffer self.object_assignments['object_assignments']={}".format(self.object_assignments['object_assignments'])) - # LOG.info("__get_authz_buffer object_id={}".format(object_id)) - # object_assignment_dict = copy.deepcopy(self.object_assignments['object_assignments'][object_id]) - # action_assignment_dict = copy.deepcopy(self.action_assignments['action_assignments'][action_id]) - # - # authz_buffer['subject_assignments'] = dict() - # authz_buffer['object_assignments'] = dict() - # authz_buffer['action_assignments'] = dict() - # - # for _subject_category in meta_data_dict['subject_categories']: - # authz_buffer['subject_assignments'][_subject_category] = list(subject_assignment_dict[_subject_category]) - # for _object_category in meta_data_dict['object_categories']: - # authz_buffer['object_assignments'][_object_category] = list(object_assignment_dict[_object_category]) - # for _action_category in meta_data_dict['action_categories']: - # authz_buffer['action_assignments'][_action_category] = list(action_assignment_dict[_action_category]) - # return authz_buffer - # - # def __get_decision_dict(self, subject_id, object_id, action_id): - # """Check authorization for a particular action. - # - # :param intra_extension_id: UUID of an IntraExtension - # :param subject_id: subject UUID of the request - # :param object_id: object UUID of the request - # :param action_id: action UUID of the request - # :return: True or False or raise an exception - # :raises: - # """ - # authz_buffer = self.__get_authz_buffer(subject_id, object_id, action_id) - # decision_buffer = dict() - # - # meta_rule_dict = copy.deepcopy(self.sub_meta_rules['sub_meta_rules']) - # rules_dict = copy.deepcopy(self.rules) - # for sub_meta_rule_id in meta_rule_dict: - # if meta_rule_dict[sub_meta_rule_id]['algorithm'] == 'inclusion': - # decision_buffer[sub_meta_rule_id] = algorithms.inclusion( - # authz_buffer, - # meta_rule_dict[sub_meta_rule_id], - # rules_dict[sub_meta_rule_id]['rules'].values()) - # elif meta_rule_dict[sub_meta_rule_id]['algorithm'] == 'comparison': - # decision_buffer[sub_meta_rule_id] = algorithms.comparison( - # authz_buffer, - # meta_rule_dict[sub_meta_rule_id], - # rules_dict[sub_meta_rule_id]['rules'].values()) - # - # return decision_buffer - # - # def __authz(self, subject_id, object_id, action_id): - # decision = False - # decision_dict = dict() - # try: - # decision_dict = self.__get_decision_dict(subject_id, object_id, action_id) - # except (exceptions.SubjectUnknown, exceptions.ObjectUnknown, exceptions.ActionUnknown) as e: - # # maybe we need to synchronize with the master - # pass - # # if CONF.slave.slave_name and CONF.slave.master_url: - # # self.get_data_from_master() - # # decision_dict = self.__get_decision_dict(subject_id, object_id, action_id) - # - # try: - # # aggregation_algorithm_id = IntraExtensionAdminManager.get_aggregation_algorithm_id( - # # "admin", - # # self.intra_extension_id)['aggregation_algorithm'] - # if not self.aggregation_algorithm_id: - # self.aggregation_algorithm_id = self.intra_extension['aggregation_algorithm'] - # except Exception as e: - # LOG.error(e, exc_info=True) - # LOG.error(self.intra_extension) - # return { - # 'authz': False, - # 'comment': "Aggregation algorithm not set" - # } - # if self.aggregation_algorithm_dict[self.aggregation_algorithm_id]['name'] == 'all_true': - # decision = algorithms.all_true(decision_dict) - # elif self.aggregation_algorithm_dict[self.aggregation_algorithm_id]['name'] == 'one_true': - # decision = algorithms.one_true(decision_dict) - # if not decision_dict or not decision: - # raise exceptions.AuthzException("{} {}-{}-{}".format(self.intra_extension['id'], subject_id, action_id, object_id)) - # return { - # 'authz': decision, - # 'comment': "{} {}-{}-{}".format(self.intra_extension['id'], subject_id, action_id, object_id) - # } - # - # def authz_bak(self, ctx, args): - # """Return the authorization for a specific request - # - # :param ctx: { - # "subject_name" : "string name", - # "action_name" : "string name", - # "object_name" : "string name" - # } - # :param args: {} - # :return: { - # "authz": "True or False", - # "message": "optional message" - # } - # """ - # intra_extension_id = ctx["id"] - # try: - # subject_id = get_uuid_from_name(ctx["subject_name"], self.subjects['subjects']) - # object_id = get_uuid_from_name(ctx["object_name"], self.objects['objects']) - # action_id = get_uuid_from_name(ctx["action_name"], self.actions['actions']) - # authz_result = self.__authz(subject_id, object_id, action_id) - # return authz_result - # except Exception as e: - # LOG.error(e, exc_info=True) - # return {"authz": False, - # "error": str(e), - # "intra_extension_id": intra_extension_id, - # "ctx": ctx, "args": args} - # - # return {"authz": False} def __check_rules(self, context): scopes_list = list() - current_header_id = context.headers[context.index]['id'] - current_pdp = context.pdp_set[current_header_id] + current_header_id = context['headers'][context['index']] + current_pdp = 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"]["action_categories"]) category_list.extend(current_pdp["meta_rules"]["object_categories"]) + category_list.extend(current_pdp["meta_rules"]["action_categories"]) for category in category_list: - if not current_pdp['target'][category]: - LOG.warning("Empty assignment detected: {} target={}".format(category, current_pdp['target'])) - return False, "Empty assignment detected..." + # if not current_pdp['target'][category]: + # LOG.warning("Empty assignment detected: {} target={}".format(category, current_pdp['target'])) scopes_list.append(current_pdp['target'][category]) - scopes_list.append([True, ]) - rules = PolicyManager.get_rules_dict(user_id="admin", - policy_id=self.policy_id, - meta_rule_id=current_header_id).values() + 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) + rules = list(map(lambda x: x['rule'], rules['rules'])) for item in itertools.product(*scopes_list): - if list(item) in rules: + req = list(item) + if req in rules: return True, "" LOG.warning("No rule match the request...") return False, "No rule match the request..." - def authz(self, ctx, args): - LOG.info("authz {}".format(ctx)) - keystone_project_id = ctx["id"] + 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("authz {} {}".format(ctxt, payload)) + keystone_project_id = payload["id"] try: - if "authz_context" not in ctx: - ctx["authz_context"] = Context(keystone_project_id, - ctx["subject_name"], - ctx["object_name"], - ctx["action_name"], - ctx["request_id"]).to_dict() - LOG.info("Context={}".format(ctx["authz_context"])) + if "authz_context" not in payload: + payload["authz_context"] = Context(keystone_project_id, + payload["subject_name"], + payload["object_name"], + payload["action_name"], + payload["request_id"]).to_dict() + else: + payload["authz_context"]["index"] += 1 + result, message = self.__check_rules(payload["authz_context"]) + current_header_id = payload["authz_context"]['headers'][payload["authz_context"]['index']] + # current_pdp = payload["authz_context"]['pdp_set'][current_header_id] + if result: + payload["authz_context"]['pdp_set'][current_header_id]["effect"] = "grant" + if payload["authz_context"]["index"]+1 < len(payload["authz_context"]["headers"]): + next_index = payload["authz_context"]["index"]+1 + notify( + request_id=payload["authz_context"]["request_id"], + container_id=payload["container_chaining"][next_index], + payload=payload) else: - ctx["authz_context"].index += 1 - result, message = self.__check_rules(ctx["authz_context"]) - # if ctx["authz_context"].index < len(ctx["authz_context"].headers): - del ctx["authz_context"] + ret = call(endpoint="security_router", + ctx={"id": self.component_id, + "call_master": False, + "method": "return_authz", + "request_id": payload["authz_context"]["request_id"]}, + method="route", + args=payload["authz_context"]) + del payload["authz_context"] return {"authz": result, "error": message, "pdp_id": self.pdp_id, - "ctx": ctx, "args": args} + "ctx": ctxt, "args": payload} except Exception as e: try: - LOG.error(ctx["authz_context"]) + LOG.error(payload["authz_context"]) # del ctx["authz_context"] except KeyError: LOG.error("Cannot find \"authz_context\" in context") @@ -441,5 +138,5 @@ class Authorization(object): return {"authz": False, "error": str(e), "pdp_id": self.pdp_id, - "ctx": ctx, "args": args} + "ctx": ctxt, "args": payload} diff --git a/moonv4/moon_authz/moon_authz/messenger.py b/moonv4/moon_authz/moon_authz/messenger.py index 8ebd1633..6fa34770 100644 --- a/moonv4/moon_authz/moon_authz/messenger.py +++ b/moonv4/moon_authz/moon_authz/messenger.py @@ -5,12 +5,10 @@ from oslo_config import cfg import oslo_messaging -import hashlib 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.security_functions import call from moon_utilities.api import APIList LOG = logging.getLogger(__name__) @@ -20,44 +18,33 @@ CONF = cfg.CONF class Server: def __init__(self, component_id, keystone_project_id): - self.TOPIC = "authz_"+hashlib.sha224(component_id.encode("utf-8")).hexdigest() - self.transport = oslo_messaging.get_transport(cfg.CONF) - self.target = oslo_messaging.Target(topic=self.TOPIC, server='moon_authz_server1') - # ctx = {'user_id': 'admin', 'id': component_id, 'method': 'get_intra_extensions'} - # if CONF.slave.slave_name: - # ctx['call_master'] = True - # intra_extension = call( - # endpoint="security_router", - # ctx=ctx, - # method='route', - # args={} - # ) - # if "intra_extensions" not in intra_extension: - # LOG.error("Error reading intra_extension from router") - # LOG.error("intra_extension: {}".format(intra_extension)) - # raise IntraExtensionUnknown - # component_id = list(intra_extension["intra_extensions"].keys())[0] - LOG.info("Starting MQ server with topic: {}".format(self.TOPIC)) + 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) ] - self.server = oslo_messaging.get_rpc_server(self.transport, self.target, self.endpoints, - executor='threading', - access_policy=oslo_messaging.DefaultRPCAccessPolicy) + 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(1) + time.sleep(0.1) except KeyboardInterrupt: print("Stopping server by crtl+c") except SystemExit: print("Stopping server") self.server.stop() - self.server.wait() + diff --git a/moonv4/moon_db/moon_db/api/policy.py b/moonv4/moon_db/moon_db/api/policy.py index 73889b85..0beaf78f 100644 --- a/moonv4/moon_db/moon_db/api/policy.py +++ b/moonv4/moon_db/moon_db/api/policy.py @@ -28,6 +28,15 @@ class PolicyManager(Managers): self.driver = connector.driver Managers.PolicyManager = self + def get_policy_from_meta_rules(self, user_id, meta_rule_id): + policies = self.PolicyManager.get_policies("admin") + models = self.ModelManager.get_models("admin") + for pdp_key, pdp_value in self.PDPManager.get_pdp(user_id).items(): + for policy_id in pdp_value["security_pipeline"]: + model_id = policies[policy_id]["model_id"] + if meta_rule_id in models[model_id]["meta_rules"]: + return policy_id + @enforce(("read", "write"), "policies") def update_policy(self, user_id, policy_id, value): return self.driver.update_policy(policy_id=policy_id, value=value) diff --git a/moonv4/moon_interface/moon_interface/tools.py b/moonv4/moon_interface/moon_interface/tools.py index 3c0fffa5..443519ac 100644 --- a/moonv4/moon_interface/moon_interface/tools.py +++ b/moonv4/moon_interface/moon_interface/tools.py @@ -29,7 +29,6 @@ def timeit(function): return wrapper -@timeit def call(topic="security_router", ctx=None, method="route", **kwargs): if not ctx: ctx = dict() diff --git a/moonv4/moon_interface/tests/apitests/scenario/mls.py b/moonv4/moon_interface/tests/apitests/scenario/mls.py index fab1d528..e36a86bc 100644 --- a/moonv4/moon_interface/tests/apitests/scenario/mls.py +++ b/moonv4/moon_interface/tests/apitests/scenario/mls.py @@ -39,6 +39,7 @@ meta_rule = { rules = { "mls": ( ("high", "medium", "vm-action"), + ("high", "low", "vm-action"), ("medium", "low", "vm-action"), ) } diff --git a/moonv4/moon_interface/tests/apitests/scenario/rbac.py b/moonv4/moon_interface/tests/apitests/scenario/rbac.py index 073f1d65..cd08308e 100644 --- a/moonv4/moon_interface/tests/apitests/scenario/rbac.py +++ b/moonv4/moon_interface/tests/apitests/scenario/rbac.py @@ -4,7 +4,7 @@ policy_name = "RBAC policy example" model_name = "RBAC" subjects = {"user0": "", "user1": "", } -objects = {"vm0": "", } +objects = {"vm0": "", "vm1": "", } actions = {"start": "", "stop": ""} subject_categories = {"role": "", } @@ -12,11 +12,11 @@ object_categories = {"id": "", } action_categories = {"action-type": "", } subject_data = {"role": {"admin": "", "employee": ""}} -object_data = {"id": {"vm1": "", "vm2": ""}} +object_data = {"id": {"vm0": "", "vm1": ""}} action_data = {"action-type": {"vm-action": "", }} subject_assignments = {"user0": {"role": "admin"}, "user1": {"role": "employee"}, } -object_assignments = {"vm0": {"id": "vm1"}} +object_assignments = {"vm0": {"id": "vm0"}, "vm1": {"id": "vm1"}} action_assignments = {"start": {"action-type": "vm-action"}, "stop": {"action-type": "vm-action"}} meta_rule = { @@ -25,6 +25,7 @@ meta_rule = { rules = { "rbac": ( + ("admin", "vm0", "vm-action"), ("admin", "vm1", "vm-action"), ) } diff --git a/moonv4/moon_interface/tests/apitests/set_authz.py b/moonv4/moon_interface/tests/apitests/set_authz.py index 7d0d5069..38b63509 100644 --- a/moonv4/moon_interface/tests/apitests/set_authz.py +++ b/moonv4/moon_interface/tests/apitests/set_authz.py @@ -40,4 +40,11 @@ if not keystone_project_id: for rule in rules: url = "http://172.18.0.11:38001/authz/{}/{}".format(keystone_project_id, "/".join(rule)) req = requests.get(url) - print(url, req.status_code) + print("\033[1m{}\033[m {}".format(url, req.status_code)) + j = req.json() + # print(j) + if j.get("authz"): + print("\t\033[32m{}\033[m {}".format(j.get("authz"), j.get("error", ""))) + else: + print("\t\033[31m{}\033[m {}".format(j.get("authz"), j.get("error", ""))) + diff --git a/moonv4/moon_orchestrator/conf/plugins/authz.py b/moonv4/moon_orchestrator/conf/plugins/authz.py index c472b36a..4a1441c9 100644 --- a/moonv4/moon_orchestrator/conf/plugins/authz.py +++ b/moonv4/moon_orchestrator/conf/plugins/authz.py @@ -47,16 +47,17 @@ RUN pip3 install . ) # note(asteroide): time to let the new docker boot time.sleep(3) - self.get_status() + # self.get_status() def get_status(self): - transport = oslo_messaging.get_transport(CONF) - target = oslo_messaging.Target(topic=self.id, version='1.0') - client = oslo_messaging.RPCClient(transport, target) - LOG.info("Calling Status on {}".format(self.id)) - ret = client.call({"component_id": self.id}, 'get_status', args=None) - LOG.info(ret) - return ret + return True + # transport = oslo_messaging.get_transport(CONF) + # target = oslo_messaging.Target(topic=self.id, version='1.0') + # client = oslo_messaging.RPCClient(transport, target) + # LOG.info("Calling Status on {}".format(self.id)) + # ret = client.call({"component_id": self.id}, 'get_status', args=None) + # LOG.info(ret) + # return ret def run(uuid, conf_file="", docker=None, network_config=None): diff --git a/moonv4/moon_secrouter/moon_secrouter/api/route.py b/moonv4/moon_secrouter/moon_secrouter/api/route.py index ec79d96b..ccdff08b 100644 --- a/moonv4/moon_secrouter/moon_secrouter/api/route.py +++ b/moonv4/moon_secrouter/moon_secrouter/api/route.py @@ -5,8 +5,10 @@ import copy import time +import itertools +from uuid import uuid4 from oslo_log import log as logging -from moon_utilities.security_functions import call +from moon_utilities.security_functions import call, notify from oslo_config import cfg from moon_secrouter.api.generic import Status, Logs @@ -90,6 +92,7 @@ API = { ), "function": ( "authz", + "return_authz", ), } @@ -97,18 +100,109 @@ API = { class Cache(object): # TODO (asteroide): set cache integer in CONF file - __UPDATE_INTERVAL = 300 + __UPDATE_INTERVAL = 10 + __CONTAINERS = {} - __LAST_UPDATE = 0 + __CONTAINERS_UPDATE = 0 + + __CONTAINER_CHAINING_UPDATE = 0 + __CONTAINER_CHAINING = {} + + __PDP = {} + __PDP_UPDATE = 0 + + __POLICIES = {} + __POLICIES_UPDATE = 0 + + __MODELS = {} + __MODELS_UPDATE = 0 + + __AUTHZ_REQUESTS = {} + + def update(self, component=None): + self.__update_container() + self.__update_pdp() + self.__update_policies() + self.__update_models() + for key, value in self.__PDP.items(): + self.__update_container_chaining(value["keystone_project_id"]) + + @property + def authz_requests(self): + return self.__AUTHZ_REQUESTS + + def __update_pdp(self): + pdp = call("moon_manager", method="get_pdp", ctx={"user_id": "admin"}, args={}) + 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): + current_time = time.time() + if self.__PDP_UPDATE + self.__UPDATE_INTERVAL < current_time: + self.__update_pdp() + self.__PDP_UPDATE = current_time + return self.__PDP + + def __update_policies(self): + policies = call("moon_manager", method="get_policies", ctx={"user_id": "admin"}, args={}) + 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 + + def __update_models(self): + models = call("moon_manager", method="get_models", ctx={"user_id": "admin"}, args={}) + 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 def __update_container(self): containers = call("orchestrator", method="get_container", ctx={}, args={}) - LOG.info("container={}".format(containers)) for key, value in containers["containers"].items(): self.__CONTAINERS[key] = value - def update(self, component=None): - self.__update_container() + @property + def container_chaining(self): + 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 + return self.__CONTAINER_CHAINING + + def __update_container_chaining(self, keystone_project_id): + container_ids = [] + for pdp_id, pdp_value, in CACHE.pdp.items(): + if pdp_value: + if pdp_value["keystone_project_id"] == keystone_project_id: + for policy_id in pdp_value["security_pipeline"]: + model_id = CACHE.policies[policy_id]['model_id'] + for meta_rule_id in CACHE.models[model_id]["meta_rules"]: + for container_id, container_values, in CACHE.containers.items(): + for container_value in container_values: + if container_value["meta_rule_id"] == meta_rule_id: + container_ids.append(container_value["container_id"]) + break + self.__CONTAINER_CHAINING[keystone_project_id] = container_ids @property def containers(self): @@ -121,21 +215,65 @@ class Cache(object): :return: """ current_time = time.time() - if self.__LAST_UPDATE + self.__UPDATE_INTERVAL < current_time: + if self.__CONTAINERS_UPDATE + self.__UPDATE_INTERVAL < current_time: self.__update_container() - self.__LAST_UPDATE = current_time + self.__CONTAINERS_UPDATE = current_time return self.__CONTAINERS CACHE = Cache() +class AuthzRequest: + + result = None + req_max_delay = 5 + + def __init__(self, ctx, args): + self.ctx = ctx + self.args = args + self.request_id = ctx["request_id"] + self.container_chaining = CACHE.container_chaining[self.ctx['id']] + ctx["container_chaining"] = copy.deepcopy(self.container_chaining) + self.pdp_container = str(self.container_chaining[0]) + self.run() + + def run(self): + notify(request_id=self.request_id, container_id=self.pdp_container, payload=self.ctx) + cpt = 0 + while cpt < self.req_max_delay*10: + time.sleep(0.1) + cpt += 1 + if CACHE.authz_requests[self.request_id]: + self.result = CACHE.authz_requests[self.request_id] + return + LOG.warning("Request {} has timed out".format(self.request_id)) + + 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": + authz_results.append(True) + else: + 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 "pdp_set" in self.result and self.result["pdp_set"]["effect"] == "grant": + return True + return False + + class Router(object): """ Route requests to all components. """ __version__ = "0.1.0" + cache_requests = {} def __init__(self, add_master_cnx): if CONF.slave.slave_name and add_master_cnx: @@ -178,13 +316,6 @@ class Router(object): LOG.info("SLAVE CONNECTION ENDED!") LOG.info(result) - @staticmethod - def __get_first_container(keystone_project_id): - for container_id, container_value, in CACHE.containers.items(): - if container_value: - if container_value[0]["keystone_project_id"] == keystone_project_id: - return container_value[0]["container_id"] - @staticmethod def check_pdp(ctx): _ctx = copy.deepcopy(ctx) @@ -228,21 +359,32 @@ class Router(object): if component == "orchestrator": return call(component, method=ctx["method"], ctx=ctx, args=args) if component == "manager": - LOG.info("Call Manager {}".format(ctx)) result = call("moon_manager", method=ctx["method"], ctx=ctx, args=args) self.send_update(api=ctx["method"], ctx=ctx, args=args) return result if component == "function": - if self.check_pdp(ctx): - LOG.info("Tenant ID={}".format(ctx['id'])) - pdp_container = self.__get_first_container(ctx['id']) - LOG.info("pdp_container={}".format(pdp_container)) - # TODO (asteroide): call the first security function through a notification - # and not an RPC call (need to play with ID in context) - result = call(pdp_container, method=ctx["method"], ctx=ctx, args=args) - return result + if ctx["method"] == "return_authz": + request_id = ctx["request_id"] + CACHE.authz_requests[request_id] = args + return args + elif self.check_pdp(ctx): + req_id = uuid4().hex + CACHE.authz_requests[req_id] = {} + ctx["request_id"] = req_id + req = AuthzRequest(ctx, args) + # result = copy.deepcopy(req.result) + if req.is_authz(): + return {"authz": True, + "pdp_id": ctx["id"], + "ctx": ctx, "args": args} + return {"authz": False, + "error": {'code': 403, 'title': 'Authz Error', + 'description': "The authz request is refused."}, + "pdp_id": ctx["id"], + "ctx": ctx, "args": args} return {"result": False, - "error": {'code': 500, 'title': 'Moon Error', 'description': "Function component not found."}, + "error": {'code': 500, 'title': 'Moon Error', + 'description': "Function component not found."}, "pdp_id": ctx["id"], "ctx": ctx, "args": args} diff --git a/moonv4/moon_utilities/moon_utilities/security_functions.py b/moonv4/moon_utilities/moon_utilities/security_functions.py index 2ad52a4c..f7cbdf2c 100644 --- a/moonv4/moon_utilities/moon_utilities/security_functions.py +++ b/moonv4/moon_utilities/moon_utilities/security_functions.py @@ -8,13 +8,10 @@ import copy import re import types import requests -from uuid import uuid4 from oslo_log import log as logging from oslo_config import cfg import oslo_messaging from moon_utilities import exceptions -from oslo_config.cfg import ConfigOpts -# from moon_db.core import PDPManager, ModelManager, PolicyManager LOG = logging.getLogger(__name__) CONF = cfg.CONF @@ -158,6 +155,22 @@ def logout(headers, url=None): __transport_master = oslo_messaging.get_transport(cfg.CONF, CONF.slave.master_url) __transport = oslo_messaging.get_transport(CONF) +__n_transport = oslo_messaging.get_notification_transport(CONF) +__n_notifier = oslo_messaging.Notifier(__n_transport, + 'router.host', + driver='messagingv2', + topics=['authz-workers']) +__n_notifier = __n_notifier.prepare(publisher_id='router') + + +def notify(request_id, container_id, payload, event_type="authz"): + ctxt = { + 'request_id': request_id, + 'container_id': container_id + } + result = __n_notifier.critical(ctxt, event_type, payload=payload) + return result + def call(endpoint, ctx=None, method="get_status", **kwargs): if not ctx: @@ -183,13 +196,11 @@ class Context: self.__keystone_project_id = _keystone_project_id self.__pdp_id = None self.__pdp_value = None - LOG.info("Context pdp={}".format(PDPManager.get_pdp("admin"))) for _pdp_key, _pdp_value in PDPManager.get_pdp("admin").items(): if _pdp_value["keystone_project_id"] == _keystone_project_id: self.__pdp_id = _pdp_key self.__pdp_value = copy.deepcopy(_pdp_value) break - LOG.info("Context pdp_value={}".format(self.__pdp_value)) self.__subject = _subject self.__object = _object self.__action = _action @@ -200,29 +211,11 @@ class Context: self.__headers = [] policies = PolicyManager.get_policies("admin") models = ModelManager.get_models("admin") - LOG.info("Context policies={}".format(policies)) - LOG.info("Context models={}".format(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") - LOG.info("Context meta_rules={}".format(self.__meta_rules)) - LOG.info("Context headers={}".format(self.__headers)) - # call("moon_manager", - # method="get_meta_rules", - # ctx={"id": self.__intra_extension_id, - # "user_id": "admin", - # "method": "get_sub_meta_rules"}, - # args={})["sub_meta_rules"] - # for key in self.__intra_extension["pdp_pipeline"]: - # LOG.info("__meta_rules={}".format(self.__meta_rules)) - # for meta_rule_key in self.__meta_rules: - # if self.__meta_rules[meta_rule_key]['name'] == key.split(":", maxsplit=1)[-1]: - # self.__headers.append({"name": self.__meta_rules[meta_rule_key]['name'], "id": meta_rule_key}) - # break - # else: - # LOG.warning("Cannot find meta_rule_key {}".format(key)) self.__pdp_set = {} self.__init_pdp_set() @@ -254,59 +247,36 @@ class Context: for header in self.__headers: self.__pdp_set[header] = dict() self.__pdp_set[header]["meta_rules"] = self.__meta_rules[header] - self.__pdp_set[header]["target"] = self.__add_target() + self.__pdp_set[header]["target"] = self.__add_target(header) # TODO (asteroide): the following information must be retrieve somewhere self.__pdp_set[header]["instruction"] = list() - self.__pdp_set[header]["effect"] = "grant" - self.__pdp_set["effect"] = "grant" + self.__pdp_set[header]["effect"] = "deny" + self.__pdp_set["effect"] = "deny" - def __add_target(self): + def __add_target(self, meta_rule_id): result = dict() _subject = self.__current_request["subject"] _object = self.__current_request["object"] _action = self.__current_request["action"] - categories = self.ModelManager.get_subject_categories("admin") - # TODO (asteroide): end the dev of that part - # for category in categories: - # result[category] = list() - # assignments = call("moon_secpolicy_{}".format(self.__intra_extension_id), - # method="get_subject_assignments", - # ctx={"id": self.__intra_extension_id, - # "sid": _subject, - # "scid": category, - # "user_id": "admin"}, - # args={})["subject_assignments"] - # result[category].extend(assignments[_subject][category]) - # categories = call("moon_secpolicy_{}".format(self.__intra_extension_id), - # method="get_object_categories", - # ctx={"id": self.__intra_extension_id, - # "user_id": "admin"}, - # args={})["object_categories"] - # for category in categories: - # result[category] = list() - # assignments = call("moon_secpolicy_{}".format(self.__intra_extension_id), - # method="get_object_assignments", - # ctx={"id": self.__intra_extension_id, - # "sid": _object, - # "scid": category, - # "user_id": "admin"}, - # args={})["object_assignments"] - # result[category].extend(assignments[_object][category]) - # categories = call("moon_secpolicy_{}".format(self.__intra_extension_id), - # method="get_action_categories", - # ctx={"id": self.__intra_extension_id, - # "user_id": "admin"}, - # args={})["action_categories"] - # for category in categories: - # result[category] = list() - # assignments = call("moon_secpolicy_{}".format(self.__intra_extension_id), - # method="get_action_assignments", - # ctx={"id": self.__intra_extension_id, - # "sid": _action, - # "scid": category, - # "user_id": "admin"}, - # args={})["action_assignments"] - # result[category].extend(assignments[_action][category]) + meta_rules = self.ModelManager.get_meta_rules("admin") + for header in self.__headers: + policy_id = self.PolicyManager.get_policy_from_meta_rules("admin", header) + for meta_rule_id in meta_rules: + 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"]) + 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"]) + 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"]) return result def __repr__(self): @@ -335,6 +305,18 @@ pdp_set: {pdp_set} "request_id": copy.deepcopy(self.__request_id), } + @property + def request_id(self): + return self.__request_id + + @request_id.setter + def request_id(self, value): + raise Exception("You cannot update the request_id") + + @request_id.deleter + def request_id(self): + raise Exception("You cannot update the request_id") + @property def initial_request(self): return { -- cgit 1.2.3-korg