# 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
     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
                 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")
             return {"authz": False,
                     "error": str(e),
                     "pdp_id": self.pdp_id,
-                    "ctx": ctx, "args": args}
+                    "ctx": ctxt, "args": payload}
 
 
 
 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
 
     ),
     "function": (
         "authz",
+        "return_authz",
     ),
 }
 
 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):
         :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:
             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)
                 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}
 
 
 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
 __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:
         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
         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()
 
         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):
             "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 {