diff options
Diffstat (limited to 'moonv4/moon_interface/moon_interface/api/authz.py')
-rw-r--r-- | moonv4/moon_interface/moon_interface/api/authz.py | 244 |
1 files changed, 225 insertions, 19 deletions
diff --git a/moonv4/moon_interface/moon_interface/api/authz.py b/moonv4/moon_interface/moon_interface/api/authz.py index 69de0f80..3847cc73 100644 --- a/moonv4/moon_interface/moon_interface/api/authz.py +++ b/moonv4/moon_interface/moon_interface/api/authz.py @@ -6,23 +6,194 @@ Authz is the endpoint to get authorization response """ -from uuid import uuid4 -import time +from flask import request from flask_restful import Resource -from oslo_log import log as logging -from moon_utilities.security_functions import call +import logging +import pickle +import requests +import time +from uuid import uuid4 + +from moon_interface.containers import DockerManager +from moon_interface.authz_requests import AuthzRequest +from moon_utilities import configuration __version__ = "0.1.0" LOG = logging.getLogger("moon.interface.api." + __name__) +def pdp_in_cache(cache, uuid): + """Check if a PDP exist with this Keystone Project ID in the cache of this component + + :param cache: Cache to use + :param uuid: Keystone Project ID + :return: True or False + """ + for item_uuid, item_value in cache.pdp.items(): + if uuid == item_value['keystone_project_id']: + return item_uuid, item_value + return None, None + + +def pdp_in_manager(cache, uuid): + """Check if a PDP exist with this Keystone Project ID in the Manager component + + :param cache: Cache to use + :param uuid: Keystone Project ID + :return: True or False + """ + cache.update() + return pdp_in_cache(cache, uuid) + + +def container_exist(cache, uuid): + """Check if a PDP exist with this Keystone Project ID in the Manager component + + :param cache: Cache to use + :param uuid: Keystone Project ID + :return: True or False + """ + for key, value in cache.containers.items(): + if "keystone_project_id" not in value: + continue + if value["keystone_project_id"] == uuid: + try: + req = requests.head("http://{}:{}/".format( + value.get("hostname"), + value.get("port")[0].get("PublicPort"))) + LOG.info("container_exist {}".format(req.status_code)) + if req.status_code in (200, 201): + return value + return + except requests.exceptions.ConnectionError: + pass + # maybe hostname is not working so trying with IP address + try: + req = requests.head("http://{}:{}/".format( + value.get("ip"), + value.get("port")[0].get("PublicPort"))) + if req.status_code in (200, 201): + return value + return + except requests.exceptions.ConnectionError: + return + + +def build_container(cache, manager_url, uuid, meta_rule_id, plugin_name="authz"): + """Create the container and update the cache with the given perimeter elements + + :param cache: Cache to use + :param manager_url: URL of the manager + :param uuid: Keystone Project ID + :param meta_rule_id: UUID of the meta_rule + :param plugin_name: name of the plugin to use + :return: True or False + """ + LOG.info("Building a new container for {}".format(plugin_name)) + manager = DockerManager() + tcp_port = configuration.increment_port() + container_name = configuration.get_plugins()[plugin_name]['container'] + name = "{}_{}".format(plugin_name, uuid4().hex) + policy_id = cache.get_policy_from_meta_rules(meta_rule_id) + container_data = { + "name": name, + "hostname": name, + "port": { + "PrivatePort": tcp_port, + "Type": "tcp", + "IP": "0.0.0.0", + "PublicPort": tcp_port + }, + "keystone_project_id": uuid, + "pdp_id": cache.get_pdp_from_keystone_project(uuid), + "meta_rule_id": meta_rule_id, + "policy_id": policy_id, + "container_name": container_name, + "plugin_name": plugin_name + } + container = manager.create_container(container_data) + container_data['container_id'] = container.id + container_data['port']["IP"] = container.ip + container_data['start_time'] = time.time() + req = requests.post("{}/containers".format(manager_url), + json=container_data) + if req.status_code == 200: + cache.add_container(container_data) + return True + + +def create_containers(cache, manager_url, uuid, plugin_name="authz"): + """Create the container and update the cache with the given perimeter elements + + :param cache: Cache to use + :param manager_url: URL of the manager + :param uuid: Keystone Project ID + :param plugin_name: name of the plugin to use + :return: True or False + """ + LOG.info("Need to create some containers for {}".format(uuid)) + for pdp_id, pdp_value in cache.pdp.items(): + LOG.info("pdp {}".format(pdp_value)) + if uuid == pdp_value.get("keystone_project_id", ""): + LOG.info("uuid {}".format(uuid)) + for policy_id in pdp_value.get("security_pipeline", []): + LOG.info("policy {}".format(policy_id)) + model_id = cache.policies[policy_id]["model_id"] + model_value = cache.models[model_id] + for meta_rule_id in model_value["meta_rules"]: + LOG.info("meta_rule {}".format(meta_rule_id)) + build_container( + cache=cache, + uuid=uuid, + manager_url=manager_url, + meta_rule_id=meta_rule_id, + plugin_name=plugin_name) + return + + +def create_authz_request(cache, interface_name, manager_url, uuid, subject_name, object_name, action_name): + """Create the authorization request and make the first call to the Authz function + + :param cache: Cache to use + :param interface_name: hostname of the interface + :param manager_url: URL of the manager + :param uuid: Keystone Project ID + :param subject_name: name of the subject + :param object_name: name of the object + :param action_name: name of the action + :return: Authorisation request + """ + req_id = uuid4().hex + ctx = { + "project_id": uuid, + "subject_name": subject_name, + "object_name": object_name, + "action_name": action_name, + "request_id": req_id, + "interface_name": interface_name, + "manager_url": manager_url, + "cookie": uuid4().hex + } + cache.authz_requests[req_id] = AuthzRequest(ctx) + return cache.authz_requests[req_id] + + class Authz(Resource): """ Endpoint for authz requests """ - __urls__ = ("/authz/<string:uuid>/<string:subject_name>/<string:object_name>/<string:action_name>", ) + __urls__ = ( + "/authz/<string:uuid>", + "/authz/<string:uuid>/<string:subject_name>/<string:object_name>/<string:action_name>", + ) + + def __init__(self, **kwargs): + self.CACHE = kwargs.get("cache") + self.INTERFACE_NAME = kwargs.get("interface_name", "interface") + self.MANAGER_URL = kwargs.get("manager_url", "http://manager:8080") + self.TIMEOUT = 5 def get(self, uuid=None, subject_name=None, object_name=None, action_name=None): """Get a response on an authorization request @@ -51,17 +222,52 @@ class Authz(Resource): } :internal_api: authz """ - # Note (asteroide): user_id default to admin to be able to read the database - # it would be better to have a read-only user. - start_time = time.time() - result = call("security_router", ctx={"id": uuid, - "call_master": False, - "method": "authz", - "subject_name": subject_name, - "object_name": object_name, - "action_name": action_name, - "user_id": "admin", - "request_id": uuid4().hex}, args={}) - end_time = time.time() - result['time'] = {"start": start_time, "end": end_time} - return result + pdp_id, pdp_value = pdp_in_cache(self.CACHE, uuid) + if not pdp_id: + pdp_id, pdp_value = pdp_in_manager(self.CACHE, uuid) + if not pdp_id: + return { + "result": False, + "message": "Unknown Project ID or " + "Project ID is not bind to a PDP."}, 403 + if not container_exist(self.CACHE, uuid): + create_containers( + cache=self.CACHE, + uuid=uuid, + manager_url=self.MANAGER_URL, + plugin_name="authz") + authz_request = create_authz_request( + cache=self.CACHE, + uuid=uuid, + interface_name=self.INTERFACE_NAME, + manager_url=self.MANAGER_URL, + subject_name=subject_name, + object_name=object_name, + action_name=action_name) + cpt = 0 + while True: + if cpt > self.TIMEOUT*10: + return {"result": False, + "message": "Authz request had timed out."}, 500 + if authz_request.is_authz(): + if authz_request.final_result == "Grant": + return {"result": True, "message": ""}, 200 + return {"result": False, "message": ""}, 401 + cpt += 1 + time.sleep(0.1) + + def patch(self, uuid=None, subject_name=None, object_name=None, action_name=None): + """Get a response on an authorization request + + :param uuid: uuid of the authorization request + :param subject_name: not used + :param object_name: not used + :param action_name: not used + :request body: a Context object + :return: {} + :internal_api: authz + """ + if uuid in self.CACHE.authz_requests: + self.CACHE.authz_requests[uuid].set_result(pickle.loads(request.data)) + return "", 201 + return {"result": False, "message": "The request ID is unknown"}, 500 |