text: # Copyright 2015 Open Platform for NFV Project, Inc. and its contributors
  # This software is distributed under the terms and conditions of the 'Apache-2.0'
  # license which can be found in the file 'LICENSE' in this package distribution
  # or at 'http://www.apache.org/licenses/LICENSE-2.0'.
  
  import logging
  import itertools
  import pickle
  import requests
  from moon_utilities import configuration, exceptions
  from moon_utilities.security_functions import Context
  from moon_utilities.cache import Cache
  
  LOG = logging.getLogger("moon.interface.authz_requests")
  
  
  CACHE = Cache()
  CACHE.update()
  
  
  class AuthzRequest:
  
      result = None
      final_result = "Deny"
      req_max_delay = 2
  
      def __init__(self, ctx, args=None):
          self.context = Context(ctx, CACHE)
          self.args = args
          self.request_id = ctx["request_id"]
          if ctx['project_id'] not in CACHE.container_chaining:
              raise exceptions.KeystoneProjectError("Unknown Project ID {}".format(ctx['project_id']))
          self.container_chaining = CACHE.container_chaining[ctx['project_id']]
          if len(self.container_chaining) == 0:
              raise exceptions.MoonError('Void container chaining')
          self.pdp_container = self.container_chaining[0]["container_id"]
          self.run()
  
      def run(self):
          self.context.delete_cache()
          try:
              req = requests.post("http://{}:{}/authz".format(
                  self.container_chaining[0]["hostname"],
                  self.container_chaining[0]["port"],
              ), data=pickle.dumps(self.context))
              if req.status_code != 200:
                  # LOG.error("Cannot connect to {}".format(
                  #     "http://{}:{}/authz".format(
                  #         self.container_chaining[0]["hostname"],
                  #         self.container_chaining[0]["port"]
                  #     )))
                  raise exceptions.AuthzException(
                      "Receive bad response from Authz function "
                      "(with hostname - {})".format(
                          req.status_code
                      ))
          except requests.exceptions.ConnectionError:
              try:
                  req = requests.post("http://{}:{}/authz".format(
                      self.container_chaining[0]["hostip"],
                      self.container_chaining[0]["port"],
                  ), data=pickle.dumps(self.context))
                  if req.status_code != 200:
                      # LOG.error("req={}".format(req))
                      raise exceptions.AuthzException(
                          "Receive bad response from Authz function "
                          "(with IP address - {})".format(
                              req.status_code
                          ))
              except requests.exceptions.ConnectionError:
                  LOG.error("Cannot connect to {}".format(
                      "http://{}:{}/authz".format(
                          self.container_chaining[0]["hostip"],
                          self.container_chaining[0]["port"]
                      )))
                  raise exceptions.AuthzException(
                      "Cannot connect to Authz function with IP address")
          self.context.set_cache(CACHE)
          if len(self.container_chaining) == 1:
              # req.raw.decode_content = True
              self.result = pickle.loads(req.content)
  
      def __exec_next_state(self, rule_found):
          index = self.context.index
          current_meta_rule = self.context.headers[index]
          current_container = self.__get_container_from_meta_rule(current_meta_rule)
          current_container_genre = current_container["genre"]
          try:
              next_meta_rule = self.context.headers[index + 1]
          except IndexError:
              next_meta_rule = None
          if current_container_genre == "authz":
              if rule_found:
                  return True
              pass
              if next_meta_rule:
                  # next will be session if current is deny and session is unset
                  if self.payload["authz_context"]['pdp_set'][next_meta_rule]['effect'] == "unset":
                      return notify(
                          request_id=self.payload["authz_context"]["request_id"],
                          container_id=self.__get_container_from_meta_rule(next_meta_rule)['container_id'],
                          payload=self.payload)
                  # next will be delegation if current is deny and session is passed or deny and delegation is unset
                  else:
                      LOG.error("Delegation is not developed!")
  
              else:
                  # else next will be None and the request is sent to router
                  return self.__return_to_router()
          elif current_container_genre == "session":
              pass
              # next will be next container in headers if current is passed
              if self.payload["authz_context"]['pdp_set'][current_meta_rule]['effect'] == "passed":
                  return notify(
                      request_id=self.payload["authz_context"]["request_id"],
                      container_id=self.__get_container_from_meta_rule(next_meta_rule)['container_id'],
                      payload=self.payload)
              # next will be None if current is grant and the request is sent to router
              else:
                  return self.__return_to_router()
          elif current_container_genre == "delegation":
              LOG.error("Delegation is not developed!")
              # next will be authz if current is deny
              # next will be None if current is grant and the request is sent to router
  
      def set_result(self, result):
          self.result = result
  
      def is_authz(self):
          if not self.result:
              return False
          authz_results = []
          for key in self.result.pdp_set:
              if "effect" in self.result.pdp_set[key]:
                  if self.result.pdp_set[key]["effect"] == "grant":
                      # the pdp is a authorization PDP and grant the request
                      authz_results.append(True)
                  elif self.result.pdp_set[key]["effect"] == "passed":
                      # the pdp is not a authorization PDP (session or delegation) and had run normally
                      authz_results.append(True)
                  elif self.result.pdp_set[key]["effect"] == "unset":
                      # the pdp is not a authorization PDP (session or delegation) and had not yep run
                      authz_results.append(True)
                  else:
                      # the pdp is (or not) a authorization PDP and had run badly
                      authz_results.append(False)
          if list(itertools.accumulate(authz_results, lambda x, y: x & y))[-1]:
              self.result.pdp_set["effect"] = "grant"
          if self.result:
              if self.result.pdp_set["effect"] == "grant":
                  self.final_result = "Grant"
                  return True
          self.final_result = "Deny"
          return True
  
      # def notify(self, request_id, container_id, payload):
      #     LOG.info("notify {} {} {}".format(request_id, container_id, payload))
      #     # TODO: send the notification and wait for the result
      #     # req = requests.get()
  