Code update for chaining policies. 51/34351/1
authorasteroide <thomas.duval@orange.com>
Fri, 5 May 2017 09:16:38 +0000 (11:16 +0200)
committerasteroide <thomas.duval@orange.com>
Fri, 5 May 2017 09:16:38 +0000 (11:16 +0200)
Change-Id: If9f6c2640492f69d0f3af2118fade72700df47e6

moonv4/moon_authz/moon_authz/api/authorization.py
moonv4/moon_authz/moon_authz/messenger.py
moonv4/moon_db/moon_db/api/policy.py
moonv4/moon_interface/moon_interface/tools.py
moonv4/moon_interface/tests/apitests/scenario/mls.py
moonv4/moon_interface/tests/apitests/scenario/rbac.py
moonv4/moon_interface/tests/apitests/set_authz.py
moonv4/moon_orchestrator/conf/plugins/authz.py
moonv4/moon_secrouter/moon_secrouter/api/route.py
moonv4/moon_utilities/moon_utilities/security_functions.py

index 248a956..5968178 100644 (file)
@@ -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}
 
index 8ebd163..6fa3477 100644 (file)
@@ -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()
+
 
index 73889b8..0beaf78 100644 (file)
@@ -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)
index 3c0fffa..443519a 100644 (file)
@@ -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()
index fab1d52..e36a86b 100644 (file)
@@ -39,6 +39,7 @@ meta_rule = {
 rules = {
     "mls": (
         ("high", "medium", "vm-action"),
+        ("high", "low", "vm-action"),
         ("medium", "low", "vm-action"),
     )
 }
index 073f1d6..cd08308 100644 (file)
@@ -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"),
     )
 }
index 7d0d506..38b6350 100644 (file)
@@ -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", "")))
+
index c472b36..4a1441c 100644 (file)
@@ -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):
index ec79d96..ccdff08 100644 (file)
@@ -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}
 
index 2ad52a4..f7cbdf2 100644 (file)
@@ -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 {