diff options
Diffstat (limited to 'moon_engine/moon_engine/api/pipeline')
-rw-r--r-- | moon_engine/moon_engine/api/pipeline/__init__.py | 11 | ||||
-rw-r--r-- | moon_engine/moon_engine/api/pipeline/authz.py | 35 | ||||
-rw-r--r-- | moon_engine/moon_engine/api/pipeline/update.py | 188 | ||||
-rw-r--r-- | moon_engine/moon_engine/api/pipeline/update_pipeline.py | 220 | ||||
-rw-r--r-- | moon_engine/moon_engine/api/pipeline/validator.py | 125 |
5 files changed, 579 insertions, 0 deletions
diff --git a/moon_engine/moon_engine/api/pipeline/__init__.py b/moon_engine/moon_engine/api/pipeline/__init__.py new file mode 100644 index 00000000..582be686 --- /dev/null +++ b/moon_engine/moon_engine/api/pipeline/__init__.py @@ -0,0 +1,11 @@ +# Software Name: MOON + +# Version: 5.4 + +# SPDX-FileCopyrightText: Copyright (c) 2018-2020 Orange and its contributors +# SPDX-License-Identifier: Apache-2.0 + +# This software is distributed under the 'Apache License 2.0', +# the text of which is available at 'http://www.apache.org/licenses/LICENSE-2.0.txt' +# or see the "LICENSE" file for more details. + diff --git a/moon_engine/moon_engine/api/pipeline/authz.py b/moon_engine/moon_engine/api/pipeline/authz.py new file mode 100644 index 00000000..02fb7d30 --- /dev/null +++ b/moon_engine/moon_engine/api/pipeline/authz.py @@ -0,0 +1,35 @@ +# Software Name: MOON + +# Version: 5.4 + +# SPDX-FileCopyrightText: Copyright (c) 2018-2020 Orange and its contributors +# SPDX-License-Identifier: Apache-2.0 + +# This software is distributed under the 'Apache License 2.0', +# the text of which is available at 'http://www.apache.org/licenses/LICENSE-2.0.txt' +# or see the "LICENSE" file for more details. + + +import hug +from moon_engine.api.pipeline.validator import Validator + + +class Authz(object): + + @staticmethod + @hug.local() + @hug.get("/authz/{subject_name}/{object_name}/{action_name}") + def get(subject_name: hug.types.text, object_name: hug.types.text, + action_name: hug.types.text, response): + """Get a response on Main Authorization request + + :param subject_name: name of the subject or the request + :param object_name: name of the object + :param action_name: name of the action + :return: + "result": {true or false } + :internal_api: authz + """ + + validator = Validator() + response.status = validator.authz(subject_name, object_name, action_name) diff --git a/moon_engine/moon_engine/api/pipeline/update.py b/moon_engine/moon_engine/api/pipeline/update.py new file mode 100644 index 00000000..deaf9c12 --- /dev/null +++ b/moon_engine/moon_engine/api/pipeline/update.py @@ -0,0 +1,188 @@ +# Software Name: MOON + +# Version: 5.4 + +# SPDX-FileCopyrightText: Copyright (c) 2018-2020 Orange and its contributors +# SPDX-License-Identifier: Apache-2.0 + +# This software is distributed under the 'Apache License 2.0', +# the text of which is available at 'http://www.apache.org/licenses/LICENSE-2.0.txt' +# or see the "LICENSE" file for more details. + + +"""Update API""" +import hug +from moon_utilities.auth_functions import api_key_authentication +from moon_engine.api.pipeline.update_pipeline import Update + + +class PipelineUpdate(object): + + @staticmethod + @hug.local() + @hug.put("/update/slave/{slave_id}", requires=api_key_authentication) + def update_slave(body, slave_id: hug.types.uuid, response): + """Tell the moon_engine wrapper that its cache should be updated + body may contain the attributes that the moon_engine should get from the manager + body example: + { + "name": "...", + "description": "..." + } + :return: 202 status code + """ + update_pipeline = Update() + response.status = update_pipeline.update_slaves(slave_id=str(slave_id).replace("-", ""), is_delete=False, + data=body) + + @staticmethod + @hug.local() + @hug.put("/update/pdp/{pdp_id}", requires=api_key_authentication) + def update_pdp(body, pdp_id: hug.types.uuid, response): + """Tell the moon_engine wrapper that its cache should be updated + body may contain the attributes that the moon_engine should get from the manager + if the attributes key is empty, all data should be retrieved + body example: + { + "vim_project_id": "...", + "security_pipeline": ["policy_id1", "policy_id2"], + "attributes": ["subjects", "subject_assignments", "subject_categories"] + } + :return: 202 status code + """ + + # todo call wrapper to update its pdp at the cache + update_pipeline = Update() + response.status = update_pipeline.update_pdp(is_delete=False, pdp_id=str(pdp_id).replace("-", ""), data=body) + + @staticmethod + @hug.local() + @hug.delete("/update/pdp/{pdp_id}", requires=api_key_authentication) + def delete_pdp(pdp_id: hug.types.uuid, response): + """Tell the moon_engine wrapper that its cache should be updated + body may contain the attributes that the moon_engine should get from the manager + if the attributes key is empty, all data should be retrieved + body example: + { + "vim_project_id": "...", + "security_pipeline": ["policy_id1", "policy_id2"], + "attributes": ["subjects", "subject_assignments", "subject_categories"] + } + :return: 202 status code + """ + + # todo call wrapper to update its pdp at the cache + update_pipeline = Update() + response.status = update_pipeline.update_pdp(is_delete=True, pdp_id=str(pdp_id).replace("-", "")) + + @staticmethod + @hug.local() + @hug.put("/update/policy/{policy_id}", requires=api_key_authentication) + def update_policy(body, policy_id: hug.types.uuid, response): + update_pipeline = Update() + response.status = update_pipeline.update_policy(is_delete=False, policy_id=str(policy_id).replace("-", ""), + data=body) + + @staticmethod + @hug.local() + @hug.delete("/update/policy/{policy_id}", requires=api_key_authentication) + def delete_policy(policy_id: hug.types.uuid, response): + update_pipeline = Update() + response.status = update_pipeline.update_policy(is_delete=True, policy_id=str(policy_id).replace("-", "")) + + @staticmethod + @hug.local() + @hug.delete("/update/assignment/{policy_id}/{type}/", requires=api_key_authentication) + @hug.delete("/update/assignment/{policy_id}/{type}/{perimeter_id}", + requires=api_key_authentication) + @hug.delete("/update/assignment/{policy_id}/{type}/{perimeter_id}/{category_id}", + requires=api_key_authentication) + @hug.delete("/update/assignment/{policy_id}/{type}/{perimeter_id}/{category_id}/{data_id}", + requires=api_key_authentication) + def delete_assignment(response, policy_id: hug.types.uuid, type: hug.types.text, + perimeter_id: hug.types.uuid = None, category_id: hug.types.uuid = None, + data_id: hug.types.uuid = None, authed_user: hug.directives.user=None): + update_pipeline = Update() + response.status = update_pipeline.delete_assignment(type=type, policy_id=str(policy_id).replace("-", ""), + perimeter_id=str(perimeter_id).replace("-", ""), + category_id=str(category_id).replace("-", ""), + data_id=data_id) + + @staticmethod + @hug.local() + @hug.put("/update/perimeter/{perimeter_id}/{policy_id}/{type}", requires=api_key_authentication) + def update_perimeter(body, perimeter_id: hug.types.uuid, policy_id: hug.types.uuid, + type: hug.types.text, response): + update_pipeline = Update() + response.status = update_pipeline.update_perimeter(is_delete=False, type=type, + perimeter_id=str(perimeter_id).replace("-", ""), data=body, + policy_id=str(policy_id).replace("-", "")) + + @staticmethod + @hug.local() + @hug.delete("/update/perimeter/{perimeter_id}/{policy_id}/{type}", requires=api_key_authentication) + def delete_perimeter(perimeter_id: hug.types.uuid, policy_id: hug.types.uuid, + type: hug.types.text, response): + update_pipeline = Update() + response.status = update_pipeline.update_perimeter(is_delete=True, type=type, + perimeter_id=str(perimeter_id).replace("-", ""), + policy_id=str(policy_id).replace("-", "")) + + @staticmethod + @hug.local() + @hug.delete("/update/rule/{policy_id}/{rule_id}", requires=api_key_authentication) + def delete_rule(policy_id: hug.types.uuid, rule_id: hug.types.uuid, response): + update_pipeline = Update() + response.status = update_pipeline.delete_rule(rule_id=str(rule_id).replace("-", ""), policy_id=str(policy_id).replace("-", "")) + + @staticmethod + @hug.local() + @hug.put("/update/model/{model_id}", requires=api_key_authentication) + def update_model(body, model_id: hug.types.uuid, response): + update_pipeline = Update() + response.status = update_pipeline.update_model(model_id=str(model_id).replace("-", ""), is_delete=False, + data=body) + + @staticmethod + @hug.local() + @hug.delete("/update/model/{model_id}", requires=api_key_authentication) + def delete_model(model_id: hug.types.uuid, response): + update_pipeline = Update() + response.status = update_pipeline.update_model(model_id=str(model_id).replace("-", ""), is_delete=True) + + @staticmethod + @hug.local() + @hug.delete("/update/meta_data/{category_id}/{type}", requires=api_key_authentication) + def delete_category(category_id: hug.types.uuid, type: hug.types.text, response): + update_pipeline = Update() + response.status = update_pipeline.delete_category(category_id=str(category_id).replace("-", ""), type=type) + + @staticmethod + @hug.local() + @hug.put("/update/meta_rule/{meta_rule_id}", requires=api_key_authentication) + def update_meta_rule(body, meta_rule_id: hug.types.uuid, response): + update_pipeline = Update() + response.status = update_pipeline.update_meta_rule(is_delete=False, + meta_rule_id=str(meta_rule_id).replace("-", ""), data=body) + + @staticmethod + @hug.local() + @hug.delete("/update/meta_rule/{meta_rule_id}", requires=api_key_authentication) + def delete_meta_rule(meta_rule_id: hug.types.uuid, response): + update_pipeline = Update() + response.status = update_pipeline.update_meta_rule(is_delete=True, + meta_rule_id=str(meta_rule_id).replace("-", "")) + + @staticmethod + @hug.local() + @hug.delete("/update/data/{data_id}/{type}", requires=api_key_authentication) + def delete_data(data_id: hug.types.uuid, type: hug.types.text, response): + update_pipeline = Update() + response.status = update_pipeline.delete_data(data_id=str(data_id).replace("-", ""), type=type) + + @staticmethod + @hug.local() + @hug.delete("/update/attributes/{name}", requires=api_key_authentication) + def delete_data(name: hug.types.text, response): + update_pipeline = Update() + response.status = update_pipeline.delete_attributes(name=name) diff --git a/moon_engine/moon_engine/api/pipeline/update_pipeline.py b/moon_engine/moon_engine/api/pipeline/update_pipeline.py new file mode 100644 index 00000000..3b312efb --- /dev/null +++ b/moon_engine/moon_engine/api/pipeline/update_pipeline.py @@ -0,0 +1,220 @@ +# Software Name: MOON + +# Version: 5.4 + +# SPDX-FileCopyrightText: Copyright (c) 2018-2020 Orange and its contributors +# SPDX-License-Identifier: Apache-2.0 + +# This software is distributed under the 'Apache License 2.0', +# the text of which is available at 'http://www.apache.org/licenses/LICENSE-2.0.txt' +# or see the "LICENSE" file for more details. + + +import hug +from moon_cache.cache import Cache +from moon_engine.api.configuration import get_configuration +import logging + +logger = logging.getLogger("moon.engine.api.pipeline.update_pipeline") + + +class Update(object): + __CACHE = None + + def __init__(self): + if not self.__CACHE: + self.__CACHE = Cache.getInstance(manager_url=get_configuration("manager_url"), + incremental=get_configuration("incremental_updates"), + manager_api_key=get_configuration("api_token")) + + def update_policy(self, is_delete, policy_id, data=None): + + policies = self.__CACHE.policies + if is_delete: + if policy_id in policies: + del policies[policy_id] + else: + if policy_id in policies: + policies[policy_id] = data + else: + return hug.HTTP_208 + return hug.HTTP_202 + + def update_pdp(self, is_delete, pdp_id, data=None): + + pdps = self.__CACHE.pdp + if is_delete: + if pdp_id in pdps: + del pdps[pdp_id] + else: + if pdp_id in pdps: + pdps[pdp_id] = data + else: + return hug.HTTP_208 + return hug.HTTP_202 + + def delete_assignment(self, type, policy_id, perimeter_id=None, category_id=None, data_id=None): + + if type == "subject": + assignments = self.__CACHE.subject_assignments + if policy_id in assignments: + for key in assignments[policy_id]: + if (perimeter_id is None or assignments[policy_id][key]['subject_id'] == + perimeter_id) and ( + category_id is None or assignments[policy_id][key]['category_id'] == category_id): + if data_id is None or data_id in assignments[policy_id][key]['assignments']: + assignments[policy_id][key]['assignments'].remove(data_id) + if len(assignments[policy_id][key]['assignments']) == 0: + del assignments[policy_id][key]; + else: + del assignments[policy_id][key] + break + + elif type == "object": + assignments = self.__CACHE.object_assignments + if policy_id in assignments: + for key in assignments[policy_id]: + if (perimeter_id is None or assignments[policy_id][key]['object_id'] == + perimeter_id) and ( + category_id is None or assignments[policy_id][key]['category_id'] == category_id): + if data_id is None or data_id in assignments[policy_id][key]['assignments']: + assignments[policy_id][key]['assignments'].remove(data_id) + if len(assignments[policy_id][key]['assignments']) == 0: + del assignments[policy_id][key]; + else: + del assignments[policy_id][key] + break + else: + assignments = self.__CACHE.action_assignments + if policy_id in assignments: + for key in assignments[policy_id]: + if (perimeter_id is None or assignments[policy_id][key]['action_id'] == + perimeter_id) and ( + category_id is None or assignments[policy_id][key]['category_id'] == category_id): + if data_id is None or data_id in assignments[policy_id][key]['assignments']: + assignments[policy_id][key]['assignments'].remove(data_id) + if len(assignments[policy_id][key]['assignments']) == 0: + del assignments[policy_id][key]; + else: + del assignments[policy_id][key] + break + return hug.HTTP_202 + + def update_perimeter(self, is_delete, type, perimeter_id, data=None, policy_id=None): + + if is_delete: + if type == "subject": + perimeters = self.__CACHE.subjects + if policy_id in perimeters and perimeter_id in perimeters[policy_id] and \ + policy_id in perimeters[policy_id][perimeter_id]['policy_list']: + del perimeters[policy_id][perimeter_id] + elif type == "object": + perimeters = self.__CACHE.objects + if policy_id in perimeters and perimeter_id in perimeters[policy_id] and \ + policy_id in perimeters[policy_id][perimeter_id]['policy_list']: + del perimeters[policy_id][perimeter_id] + else: + perimeters = self.__CACHE.actions + if policy_id in perimeters and perimeter_id in perimeters[policy_id] and \ + policy_id in perimeters[policy_id][perimeter_id]['policy_list']: + del perimeters[policy_id][perimeter_id] + else: + if type == "subject": + perimeters = self.__CACHE.subjects + if policy_id in perimeters and perimeter_id in perimeters[policy_id] and \ + policy_id in perimeters[policy_id][perimeter_id]['policy_list']: + perimeters[policy_id][perimeter_id]['name'] = data['name'] + perimeters[policy_id][perimeter_id]['description'] = data['description'] + else: + return hug.HTTP_208 + elif type == "object": + perimeters = self.__CACHE.objects + if policy_id in perimeters and perimeter_id in perimeters[policy_id] and \ + policy_id in perimeters[policy_id][perimeter_id]['policy_list']: + perimeters[policy_id][perimeter_id]['name'] = data['name'] + perimeters[policy_id][perimeter_id]['description'] = data['description'] + else: + return hug.HTTP_208 + else: + perimeters = self.__CACHE.actions + if policy_id in perimeters and perimeter_id in perimeters[policy_id] and \ + policy_id in perimeters[policy_id][perimeter_id]['policy_list']: + perimeters[policy_id][perimeter_id]['name'] = data['name'] + perimeters[policy_id][perimeter_id]['description'] = data['description'] + else: + return hug.HTTP_208 + return hug.HTTP_202 + + def delete_rule(self, rule_id, policy_id): + + rules = self.__CACHE.rules + if policy_id in rules and rule_id in rules[policy_id]: + del rules[policy_id][rule_id] + return hug.HTTP_202 + + def update_model(self, model_id, is_delete, data=None): + if is_delete: + models = self.__CACHE.models + if model_id in models: + del models[model_id] + else: + models = self.__CACHE.models + if model_id in models: + models[model_id] = data + else: + return hug.HTTP_208 + return hug.HTTP_202 + + def delete_category(self, category_id, type): + + if type == "subject": + categories = self.__CACHE.subject_categories + if category_id in categories: + del categories[category_id] + elif type == 'object': + categories = self.__CACHE.object_categories + if category_id in categories: + del categories[category_id] + else: + categories = self.__CACHE.action_categories + if category_id in categories: + del categories[category_id] + return hug.HTTP_202 + + def update_meta_rule(self, is_delete, meta_rule_id, data=None): + + if is_delete: + meta_rules = self.__CACHE.meta_rules + if meta_rule_id in meta_rules: + del meta_rules[meta_rule_id] + else: + meta_rules = self.__CACHE.meta_rules + if meta_rule_id in meta_rules: + meta_rules[meta_rule_id] = data + else: + return hug.HTTP_208 + return hug.HTTP_202 + + def delete_data(self, data_id, type): + + if type == 'subject': + data = self.__CACHE.subject_data + if data_id in data: + del data[data_id] + elif type == 'object': + data = self.__CACHE.object_data + if data_id in data: + del data[data_id] + else: + data = self.__CACHE.action_data + if data_id in data: + del data[data_id] + + return hug.HTTP_202 + + def delete_attributes(self, name): + + attributes = self.__CACHE.attributes + self.__CACHE.set_attribute(name) + + return hug.HTTP_202 diff --git a/moon_engine/moon_engine/api/pipeline/validator.py b/moon_engine/moon_engine/api/pipeline/validator.py new file mode 100644 index 00000000..1fc8588a --- /dev/null +++ b/moon_engine/moon_engine/api/pipeline/validator.py @@ -0,0 +1,125 @@ +# Software Name: MOON + +# Version: 5.4 + +# SPDX-FileCopyrightText: Copyright (c) 2018-2020 Orange and its contributors +# SPDX-License-Identifier: Apache-2.0 + +# This software is distributed under the 'Apache License 2.0', +# the text of which is available at 'http://www.apache.org/licenses/LICENSE-2.0.txt' +# or see the "LICENSE" file for more details. + + +from moon_cache.context import Context +from moon_utilities import exceptions +import itertools +import logging +import hug +from moon_cache.cache import Cache +from moon_engine.api.configuration import get_configuration + +LOGGER = logging.getLogger("moon.authz.api." + __name__) + + +class Validator(object): + __CACHE = None + + def __init__(self): + + self.__CACHE = Cache.getInstance(manager_url=get_configuration("manager_url"), + incremental=get_configuration("incremental_updates"), + manager_api_key=get_configuration("api_token")) + self.context = None + + def authz(self, subject_name, object_name, action_name): + + ctx = { + "pdp_id": get_configuration("uuid"), + "subject_name": subject_name, + "object_name": object_name, + "action_name": action_name + } + self.context = Context(ctx, self.__CACHE) + + self.context.set_cache(self.__CACHE) + self.context.increment_index() + response = self.__authz_request() + self.context.delete_cache() + return response + + def __authz_request(self): + + LOGGER.debug("self.context.pdp_set={}".format(self.context.pdp_set)) + result, message = self.__check_rules() + if result: + if self.__exec_instructions(result): + return hug.HTTP_204 + else: + self.context.current_state = "deny" + return hug.HTTP_403 + + def __check_rules(self): + + scopes_list = list() + current_header_id = self.context.headers[self.context.index] + if not self.context.pdp_set: + raise exceptions.PdpUnknown + if current_header_id not in self.context.pdp_set: + raise Exception('Invalid index') + current_pdp = self.context.pdp_set[current_header_id] + category_list = list() + if 'meta_rules' not in current_pdp: + raise exceptions.PdpContentError + try: + category_list.extend(current_pdp["meta_rules"]["subject_categories"]) + category_list.extend(current_pdp["meta_rules"]["object_categories"]) + category_list.extend(current_pdp["meta_rules"]["action_categories"]) + except Exception: + raise exceptions.MetaRuleContentError + if 'target' not in current_pdp: + raise exceptions.PdpContentError + for category in category_list: + scope = list(current_pdp['target'][category]) + scopes_list.append(scope) + if self.context.current_policy_id not in self.__CACHE.rules: + raise exceptions.PolicyUnknown + if 'rules' not in self.__CACHE.rules[self.context.current_policy_id]: + raise exceptions.RuleUnknown + + for item in itertools.product(*scopes_list): + req = list(item) + for rule in self.__CACHE.rules[self.context.current_policy_id]["rules"]: + if req == rule['rule']: + return rule['instructions'], "" + if not list(itertools.product(*scopes_list)): + LOGGER.error("There is an error in retrieved scopes ({})".format(scopes_list)) + cat_list = [] + categories = dict(self.__CACHE.subject_categories) + categories.update(dict(self.__CACHE.object_categories)) + categories.update(dict(self.__CACHE.action_categories)) + for category in category_list: + if category.startswith("attributes:"): + cat_list.append(category) + else: + cat_list.append(categories[category].get('name')) + LOGGER.error("Categories are ({})".format(", ".join(cat_list))) + return False, "There is an error in retrieved scopes" + LOGGER.warning("No rule match the request...") + return False, "No rule match the request..." + + def __exec_instructions(self, instructions): + + for instruction in instructions: + for key in instruction: + if key == "decision": + if instruction["decision"] == "grant": + self.context.current_state = "grant" + LOGGER.info("__exec_instructions True {}".format( + self.context.current_state)) + return True + else: + self.context.current_state = instruction["decision"].lower() + + LOGGER.info("__exec_instructions False {}".format(self.context.current_state)) + + return False |