From 7bb53c64da2dcf88894bfd31503accdd81498f3d Mon Sep 17 00:00:00 2001 From: Thomas Duval Date: Wed, 3 Jun 2020 10:06:52 +0200 Subject: Update to new version 5.4 Signed-off-by: Thomas Duval Change-Id: Idcd868133d75928a1ffd74d749ce98503e0555ea --- moon_manager/moon_manager/api/__init__.py | 28 + moon_manager/moon_manager/api/assignments.py | 751 +++++++++++----- moon_manager/moon_manager/api/attributes.py | 207 +++++ moon_manager/moon_manager/api/auth.py | 42 + moon_manager/moon_manager/api/base_exception.py | 17 - moon_manager/moon_manager/api/checks.py | 211 +++++ moon_manager/moon_manager/api/configuration.py | 237 +++++ moon_manager/moon_manager/api/data.py | 649 +++++++++++--- moon_manager/moon_manager/api/db/__init__.py | 12 + moon_manager/moon_manager/api/db/managers.py | 24 + .../moon_manager/api/db/migrations/__init__.py | 12 + .../moon_manager/api/db/migrations/moon_001.py | 336 +++++++ moon_manager/moon_manager/api/db/model.py | 429 +++++++++ moon_manager/moon_manager/api/db/pdp.py | 115 +++ moon_manager/moon_manager/api/db/policy.py | 971 +++++++++++++++++++++ moon_manager/moon_manager/api/db/slave.py | 55 ++ moon_manager/moon_manager/api/generic.py | 144 --- .../moon_manager/api/information/__init__.py | 11 + .../moon_manager/api/information/global_attrs.py | 145 +++ .../moon_manager/api/information/information.py | 106 +++ .../moon_manager/api/information/managers.py | 19 + moon_manager/moon_manager/api/json_export.py | 282 +----- moon_manager/moon_manager/api/json_import.py | 600 +------------ moon_manager/moon_manager/api/json_utils.py | 282 ------ moon_manager/moon_manager/api/logs.py | 25 + moon_manager/moon_manager/api/meta_data.py | 526 +++++++++-- moon_manager/moon_manager/api/meta_rules.py | 253 +++++- moon_manager/moon_manager/api/models.py | 233 ++++- .../moon_manager/api/orchestration/__init__.py | 12 + .../moon_manager/api/orchestration/managers.py | 21 + .../moon_manager/api/orchestration/pipeline.py | 44 + .../moon_manager/api/orchestration/slave.py | 44 + moon_manager/moon_manager/api/pdp.py | 468 +++++++--- moon_manager/moon_manager/api/perimeter.py | 838 +++++++++++++++--- moon_manager/moon_manager/api/policies.py | 125 --- moon_manager/moon_manager/api/policy.py | 293 +++++++ moon_manager/moon_manager/api/rules.py | 319 +++++-- moon_manager/moon_manager/api/slave.py | 341 ++++++++ moon_manager/moon_manager/api/slaves.py | 111 --- moon_manager/moon_manager/api/status.py | 127 +++ moon_manager/moon_manager/api/users.py | 95 ++ 41 files changed, 7239 insertions(+), 2321 deletions(-) create mode 100644 moon_manager/moon_manager/api/attributes.py create mode 100644 moon_manager/moon_manager/api/auth.py delete mode 100644 moon_manager/moon_manager/api/base_exception.py create mode 100644 moon_manager/moon_manager/api/checks.py create mode 100644 moon_manager/moon_manager/api/configuration.py create mode 100644 moon_manager/moon_manager/api/db/__init__.py create mode 100644 moon_manager/moon_manager/api/db/managers.py create mode 100644 moon_manager/moon_manager/api/db/migrations/__init__.py create mode 100644 moon_manager/moon_manager/api/db/migrations/moon_001.py create mode 100644 moon_manager/moon_manager/api/db/model.py create mode 100644 moon_manager/moon_manager/api/db/pdp.py create mode 100644 moon_manager/moon_manager/api/db/policy.py create mode 100644 moon_manager/moon_manager/api/db/slave.py delete mode 100644 moon_manager/moon_manager/api/generic.py create mode 100644 moon_manager/moon_manager/api/information/__init__.py create mode 100644 moon_manager/moon_manager/api/information/global_attrs.py create mode 100644 moon_manager/moon_manager/api/information/information.py create mode 100644 moon_manager/moon_manager/api/information/managers.py delete mode 100644 moon_manager/moon_manager/api/json_utils.py create mode 100644 moon_manager/moon_manager/api/logs.py create mode 100644 moon_manager/moon_manager/api/orchestration/__init__.py create mode 100644 moon_manager/moon_manager/api/orchestration/managers.py create mode 100644 moon_manager/moon_manager/api/orchestration/pipeline.py create mode 100644 moon_manager/moon_manager/api/orchestration/slave.py delete mode 100644 moon_manager/moon_manager/api/policies.py create mode 100644 moon_manager/moon_manager/api/policy.py create mode 100644 moon_manager/moon_manager/api/slave.py delete mode 100644 moon_manager/moon_manager/api/slaves.py create mode 100644 moon_manager/moon_manager/api/status.py create mode 100644 moon_manager/moon_manager/api/users.py (limited to 'moon_manager/moon_manager/api') diff --git a/moon_manager/moon_manager/api/__init__.py b/moon_manager/moon_manager/api/__init__.py index e69de29b..bf70b330 100644 --- a/moon_manager/moon_manager/api/__init__.py +++ b/moon_manager/moon_manager/api/__init__.py @@ -0,0 +1,28 @@ +# 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 falcon import HTTP_400, HTTP_401, HTTP_402, HTTP_403, HTTP_404, HTTP_405, \ + HTTP_406, HTTP_407, HTTP_408, HTTP_409, HTTP_500 + +ERROR_CODE = { + 400: HTTP_400, + 401: HTTP_401, + 402: HTTP_402, + 403: HTTP_403, + 404: HTTP_404, + 405: HTTP_405, + 406: HTTP_406, + 407: HTTP_407, + 408: HTTP_408, + 409: HTTP_409, + 500: HTTP_500 +} diff --git a/moon_manager/moon_manager/api/assignments.py b/moon_manager/moon_manager/api/assignments.py index 9bc54b2d..742a043b 100644 --- a/moon_manager/moon_manager/api/assignments.py +++ b/moon_manager/moon_manager/api/assignments.py @@ -1,78 +1,57 @@ -# 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'. +# 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. + """ Assignments allow to connect data with elements of perimeter """ -import flask -from flask import request -from flask_restful import Resource +import hug import logging import requests -from python_moonutilities.security_functions import check_auth -from python_moondb.core import PolicyManager -from python_moonutilities.security_functions import validate_input - -__version__ = "4.3.2" - -logger = logging.getLogger("moon.manager.api." + __name__) - - -def invalidate_data_in_slaves( - policy_id, - perimeter_id, - category_id, - data_id): - slaves = requests.get("http://{}/slaves".format(request.host)).json().get("slaves") - for slave in slaves: - if not slave.get("configured", False): - continue - try: - update = requests.put("http://{}:{}/update".format( - slave.get("wrapper_name"), slave.get("internal_port")), - data={ - "policy_id": policy_id, - "perimeter_id": perimeter_id, - "category_id": category_id, - "data_id": data_id - }, - timeout=1 - ) - logger.info("result {} {}:{} = {}".format( - update.status_code, - slave.get("wrapper_name"), - slave.get("internal_port"), - update.text)) - except requests.exceptions.ConnectionError: - logger.warning("Cannot reach {}:{}".format(slave.get("wrapper_name"), slave.get("port"))) - - -class SubjectAssignments(Resource): +from moon_manager import db_driver as driver +from moon_utilities.security_functions import validate_input +from moon_manager.api import slave as slave_class +from moon_manager.api import configuration +from moon_manager.api import policy +from moon_manager.api import perimeter +from moon_manager.api import meta_data +from moon_manager.api import data +from moon_utilities.auth_functions import api_key_authentication, connect_from_env +from moon_utilities.invalided_functions import invalidate_assignment_in_slaves + +# from moon_manager.server import handle_exception, handle_custom_exceptions + +LOGGER = logging.getLogger("moon.manager.api." + __name__) + + +class SubjectAssignments(object): """ Endpoint for subject assignment requests """ - __urls__ = ( - "/policies//subject_assignments", - "/policies//subject_assignments/", - "/policies//subject_assignments/", - "/policies//subject_assignments//", - "/policies//subject_assignments///", - ) - - @validate_input("get", kwargs_state=[True, False, False, False, False]) - @check_auth - def get(self, uuid=None, perimeter_id=None, category_id=None, - data_id=None, user_id=None): + @staticmethod + @hug.local() + @hug.get("/policies/{uuid}/subject_assignments", requires=api_key_authentication) + @hug.get("/policies/{uuid}/subject_assignments/{perimeter_id}", + requires=api_key_authentication) + @hug.get("/policies/{uuid}/subject_assignments/{perimeter_id}/{category_id}", + requires=api_key_authentication) + def get(uuid: hug.types.text, perimeter_id: hug.types.text = None, + category_id: hug.types.text = None, authed_user: hug.directives.user = None): """Retrieve all subject assignments or a specific one for a given policy :param uuid: uuid of the policy :param perimeter_id: uuid of the subject :param category_id: uuid of the subject category - :param data_id: uuid of the subject scope (not used here) - :param user_id: user ID who do the request + :param authed_user: user ID who do the request :return: { "subject_data_id": { "policy_id": "ID of the policy", @@ -84,24 +63,22 @@ class SubjectAssignments(Resource): :internal_api: get_subject_assignments """ - data = PolicyManager.get_subject_assignments( - user_id=user_id, policy_id=uuid, + data = driver.PolicyManager.get_subject_assignments( + moon_user_id=authed_user, policy_id=uuid, subject_id=perimeter_id, category_id=category_id) return {"subject_assignments": data} - @validate_input("post", kwargs_state=[True, False, False, False, False], - body_state={"id": True, "category_id": True, "data_id": True}) - @check_auth - def post(self, uuid=None, perimeter_id=None, category_id=None, - data_id=None, user_id=None): + @staticmethod + @hug.local() + @hug.post("/policies/{uuid}/subject_assignments", requires=api_key_authentication) + def post(body: validate_input("id", "category_id", "data_id"), uuid: hug.types.text, + authed_user: hug.directives.user = None): """Create a subject assignment. + :param body: body of the request :param uuid: uuid of the policy - :param perimeter_id: uuid of the subject (not used here) - :param category_id: uuid of the subject category (not used here) - :param data_id: uuid of the subject scope (not used here) - :param user_id: user ID who do the request + :param authed_user: user ID who do the request :request body: { "id": "UUID of the subject (mandatory)", "category_id": "UUID of the category (mandatory)" @@ -117,32 +94,35 @@ class SubjectAssignments(Resource): } :internal_api: update_subject_assignment """ - data_id = request.json.get("data_id") - category_id = request.json.get("category_id") - perimeter_id = request.json.get("id") - data = PolicyManager.add_subject_assignment( - user_id=user_id, policy_id=uuid, - subject_id=perimeter_id, category_id=category_id, - data_id=data_id) - invalidate_data_in_slaves( - policy_id=uuid, - perimeter_id=perimeter_id, - category_id=category_id, - data_id=data_id) + data_id = body.get("data_id") + category_id = body.get("category_id") + perimeter_id = body.get("id") + + data = driver.PolicyManager.add_subject_assignment( + moon_user_id=authed_user, policy_id=uuid, + subject_id=perimeter_id, category_id=category_id, data_id=data_id) return {"subject_assignments": data} - @validate_input("delete", kwargs_state=[True, True, True, True, False]) - @check_auth - def delete(self, uuid=None, perimeter_id=None, category_id=None, - data_id=None, user_id=None): + @staticmethod + @hug.local() + @hug.delete("/policies/{uuid}/subject_assignments", requires=api_key_authentication) + @hug.delete("/policies/{uuid}/subject_assignments/{perimeter_id}", + requires=api_key_authentication) + @hug.delete("/policies/{uuid}/subject_assignments/{perimeter_id}/{category_id}", + requires=api_key_authentication) + @hug.delete("/policies/{uuid}/subject_assignments/{perimeter_id}/{category_id}/{data_id}", + requires=api_key_authentication) + def delete(uuid: hug.types.text, perimeter_id: hug.types.text = None, + category_id: hug.types.text = None, data_id: hug.types.text = None, + authed_user: hug.directives.user = None): """Delete a subject assignment for a given policy :param uuid: uuid of the policy :param perimeter_id: uuid of the subject :param category_id: uuid of the subject category :param data_id: uuid of the subject scope - :param user_id: user ID who do the request + :param authed_user: user ID who do the request :return: { "result": "True or False", "message": "optional message" @@ -150,43 +130,36 @@ class SubjectAssignments(Resource): :internal_api: delete_subject_assignment """ - data = PolicyManager.delete_subject_assignment( - user_id=user_id, policy_id=uuid, + driver.PolicyManager.delete_subject_assignment( + moon_user_id=authed_user, policy_id=uuid, subject_id=perimeter_id, category_id=category_id, data_id=data_id) - invalidate_data_in_slaves( - policy_id=uuid, - perimeter_id=perimeter_id, - category_id=category_id, - data_id=data_id) + slaves = slave_class.Slaves.get().get("slaves") + invalidate_assignment_in_slaves(slaves=slaves, policy_id=uuid, perimeter_id=perimeter_id, + category_id=category_id, data_id=data_id, type="subject") return {"result": True} -class ObjectAssignments(Resource): +class ObjectAssignments(object): """ Endpoint for object assignment requests """ - __urls__ = ( - "/policies//object_assignments", - "/policies//object_assignments/", - "/policies//object_assignments/", - "/policies//object_assignments//", - "/policies//object_assignments///", - ) - - @validate_input("get", kwargs_state=[True, False, False, False, False]) - @check_auth - def get(self, uuid=None, perimeter_id=None, category_id=None, - data_id=None, user_id=None): + @staticmethod + @hug.local() + @hug.get("/policies/{uuid}/object_assignments", requires=api_key_authentication) + @hug.get("/policies/{uuid}/object_assignments/{perimeter_id}", requires=api_key_authentication) + @hug.get("/policies/{uuid}/object_assignments/{perimeter_id}/{category_id}", + requires=api_key_authentication) + def get(uuid: hug.types.text, perimeter_id: hug.types.text = None, + category_id: hug.types.text = None, authed_user: hug.directives.user = None): """Retrieve all object assignment or a specific one for a given policy :param uuid: uuid of the policy :param perimeter_id: uuid of the object :param category_id: uuid of the object category - :param data_id: uuid of the object scope (not used here) - :param user_id: user ID who do the request + :param authed_user: user ID who do the request :return: { "object_data_id": { "policy_id": "ID of the policy", @@ -198,24 +171,22 @@ class ObjectAssignments(Resource): :internal_api: get_object_assignments """ - data = PolicyManager.get_object_assignments( - user_id=user_id, policy_id=uuid, + data = driver.PolicyManager.get_object_assignments( + moon_user_id=authed_user, policy_id=uuid, object_id=perimeter_id, category_id=category_id) return {"object_assignments": data} - @validate_input("post", kwargs_state=[True, False, False, False, False], - body_state={"id": True, "category_id": True, "data_id": True}) - @check_auth - def post(self, uuid=None, perimeter_id=None, category_id=None, - data_id=None, user_id=None): + @staticmethod + @hug.local() + @hug.post("/policies/{uuid}/object_assignments", requires=api_key_authentication) + def post(body: validate_input("id", "category_id", "data_id"), uuid, + authed_user: hug.directives.user = None): """Create an object assignment. + :param body: body of the request :param uuid: uuid of the policy - :param perimeter_id: uuid of the object (not used here) - :param category_id: uuid of the object category (not used here) - :param data_id: uuid of the object scope (not used here) - :param user_id: user ID who do the request + :param authed_user: user ID who do the request :request body: { "id": "UUID of the action (mandatory)", "category_id": "UUID of the category (mandatory)", @@ -232,75 +203,70 @@ class ObjectAssignments(Resource): :internal_api: update_object_assignment """ - data_id = request.json.get("data_id") - category_id = request.json.get("category_id") - perimeter_id = request.json.get("id") - data = PolicyManager.add_object_assignment( - user_id=user_id, policy_id=uuid, - object_id=perimeter_id, category_id=category_id, - data_id=data_id) - invalidate_data_in_slaves( - policy_id=uuid, - perimeter_id=perimeter_id, - category_id=category_id, - data_id=data_id) + data_id = body.get("data_id") + category_id = body.get("category_id") + perimeter_id = body.get("id") + data = driver.PolicyManager.add_object_assignment(moon_user_id=authed_user, policy_id=uuid, + object_id=perimeter_id, + category_id=category_id, data_id=data_id) return {"object_assignments": data} - @validate_input("delete", kwargs_state=[True, True, True, True, False]) - @check_auth - def delete(self, uuid=None, perimeter_id=None, category_id=None, - data_id=None, user_id=None): + @staticmethod + @hug.local() + @hug.delete("/policies/{uuid}/object_assignments", requires=api_key_authentication) + @hug.delete("/policies/{uuid}/object_assignments/{perimeter_id}", + requires=api_key_authentication) + @hug.delete("/policies/{uuid}/object_assignments/{perimeter_id}/{category_id}", + requires=api_key_authentication) + @hug.delete("/policies/{uuid}/object_assignments/{perimeter_id}/{category_id}/{data_id}", + requires=api_key_authentication) + def delete(uuid: hug.types.text, perimeter_id: hug.types.text = None, + category_id: hug.types.text = None, data_id: hug.types.text = None, + authed_user: hug.directives.user = None): """Delete a object assignment for a given policy :param uuid: uuid of the policy :param perimeter_id: uuid of the object :param category_id: uuid of the object category :param data_id: uuid of the object scope - :param user_id: user ID who do the request + :param authed_user: user ID who do the request :return: { "result": "True or False", "message": "optional message" } :internal_api: delete_object_assignment """ - data = PolicyManager.delete_object_assignment( - user_id=user_id, policy_id=uuid, - object_id=perimeter_id, category_id=category_id, - data_id=data_id) - invalidate_data_in_slaves( - policy_id=uuid, - perimeter_id=perimeter_id, - category_id=category_id, - data_id=data_id) + driver.PolicyManager.delete_object_assignment( + moon_user_id=authed_user, policy_id=uuid, object_id=perimeter_id, + category_id=category_id, data_id=data_id) + + slaves = slave_class.Slaves.get().get("slaves") + invalidate_assignment_in_slaves(slaves=slaves, policy_id=uuid, perimeter_id=perimeter_id, + category_id=category_id, data_id=data_id, type="object") return {"result": True} -class ActionAssignments(Resource): +class ActionAssignments(object): """ Endpoint for action assignment requests """ - __urls__ = ( - "/policies//action_assignments", - "/policies//action_assignments/", - "/policies//action_assignments/", - "/policies//action_assignments//", - "/policies//action_assignments///", - ) - - @validate_input("get", kwargs_state=[True, False, False, False, False]) - @check_auth - def get(self, uuid=None, perimeter_id=None, category_id=None, - data_id=None, user_id=None): + @staticmethod + @hug.local() + @hug.get("/policies/{uuid}/action_assignments", requires=api_key_authentication) + @hug.get("/policies/{uuid}/action_assignments/{perimeter_id}", requires=api_key_authentication) + @hug.get("/policies/{uuid}/action_assignments/{perimeter_id}/{category_id}", + requires=api_key_authentication) + def get(uuid: hug.types.text, perimeter_id: hug.types.text = None, + category_id: hug.types.text = None, authed_user: hug.directives.user = None): """Retrieve all action assignment or a specific one for a given policy :param uuid: uuid of the policy :param perimeter_id: uuid of the action :param category_id: uuid of the action category - :param data_id: uuid of the action scope - :param user_id: user ID who do the request + :param authed_user: user ID who do the request :return: { "action_data_id": { "policy_id": "ID of the policy", @@ -311,24 +277,22 @@ class ActionAssignments(Resource): } :internal_api: get_action_assignments """ - data = PolicyManager.get_action_assignments( - user_id=user_id, policy_id=uuid, + data = driver.PolicyManager.get_action_assignments( + moon_user_id=authed_user, policy_id=uuid, action_id=perimeter_id, category_id=category_id) return {"action_assignments": data} - @validate_input("post", kwargs_state=[True, False, False, False, False], - body_state={"id": True, "category_id": True, "data_id": True}) - @check_auth - def post(self, uuid=None, perimeter_id=None, category_id=None, - data_id=None, user_id=None): + @staticmethod + @hug.local() + @hug.post("/policies/{uuid}/action_assignments", requires=api_key_authentication) + def post(body: validate_input("id", "category_id", "data_id"), uuid, + authed_user: hug.directives.user = None): """Create an action assignment. + :param body: body of the request :param uuid: uuid of the policy - :param perimeter_id: uuid of the action (not used here) - :param category_id: uuid of the action category (not used here) - :param data_id: uuid of the action scope (not used here) - :param user_id: user ID who do the request + :param authed_user: user ID who do the request :request body: { "id": "UUID of the action (mandatory)", "category_id": "UUID of the category (mandatory)", @@ -345,32 +309,34 @@ class ActionAssignments(Resource): :internal_api: update_action_assignment """ - data_id = request.json.get("data_id") - category_id = request.json.get("category_id") - perimeter_id = request.json.get("id") - data = PolicyManager.add_action_assignment( - user_id=user_id, policy_id=uuid, - action_id=perimeter_id, category_id=category_id, - data_id=data_id) - invalidate_data_in_slaves( - policy_id=uuid, - perimeter_id=perimeter_id, - category_id=category_id, - data_id=data_id) + data_id = body.get("data_id") + category_id = body.get("category_id") + perimeter_id = body.get("id") + data = driver.PolicyManager.add_action_assignment( + moon_user_id=authed_user, policy_id=uuid, + action_id=perimeter_id, category_id=category_id, data_id=data_id) return {"action_assignments": data} - @validate_input("delete", kwargs_state=[True, True, True, True, False]) - @check_auth - def delete(self, uuid=None, perimeter_id=None, category_id=None, - data_id=None, user_id=None): + @staticmethod + @hug.local() + @hug.delete("/policies/{uuid}/action_assignments", requires=api_key_authentication) + @hug.delete("/policies/{uuid}/action_assignments/{perimeter_id}", + requires=api_key_authentication) + @hug.delete("/policies/{uuid}/action_assignments/{perimeter_id}/{category_id}", + requires=api_key_authentication) + @hug.delete("/policies/{uuid}/action_assignments/{perimeter_id}/{category_id}/{data_id}", + requires=api_key_authentication) + def delete(uuid: hug.types.text, perimeter_id: hug.types.text = None, + category_id: hug.types.text = None, data_id: hug.types.text = None, + authed_user: hug.directives.user = None): """Delete a action assignment for a given policy :param uuid: uuid of the policy :param perimeter_id: uuid of the action :param category_id: uuid of the action category :param data_id: uuid of the action scope - :param user_id: user ID who do the request + :param authed_user: user ID who do the request :return: { "result": "True or False", "message": "optional message" @@ -378,14 +344,401 @@ class ActionAssignments(Resource): :internal_api: delete_action_assignment """ - data = PolicyManager.delete_action_assignment( - user_id=user_id, policy_id=uuid, - action_id=perimeter_id, category_id=category_id, - data_id=data_id) - invalidate_data_in_slaves( - policy_id=uuid, - perimeter_id=perimeter_id, - category_id=category_id, - data_id=data_id) + driver.PolicyManager.delete_action_assignment( + moon_user_id=authed_user, policy_id=uuid, + action_id=perimeter_id, category_id=category_id, data_id=data_id) + + slaves = slave_class.Slaves.get().get("slaves") + invalidate_assignment_in_slaves(slaves=slaves, policy_id=uuid, perimeter_id=perimeter_id, + category_id=category_id, data_id=data_id, type="action") return {"result": True} + + +SubjectAssignmentsAPI = hug.API(name='subject_assignments', doc=SubjectAssignments.__doc__) +ObjectAssignmentsAPI = hug.API(name='object_assignments', doc=ObjectAssignments.__doc__) +ActionAssignmentsAPI = hug.API(name='action_assignments', doc=ActionAssignments.__doc__) + + +@hug.object(name='subjects', version='1.0.0', api=SubjectAssignmentsAPI) +class SubjectAssignmentsCLI(object): + """An example of command like calls via an Object""" + + @staticmethod + @hug.object.cli + def list(policy_name_or_id, name_or_id="", human: bool = False): + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + policy_id = list(policy.PoliciesCLI.list(policy_name_or_id).get("policies").keys())[0] + _assignments_req = requests.get("{}/policies/{}/subject_assignments".format( + db_conf.get("url"), policy_id), + headers={"x-api-key": manager_api_key} + ) + _assignment_key = None + if _assignments_req.status_code == 200: + _subject_assignments = _assignments_req.json().get("subject_assignments") + if name_or_id: + _assignments = None + if name_or_id in _subject_assignments: + _assignments = _subject_assignments.get(name_or_id) + _assignment_key = name_or_id + else: + for _key in _subject_assignments: + try: + _subject = perimeter.SubjectsCLI.list(name_or_id).get("subjects")[0] + _subject_key = list(_subject.keys())[0] + except Exception as e: + # FIXME: should upgrade this exception + LOGGER.exception(e) + continue + else: + if _subject_assignments.get(_key).get("subject_id") == _subject_key: + _assignments = _subject_assignments.get(_key) + _assignment_key = _key + break + if not _assignments: + raise Exception("Cannot find Subject Assignments with ID {}".format(name_or_id)) + result = {"subject_assignments": [{_assignment_key: _assignments}]} + else: + result = _assignments_req.json() + + if human: + return SubjectAssignmentsCLI.human_display(result) + else: + return result + LOGGER.error('Cannot list Subject Assignments {}'.format(_assignments_req.status_code)) + + @staticmethod + @hug.object.cli + def add(policy_name_or_id, perimeter_name_or_id, category_name_or_id, data_name_or_id, human: bool = False): + """ + Add subject assignment in database + :return: JSON status output + """ + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + policy_id = list(policy.PoliciesCLI.list(policy_name_or_id).get("policies").keys())[0] + perimeter_id = list(perimeter.SubjectsCLI.list(perimeter_name_or_id).get("subjects")[0].keys())[0] + category_id = list(meta_data.SubjectCategoriesCLI.list(category_name_or_id).get("subject_categories").keys())[0] + data_id = data.SubjectDataCLI.list(policy_id, data_name_or_id).get("subject_data").get("id") + _url = "{}/policies/{}/subject_assignments".format(db_conf.get("url"), policy_id) + + _assignments = requests.post( + _url, + json={ + "id": perimeter_id, + "category_id": category_id, + "data_id": data_id, + }, + headers={ + "x-api-key": manager_api_key, + "Content-Type": "application/json" + } + ) + if _assignments.status_code == 200: + LOGGER.warning('Create {}'.format(_assignments.content)) + if human: + return SubjectAssignmentsCLI.human_display(_assignments.json()) + else: + return _assignments.json() + LOGGER.error('Cannot create assignment for {}/{}/{} ({})'.format( + perimeter_name_or_id, category_name_or_id, data_name_or_id, _assignments.content[:40])) + LOGGER.error("{}/{}/{}".format(perimeter_id, category_id, data_id)) + + @staticmethod + @hug.object.cli + def delete(policy_name_or_id, perimeter_name_or_id, category_name_or_id, data_name_or_id): + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + policy_id = list(policy.PoliciesCLI.list(policy_name_or_id).get("policies").keys())[0] + perimeter_id = list(perimeter.SubjectsCLI.list(perimeter_name_or_id).get("subjects")[0].keys())[0] + category_id = list(meta_data.SubjectCategoriesCLI.list(category_name_or_id).get("subject_categories").keys())[0] + data_id = data.SubjectDataCLI.list(policy_id, data_name_or_id).get("subject_data").get("id") + _url = "{}/policies/{}/subject_assignments/{}/{}/{}".format( + db_conf.get("url"), + policy_id, + perimeter_id, + category_id, + data_id) + req = requests.delete( + _url, + headers={"x-api-key": manager_api_key} + ) + if req.status_code == 200: + LOGGER.warning('Deleted {}-{}-{}-{}'.format( + policy_name_or_id, perimeter_name_or_id, category_name_or_id, data_name_or_id)) + return True + LOGGER.error("Cannot delete Assignment with {}-{}-{}-{}".format( + policy_name_or_id, perimeter_name_or_id, category_name_or_id, data_name_or_id + )) + return False + + @staticmethod + def human_display(subject_assigment_json): + human_result = "Subject Assignments" + for subject_assignment in subject_assigment_json.get("subject_assignments"): + human_result += "\n" + subject_assigment_json.get("subject_assignments").get(subject_assignment).get("id") + "\n" + human_result += "\tid : " + subject_assigment_json.get("subject_assignments").get(subject_assignment).get("id") + "\n" + human_result += "\tpolicy_id : " + subject_assigment_json.get("subject_assignments").get(subject_assignment).get("policy_id") + "\n" + human_result += "\tsubject_id : " + subject_assigment_json.get("subject_assignments").get(subject_assignment).get("subject_id") + "\n" + human_result += "\tcategory_id : " + subject_assigment_json.get("subject_assignments").get(subject_assignment).get("category_id") + "\n" + human_result += "\tassignments : \n" + for assignment in subject_assigment_json.get("subject_assignments").get(subject_assignment).get("assignments"): + human_result += "\t\t" + assignment + "\n" + return human_result + + +@hug.object(name='objects', version='1.0.0', api=ObjectAssignmentsAPI) +class ObjectAssignmentsCLI(object): + """An example of command like calls via an Object""" + + @staticmethod + @hug.object.cli + def list(policy_name_or_id, name_or_id="", human: bool = False): + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + policy_id = list(policy.PoliciesCLI.list(policy_name_or_id).get("policies").keys())[0] + _assignments_req = requests.get("{}/policies/{}/object_assignments".format( + db_conf.get("url"), policy_id), + headers={"x-api-key": manager_api_key} + ) + _assignment_key = None + if _assignments_req.status_code == 200: + _object_assignments = _assignments_req.json().get("object_assignments") + if name_or_id: + _assignments = None + if name_or_id in _object_assignments: + _assignments = _object_assignments.get(name_or_id) + _assignment_key = name_or_id + else: + for _key in _object_assignments: + try: + _object = perimeter.ObjectsCLI.list(name_or_id).get("objects")[0] + _object_key = list(_object.keys())[0] + except Exception as e: + # FIXME: should upgrade this exception + LOGGER.exception(e) + continue + else: + if _object_assignments.get(_key).get("object_id") == _object_key: + _assignments = _object_assignments.get(_key) + _assignment_key = _key + break + if not _assignments: + raise Exception("Cannot find Object Assignments with ID {}".format(name_or_id)) + result = {"object_assignments": [{_assignment_key: _assignments}]} + else: + result = _assignments_req.json() + + if human: + return ObjectAssignmentsCLI.human_display(result) + else: + return result + LOGGER.error('Cannot list Object Assignments {}'.format(_assignments_req.status_code)) + + @staticmethod + @hug.object.cli + def add(policy_name_or_id, perimeter_name_or_id, category_name_or_id, data_name_or_id, human: bool = False): + """ + Add object assignment in database + :return: JSON status output + """ + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + policy_id = list(policy.PoliciesCLI.list(policy_name_or_id).get("policies").keys())[0] + perimeter_id = list(perimeter.ObjectsCLI.list(perimeter_name_or_id).get("objects")[0].keys())[0] + category_id = list(meta_data.ObjectCategoriesCLI.list(category_name_or_id).get("object_categories").keys())[0] + data_id = data.ObjectDataCLI.list(policy_id, data_name_or_id).get("object_data").get("id") + _url = "{}/policies/{}/object_assignments".format(db_conf.get("url"), policy_id) + + _assignments = requests.post( + _url, + json={ + "id": perimeter_id, + "category_id": category_id, + "data_id": data_id, + }, + headers={ + "x-api-key": manager_api_key, + "Content-Type": "application/json" + } + ) + if _assignments.status_code == 200: + LOGGER.warning('Create {}'.format(_assignments.content)) + if human: + return ObjectAssignmentsCLI.human_display(_assignments.json()) + else: + return _assignments.json() + LOGGER.error('Cannot create assignment for {}/{}/{} ({})'.format( + perimeter_name_or_id, category_name_or_id, data_name_or_id, _assignments.content[:40])) + LOGGER.error("{}/{}/{}".format(perimeter_id, category_id, data_id)) + + @staticmethod + @hug.object.cli + def delete(policy_name_or_id, perimeter_name_or_id, category_name_or_id, data_name_or_id): + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + policy_id = list(policy.PoliciesCLI.list(policy_name_or_id).get("policies").keys())[0] + perimeter_id = list(perimeter.ObjectsCLI.list(perimeter_name_or_id).get("objects")[0].keys())[0] + category_id = list(meta_data.ObjectCategoriesCLI.list(category_name_or_id).get("object_categories").keys())[0] + data_id = data.ObjectDataCLI.list(policy_id, data_name_or_id).get("object_data").get("id") + _url = "{}/policies/{}/object_assignments/{}/{}/{}".format( + db_conf.get("url"), + policy_id, + perimeter_id, + category_id, + data_id) + req = requests.delete( + _url, + headers={"x-api-key": manager_api_key} + ) + if req.status_code == 200: + LOGGER.warning('Deleted {}-{}-{}-{}'.format( + policy_name_or_id, perimeter_name_or_id, category_name_or_id, data_name_or_id)) + return True + LOGGER.error("Cannot delete Assignment with {}-{}-{}-{}".format( + policy_name_or_id, perimeter_name_or_id, category_name_or_id, data_name_or_id + )) + return False + + @staticmethod + def human_display(object_assigment_json): + human_result = "Object Assignments" + for object_assignment in object_assigment_json.get("object_assignments"): + human_result += "\n" + object_assigment_json.get("object_assignments").get(object_assignment).get("id") + "\n" + human_result += "\tid : " + object_assigment_json.get("object_assignments").get(object_assignment).get("id") + "\n" + human_result += "\tpolicy_id : " + object_assigment_json.get("object_assignments").get(object_assignment).get("policy_id") + "\n" + human_result += "\tobject_id : " + object_assigment_json.get("object_assignments").get(object_assignment).get("object_id") + "\n" + human_result += "\tcategory_id : " + object_assigment_json.get("object_assignments").get(object_assignment).get("category_id") + "\n" + human_result += "\tassignments : \n" + for assignment in object_assigment_json.get("object_assignments").get(object_assignment).get("assignments"): + human_result += "\t\t" + assignment + "\n" + return human_result + + +@hug.object(name='actions', version='1.0.0', api=ActionAssignmentsAPI) +class ActionAssignmentsCLI(object): + """An example of command like calls via an Action""" + + @staticmethod + @hug.object.cli + def list(policy_name_or_id, name_or_id="", human: bool = False): + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + policy_id = list(policy.PoliciesCLI.list(policy_name_or_id).get("policies").keys())[0] + _assignments_req = requests.get("{}/policies/{}/action_assignments".format( + db_conf.get("url"), policy_id), + headers={"x-api-key": manager_api_key} + ) + _assignment_key = None + if _assignments_req.status_code == 200: + _action_assignments = _assignments_req.json().get("action_assignments") + if name_or_id: + _assignments = None + if name_or_id in _action_assignments: + _assignments = _action_assignments.get(name_or_id) + _assignment_key = name_or_id + else: + for _key in _action_assignments: + try: + _action = perimeter.ActionsCLI.list(name_or_id).get("actions")[0] + _action_key = list(_action.keys())[0] + except Exception as e: + # FIXME: should upgrade this exception + LOGGER.exception(e) + continue + else: + if _action_assignments.get(_key).get("action_id") == _action_key: + _assignments = _action_assignments.get(_key) + _assignment_key = _key + break + if not _assignments: + raise Exception("Cannot find Action Assignments with ID {}".format(name_or_id)) + result = {"action_assignments": [{_assignment_key: _assignments}]} + else: + result = _assignments_req.json() + + if human: + return ActionAssignmentsCLI.human_display(result) + else: + return result + LOGGER.error('Cannot list Action Assignments {}'.format(_assignments_req.status_code)) + + @staticmethod + @hug.object.cli + def add(policy_name_or_id, perimeter_name_or_id, category_name_or_id, data_name_or_id, human: bool = False): + """ + Add action assignment in database + :return: JSON status output + """ + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + policy_id = list(policy.PoliciesCLI.list(policy_name_or_id).get("policies").keys())[0] + perimeter_id = list(perimeter.ActionsCLI.list(perimeter_name_or_id).get("actions")[0].keys())[0] + category_id = list(meta_data.ActionCategoriesCLI.list(category_name_or_id).get("action_categories").keys())[0] + data_id = data.ActionDataCLI.list(policy_id, data_name_or_id).get("action_data").get("id") + _url = "{}/policies/{}/action_assignments".format(db_conf.get("url"), policy_id) + + _assignments = requests.post( + _url, + json={ + "id": perimeter_id, + "category_id": category_id, + "data_id": data_id, + }, + headers={ + "x-api-key": manager_api_key, + "Content-Type": "application/json" + } + ) + if _assignments.status_code == 200: + LOGGER.warning('Create {}'.format(_assignments.content)) + if human: + return ActionAssignmentsCLI.human_display(_assignments.json()) + else: + return _assignments.json() + LOGGER.error('Cannot create assignment for {}/{}/{} ({})'.format( + perimeter_name_or_id, category_name_or_id, data_name_or_id, _assignments.content[:40])) + LOGGER.error("{}/{}/{}".format(perimeter_id, category_id, data_id)) + + @staticmethod + @hug.object.cli + def delete(policy_name_or_id, perimeter_name_or_id, category_name_or_id, data_name_or_id): + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + policy_id = list(policy.PoliciesCLI.list(policy_name_or_id).get("policies").keys())[0] + perimeter_id = list(perimeter.ActionsCLI.list(perimeter_name_or_id).get("actions")[0].keys())[0] + category_id = list(meta_data.ActionCategoriesCLI.list(category_name_or_id).get("action_categories").keys())[0] + data_id = data.ActionDataCLI.list(policy_id, data_name_or_id).get("action_data").get("id") + _url = "{}/policies/{}/action_assignments/{}/{}/{}".format( + db_conf.get("url"), + policy_id, + perimeter_id, + category_id, + data_id) + req = requests.delete( + _url, + headers={"x-api-key": manager_api_key} + ) + if req.status_code == 200: + LOGGER.warning('Deleted {}-{}-{}-{}'.format( + policy_name_or_id, perimeter_name_or_id, category_name_or_id, data_name_or_id)) + return True + LOGGER.error("Cannot delete Assignment with {}-{}-{}-{}".format( + policy_name_or_id, perimeter_name_or_id, category_name_or_id, data_name_or_id + )) + return False + + @staticmethod + def human_display(action_assigment_json): + human_result = "Action Assignments" + for action_assignment in action_assigment_json.get("action_assignments"): + human_result += "\n" + action_assigment_json.get("action_assignments").get(action_assignment).get("id") + "\n" + human_result += "\tid : " + action_assigment_json.get("action_assignments").get(action_assignment).get("id") + "\n" + human_result += "\tpolicy_id : " + action_assigment_json.get("action_assignments").get(action_assignment).get("policy_id") + "\n" + human_result += "\taction_id : " + action_assigment_json.get("action_assignments").get(action_assignment).get("action_id") + "\n" + human_result += "\tcategory_id : " + action_assigment_json.get("action_assignments").get(action_assignment).get("category_id") + "\n" + human_result += "\tassignments : \n" + for assignment in action_assigment_json.get("action_assignments").get(action_assignment).get("assignments"): + human_result += "\t\t" + assignment + "\n" + return human_result diff --git a/moon_manager/moon_manager/api/attributes.py b/moon_manager/moon_manager/api/attributes.py new file mode 100644 index 00000000..4e5cb282 --- /dev/null +++ b/moon_manager/moon_manager/api/attributes.py @@ -0,0 +1,207 @@ +# 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. + +""" +Global attributes allow to save a specific piece of information inside Moon + +""" + +import logging +import hug +import requests +from moon_manager.api import ERROR_CODE +# from moon_manager import db_driver +# from moon_manager import orchestration_driver +from moon_manager import pip_driver +from moon_manager.api import configuration +from moon_utilities import exceptions +from moon_utilities.auth_functions import init_db, api_key_authentication, connect_from_env +from moon_manager.api import slave as slave_class +from moon_utilities.invalided_functions import invalidate_attributes_in_slaves + +LOGGER = logging.getLogger("moon.manager.api." + __name__) + + +class Attributes(object): + """ + Endpoint for attributes requests + """ + + @staticmethod + @hug.local() + @hug.get("/attributes/", requires=api_key_authentication) + @hug.get("/attributes/{name}", requires=api_key_authentication) + def get(name: str = None, authed_user: hug.directives.user = None): + """Retrieve all attributes + + :param name: name of the attribute + :param authed_user: the name of the authenticated user + :return: { + "attributes": { + "id": "name", + "value": "value1", + "values": ["value1", "value2"], + "default": "value2" + }, + { + "id": "name2", + "value": "value4", + "values": ["value4", "value"3"], + "default": "value3" + } + } + } + """ + if not name: + data = pip_driver.AttrsManager.get_objects(moon_user_id=authed_user, object_type=name) + else: + data = pip_driver.AttrsManager.get_object(moon_user_id=authed_user, object_type=name) + return {"attributes": data} + + @staticmethod + @hug.local() + @hug.put("/attributes/{name}/{value}", requires=api_key_authentication) + def put(name: str, value: str, authed_user: hug.directives.user = None): + """Initialize an attribute. + + :param name: name of the attribute + :param value: value of the attribute + :param authed_user: the name of the authenticated user + :return: { + "attributes": { + "name": "value1" + "values": ["value1", "value2"], + "default": "value2" + }, + { + "name": "value3" + "values": ["value4", "value"3"], + "default": "value3" + } + } + } + """ + data = pip_driver.AttrsManager.update_object(moon_user_id=authed_user, + object_id=value, + object_type=name) + slaves = slave_class.Slaves.get().get("slaves") + ret = invalidate_attributes_in_slaves( + slaves, + name) + return {"attributes": data} + + @staticmethod + @hug.local() + @hug.delete("/attributes/{name}", requires=api_key_authentication) + def delete(name: str, authed_user: hug.directives.user = None): + """Re-initialize an attribute + + :param name: the name of the attribute + :param authed_user: the name of the authenticated user + :return: { + "result": "True or False", + "message": "optional message (optional)" + } + """ + data = pip_driver.AttrsManager.add_object(moon_user_id=authed_user, object_type=name) + return {"attributes": data} + + +AttrsAPI = hug.API(name='attributes', doc=Attributes.__doc__) +db_conf = configuration.get_configuration(key='management') +init_db(db_conf.get("token_file")) + + +@hug.object(name='attributes', version='1.0.0', api=AttrsAPI) +class AttributesCLI(object): + """An example of command like calls via an Object""" + + @staticmethod + @hug.object.cli + def get(name): + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + _attrs = requests.get("{}/attributes/{}".format(db_conf.get("url"), name), + headers={"x-api-key": manager_api_key} + ) + if _attrs.status_code == 200: + return _attrs.json() + else: + LOGGER.error("An error occurs ({}): {}...".format(_attrs.status_code, _attrs.text[:80])) + + @staticmethod + @hug.object.cli + def list(human: bool = False): + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + _attrs = requests.get("{}/attributes".format(db_conf.get("url")), + headers={"x-api-key": manager_api_key} + ) + if _attrs.status_code == 200: + if human: + return AttributesCLI.human_display(_attrs.json()) + else: + return _attrs.json() + else: + LOGGER.error("An error occurs ({}): {}...".format(_attrs.status_code, _attrs.text[:80])) + + @staticmethod + def human_display(attributes_json): + human_result = "Attributes" + for attribute in attributes_json.get("attributes"): + human_result += "\n" + attribute + "\n" + human_result += "\tid : " + attributes_json.get("attributes").get(attribute).get("id") + "\n" + human_result += "\tvalue :" + attributes_json.get("attributes").get(attribute).get("value") + "\n" + human_result += "\tvalues : \n" + for value in attributes_json.get("attributes").get(attribute).get("values"): + human_result += "\t\t" + value + "\n" + human_result += "\tdefault :" + attributes_json.get("attributes").get(attribute).get("default") + "\n" + return human_result + + @staticmethod + @hug.object.cli + def init(name): + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + _attrs = requests.delete("{}/attributes/{}".format(db_conf.get("url"), name), + headers={"x-api-key": manager_api_key} + ) + if _attrs.status_code == 200: + return _attrs.json() + else: + LOGGER.error("An error occurs ({}): {}...".format(_attrs.status_code, _attrs.text[:80])) + + @staticmethod + @hug.object.cli + def set(name, value): + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + _attrs = requests.put("{}/attributes/{}/{}".format(db_conf.get("url"), name, value), + headers={"x-api-key": manager_api_key} + ) + if _attrs.status_code == 200: + return _attrs.json() + else: + LOGGER.error("An error occurs ({}): {}...".format(_attrs.status_code, _attrs.text[:80])) + + @staticmethod + @hug.object.cli + def delete(name): + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + _attrs = requests.delete("{}/attributes/{}".format(db_conf.get("url"), name), + headers={"x-api-key": manager_api_key} + ) + if _attrs.status_code == 200: + return _attrs.json() + else: + LOGGER.error("An error occurs ({}): {}...".format(_attrs.status_code, _attrs.text[:80])) + diff --git a/moon_manager/moon_manager/api/auth.py b/moon_manager/moon_manager/api/auth.py new file mode 100644 index 00000000..a60fd727 --- /dev/null +++ b/moon_manager/moon_manager/api/auth.py @@ -0,0 +1,42 @@ +# 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. + + +"""Auth API""" +from falcon import HTTP_204, HTTP_400 +import hug +import logging +from moon_utilities.auth_functions import basic_authentication, api_key_authentication +from moon_utilities.auth_functions import get_api_key_for_user, del_api_key_for_user + +logger = logging.getLogger("moon.manager.api.status") + + +@hug.get("/auth/", requires=basic_authentication) +def get_api_key(authed_user: hug.directives.user = None): + """ + Get API key + :return: API key + """ + return get_api_key_for_user(authed_user) + + +@hug.delete("/auth/", requires=api_key_authentication) +def del_api_key(response, authed_user: hug.directives.user = None): + """ + Delete API key + :return: None + """ + if del_api_key_for_user(authed_user): + response.status = HTTP_204 + else: + response.status = HTTP_400 + return diff --git a/moon_manager/moon_manager/api/base_exception.py b/moon_manager/moon_manager/api/base_exception.py deleted file mode 100644 index 0a414a59..00000000 --- a/moon_manager/moon_manager/api/base_exception.py +++ /dev/null @@ -1,17 +0,0 @@ -class BaseException(Exception): - def __init__(self, message): - self._code = 500 - self._message = message - # Call the base class constructor with the parameters it needs - super(BaseException, self).__init__(message) - - @property - def code(self): - return self._code - - @property - def message(self): - return self._message - - def __str__(self): - return "Error " + str(self._code) + " " + self.__class__.__name__ + ': ' + self.message diff --git a/moon_manager/moon_manager/api/checks.py b/moon_manager/moon_manager/api/checks.py new file mode 100644 index 00000000..bc44b905 --- /dev/null +++ b/moon_manager/moon_manager/api/checks.py @@ -0,0 +1,211 @@ +# 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. + +""" +Run tests from a Moon policy file. +""" + +import json +import logging +import time +import requests +import hug +import sys +from moon_manager.api import configuration +from moon_manager.api import pdp + +LOGGER = logging.getLogger("moon.manager.api." + __name__) +ChecksAPI = hug.API(name='checks', doc=__doc__) +if sys.version_info[0] == 2: + raise Exception("Using Python2 is not secure enough!") + + +@hug.object(name='rules', version='1.0.0', api=ChecksAPI) +class ChecksCLI(object): + """An example of command like calls via an Object""" + + verbose = False + output_file = None + pipeline_data = {} + + @staticmethod + def launch_standalone_pipeline(endpoint, policy_file): + LOGGER.info("Launching Engine for a self test...") + from moon_engine.plugins import pyorchestrator + from uuid import uuid4 + import subprocess # nosec (command attribute is safe) + host = endpoint.replace("http://", "").split(":")[0] + port = endpoint.split(":")[2].split("/")[0] + _uuid = uuid4().hex + gunicorn_config = pyorchestrator.create_gunicorn_config( + host, port, server_type="pipeline", uuid=_uuid) + pyorchestrator.create_moon_config(_uuid, False, policy_file) + pid_file = _uuid + ".pid" + command = ["gunicorn", "moon_engine.server:__hug_wsgi__", "--threads", "10", + "-p", pid_file, "-D", "-c", gunicorn_config] + LOGGER.info("Executing {}".format(" ".join(command))) + subprocess.Popen(command, stdout=subprocess.PIPE, close_fds=True) # nosec + # (command attribute is safe) + ChecksCLI.pipeline_data["pid_file"] = pid_file + ChecksCLI.pipeline_data["gunicorn_config"] = gunicorn_config + time.sleep(2) + + @staticmethod + def kill_standalone_pipeline(): + import os + pid = int(open(ChecksCLI.pipeline_data["pid_file"]).read()) + os.kill(pid, 15) + + @staticmethod + def log(message, color="", force_console=False): + """ + Send application logs to conole and output file + :param message: the message to send + :param color: optionally the color in the console + :param force_console: if the message should be always send in the console + :return: None + """ + if ChecksCLI.verbose or force_console: + if color: + print("\033[" + color + "m" + message + "\033[m") + else: + print(message) + if ChecksCLI.output_file: + open(ChecksCLI.output_file, "a").write(message + "\n") + + @staticmethod + def run_tests(endpoint, vim_project_id, test_list, status_code, test_number): + """ + Run the tests given + :param endpoint: the endpoint to send the requests + :param vim_project_id: the tested project ID + :param test_list: the list of tests to run + :param status_code: the expected status code + :param test_number: the number of tests to run + :return: + """ + cpt = 0 + bad_response = 0 + good_response = 0 + start_time = time.time() + for test in test_list: + if test_number and cpt > test_number: + break + if vim_project_id: + url = "{endpoint}/{project_id}/{subject_name}/{object_name}/{action_name}".format( + endpoint=endpoint, + project_id=vim_project_id, + subject_name=test[0], + object_name=test[1], + action_name=test[2], + ) + else: + url = "{endpoint}/{subject_name}/{object_name}/{action_name}".format( + endpoint=endpoint, + subject_name=test[0], + object_name=test[1], + action_name=test[2], + ) + req = requests.get(url) + cpt += 1 + ChecksCLI.log("Contacting {}".format(url)) + if isinstance(status_code, str): + status_code = (int(status_code), ) + if isinstance(status_code, int): + status_code = (status_code, ) + if req.status_code in status_code: + ChecksCLI.log("{} OK".format(", ".join(test))) + good_response += 1 + else: + ChecksCLI.log("{} KO ({}: {})".format(", ".join(test), req.status_code, req.text[:80]), + force_console=True) + bad_response += 1 + end_time = time.time() + return "Run {} tests ({} OK and {} KO) in {:.2f} seconds ({:.2f} req/s)".format( + cpt, good_response, bad_response, + end_time - start_time, cpt / (end_time - start_time)) + + @staticmethod + @hug.object.cli + def run(policy_file, + endpoint: str = "", + test_number: int = None, + verbose: bool = False, + output_file: str = None, + dont_kill_server: bool = False): + """ + Run tests given in a policy file + :param policy_file: the policy file which contains the tests + :param endpoint: the endpoint to test + :param test_number: the number of tests to run + :param verbose: set the verbosity + :param output_file: the name of the output file to send logs + :param dont_kill_server: do we have to kill the engine before quitting + :return: None + """ + ChecksCLI.output_file = output_file + ChecksCLI.verbose = verbose + ChecksCLI.log("Tests run on " + time.strftime("%Y/%m/%d %H:%M:%S"), + force_console=True, color="1") + if not endpoint: + _conf = configuration.get_configuration(key='management') + endpoint = "http://{}:10000/authz".format( + _conf['url'].replace("http://", "").split(":")[0]) + need_standalone_pipeline = False + try: + requests.get(endpoint) + except requests.exceptions.ConnectionError: + need_standalone_pipeline = True + ChecksCLI.launch_standalone_pipeline(endpoint, policy_file) + try: + _pdps = pdp.PDPCLI.list().get("pdps") + vim_project_ids = [] + for _project in _pdps.values(): + if _project.get("vim_project_id", "").strip(): + vim_project_ids.append(_project.get("vim_project_id", "").strip()) + except (requests.exceptions.ConnectionError, AttributeError): + vim_project_id = "" + else: + if len(vim_project_ids) > 1: + ChecksCLI.log("VIM Project ID:", force_console=True) + for project in vim_project_ids: + ChecksCLI.log(" - {}".format(project), force_console=True) + response = input("Choose a project ID in list: ") # nosec + # (forbidden use of Python2) + vim_project_id = response + else: + vim_project_id = vim_project_ids[0] + vim_project_id = vim_project_id.replace("/", "") + ChecksCLI.log("Using '{}' as project ID".format(vim_project_id), force_console=True) + ChecksCLI.log("Endpoint: {}".format(endpoint), force_console=True) + policy = json.loads(open(policy_file).read()) + if "checks" not in policy: + raise Exception("Cannot find checks attribute in {}".format(policy_file)) + endpoint = endpoint.strip('/') + ChecksCLI.log("Run grant tests", color="32", force_console=True) + output = "" + output += ChecksCLI.run_tests(endpoint, + vim_project_id, + policy['checks'].get("granted", []), + (200, 204), + test_number) + output += "\n" + ChecksCLI.log("Run deny tests", color="32", force_console=True) + output += ChecksCLI.run_tests(endpoint, + vim_project_id, + policy['checks'].get("denied", []), + 403, + test_number) + ChecksCLI.log(output, force_console=True, color="1") + + if need_standalone_pipeline and not dont_kill_server: + ChecksCLI.kill_standalone_pipeline() + diff --git a/moon_manager/moon_manager/api/configuration.py b/moon_manager/moon_manager/api/configuration.py new file mode 100644 index 00000000..601b3d53 --- /dev/null +++ b/moon_manager/moon_manager/api/configuration.py @@ -0,0 +1,237 @@ +# 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. + + +"""Configuration API""" +import glob +import hug.interface +import json +import logging +import logging.config +import os +import requests +import sys +import yaml +import importlib +from importlib.machinery import SourceFileLoader +from moon_utilities.auth_functions import init_db, get_api_key_for_user + +LOGGER = logging.getLogger("moon.manager.api.configuration") +__CONF = {} + + +def init_logging(): + """Initialize the logging system + + :return: nothing + """ + logging_conf = get_configuration(key='logging') + if get_configuration(key='debug', default=False): + logging_conf.get("handlers", {}).get("console", {})['level'] = logging.DEBUG + LOGGER.info("Setting debug to True!") + logging.config.dictConfig(logging_conf) + + +def load_plugin(plugname): + """Load a python module + + :param plugname: the name of the module to load + :return: a reference to the module + """ + plugins_dir = __CONF["plugins"]["directory"] + try: + return __import__(plugname, fromlist=["plugins", ]) + except ImportError as e: + LOGGER.warning("Cannot import module ({})".format(e)) + try: + m = SourceFileLoader("myplugs", os.path.join(plugins_dir, plugname+".py")) + return m.load_module() + except ImportError as e: + LOGGER.error("Error in importing plugin {} from {}".format(plugname, plugins_dir)) + LOGGER.exception(e) + + +def get_db_driver(): + """Load and check the plugin module + + :return: a reference to the module + """ + plug = load_plugin(__CONF["database"]["driver"]) + if plug.PLUGIN_TYPE != "db": + raise Exception("Trying to load a bad DB plugin (got {} plugin instead)".format( + plug.PLUGIN_TYPE)) + if "Connector" not in dir(plug): + raise Exception("Trying to load a bad DB plugin (cannot find Connector)") + return plug + + +def get_orchestration_driver(): + """Load and check the plugin module + + :return: a reference to the module + """ + plug = load_plugin(__CONF["orchestration"]["driver"]) + if plug.PLUGIN_TYPE != "orchestration": + raise Exception("Trying to load a bad Orchestration plugin (got {} plugin instead)".format( + plug.PLUGIN_TYPE)) + if "Connector" not in dir(plug): + raise Exception("Trying to load a bad Orchestration plugin (cannot find Connector)") + return plug + + +def get_information_driver(driver_name): + """Load and check the plugin module + + :return: a reference to the module + """ + plug = load_plugin(driver_name) + if plug.PLUGIN_TYPE != "information": + raise Exception("Trying to load a bad Information plugin (got {} plugin instead)".format( + plug.PLUGIN_TYPE)) + if "Connector" not in dir(plug): + raise Exception("Trying to load a bad Information plugin (cannot find Connector)") + return plug + + +def get_global_attrs_driver(): + """Load and check the plugin module + + :return: a reference to the module + """ + driver_name = __CONF["information"].get("global_attrs", {}).get("driver") + if not driver_name: + return + plug = load_plugin(driver_name) + if plug.PLUGIN_TYPE != "information": + raise Exception("Trying to load a bad Information plugin (got {} plugin instead)".format( + plug.PLUGIN_TYPE)) + if "Connector" not in dir(plug): + raise Exception("Trying to load a bad Information plugin (cannot find Connector)") + return plug + + +def search_config_file(filename): + """Look for the configuration file + + :param filename: a filename to search for + :return: the content of the configuration file + """ + data_config = None + for _dir in ( + "{}", + "/conf/{}", + "../{}", + "../conf/{}", + "/etc/moon/{}", + "conf/{}", + ): + for _filename in (filename, "moon.conf", "moon.yaml"): + _file = _dir.format(_filename) + try: + data_config = yaml.safe_load(open(_file)) + except FileNotFoundError: + data_config = None + continue + else: + break + if data_config: + LOGGER.warning("Using {} as configuration file".format(_file)) + break + if not data_config: + raise Exception("Configuration file not found...") + return data_config + + +def set_configuration(conf): + """ Force the configuration dictionary + + :param conf: the configuration dictionary + :return: nothing + """ + global __CONF + __CONF = conf + + +@hug.cli("get_conf") +@hug.local() +@hug.get("/conf") +@hug.get("/conf/{key}") +def get_configuration(key=None, default=None): + """ + List configuration attributes + :return: JSON configuration output + """ + global __CONF + if not __CONF: + __CONF = search_config_file("moon.yaml") + init_logging() + if not key: + # TODO: delete passwords! + return __CONF + else: + return __CONF.get(key, default) + + +@hug.cli("import_json") +def import_json(filename): + """ + Import data in json file + """ + LOGGER.info("Importing policy from {}".format(filename)) + db_conf = get_configuration(key='management') + init_db(db_conf.get("token_file")) + manager_api_key = get_api_key_for_user("admin") + try: + dict_to_import = json.loads(open(filename).read()) + except json.JSONDecodeError as e: + LOGGER.error("Error in decoding the input file") + LOGGER.exception(e) + else: + req = requests.post("{}/import".format(db_conf.get("url")), + data=json.dumps(dict_to_import), + headers={ + "x-api-key": manager_api_key, + "Content-Type": "application/json" + }) + if req.status_code == 200: + LOGGER.warning("Import OK!") + parsed = json.loads(req.content) + LOGGER.info("Response: {}".format(json.dumps(parsed, indent=4, sort_keys=True))) + else: + LOGGER.error("Error when importing data: {} {}".format(req.status_code, req.content)) + + +@hug.cli("init_db") +@hug.local() +def init_database(): + """Initialize the database + + :return: nothing + """ + LOGGER.info("Initialize the database") + cwd = os.getcwd() + db_conf = get_configuration(key='database') + migration_dir = db_conf.get("migration_dir", ".") + migration_files = glob.glob(os.path.join(migration_dir, "*[0-9][0-9][0-9].py")) + migration_files.sort() + if not migration_files: + # the migration_dir is a python module so we must find the files inside this dir + mod = __import__(migration_dir, fromlist=[migration_dir.split(".")[-1], ]) + migration_files = glob.glob(os.path.join(mod.__path__[0], "*[0-9][0-9][0-9].py")) + for filename in migration_files: + # we execute the upgrade/downgrade functions inside each file + os.chdir(os.path.dirname(filename)) + # we add the current directory in order to import the file + sys.path.append("") + mod = importlib.import_module(os.path.basename(filename.replace(".py", ""))) + # TODO: manage the downgrade function + mod.upgrade(db_conf.get("url")) + os.chdir(cwd) diff --git a/moon_manager/moon_manager/api/data.py b/moon_manager/moon_manager/api/data.py index 92d7b2c6..570bb9cd 100644 --- a/moon_manager/moon_manager/api/data.py +++ b/moon_manager/moon_manager/api/data.py @@ -1,46 +1,60 @@ -# 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'. +# 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. + + """ Data are elements used to create rules +* Subjects are the source of an action on an object + (examples : users, virtual machines) +* Objects are the destination of an action + (examples virtual machines, virtual Routers) +* Actions are what subject wants to do on an object """ -from flask import request -from flask_restful import Resource +import hug import logging -from python_moonutilities.security_functions import check_auth -from python_moondb.core import PolicyManager -from python_moonutilities.security_functions import validate_input +import requests +from moon_manager import db_driver as driver +from moon_utilities.security_functions import validate_input +from moon_utilities.auth_functions import api_key_authentication, connect_from_env +from moon_utilities.invalided_functions import invalidate_data_in_slaves +from moon_manager.api import slave as slave_class +from moon_manager.api import configuration +from moon_manager.api import policy +from moon_manager.api import meta_data -__version__ = "4.3.2" +LOGGER = logging.getLogger("moon.manager.api." + __name__) -logger = logging.getLogger("moon.manager.api." + __name__) - -class SubjectData(Resource): +class SubjectData(object): """ Endpoint for subject data requests """ - __urls__ = ( - "/policies//subject_data", - "/policies//subject_data/", - "/policies//subject_data/", - "/policies//subject_data//", - ) - - @validate_input("get", kwargs_state=[True, False, False, False]) - @check_auth - def get(self, uuid=None, category_id=None, data_id=None, user_id=None): + @staticmethod + @hug.local() + @hug.get("/policies/{uuid}/subject_data", requires=api_key_authentication) + @hug.get("/policies/{uuid}/subject_data/{category_id}", requires=api_key_authentication) + @hug.get("/policies/{uuid}/subject_data/{category_id}/{data_id}", + requires=api_key_authentication) + def get(uuid: hug.types.text, category_id: hug.types.text = None, + data_id: hug.types.text = None, authed_user: hug.directives.user = None): """Retrieve all subject categories or a specific one if data_id is given for a given policy :param uuid: uuid of the policy :param category_id: uuid of the subject category :param data_id: uuid of the subject data - :param user_id: user ID who do the request + :param authed_user: user ID who do the request :return: [{ "policy_id": "policy_id1", "category_id": "category_id1", @@ -53,24 +67,22 @@ class SubjectData(Resource): }] :internal_api: get_subject_data """ - logger.info("api.get {} {} {}".format(uuid, category_id, data_id)) - data = PolicyManager.get_subject_data(user_id=user_id, - policy_id=uuid, - category_id=category_id, - data_id=data_id) - logger.info("api.get data = {}".format(data)) + data = driver.PolicyManager.get_subject_data(moon_user_id=authed_user, policy_id=uuid, + category_id=category_id, data_id=data_id) return {"subject_data": data} - @validate_input("post", kwargs_state=[True, True, False, False], body_state={"name": True}) - @check_auth - def post(self, uuid=None, category_id=None, data_id=None, user_id=None): + @staticmethod + @hug.local() + @hug.post("/policies/{uuid}/subject_data/{category_id}", requires=api_key_authentication) + def post(body: validate_input("name"), uuid: hug.types.text, category_id: hug.types.text, + authed_user: hug.directives.user = None): """Create or update a subject. + :param body: body of the request :param uuid: uuid of the policy :param category_id: uuid of the subject category - :param data_id: uuid of the subject data (not used here) - :param user_id: user ID who do the request + :param authed_user: user ID who do the request :request body: { "name": "name of the data (mandatory)", "description": "description of the data (optional)" @@ -87,60 +99,64 @@ class SubjectData(Resource): } :internal_api: add_subject_data """ - data = PolicyManager.set_subject_data(user_id=user_id, - policy_id=uuid, - category_id=category_id, - value=request.json) + data = driver.PolicyManager.set_subject_data(moon_user_id=authed_user, policy_id=uuid, + category_id=category_id, value=body) + + slaves = slave_class.Slaves.get().get("slaves") + invalidate_data_in_slaves(slaves=slaves, policy_id=uuid, category_id=category_id, + data_id=None, type="subject") return {"subject_data": data} - @validate_input("delete", kwargs_state=[True, False, False, False]) - @check_auth - def delete(self, uuid=None, category_id=None, data_id=None, user_id=None): + @staticmethod + @hug.local() + @hug.delete("/policies/{uuid}/subject_data/{category_id}/{data_id}", + requires=api_key_authentication) + def delete(uuid: hug.types.text, data_id: hug.types.text, + category_id: hug.types.text = None, authed_user: hug.directives.user = None): """Delete a subject for a given policy :param uuid: uuid of the policy :param category_id: uuid of the subject category :param data_id: uuid of the subject data - :param user_id: user ID who do the request + :param authed_user: user ID who do the request :return: [{ "result": "True or False", "message": "optional message (optional)" }] :internal_api: delete_subject_data """ - logger.info("api.delete {} {}".format(uuid, data_id)) - data = PolicyManager.delete_subject_data(user_id=user_id, - policy_id=uuid, - category_id=category_id, - data_id=data_id) + LOGGER.info("api.delete {} {}".format(uuid, data_id)) + driver.PolicyManager.delete_subject_data(moon_user_id=authed_user, policy_id=uuid, + category_id=category_id, data_id=data_id) + + slaves = slave_class.Slaves.get().get("slaves") + invalidate_data_in_slaves(slaves=slaves, policy_id=None, category_id=None, + data_id=data_id, type="subject") return {"result": True} -class ObjectData(Resource): +class ObjectData(object): """ Endpoint for object data requests """ - __urls__ = ( - "/policies//object_data", - "/policies//object_data/", - "/policies//object_data/", - "/policies//object_data//" - "", - ) - - @validate_input("get", kwargs_state=[True, False, False, False]) - @check_auth - def get(self, uuid=None, category_id=None, data_id=None, user_id=None): + @staticmethod + @hug.local() + @hug.get("/policies/{uuid}/object_data", requires=api_key_authentication) + @hug.get("/policies/{uuid}/object_data/{category_id}", requires=api_key_authentication) + @hug.get("/policies/{uuid}/object_data/{category_id}/{data_id}", + requires=api_key_authentication) + def get(uuid: hug.types.text, category_id: hug.types.text = None, + data_id: hug.types.text = None, authed_user: hug.directives.user = None): """Retrieve all object categories or a specific one if sid is given for a given policy :param uuid: uuid of the policy :param category_id: uuid of the object category :param data_id: uuid of the object data - :param user_id: user ID who do the request + :param authed_user: user ID who do the request :return: [{ "policy_id": "policy_id1", "category_id": "category_id1", @@ -153,22 +169,21 @@ class ObjectData(Resource): }] :internal_api: get_object_data """ - data = PolicyManager.get_object_data(user_id=user_id, - policy_id=uuid, - category_id=category_id, - data_id=data_id) - + data = driver.PolicyManager.get_object_data(moon_user_id=authed_user, policy_id=uuid, + category_id=category_id, data_id=data_id) return {"object_data": data} - @validate_input("post", kwargs_state=[True, True, False, False], body_state={"name": True}) - @check_auth - def post(self, uuid=None, category_id=None, data_id=None, user_id=None): + @staticmethod + @hug.local() + @hug.post("/policies/{uuid}/object_data/{category_id}", requires=api_key_authentication) + def post(body: validate_input("name"), uuid: hug.types.text, category_id: hug.types.text, + authed_user: hug.directives.user = None): """Create or update a object. + :param body: body of the request :param uuid: uuid of the policy :param category_id: uuid of the object category - :param data_id: uuid of the object data (not used here) - :param user_id: user ID who do the request + :param authed_user: user ID who do the request :request body: { "name": "name of the data (mandatory)", "description": "description of the data (optional)" @@ -185,59 +200,65 @@ class ObjectData(Resource): } :internal_api: add_object_data """ - data = PolicyManager.add_object_data(user_id=user_id, - policy_id=uuid, - category_id=category_id, - value=request.json) + data = driver.PolicyManager.add_object_data(moon_user_id=authed_user, policy_id=uuid, + category_id=category_id, value=body) + + slaves = slave_class.Slaves.get().get("slaves") + invalidate_data_in_slaves(slaves=slaves, policy_id=uuid, category_id=category_id, + data_id=None, type="object") + return {"object_data": data} - @validate_input("delete", kwargs_state=[True, False, False, False]) - @check_auth - def delete(self, uuid=None, category_id=None, data_id=None, user_id=None): + @staticmethod + @hug.local() + @hug.delete("/policies/{uuid}/object_data/{category_id}/{data_id}", + requires=api_key_authentication) + def delete(uuid: hug.types.text, data_id: hug.types.text, + category_id: hug.types.text = None, authed_user: hug.directives.user = None): """Delete a object for a given policy :param uuid: uuid of the policy :param category_id: uuid of the object category :param data_id: uuid of the object data - :param user_id: user ID who do the request + :param authed_user: user ID who do the request :return: { "result": "True or False", "message": "optional message (optional)" } :internal_api: delete_object_data """ - data = PolicyManager.delete_object_data(user_id=user_id, - policy_id=uuid, - category_id=category_id, - data_id=data_id) + driver.PolicyManager.delete_object_data(moon_user_id=authed_user, policy_id=uuid, + category_id=category_id, data_id=data_id) + + slaves = slave_class.Slaves.get().get("slaves") + + invalidate_data_in_slaves(slaves=slaves, policy_id=None, category_id=None, + data_id=data_id, type="object") return {"result": True} -class ActionData(Resource): +class ActionData(object): """ Endpoint for action data requests """ - __urls__ = ( - "/policies//action_data", - "/policies//action_data/", - "/policies//action_data/", - "/policies//action_data//" - "", - ) - - @validate_input("get", kwargs_state=[True, False, False, False]) - @check_auth - def get(self, uuid=None, category_id=None, data_id=None, user_id=None): + @staticmethod + @hug.local() + @hug.get("/policies/{uuid}/action_data", requires=api_key_authentication) + @hug.get("/policies/{uuid}/action_data/{category_id}", requires=api_key_authentication) + @hug.get("/policies/{uuid}/action_data/{category_id}/{data_id}", + requires=api_key_authentication) + def get(uuid: hug.types.text, category_id: hug.types.text = None, + data_id: hug.types.text = None, authed_user: hug.directives.user = None): """Retrieve all action categories or a specific one if sid is given for a given policy :param uuid: uuid of the policy :param category_id: uuid of the action category :param data_id: uuid of the action data - :param user_id: user ID who do the request + :param authed_user: user ID who do the request :return: [{ "policy_id": "policy_id1", "category_id": "category_id1", @@ -250,22 +271,22 @@ class ActionData(Resource): }] :internal_api: get_action_data """ - data = PolicyManager.get_action_data(user_id=user_id, - policy_id=uuid, - category_id=category_id, - data_id=data_id) + data = driver.PolicyManager.get_action_data(moon_user_id=authed_user, policy_id=uuid, + category_id=category_id, data_id=data_id) return {"action_data": data} - @validate_input("post", kwargs_state=[True, True, False, False], body_state={"name": True}) - @check_auth - def post(self, uuid=None, category_id=None, data_id=None, user_id=None): + @staticmethod + @hug.local() + @hug.post("/policies/{uuid}/action_data/{category_id}", requires=api_key_authentication) + def post(body: validate_input("name"), uuid: hug.types.text, category_id: hug.types.text, + authed_user: hug.directives.user = None): """Create or update a action. + :param body: body of the request :param uuid: uuid of the policy :param category_id: uuid of the action category - :param data_id: uuid of the action data - :param user_id: user ID who do the request + :param authed_user: user ID who do the request :request body: { "name": "name of the data (mandatory)", "description": "description of the data (optional)" @@ -282,30 +303,424 @@ class ActionData(Resource): } :internal_api: add_action_data """ - data = PolicyManager.add_action_data(user_id=user_id, - policy_id=uuid, - category_id=category_id, - value=request.json) + data = driver.PolicyManager.add_action_data(moon_user_id=authed_user, policy_id=uuid, + category_id=category_id, value=body) + + slaves = slave_class.Slaves.get().get("slaves") + invalidate_data_in_slaves(slaves=slaves, policy_id=uuid, category_id=category_id, + data_id=None, type="action") + return {"action_data": data} - @validate_input("delete", kwargs_state=[True, False, False, False]) - @check_auth - def delete(self, uuid=None, category_id=None, data_id=None, user_id=None): + @staticmethod + @hug.local() + @hug.delete("/policies/{uuid}/action_data/{category_id}/{data_id}", + requires=api_key_authentication) + def delete(uuid: hug.types.text, data_id: hug.types.text, + category_id: hug.types.text = None, authed_user: hug.directives.user = None): """Delete a action for a given policy :param uuid: uuid of the policy :param category_id: uuid of the action category :param data_id: uuid of the action data - :param user_id: user ID who do the request + :param authed_user: user ID who do the request :return: { "result": "True or False", "message": "optional message (optional)" } :internal_api: delete_action_data """ - data = PolicyManager.delete_action_data(user_id=user_id, - policy_id=uuid, - category_id=category_id, - data_id=data_id) + driver.PolicyManager.delete_action_data(moon_user_id=authed_user, policy_id=uuid, + category_id=category_id, data_id=data_id) + slaves = slave_class.Slaves.get().get("slaves") + + invalidate_data_in_slaves(slaves=slaves, policy_id=None, category_id=None, + data_id=data_id, type="action") return {"result": True} + + +def human_display_entity_data(entity_data_json): + """ + Common static method for entity (subject, object) + :param entity_data_json: subject_data_json or object_data_json + :return: + """ + human_result = "Data\n" + human_result += "\tpolicy_id : " + entity_data_json.get("policy_id") + "\n" + human_result += "\tcategory_id : " + entity_data_json.get("category_id") + "\n" + human_result += "\tdata : \n" + for data in entity_data_json.get("data"): + human_result += "\t\t" + data + "\n" + human_result += human_display_data(entity_data_json.get("data").get(data)) + return human_result + +def human_display_data(data_json, tabulations: int = 3): + """ + :param data_json: + :param tabulations: nombre de caractères de tabulations + :return: + """ + tab = "" + for i in range(tabulations): + tab += "\t" + human_result = tab + "id:" + data_json.get("id") + "\n" + human_result += tab + "name:" + data_json.get("name") + "\n" + human_result += tab + "description:" + data_json.get("description") + "\n" + human_result += tab + "category_id:" + data_json.get("category_id") + "\n" + human_result += tab + "policy_id:" + data_json.get("policy_id") + "\n" + + return human_result + + +SubjectDataAPI = hug.API(name='subject_data', doc=SubjectData.__doc__) + + +@hug.object(name='subject_data', version='1.0.0', api=SubjectDataAPI) +class SubjectDataCLI(object): + """An example of command like calls via an Object""" + + @staticmethod + @hug.object.cli + def list(policy_name_or_id, name_or_id="", human: bool = False): + """Retrieve all subject categories or a specific one if data_id is give for a given policy""" + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + policy_id = list(policy.PoliciesCLI.list(policy_name_or_id).get("policies").keys())[0] + _data_req = requests.get( + "{}/policies/{}/subject_data".format(db_conf.get("url"), policy_id), + headers={"x-api-key": manager_api_key} + ) + _data_to_return = None + _data_list = _data_req.json().get("subject_data") + if _data_req.status_code == 200: + if name_or_id: + _data = None + for _data_item in _data_list: + if policy_id != _data_item["policy_id"]: + continue + for _data_key, _data_value in _data_item.get("data").items(): + if _data_value.get("name") == name_or_id: + _data_to_return = _data_value + break + elif _data_key == name_or_id: + _data_to_return = _data_value + break + if not _data_to_return: + raise Exception("Cannot find Subject Data with name or ID {}".format( + name_or_id)) + if human: + result = _data_to_return + else: + result = {"subject_data": _data_to_return} + else: + result = _data_req.json() + + if human: + if name_or_id: + return human_display_data(result, 1) + else: + return SubjectDataCLI.human_display(result) + else: + return result + LOGGER.error('Cannot list Subject Data {}'.format(_data_req.status_code)) + + @staticmethod + @hug.object.cli + def add(name, category_name_or_id, policy_name_or_id, description="", human: bool=False): + """ + Add an subject data in the database + :return: JSON status output + """ + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + category_id = list(meta_data.SubjectCategoriesCLI.list(category_name_or_id) + .get("subject_categories").keys())[0] + policy_id = list(policy.PoliciesCLI.list(policy_name_or_id).get("policies").keys())[0] + _url = "{}/policies/{}/subject_data/{}".format( + db_conf.get("url"), policy_id, category_id) + _data_req = requests.post( + _url, + json={ + "name": name, + "description": description, + }, + headers={ + "x-api-key": manager_api_key, + "Content-Type": "application/json" + } + ) + if _data_req.status_code == 200: + LOGGER.warning('Create {}'.format(_data_req.content)) + if human: + return human_display_entity_data(_data_req.json().get("subject_data")) + else: + return _data_req.json() + LOGGER.error('Cannot create {}'.format(name, _data_req.content[:40])) + + @staticmethod + @hug.object.cli + def delete(name_or_id, category_name_or_id, policy_name_or_id): + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + _subject_data = SubjectDataCLI.list(policy_name_or_id) + category_id = list(meta_data.SubjectCategoriesCLI.list(category_name_or_id) + .get("subject_categories").keys())[0] + for element in _subject_data.get("subject_data"): + for _data_id, _data_value in element.get("data").items(): + if _data_value.get("name") == name_or_id: + policy_id = list( + policy.PoliciesCLI.list(policy_name_or_id).get("policies").keys())[0] + _url = "{}/policies/{}/subject_data/{}/{}".format( + db_conf.get("url"), policy_id, category_id, _data_id) + req = requests.delete( + _url, + headers={"x-api-key": manager_api_key} + ) + if req.status_code == 200: + LOGGER.warning('Deleted {}'.format(name_or_id)) + continue + LOGGER.error("Cannot delete Subject Data with name {}".format(name_or_id)) + + @staticmethod + def human_display(subject_data_json): + human_result = "Subjects Data\n" + for subject_data in subject_data_json.get('subject_data'): + human_result += human_display_entity_data(subject_data) + return human_result + +ObjectDataAPI = hug.API(name='object_data', doc=ObjectData.__doc__) + + +@hug.object(name='object_data', version='1.0.0', api=ObjectDataAPI) +class ObjectDataCLI(object): + """An example of command like calls via an Object""" + + @staticmethod + @hug.object.cli + def list(policy_name_or_id, name_or_id="", human: bool = False): + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + policy_id = list(policy.PoliciesCLI.list(policy_name_or_id).get("policies").keys())[0] + _data_req = requests.get( + "{}/policies/{}/object_data".format(db_conf.get("url"), policy_id), + headers={"x-api-key": manager_api_key} + ) + _data_to_return = None + _data_list = _data_req.json().get("object_data") + if _data_req.status_code == 200: + if name_or_id: + _data = None + for _data_item in _data_list: + if policy_id != _data_item["policy_id"]: + continue + for _data_key, _data_value in _data_item.get("data").items(): + if _data_value.get("name") == name_or_id: + _data_to_return = _data_value + break + elif _data_key == name_or_id: + _data_to_return = _data_value + break + if not _data_to_return: + raise Exception("Cannot find Object Data with name or ID {}".format( + name_or_id)) + if human: + result = _data_to_return + else: + result = {"object_data": _data_to_return} + else: + result = _data_req.json() + + if human: + if name_or_id: + return human_display_data(result, 1) + else: + return ObjectDataCLI.human_display(result) + else: + return result + LOGGER.error('Cannot list Object Data {}'.format(_data_req.status_code)) + + @staticmethod + @hug.object.cli + def add(name, category_name_or_id, policy_name_or_id, description="", human: bool = False): + """ + Add + :param name: + :param category_name_or_id: + :param policy_name_or_id: + :param description: + :param human: + :return: + """ + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + category_id = list(meta_data.ObjectCategoriesCLI.list(category_name_or_id) + .get("object_categories").keys())[0] + policy_id = list(policy.PoliciesCLI.list(policy_name_or_id).get("policies").keys())[0] + _url = "{}/policies/{}/object_data/{}".format( + db_conf.get("url"), policy_id, category_id) + _data_req = requests.post( + _url, + json={ + "name": name, + "description": description, + }, + headers={ + "x-api-key": manager_api_key, + "Content-Type": "application/json" + } + ) + if _data_req.status_code == 200: + LOGGER.warning('Create {}'.format(_data_req.content)) + if human: + return human_display_entity_data(_data_req.json().get("object_data")) + else: + return _data_req.json() + LOGGER.error('Cannot create {}'.format(name, _data_req.content[:40])) + + @staticmethod + @hug.object.cli + def delete(name_or_id, category_name_or_id, policy_name_or_id): + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + _object_data = ObjectDataCLI.list(policy_name_or_id) + category_id = list(meta_data.ObjectCategoriesCLI.list(category_name_or_id) + .get("object_categories").keys())[0] + for element in _object_data.get("object_data"): + for _data_id, _data_value in element.get("data").items(): + if _data_value.get("name") == name_or_id: + policy_id = list( + policy.PoliciesCLI.list(policy_name_or_id).get("policies").keys())[0] + _url = "{}/policies/{}/object_data/{}/{}".format( + db_conf.get("url"), policy_id, category_id, _data_id) + req = requests.delete( + _url, + headers={"x-api-key": manager_api_key} + ) + if req.status_code == 200: + LOGGER.warning('Deleted {}'.format(name_or_id)) + continue + LOGGER.error("Cannot delete Object Data with name {}".format(name_or_id)) + + @staticmethod + def human_display(object_data_json): + human_result = "Objects Data\n" + for object_data in object_data_json.get('object_data'): + human_result += human_display_entity_data(object_data) + return human_result + +ActionDataAPI = hug.API(name='action_data', doc=ActionData.__doc__) + + +@hug.object(name='action_data', version='1.0.0', api=ActionDataAPI) +class ActionDataCLI(object): + """An example of command like calls via an Action""" + + @staticmethod + @hug.object.cli + def list(policy_name_or_id, name_or_id="", human: bool = False): + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + policy_id = list(policy.PoliciesCLI.list(policy_name_or_id).get("policies").keys())[0] + _data_req = requests.get( + "{}/policies/{}/action_data".format(db_conf.get("url"), policy_id), + headers={"x-api-key": manager_api_key} + ) + _data_to_return = None + _data_list = _data_req.json().get("action_data") + if _data_req.status_code == 200: + if name_or_id: + _data = None + for _data_item in _data_list: + if policy_id != _data_item["policy_id"]: + continue + for _data_key, _data_value in _data_item.get("data").items(): + if _data_value.get("name") == name_or_id: + _data_to_return = _data_value + break + elif _data_key == name_or_id: + _data_to_return = _data_value + break + if not _data_to_return: + raise Exception("Cannot find Action Data with name or ID {}".format( + name_or_id)) + if human: + result = _data_to_return + else: + result = {"action_data": _data_to_return} + else: + result = _data_req.json() + + if human: + if name_or_id: + return human_display_data(result, 1) + else: + return ActionDataCLI.human_display(result) + else: + return result + LOGGER.error('Cannot list Action Data {}'.format(_data_req.status_code)) + + @staticmethod + @hug.object.cli + def add(name, category_name_or_id, policy_name_or_id, description="", human:bool = False): + """ + Add an action data in the database + :return: JSON status output + """ + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + category_id = list(meta_data.ActionCategoriesCLI.list(category_name_or_id) + .get("action_categories").keys())[0] + policy_id = list(policy.PoliciesCLI.list(policy_name_or_id).get("policies").keys())[0] + _url = "{}/policies/{}/action_data/{}".format( + db_conf.get("url"), policy_id, category_id) + _data_req = requests.post( + _url, + json={ + "name": name, + "description": description, + }, + headers={ + "x-api-key": manager_api_key, + "Content-Type": "application/json" + } + ) + if _data_req.status_code == 200: + LOGGER.warning('Create {}'.format(_data_req.content)) + if human: + return human_display_entity_data(_data_req.json().get("action_data")) + else: + return _data_req.json() + LOGGER.error('Cannot create {}'.format(name, _data_req.content[:40])) + + + @staticmethod + def human_display(action_data_json): + human_result = "Actions Data\n" + for action_data in action_data_json.get('action_data'): + human_result += human_display_entity_data(action_data) + return human_result + + @staticmethod + @hug.object.cli + def delete(name_or_id, category_name_or_id, policy_name_or_id): + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + _action_data = ActionDataCLI.list(policy_name_or_id) + category_id = list(meta_data.ActionCategoriesCLI.list(category_name_or_id) + .get("action_categories").keys())[0] + for element in _action_data.get("action_data"): + for _data_id, _data_value in element.get("data").items(): + if _data_value.get("name") == name_or_id: + policy_id = list( + policy.PoliciesCLI.list(policy_name_or_id).get("policies").keys())[0] + _url = "{}/policies/{}/action_data/{}/{}".format( + db_conf.get("url"), policy_id, category_id, _data_id) + req = requests.delete( + _url, + headers={"x-api-key": manager_api_key} + ) + if req.status_code == 200: + LOGGER.warning('Deleted {}'.format(name_or_id)) + continue + LOGGER.error("Cannot delete Action Data with name {}".format(name_or_id)) diff --git a/moon_manager/moon_manager/api/db/__init__.py b/moon_manager/moon_manager/api/db/__init__.py new file mode 100644 index 00000000..1856aa2c --- /dev/null +++ b/moon_manager/moon_manager/api/db/__init__.py @@ -0,0 +1,12 @@ +# 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_manager/moon_manager/api/db/managers.py b/moon_manager/moon_manager/api/db/managers.py new file mode 100644 index 00000000..23be03fc --- /dev/null +++ b/moon_manager/moon_manager/api/db/managers.py @@ -0,0 +1,24 @@ +# 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 logging + +logger = logging.getLogger("moon.db.api.managers") + + +class Managers(object): + """Object that links managers together""" + ModelManager = None + KeystoneManager = None + PDPManager = None + PolicyManager = None + SlaveManager = None diff --git a/moon_manager/moon_manager/api/db/migrations/__init__.py b/moon_manager/moon_manager/api/db/migrations/__init__.py new file mode 100644 index 00000000..1856aa2c --- /dev/null +++ b/moon_manager/moon_manager/api/db/migrations/__init__.py @@ -0,0 +1,12 @@ +# 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_manager/moon_manager/api/db/migrations/moon_001.py b/moon_manager/moon_manager/api/db/migrations/moon_001.py new file mode 100644 index 00000000..8e604d2b --- /dev/null +++ b/moon_manager/moon_manager/api/db/migrations/moon_001.py @@ -0,0 +1,336 @@ +# 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 json +import sqlalchemy as sql +from sqlalchemy import types as sql_types +from sqlalchemy import create_engine +import sys + + +class JsonBlob(sql_types.TypeDecorator): + impl = sql.Text + + def process_bind_param(self, value, dialect): + return json.dumps(value) + + def process_result_value(self, value, dialect): + return json.loads(value) + + +def upgrade(migrate_engine): + if isinstance(migrate_engine, str): + migrate_engine = create_engine(migrate_engine) + meta = sql.MetaData() + meta.bind = migrate_engine + sys.stdout.write("Creating ") + sys.stdout.flush() + + table = sql.Table( + 'pdp', + meta, + sql.Column('id', sql.String(64), primary_key=True), + sql.Column('name', sql.String(256), nullable=False), + sql.Column('vim_project_id', sql.String(64), nullable=True, default=""), + sql.Column('value', JsonBlob(), nullable=True), + sql.UniqueConstraint('name', name='unique_constraint_models'), + mysql_engine='InnoDB', + mysql_charset='utf8') + table.create(migrate_engine, checkfirst=True) + sys.stdout.write(str(table) + " ") + sys.stdout.flush() + + table = sql.Table( + 'slaves', + meta, + sql.Column('id', sql.String(64), primary_key=True), + sql.Column('name', sql.String(256), nullable=False), + sql.Column('address', sql.String(256), nullable=True, default=""), + sql.Column('grant_if_unknown_project', sql.Boolean, nullable=True, default=""), + sql.Column('process', sql.String(256), nullable=False, default=""), + sql.Column('log', sql.String(256), nullable=False, default=""), + sql.Column('api_key', sql.String(256), nullable=False, default=""), + sql.Column('value', JsonBlob(), nullable=True), + sql.UniqueConstraint('name', name='unique_constraint_models'), + mysql_engine='InnoDB', + mysql_charset='utf8') + table.create(migrate_engine, checkfirst=True) + sys.stdout.write(str(table) + " ") + sys.stdout.flush() + + table = sql.Table( + 'policies', + meta, + sql.Column('id', sql.String(64), primary_key=True), + sql.Column('name', sql.String(256), nullable=False), + sql.Column('model_id', sql.String(64), nullable=True, default=""), + sql.Column('value', JsonBlob(), nullable=True), + sql.UniqueConstraint('name', 'model_id', name='unique_constraint_models'), + mysql_engine='InnoDB', + mysql_charset='utf8') + table.create(migrate_engine, checkfirst=True) + sys.stdout.write(str(table) + " ") + sys.stdout.flush() + + table = sql.Table( + 'models', + meta, + sql.Column('id', sql.String(64), primary_key=True), + sql.Column('name', sql.String(256), nullable=False), + sql.Column('value', JsonBlob(), nullable=True), + sql.UniqueConstraint('name', name='unique_constraint_models'), + mysql_engine='InnoDB', + mysql_charset='utf8') + table.create(migrate_engine, checkfirst=True) + sys.stdout.write(str(table) + " ") + sys.stdout.flush() + + subject_categories_table = sql.Table( + 'subject_categories', + meta, + sql.Column('id', sql.String(64), primary_key=True), + sql.Column('name', sql.String(256), nullable=False), + sql.Column('description', sql.String(256), nullable=True), + + sql.UniqueConstraint('name', name='unique_constraint_subject_categories'), + mysql_engine='InnoDB', + mysql_charset='utf8') + subject_categories_table.create(migrate_engine, checkfirst=True) + sys.stdout.write(str(subject_categories_table) + " ") + sys.stdout.flush() + + object_categories_table = sql.Table( + 'object_categories', + meta, + sql.Column('id', sql.String(64), primary_key=True), + sql.Column('name', sql.String(256), nullable=False), + sql.Column('description', sql.String(256), nullable=True), + + sql.UniqueConstraint('name', name='unique_constraint_object_categories'), + mysql_engine='InnoDB', + mysql_charset='utf8') + object_categories_table.create(migrate_engine, checkfirst=True) + sys.stdout.write(str(object_categories_table) + " ") + sys.stdout.flush() + + action_categories_table = sql.Table( + 'action_categories', + meta, + sql.Column('id', sql.String(64), primary_key=True), + sql.Column('name', sql.String(256), nullable=False), + sql.Column('description', sql.String(256), nullable=True), + + sql.UniqueConstraint('name', name='unique_constraint_action_categories'), + mysql_engine='InnoDB', + mysql_charset='utf8') + action_categories_table.create(migrate_engine, checkfirst=True) + sys.stdout.write(str(action_categories_table) + " ") + sys.stdout.flush() + + subjects_table = sql.Table( + 'subjects', + meta, + sql.Column('id', sql.String(64), primary_key=True), + sql.Column('name', sql.String(256), nullable=False), + sql.Column('value', JsonBlob(), nullable=True), + sql.UniqueConstraint('name', name='unique_constraint_subjects'), + mysql_engine='InnoDB', + mysql_charset='utf8') + subjects_table.create(migrate_engine, checkfirst=True) + sys.stdout.write(str(subjects_table) + " ") + sys.stdout.flush() + + objects_table = sql.Table( + 'objects', + meta, + sql.Column('id', sql.String(64), primary_key=True), + sql.Column('name', sql.String(256), nullable=False), + sql.Column('value', JsonBlob(), nullable=True), + sql.UniqueConstraint('name', name='unique_constraint_objects'), + mysql_engine='InnoDB', + mysql_charset='utf8') + objects_table.create(migrate_engine, checkfirst=True) + sys.stdout.write(str(objects_table) + " ") + sys.stdout.flush() + + actions_table = sql.Table( + 'actions', + meta, + sql.Column('id', sql.String(64), primary_key=True), + sql.Column('name', sql.String(256), nullable=False), + sql.Column('value', JsonBlob(), nullable=True), + sql.UniqueConstraint('name', name='unique_constraint_actions'), + mysql_engine='InnoDB', + mysql_charset='utf8') + actions_table.create(migrate_engine, checkfirst=True) + sys.stdout.write(str(actions_table) + " ") + sys.stdout.flush() + + subject_data_table = sql.Table( + 'subject_data', + meta, + sql.Column('id', sql.String(64), primary_key=True), + sql.Column('name', sql.String(256), nullable=False), + sql.Column('value', JsonBlob(), nullable=True), + sql.Column('category_id', sql.ForeignKey("subject_categories.id"), nullable=False), + sql.Column('policy_id', sql.ForeignKey("policies.id"), nullable=False), + sql.UniqueConstraint('name', 'category_id', 'policy_id', + name='unique_constraint_subject_data'), + mysql_engine='InnoDB', + mysql_charset='utf8') + subject_data_table.create(migrate_engine, checkfirst=True) + sys.stdout.write(str(subject_data_table) + " ") + sys.stdout.flush() + + object_data_table = sql.Table( + 'object_data', + meta, + sql.Column('id', sql.String(64), primary_key=True), + sql.Column('name', sql.String(256), nullable=False), + sql.Column('value', JsonBlob(), nullable=True), + sql.Column('category_id', sql.ForeignKey("object_categories.id"), nullable=False), + sql.Column('policy_id', sql.ForeignKey("policies.id"), nullable=False), + sql.UniqueConstraint('name', 'category_id', 'policy_id', + name='unique_constraint_object_data'), + mysql_engine='InnoDB', + mysql_charset='utf8') + object_data_table.create(migrate_engine, checkfirst=True) + sys.stdout.write(str(object_data_table) + " ") + sys.stdout.flush() + + action_data_table = sql.Table( + 'action_data', + meta, + sql.Column('id', sql.String(64), primary_key=True), + sql.Column('name', sql.String(256), nullable=False), + sql.Column('value', JsonBlob(), nullable=True), + sql.Column('category_id', sql.ForeignKey("action_categories.id"), nullable=False), + sql.Column('policy_id', sql.ForeignKey("policies.id"), nullable=False), + sql.UniqueConstraint('name', 'category_id', 'policy_id', + name='unique_constraint_action_data'), + mysql_engine='InnoDB', + mysql_charset='utf8') + action_data_table.create(migrate_engine, checkfirst=True) + sys.stdout.write(str(action_data_table) + " ") + sys.stdout.flush() + + subject_assignments_table = sql.Table( + 'subject_assignments', + meta, + sql.Column('id', sql.String(64), primary_key=True), + sql.Column('assignments', sql.String(256), nullable=True), + sql.Column('policy_id', sql.ForeignKey("policies.id"), nullable=False), + sql.Column('subject_id', sql.ForeignKey("subjects.id"), nullable=False), + sql.Column('category_id', sql.ForeignKey("subject_categories.id"), nullable=False), + sql.UniqueConstraint('policy_id', 'subject_id', 'category_id', + name='unique_constraint_subject_assignment'), + mysql_engine='InnoDB', + mysql_charset='utf8') + subject_assignments_table.create(migrate_engine, checkfirst=True) + sys.stdout.write(str(subject_assignments_table) + " ") + sys.stdout.flush() + + object_assignments_table = sql.Table( + 'object_assignments', + meta, + sql.Column('id', sql.String(64), primary_key=True), + sql.Column('assignments', sql.String(256), nullable=True), + sql.Column('policy_id', sql.ForeignKey("policies.id"), nullable=False), + sql.Column('object_id', sql.ForeignKey("objects.id"), nullable=False), + sql.Column('category_id', sql.ForeignKey("object_categories.id"), nullable=False), + sql.UniqueConstraint('policy_id', 'object_id', 'category_id', + name='unique_constraint_object_assignment'), + mysql_engine='InnoDB', + mysql_charset='utf8') + object_assignments_table.create(migrate_engine, checkfirst=True) + sys.stdout.write(str(object_assignments_table) + " ") + sys.stdout.flush() + + action_assignments_table = sql.Table( + 'action_assignments', + meta, + sql.Column('id', sql.String(64), primary_key=True), + sql.Column('assignments', sql.String(256), nullable=True), + sql.Column('policy_id', sql.ForeignKey("policies.id"), nullable=False), + sql.Column('action_id', sql.ForeignKey("actions.id"), nullable=False), + sql.Column('category_id', sql.ForeignKey("action_categories.id"), nullable=False), + sql.UniqueConstraint('policy_id', 'action_id', 'category_id', + name='unique_constraint_action_assignment'), + mysql_engine='InnoDB', + mysql_charset='utf8') + action_assignments_table.create(migrate_engine, checkfirst=True) + sys.stdout.write(str(action_assignments_table) + " ") + sys.stdout.flush() + + meta_rules_table = sql.Table( + 'meta_rules', + meta, + sql.Column('id', sql.String(64), primary_key=True), + sql.Column('name', sql.String(256), nullable=False), + sql.Column('subject_categories', JsonBlob(), nullable=False), + sql.Column('object_categories', JsonBlob(), nullable=False), + sql.Column('action_categories', JsonBlob(), nullable=False), + sql.Column('value', JsonBlob(), nullable=True), + sql.UniqueConstraint('name', name='unique_constraint_meta_rule_name'), + # sql.UniqueConstraint('subject_categories', 'object_categories', 'action_categories', name='unique_constraint_meta_rule_def'), + mysql_engine='InnoDB', + mysql_charset='utf8') + meta_rules_table.create(migrate_engine, checkfirst=True) + sys.stdout.write(str(meta_rules_table) + " ") + sys.stdout.flush() + + rules_table = sql.Table( + 'rules', + meta, + sql.Column('id', sql.String(64), primary_key=True), + sql.Column('rule', JsonBlob(), nullable=True), + sql.Column('policy_id', sql.ForeignKey("policies.id"), nullable=False), + sql.Column('meta_rule_id', sql.ForeignKey("meta_rules.id"), nullable=False), + mysql_engine='InnoDB', + mysql_charset='utf8') + rules_table.create(migrate_engine, checkfirst=True) + sys.stdout.write(str(rules_table) + " ") + sys.stdout.flush() + print("") + + +def downgrade(migrate_engine): + if isinstance(migrate_engine, str): + migrate_engine = create_engine(migrate_engine) + meta = sql.MetaData() + meta.bind = migrate_engine + + for _table in ( + 'rules', + 'meta_rules', + 'action_assignments', + 'object_assignments', + 'subject_assignments', + 'action_data', + 'object_data', + 'subject_data', + 'actions', + 'objects', + 'subjects', + 'action_categories', + 'object_categories', + 'subject_categories', + 'models', + 'policies', + 'pdp', + 'slaves' + ): + try: + table = sql.Table(_table, meta, autoload=True) + table.drop(migrate_engine, checkfirst=True) + except Exception as e: + print(e) diff --git a/moon_manager/moon_manager/api/db/model.py b/moon_manager/moon_manager/api/db/model.py new file mode 100644 index 00000000..9dc6273a --- /dev/null +++ b/moon_manager/moon_manager/api/db/model.py @@ -0,0 +1,429 @@ +# 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 uuid import uuid4 +import logging +from moon_utilities import exceptions +from moon_utilities.security_functions import enforce +from moon_manager.api.db.managers import Managers +import copy +from moon_manager import pip_driver + +logger = logging.getLogger("moon.db.api.model") + + +class ModelManager(Managers): + + def __init__(self, connector=None): + self.driver = connector.driver + Managers.ModelManager = self + + @enforce(("read", "write"), "models") + def update_model(self, moon_user_id, model_id, value): + if model_id not in self.driver.get_models(model_id=model_id): + raise exceptions.ModelUnknown + + if not value['name'].strip(): + raise exceptions.ModelContentError('Model name invalid') + + if 'meta_rules' not in value: + raise exceptions.MetaRuleUnknown + + model = self.get_models(moon_user_id=moon_user_id, model_id=model_id) + model = model[next(iter(model))] + if ((model['meta_rules'] and value['meta_rules'] and model['meta_rules'] != value[ + 'meta_rules']) \ + or (model['meta_rules'] and not value['meta_rules'])): + policies = Managers.PolicyManager.get_policies(moon_user_id=moon_user_id) + for policy_id in policies: + if policies[policy_id]["model_id"] == model_id: + raise exceptions.DeleteModelWithPolicy + + if value and 'meta_rules' in value: + for meta_rule_id in value['meta_rules']: + if meta_rule_id: + meta_rule_tmp = self.driver.get_meta_rules(meta_rule_id=meta_rule_id) + if (not meta_rule_id) or (not meta_rule_tmp) : + raise exceptions.MetaRuleUnknown + + return self.driver.update_model(model_id=model_id, value=value) + + @enforce(("read", "write"), "models") + def delete_model(self, moon_user_id, model_id): + if model_id not in self.driver.get_models(model_id=model_id): + raise exceptions.ModelUnknown + # TODO (asteroide): check that no policy is connected to this model + policies = Managers.PolicyManager.get_policies(moon_user_id=moon_user_id) + for policy in policies: + if policies[policy]['model_id'] == model_id: + raise exceptions.DeleteModelWithPolicy + return self.driver.delete_model(model_id=model_id) + + @enforce(("read", "write"), "models") + def add_model(self, moon_user_id, model_id=None, value=None): + + if not value['name'].strip(): + raise exceptions.ModelContentError('Model name invalid') + + models = self.driver.get_models() + if model_id in models: + raise exceptions.ModelExisting + + if value.get('meta_rules', []): + for model in models: + if models[model]['name'] == value['name']: + raise exceptions.ModelExisting("Model Name Existed") + if sorted(models[model].get('meta_rules', [])) == sorted(value.get('meta_rules', [])): + raise exceptions.ModelExisting("Meta Rules List Existed in another Model") + + if not model_id: + model_id = uuid4().hex + if value and 'meta_rules' in value: + for meta_rule_id in value['meta_rules']: + if not meta_rule_id: + raise exceptions.MetaRuleUnknown + meta_rule = self.driver.get_meta_rules(meta_rule_id=meta_rule_id) + if not meta_rule: + raise exceptions.MetaRuleUnknown + + return self.driver.add_model(model_id=model_id, value=value) + + @enforce("read", "models") + def get_models(self, moon_user_id, model_id=None): + return self.driver.get_models(model_id=model_id) + + @enforce("read", "policies") + def get_policies(self, moon_user_id, policy_id=None): + return self.driver.get_policies(policy_id=policy_id) + + @enforce(("read", "write"), "meta_rules") + def update_meta_rule(self, moon_user_id, meta_rule_id, value): + meta_rules = self.driver.get_meta_rules() + if not meta_rule_id or meta_rule_id not in meta_rules: + raise exceptions.MetaRuleUnknown + self.__check_meta_rule_dependencies(moon_user_id=moon_user_id, meta_rule_id=meta_rule_id) + if value: + if not value['name'].strip(): + raise exceptions.MetaRuleContentError('Meta_rule name invalid') + + if 'subject_categories' in value: + if (len(value['subject_categories']) == 1 and (value['subject_categories'][0] is None or value[ + 'subject_categories'][0].strip() == "")): + value['subject_categories'] = []; + else: + for subject_category_id in value['subject_categories']: + if (not subject_category_id) or (not self.driver.get_subject_categories( + category_id=subject_category_id)): + raise exceptions.SubjectCategoryUnknown + if 'object_categories' in value: + if (len(value['object_categories']) == 1 and (value['object_categories'][0] is None or value[ + 'object_categories'][0].strip() == "")): + value['object_categories'] = []; + else: + for object_category_id in value['object_categories']: + if (not object_category_id) or (not self.driver.get_object_categories( + category_id=object_category_id)): + raise exceptions.ObjectCategoryUnknown + if 'action_categories' in value: + if (len(value['action_categories']) == 1 and (value['action_categories'][0] is None or value[ + 'action_categories'][0].strip() == "")): + value['action_categories'] = []; + else: + for action_category_id in value['action_categories']: + if (not action_category_id) or (not self.driver.get_action_categories( + category_id=action_category_id)): + raise exceptions.ActionCategoryUnknown + + for meta_rule_obj_id in meta_rules: + counter_matched_list = 0 + counter_matched_list += self.check_combination( + meta_rules[meta_rule_obj_id]['subject_categories'], + value['subject_categories']) + counter_matched_list += self.check_combination( + meta_rules[meta_rule_obj_id]['object_categories'], + value['object_categories']) + counter_matched_list += self.check_combination( + meta_rules[meta_rule_obj_id]['action_categories'], + value['action_categories']) + if counter_matched_list == 3 and meta_rule_obj_id != meta_rule_id: + raise exceptions.MetaRuleExisting("Same categories combination existed") + + return self.driver.set_meta_rule(meta_rule_id=meta_rule_id, value=value) + + def __check_meta_rule_dependencies(self, moon_user_id, meta_rule_id): + policies = self.get_policies(moon_user_id=moon_user_id) + for policy in policies: + model_id = policies[policy]["model_id"] + model = self.get_models(moon_user_id=moon_user_id, model_id=model_id)[model_id] + if meta_rule_id in model["meta_rules"]: + raise exceptions.MetaRuleUpdateError("This meta_rule is already in use in a policy") + + policies = Managers.PolicyManager.get_policies(moon_user_id=moon_user_id) + for policy_id in policies: + rules = Managers.PolicyManager.get_rules(moon_user_id=moon_user_id, policy_id=policy_id, + meta_rule_id=meta_rule_id) + if rules['rules']: + raise exceptions.MetaRuleUpdateError + + @enforce("read", "meta_rules") + def get_meta_rules(self, moon_user_id, meta_rule_id=None): + return self.driver.get_meta_rules(meta_rule_id=meta_rule_id) + + @enforce(("read", "write"), "meta_rules") + def add_meta_rule(self, moon_user_id, meta_rule_id=None, value=None): + + if not value['name'].strip(): + raise exceptions.MetaRuleContentError('Meta_rule name invalid') + + meta_rules = self.driver.get_meta_rules() + + if meta_rule_id in meta_rules: + raise exceptions.MetaRuleExisting + + if value: + if 'subject_categories' in value: + if (len(value['subject_categories']) == 1 and (value['subject_categories'][0] is None or value[ + 'subject_categories'][0].strip() == "")): + value['subject_categories'] = []; + else: + for subject_category_id in value['subject_categories']: + if ((not subject_category_id) or (not self.driver.get_subject_categories( + category_id=subject_category_id))): + if subject_category_id.startswith("attributes:"): + _attributes = pip_driver.AttrsManager.get_objects( + moon_user_id="admin", + object_type=subject_category_id.replace("attributes:", "") + ) + action_category_id = subject_category_id.replace("attributes:", "") + if action_category_id != _attributes['id']: + raise exceptions.SubjectCategoryUnknown + else: + raise exceptions.SubjectCategoryUnknown + if 'object_categories' in value: + if(len(value['object_categories']) == 1 and (value['object_categories'][0] is None or value[ + 'object_categories'][0].strip() == "")): + value['object_categories'] = []; + else: + for object_category_id in value['object_categories']: + if ((not object_category_id) or (not self.driver.get_object_categories( + category_id=object_category_id))): + if object_category_id.startswith("attributes:"): + _attributes = pip_driver.AttrsManager.get_objects( + moon_user_id="admin", + object_type=object_category_id.replace("attributes:", "") + ) + action_category_id = object_category_id.replace("attributes:", "") + if action_category_id != _attributes['id']: + raise exceptions.ObjectCategoryUnknown + else: + raise exceptions.ObjectCategoryUnknown + if 'action_categories' in value: + if (len(value['action_categories']) == 1 and (value['action_categories'][0] is None or value[ + 'action_categories'][0].strip() == "")): + value['action_categories'] = []; + else: + for action_category_id in value['action_categories']: + if ((not action_category_id) or (not self.driver.get_action_categories( + category_id=action_category_id))): + if action_category_id.startswith("attributes:"): + _attributes = pip_driver.AttrsManager.get_objects( + moon_user_id="admin", + object_type=action_category_id.replace("attributes:", "") + ) + action_category_id = action_category_id.replace("attributes:", "") + if action_category_id not in _attributes.keys(): + raise exceptions.ActionCategoryUnknown + else: + raise exceptions.ActionCategoryUnknown + + for meta_rule_obj_id in meta_rules: + counter_matched_list = 0 + + counter_matched_list += self.check_combination( + meta_rules[meta_rule_obj_id]['subject_categories'], value['subject_categories']) + + counter_matched_list += self.check_combination( + meta_rules[meta_rule_obj_id]['object_categories'], value['object_categories']) + + counter_matched_list += self.check_combination( + meta_rules[meta_rule_obj_id]['action_categories'], value['action_categories']) + + if counter_matched_list == 3: + raise exceptions.MetaRuleExisting("Same categories combination existed") + + return self.driver.set_meta_rule(meta_rule_id=meta_rule_id, value=value) + + # @enforce(("read", "write"), "meta_rules") + def check_combination(self, list_one, list_two): + counter_removed_items = 0 + temp_list_two = copy.deepcopy(list_two) + for item in list_one: + if item in temp_list_two: + temp_list_two.remove(item) + counter_removed_items += 1 + + if list_two and counter_removed_items == len(list_two) and len(list_two) == len(list_one): + return 1 + return 0 + + @enforce(("read", "write"), "meta_rules") + def delete_meta_rule(self, moon_user_id, meta_rule_id=None): + if meta_rule_id not in self.driver.get_meta_rules(meta_rule_id=meta_rule_id): + raise exceptions.MetaRuleUnknown + # TODO (asteroide): check and/or delete data and assignments and rules linked to that meta_rule + models = self.get_models(moon_user_id=moon_user_id) + for model_id in models: + for id in models[model_id]['meta_rules']: + if id == meta_rule_id: + raise exceptions.DeleteMetaRuleWithModel + return self.driver.delete_meta_rule(meta_rule_id=meta_rule_id) + + @enforce("read", "meta_data") + def get_subject_categories(self, moon_user_id, category_id=None): + return self.driver.get_subject_categories(category_id=category_id) + + @enforce(("read", "write"), "meta_data") + def add_subject_category(self, moon_user_id, category_id=None, value=None): + + if not value['name'].strip(): + raise exceptions.CategoryNameInvalid + + subject_categories = [] + if category_id is not None: + subject_categories = self.driver.get_subject_categories(category_id=category_id) + + subject_categories_names = self.driver.get_subject_categories(category_name=value['name'].strip()) + + if subject_categories_names or subject_categories: + raise exceptions.SubjectCategoryExisting + + + if not ('description' in value): + value['description'] = "" + return self.driver.add_subject_category(name=value["name"], + description=value["description"], uuid=category_id) + + @enforce(("read", "write"), "meta_data") + def delete_subject_category(self, moon_user_id, category_id): + # TODO (asteroide): delete all data linked to that category + # TODO (asteroide): delete all meta_rules linked to that category + if category_id not in self.driver.get_subject_categories(category_id=category_id): + raise exceptions.SubjectCategoryUnknown + meta_rules = self.get_meta_rules(moon_user_id=moon_user_id) + for meta_rule_id in meta_rules: + for subject_category_id in meta_rules[meta_rule_id]['subject_categories']: + logger.info( + "delete_subject_category {} {}".format(subject_category_id, meta_rule_id)) + logger.info("delete_subject_category {}".format(meta_rules[meta_rule_id])) + if subject_category_id == category_id: + # has_rules = self.driver.is_meta_rule_has_rules(meta_rule_id) + # if has_rules: + raise exceptions.DeleteSubjectCategoryWithMetaRule + + if self.driver.is_subject_category_has_assignment(category_id): + raise exceptions.DeleteCategoryWithAssignment + + if self.driver.is_subject_data_exist(category_id=category_id): + raise exceptions.DeleteCategoryWithData + + return self.driver.delete_subject_category(category_id=category_id) + + @enforce("read", "meta_data") + def get_object_categories(self, moon_user_id, category_id=None): + return self.driver.get_object_categories(category_id) + + @enforce(("read", "write"), "meta_data") + def add_object_category(self, moon_user_id, category_id=None, value=None): + if not value['name'].strip(): + raise exceptions.CategoryNameInvalid + + object_categories = [] + if category_id is not None: + object_categories = self.driver.get_object_categories(category_id=category_id) + + object_categories_names = self.driver.get_object_categories(category_name=value['name'].strip()) + if object_categories_names or object_categories: + raise exceptions.ObjectCategoryExisting + + if not ('description' in value): + value['description'] = "" + + return self.driver.add_object_category(name=value["name"], description=value["description"], + uuid=category_id) + + @enforce(("read", "write"), "meta_data") + def delete_object_category(self, moon_user_id, category_id): + # TODO (asteroide): delete all data linked to that category + # TODO (asteroide): delete all meta_rules linked to that category + if category_id not in self.driver.get_object_categories(category_id=category_id): + raise exceptions.ObjectCategoryUnknown + meta_rules = self.get_meta_rules(moon_user_id=moon_user_id) + for meta_rule_id in meta_rules: + for object_category_id in meta_rules[meta_rule_id]['object_categories']: + if object_category_id == category_id: + # has_rules = self.driver.is_meta_rule_has_rules(meta_rule_id) + # if has_rules: + raise exceptions.DeleteObjectCategoryWithMetaRule + + if self.driver.is_object_category_has_assignment(category_id): + raise exceptions.DeleteCategoryWithAssignment + + if self.driver.is_object_data_exist(category_id=category_id): + raise exceptions.DeleteCategoryWithData + + return self.driver.delete_object_category(category_id=category_id) + + @enforce("read", "meta_data") + def get_action_categories(self, moon_user_id, category_id=None): + return self.driver.get_action_categories(category_id=category_id) + + @enforce(("read", "write"), "meta_data") + def add_action_category(self, moon_user_id, category_id=None, value=None): + + if not value['name'].strip(): + raise exceptions.CategoryNameInvalid + + action_categories = [] + if category_id is not None: + action_categories = self.driver.get_action_categories(category_id=category_id) + + action_categories_names = self.driver.get_action_categories(category_name=value['name'].strip()) + if action_categories_names or action_categories: + raise exceptions.ActionCategoryExisting + + if not ('description' in value): + value['description'] = "" + + return self.driver.add_action_category(name=value["name"], description=value["description"], + uuid=category_id) + + @enforce(("read", "write"), "meta_data") + def delete_action_category(self, moon_user_id, category_id): + # TODO (asteroide): delete all data linked to that category + # TODO (asteroide): delete all meta_rules linked to that category + if category_id not in self.driver.get_action_categories(category_id=category_id): + raise exceptions.ActionCategoryUnknown + meta_rules = self.get_meta_rules(moon_user_id=moon_user_id) + for meta_rule_id in meta_rules: + for action_category_id in meta_rules[meta_rule_id]['action_categories']: + if action_category_id == category_id: + # has_rules = self.driver.is_meta_rule_has_rules(meta_rule_id) + # if has_rules: + raise exceptions.DeleteActionCategoryWithMetaRule + + if self.driver.is_action_category_has_assignment(category_id): + raise exceptions.DeleteCategoryWithAssignment + + if self.driver.is_action_data_exist(category_id=category_id): + raise exceptions.DeleteCategoryWithData + + return self.driver.delete_action_category(category_id=category_id) diff --git a/moon_manager/moon_manager/api/db/pdp.py b/moon_manager/moon_manager/api/db/pdp.py new file mode 100644 index 00000000..a4ca08f6 --- /dev/null +++ b/moon_manager/moon_manager/api/db/pdp.py @@ -0,0 +1,115 @@ +# 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 uuid import uuid4 +import logging +from moon_utilities.security_functions import enforce +from moon_manager.api.db.managers import Managers +from moon_utilities import exceptions + +logger = logging.getLogger("moon.db.api.pdp") + + +class PDPManager(Managers): + + def __init__(self, connector=None): + self.driver = connector.driver + Managers.PDPManager = self + + @enforce(("read", "write"), "pdp") + def update_pdp(self, moon_user_id, pdp_id, value): + if not value or 'name' not in value or not value['name'].strip(): + raise exceptions.PdpContentError + + exists_security_pipeline = value and 'security_pipeline' in value and \ + len(value['security_pipeline']) > 0 + exists_vim_project_id = value and 'vim_project_id' in value and \ + value['vim_project_id'] != None and \ + value['vim_project_id'].strip() + if not exists_security_pipeline and exists_vim_project_id: + raise exceptions.PdpContentError + if exists_security_pipeline and not exists_vim_project_id: + raise exceptions.PdpContentError + + self.__pdp_validated_pipeline_name_id(pdp_id, value, "update") + + if value and 'security_pipeline' in value: + for policy_id in value['security_pipeline']: + if not policy_id or not policy_id.strip() or not \ + Managers.PolicyManager.get_policies(moon_user_id=moon_user_id, policy_id=policy_id): + raise exceptions.PolicyUnknown + + return self.driver.update_pdp(pdp_id=pdp_id, value=value) + + @enforce(("read", "write"), "pdp") + def delete_pdp(self, moon_user_id, pdp_id): + if pdp_id not in self.driver.get_pdp(pdp_id=pdp_id): + raise exceptions.PdpUnknown + return self.driver.delete_pdp(pdp_id=pdp_id) + + @enforce(("read", "write"), "pdp") + def add_pdp(self, moon_user_id, pdp_id=None, value=None): + if not value or 'name' not in value or not value['name'].strip(): + raise exceptions.PdpContentError + + exists_security_pipeline = value and 'security_pipeline' in value and \ + len(value['security_pipeline']) > 0 + exists_vim_project_id = value and 'vim_project_id' in value and \ + value['vim_project_id'] is not None and \ + value['vim_project_id'].strip() + if not exists_security_pipeline and exists_vim_project_id: + raise exceptions.PdpContentError + if exists_security_pipeline and not exists_vim_project_id: + raise exceptions.PdpContentError + + self.__pdp_validated_pipeline_name_id(pdp_id, value, "add") + + if value and 'security_pipeline' in value: + for policy_id in value['security_pipeline']: + if not policy_id or not policy_id.strip() or not \ + Managers.PolicyManager.get_policies(moon_user_id=moon_user_id, policy_id=policy_id): + raise exceptions.PolicyUnknown + + return self.driver.add_pdp(pdp_id=pdp_id, value=value) + + @enforce("read", "pdp") + def get_pdp(self, moon_user_id, pdp_id=None): + return self.driver.get_pdp(pdp_id=pdp_id) + + @enforce("read", "pdp") + def delete_policy_from_pdp(self, moon_user_id, pdp_id, policy_id): + + if pdp_id not in self.driver.get_pdp(pdp_id=pdp_id): + raise exceptions.PdpUnknown + if policy_id not in self.driver.get_policies(policy_id=policy_id): + raise exceptions.PolicyUnknown + x = self.driver.delete_policy_from_pdp(pdp_id=pdp_id, policy_id=policy_id) + return x + + def __pdp_validated_pipeline_name_id(self, pdp_id, value, method_type=None): + all_pdps = self.driver.get_pdp() + if method_type == 'update': + if pdp_id not in all_pdps: + raise exceptions.PdpUnknown + else: + if pdp_id in all_pdps: + raise exceptions.PdpExisting + if not pdp_id: + pdp_id = uuid4().hex + + for key in all_pdps: + if pdp_id != key: + if all_pdps[key]['name'] == value['name']: + raise exceptions.PdpExisting + for policy_id in value['security_pipeline']: + if policy_id in all_pdps[key]['security_pipeline']: + raise exceptions.PdpInUse diff --git a/moon_manager/moon_manager/api/db/policy.py b/moon_manager/moon_manager/api/db/policy.py new file mode 100644 index 00000000..e736aca7 --- /dev/null +++ b/moon_manager/moon_manager/api/db/policy.py @@ -0,0 +1,971 @@ +# 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 logging +from uuid import uuid4 +from moon_manager.api.db.managers import Managers +from moon_manager.pip_driver import InformationManager +from moon_utilities.security_functions import enforce +from moon_utilities import exceptions +from moon_manager import pip_driver + + +logger = logging.getLogger("moon.db.api.policy") + + +class PolicyManager(Managers): + + def __init__(self, connector=None): + self.driver = connector.driver + Managers.PolicyManager = self + + def get_policy_from_meta_rules(self, moon_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(moon_user_id=moon_user_id).items(): + if 'security_pipeline' not in pdp_value: + raise exceptions.PdpContentError + for policy_id in pdp_value["security_pipeline"]: + if not policies or policy_id not in policies: + raise exceptions.PolicyUnknown + model_id = policies[policy_id]["model_id"] + if not models: + raise exceptions.ModelUnknown + if model_id not in models: + raise exceptions.ModelUnknown + if meta_rule_id in models[model_id]["meta_rules"]: + return policy_id + + @enforce(("read", "write"), "policies") + def update_policy(self, moon_user_id, policy_id, value): + + if not value or not value['name'].strip(): + raise exceptions.PolicyContentError + + policy_list = self.driver.get_policies(policy_id=policy_id) + if not policy_id or policy_id not in policy_list: + raise exceptions.PolicyUnknown + + policies = self.driver.get_policies(policy_name=value['name']) + if policies and not (policy_id in policies): + raise exceptions.PolicyExisting("Policy name Existed") + + if 'model_id' in value and value['model_id']: + if not value['model_id'].strip() or not Managers.ModelManager.get_models( + moon_user_id=moon_user_id, model_id=value['model_id']): + raise exceptions.ModelUnknown + + policy_obj = policy_list[policy_id] + if policy_obj["model_id"] and policy_obj["model_id"] != value['model_id']: + raise exceptions.PolicyUpdateError("Model is not empty") + + return self.driver.update_policy(policy_id=policy_id, value=value) + + @enforce(("read", "write"), "policies") + def delete_policy(self, moon_user_id, policy_id): + # TODO (asteroide): unmap PDP linked to that policy + if policy_id not in self.driver.get_policies(policy_id=policy_id): + raise exceptions.PolicyUnknown + pdps = self.PDPManager.get_pdp(moon_user_id=moon_user_id) + for pdp in pdps: + if policy_id in pdps[pdp]['security_pipeline']: + self.PDPManager.delete_policy_from_pdp(moon_user_id=moon_user_id, + pdp_id=pdp, + policy_id=policy_id) + + subject_data = self.get_subject_data(moon_user_id=moon_user_id, policy_id=policy_id) + if subject_data: + for subject_data_obj in subject_data: + if subject_data_obj and subject_data_obj["data"]: + for subject_data_id in subject_data_obj['data']: + self.delete_subject_data(moon_user_id=moon_user_id, policy_id=policy_id, + data_id=subject_data_id) + + object_data = self.get_object_data(moon_user_id=moon_user_id, policy_id=policy_id) + if object_data: + for object_data_obj in object_data: + if object_data_obj and object_data_obj["data"]: + for object_data_id in object_data_obj['data']: + self.delete_object_data(moon_user_id=moon_user_id, policy_id=policy_id, + data_id=object_data_id) + action_data = self.get_action_data(moon_user_id=moon_user_id, policy_id=policy_id) + if action_data: + for action_data_obj in action_data: + if action_data_obj and action_data_obj["data"]: + for action_data_id in action_data_obj['data']: + self.delete_action_data(moon_user_id=moon_user_id, policy_id=policy_id, + data_id=action_data_id) + + subjects = self.driver.get_subjects(policy_id=policy_id) + if subjects: + for subject_id in subjects: + self.delete_subject(moon_user_id=moon_user_id, policy_id=policy_id, + perimeter_id=subject_id) + objects = self.driver.get_objects(policy_id=policy_id) + if objects: + for object_id in objects: + self.delete_object(moon_user_id=moon_user_id, policy_id=policy_id, + perimeter_id=object_id) + actions = self.driver.get_actions(policy_id=policy_id) + if actions: + for action_id in actions: + self.delete_action(moon_user_id=moon_user_id, policy_id=policy_id, + perimeter_id=action_id) + + # rules = self.driver.get_rules(policy_id=policy_id)["rules"] + # if rules: + # for rule_id in rules: + # self.delete_rule(moon_user_id=moon_user_id, policy_id=policy_id, rules=rule_id) + + return self.driver.delete_policy(policy_id=policy_id) + + @enforce(("read", "write"), "policies") + def add_policy(self, moon_user_id, policy_id=None, value=None): + + if not value or not value['name'].strip(): + raise exceptions.PolicyContentError + if policy_id in self.driver.get_policies(policy_id=policy_id): + raise exceptions.PolicyExisting + + if self.driver.get_policies(policy_name=value['name']): + raise exceptions.PolicyExisting("Policy name Existed") + + if not policy_id: + policy_id = uuid4().hex + if 'model_id' in value and value['model_id'] != "": + model_id = value['model_id'] + if model_id is None: + raise exceptions.ModelUnknown + else: + model_list = Managers.ModelManager.get_models(moon_user_id=moon_user_id, + model_id=model_id) + if not model_list: + raise exceptions.ModelUnknown + + self.__check_blank_model(model_list[model_id]) + + return self.driver.add_policy(policy_id=policy_id, value=value) + + @enforce("read", "policies") + def get_policies(self, moon_user_id, policy_id=None): + return self.driver.get_policies(policy_id=policy_id) + + @enforce("read", "perimeter") + def get_subjects(self, moon_user_id, policy_id, perimeter_id=None): + # if not policy_id: + # raise exceptions.PolicyUnknown + if policy_id and (not self.get_policies(moon_user_id=moon_user_id, policy_id=policy_id)): + raise exceptions.PolicyUnknown + return self.driver.get_subjects(policy_id=policy_id, perimeter_id=perimeter_id) + + @enforce(("read", "write"), "perimeter") + def add_subject(self, moon_user_id, policy_id=None, perimeter_id=None, value=None): + + logger.debug("add_subject {}".format(policy_id)) + if not value or "name" not in value or not value["name"].strip(): + raise exceptions.PerimeterContentError('invalid name') + + if 'policy_list' in value: + raise exceptions.PerimeterContentError("body should not contain policy_list") + + if policy_id and (not self.get_policies(moon_user_id=moon_user_id, policy_id=policy_id)): + raise exceptions.PolicyUnknown + + if perimeter_id: + subjects = self.driver.get_subjects(policy_id=None, perimeter_id=perimeter_id) + if subjects and subjects[perimeter_id]['name'] != value['name']: + raise exceptions.PerimeterContentError + + if not perimeter_id: + subject_per = self.driver.get_subject_by_name(value['name']) + if subject_per: + perimeter_id = next(iter(subject_per)) + + # should get k_user = {'users':[{"id":"11111"}]} from Keystone + # FIXME: need to check other input + # k_user = InformationManager.get_users(username=value.get('name')) + # + # if not k_user.get('users', {}): + # k_user = InformationManager.add_user(**value) + # if k_user: + # if not perimeter_id: + # try: + # logger.info("k_user={}".format(k_user)) + # perimeter_id = k_user['users'][0].get('id', uuid4().hex) + # except IndexError: + # k_user = InformationManager.get_users(value.get('name')) + # perimeter_id = uuid4().hex + # except KeyError: + # k_user = InformationManager.get_users(value.get('name')) + # perimeter_id = uuid4().hex + # + # try: + # value.update(k_user['users'][0]) + # except IndexError: + # logger.error("Cannot update user from external server data, got {}".format(k_user)) + + return self.driver.set_subject(policy_id=policy_id, perimeter_id=perimeter_id, value=value) + + @enforce(("read", "write"), "perimeter") + def update_subject(self, moon_user_id, perimeter_id, value): + logger.debug("update_subject perimeter_id = {}".format(perimeter_id)) + + if not perimeter_id: + raise exceptions.SubjectUnknown + + subjects = self.driver.get_subjects(policy_id=None, perimeter_id=perimeter_id) + if not subjects or not (perimeter_id in subjects): + raise exceptions.PerimeterContentError + + if 'policy_list' in value or ('name' in value and not value['name']): + raise exceptions.PerimeterContentError + + return self.driver.update_subject(perimeter_id=perimeter_id, value=value) + + @enforce(("read", "write"), "perimeter") + def delete_subject(self, moon_user_id, policy_id, perimeter_id): + + if not perimeter_id: + raise exceptions.SubjectUnknown + + # if not policy_id: + # raise exceptions.PolicyUnknown + + if not self.get_subjects(moon_user_id=moon_user_id, policy_id=policy_id, + perimeter_id=perimeter_id): + raise exceptions.SubjectUnknown + + if policy_id: + if not self.get_policies(moon_user_id=moon_user_id, policy_id=policy_id): + raise exceptions.PolicyUnknown + + subj_assig = self.driver.get_subject_assignments(policy_id=policy_id, + subject_id=perimeter_id) + if subj_assig: + assign_id = next(iter(subj_assig)) + for data_id in subj_assig[assign_id]['assignments']: + self.delete_subject_assignment(moon_user_id=moon_user_id, + policy_id=policy_id, + subject_id=perimeter_id, + category_id=subj_assig[assign_id]['category_id'], + data_id=data_id) + + return self.driver.delete_subject(policy_id=policy_id, perimeter_id=perimeter_id) + + @enforce("read", "perimeter") + def get_objects(self, moon_user_id, policy_id, perimeter_id=None): + # if not policy_id: + # pass + if policy_id and (not self.get_policies(moon_user_id=moon_user_id, policy_id=policy_id)): + raise exceptions.PolicyUnknown + return self.driver.get_objects(policy_id=policy_id, perimeter_id=perimeter_id) + + @enforce(("read", "write"), "perimeter") + def add_object(self, moon_user_id, policy_id, perimeter_id=None, value=None): + logger.debug("add_object {}".format(policy_id)) + + if not value or "name" not in value or not value["name"].strip(): + raise exceptions.PerimeterContentError('invalid name') + + if 'policy_list' in value: + raise exceptions.PerimeterContentError("body should not contain policy_list") + + if policy_id and (not self.get_policies(moon_user_id=moon_user_id, policy_id=policy_id)): + raise exceptions.PolicyUnknown + + # object_perimeter = {} + # if perimeter_id: + # object_perimeter = self.driver.get_objects(policy_id=None, perimeter_id=perimeter_id) + # if not object_perimeter: + # raise exceptions.PerimeterContentError + # empeche l'ajout d'un objet avec un id prédéterminé + + if not perimeter_id: + object_perimeter = self.driver.get_object_by_name(value['name']) + if object_perimeter: + perimeter_id = next(iter(object_perimeter)) + + if perimeter_id: + objects = self.driver.get_objects(policy_id=None, perimeter_id=perimeter_id) + if objects and objects[perimeter_id]['name'] != value['name']: + raise exceptions.PerimeterContentError + + if not perimeter_id: + perimeter_id = uuid4().hex + return self.driver.set_object(policy_id=policy_id, perimeter_id=perimeter_id, value=value) + + @enforce(("read", "write"), "perimeter") + def update_object(self, moon_user_id, perimeter_id, value): + logger.debug("update_object perimeter_id = {}".format(perimeter_id)) + + if not perimeter_id: + raise exceptions.ObjectUnknown + + objects = self.driver.get_objects(policy_id=None, perimeter_id=perimeter_id) + if not objects or not (perimeter_id in objects): + raise exceptions.PerimeterContentError + + if 'policy_list' in value or ('name' in value and not value['name']): + raise exceptions.PerimeterContentError + + return self.driver.update_object(perimeter_id=perimeter_id, value=value) + + @enforce(("read", "write"), "perimeter") + def delete_object(self, moon_user_id, policy_id, perimeter_id): + + if not perimeter_id: + raise exceptions.ObjectUnknown + + # if not policy_id: + # raise exceptions.PolicyUnknown + + if not self.get_objects(moon_user_id=moon_user_id, policy_id=policy_id, + perimeter_id=perimeter_id): + raise exceptions.ObjectUnknown + + if policy_id: + + if not self.get_policies(moon_user_id=moon_user_id, policy_id=policy_id): + raise exceptions.PolicyUnknown + + obj_assig = self.driver.get_object_assignments(policy_id=policy_id, object_id=perimeter_id) + + if obj_assig: + assign_id = next(iter(obj_assig)) + for data_id in obj_assig[assign_id]['assignments']: + self.delete_object_assignment(moon_user_id, + policy_id=policy_id, + object_id=perimeter_id, + category_id=obj_assig[assign_id]['category_id'], + data_id=data_id) + + return self.driver.delete_object(policy_id=policy_id, perimeter_id=perimeter_id) + + @enforce("read", "perimeter") + def get_actions(self, moon_user_id, policy_id, perimeter_id=None): + + if policy_id and (not self.get_policies(moon_user_id=moon_user_id, policy_id=policy_id)): + raise exceptions.PolicyUnknown + return self.driver.get_actions(policy_id=policy_id, perimeter_id=perimeter_id) + + @enforce(("read", "write"), "perimeter") + def add_action(self, moon_user_id, policy_id, perimeter_id=None, value=None): + logger.debug("add_action {}".format(policy_id)) + + if not value or "name" not in value or not value["name"].strip(): + raise exceptions.PerimeterContentError('invalid name') + + if 'policy_list' in value: + raise exceptions.PerimeterContentError("body should not contain policy_list") + + if policy_id and (not self.get_policies(moon_user_id=moon_user_id, policy_id=policy_id)): + raise exceptions.PolicyUnknown + + action_perimeter = {} + if perimeter_id: + action_perimeter = self.driver.get_actions(policy_id=None, perimeter_id=perimeter_id) + if not action_perimeter: + raise exceptions.PerimeterContentError + + if not perimeter_id: + action_perimeter = self.driver.get_action_by_name(value['name']) + if action_perimeter: + perimeter_id = next(iter(action_perimeter)) + + if perimeter_id and action_perimeter[perimeter_id]['name'] != value['name']: + raise exceptions.PerimeterContentError + + if not perimeter_id: + perimeter_id = uuid4().hex + + return self.driver.set_action(policy_id=policy_id, perimeter_id=perimeter_id, value=value) + + @enforce(("read", "write"), "perimeter") + def update_action(self, moon_user_id, perimeter_id, value): + logger.debug("update_action perimeter_id = {}".format(perimeter_id)) + + if not perimeter_id: + raise exceptions.ActionUnknown + + actions = self.driver.get_actions(policy_id=None, perimeter_id=perimeter_id) + if not actions or not (perimeter_id in actions): + raise exceptions.PerimeterContentError + + if 'policy_list' in value or ('name' in value and not value['name'].strip()): + raise exceptions.PerimeterContentError + + return self.driver.update_action(perimeter_id=perimeter_id, value=value) + + @enforce(("read", "write"), "perimeter") + def delete_action(self, moon_user_id, policy_id, perimeter_id): + + if not perimeter_id: + raise exceptions.ActionUnknown + + # if not policy_id: + # raise exceptions.PolicyUnknown + + if not self.get_actions(moon_user_id=moon_user_id, policy_id=policy_id, + perimeter_id=perimeter_id): + raise exceptions.ActionUnknown + + logger.debug("delete_action {} {} {}".format(policy_id, perimeter_id, + self.get_policies(moon_user_id=moon_user_id, + policy_id=policy_id))) + if policy_id: + if not self.get_policies(moon_user_id=moon_user_id, policy_id=policy_id): + raise exceptions.PolicyUnknown + + act_assig = self.driver.get_action_assignments(policy_id=policy_id, action_id=perimeter_id) + + if act_assig: + assign_id = next(iter(act_assig)) + for data_id in act_assig[assign_id]['assignments']: + self.delete_action_assignment(moon_user_id=moon_user_id, + policy_id=policy_id, + action_id=perimeter_id, + category_id=act_assig[assign_id]['category_id'], + data_id=data_id) + + return self.driver.delete_action(policy_id=policy_id, perimeter_id=perimeter_id) + + @enforce("read", "data") + def get_subject_data(self, moon_user_id, policy_id, data_id=None, category_id=None): + available_metadata = self.get_available_metadata(moon_user_id=moon_user_id, + policy_id=policy_id) + results = [] + if not category_id: + for cat in available_metadata["subject"]: + _value = self.driver.get_subject_data(policy_id=policy_id, data_id=data_id, + category_id=cat) + results.append(_value) + if category_id and category_id in available_metadata["subject"]: + results.append(self.driver.get_subject_data(policy_id=policy_id, data_id=data_id, + category_id=category_id)) + return results + + @enforce(("read", "write"), "data") + def set_subject_data(self, moon_user_id, policy_id, data_id=None, category_id=None, value=None): + + logger.debug("set_subject_data policyID {}".format(policy_id)) + + if not value or 'name' not in value or not value['name'].strip(): + raise exceptions.DataContentError + + if not policy_id or not self.get_policies(moon_user_id=moon_user_id, policy_id=policy_id): + raise exceptions.PolicyUnknown + + if not category_id or ( + not Managers.ModelManager.get_subject_categories(moon_user_id=moon_user_id, + category_id=category_id)): + raise exceptions.SubjectCategoryUnknown + + self.__category_dependency_validation(moon_user_id, policy_id, category_id, + 'subject_categories') + + if not data_id: + data_id = uuid4().hex + return self.driver.set_subject_data(policy_id=policy_id, data_id=data_id, + category_id=category_id, value=value) + + @enforce(("read", "write"), "data") + def delete_subject_data(self, moon_user_id, policy_id, data_id, category_id=None): + # TODO (asteroide): check and/or delete assignments linked to that data + subject_assignments = self.get_subject_assignments(moon_user_id=moon_user_id, + policy_id=policy_id, + category_id=category_id) + if subject_assignments: + for assign_id in subject_assignments: + self.driver.delete_subject_assignment( + policy_id=subject_assignments[assign_id]['policy_id'], + subject_id=subject_assignments[assign_id]['subject_id'], + category_id=subject_assignments[assign_id]['category_id'], + data_id=subject_assignments[assign_id]['id']) + + rules = self.driver.get_rules(policy_id=policy_id) + if rules['rules']: + for rule in rules['rules']: + if data_id in rule['rule']: + self.driver.delete_rule(policy_id, rule['id']) + + return self.driver.delete_subject_data(policy_id=policy_id, category_id=category_id, + data_id=data_id) + + @enforce("read", "data") + def get_object_data(self, moon_user_id, policy_id, data_id=None, category_id=None): + available_metadata = self.get_available_metadata(moon_user_id=moon_user_id, + policy_id=policy_id) + results = [] + if not category_id: + for cat in available_metadata["object"]: + results.append(self.driver.get_object_data(policy_id=policy_id, data_id=data_id, + category_id=cat)) + if category_id and category_id in available_metadata["object"]: + results.append(self.driver.get_object_data(policy_id=policy_id, data_id=data_id, + category_id=category_id)) + return results + + @enforce(("read", "write"), "data") + def add_object_data(self, moon_user_id, policy_id, data_id=None, category_id=None, value=None): + logger.debug("add_object_data policyID {}".format(policy_id)) + + if not value or 'name' not in value or not value['name'].strip(): + raise exceptions.DataContentError + + if not policy_id or not self.get_policies(moon_user_id=moon_user_id, policy_id=policy_id): + raise exceptions.PolicyUnknown + + if not category_id or ( + not Managers.ModelManager.get_object_categories(moon_user_id=moon_user_id, + category_id=category_id)): + raise exceptions.ObjectCategoryUnknown + + self.__category_dependency_validation(moon_user_id, policy_id, category_id, + 'object_categories') + + if not data_id: + data_id = uuid4().hex + return self.driver.set_object_data(policy_id=policy_id, data_id=data_id, + category_id=category_id, value=value) + + @enforce(("read", "write"), "data") + def delete_object_data(self, moon_user_id, policy_id, data_id, category_id=None): + # TODO (asteroide): check and/or delete assignments linked to that data + object_assignments = self.get_object_assignments(moon_user_id=moon_user_id, + policy_id=policy_id, + category_id=category_id) + + if object_assignments: + for assign_id in object_assignments: + self.driver.delete_object_assignment( + policy_id=object_assignments[assign_id]['policy_id'], + object_id=object_assignments[assign_id]['object_id'], + category_id=object_assignments[assign_id]['category_id'], + data_id=object_assignments[assign_id]['id']) + + rules = self.driver.get_rules(policy_id=policy_id) + if rules['rules']: + for rule in rules['rules']: + if data_id in rule['rule']: + self.driver.delete_rule(policy_id, rule['id']) + + return self.driver.delete_object_data(policy_id=policy_id, category_id=category_id, + data_id=data_id) + + @enforce("read", "data") + def get_action_data(self, moon_user_id, policy_id, data_id=None, category_id=None): + available_metadata = self.get_available_metadata(moon_user_id=moon_user_id, + policy_id=policy_id) + results = [] + if not category_id: + for cat in available_metadata["action"]: + results.append(self.driver.get_action_data(policy_id=policy_id, data_id=data_id, + category_id=cat)) + if category_id and category_id in available_metadata["action"]: + if category_id.startswith("attributes:"): + data = {} + attrs = pip_driver.AttrsManager.get_objects( + moon_user_id="admin", + object_type=category_id.replace("attributes:", "")) + for item in attrs.keys(): + data[item] = {"id": item, "name": item, "description": item} + results.append( + { + "policy_id": policy_id, + "category_id": category_id, + "data": data, + } + ) + else: + results.append(self.driver.get_action_data(policy_id=policy_id, + data_id=data_id, + category_id=category_id)) + return results + + @enforce(("read", "write"), "data") + def add_action_data(self, moon_user_id, policy_id, data_id=None, category_id=None, value=None): + + logger.debug("add_action_data policyID {}".format(policy_id)) + + if not value or 'name' not in value or not value['name'].strip(): + raise exceptions.DataContentError + + if not policy_id or not self.get_policies(moon_user_id=moon_user_id, policy_id=policy_id): + raise exceptions.PolicyUnknown + + if not category_id or ( + not Managers.ModelManager.get_action_categories(moon_user_id=moon_user_id, + category_id=category_id)): + raise exceptions.ActionCategoryUnknown + + self.__category_dependency_validation(moon_user_id, policy_id, category_id, + 'action_categories') + + if not data_id: + data_id = uuid4().hex + return self.driver.set_action_data(policy_id=policy_id, data_id=data_id, + category_id=category_id, value=value) + + @enforce(("read", "write"), "data") + def delete_action_data(self, moon_user_id, policy_id, data_id, category_id=None): + # TODO (asteroide): check and/or delete assignments linked to that data + action_assignments = self.get_action_assignments(moon_user_id=moon_user_id, + policy_id=policy_id, + category_id=category_id) + if action_assignments: + for assign_id in action_assignments: + self.driver.delete_action_assignment( + policy_id=action_assignments[assign_id]['policy_id'], + action_id=action_assignments[assign_id]['action_id'], + category_id=action_assignments[assign_id]['category_id'], + data_id=action_assignments[assign_id]['id']) + + rules = self.driver.get_rules(policy_id=policy_id) + if rules['rules']: + for rule in rules['rules']: + if data_id in rule['rule']: + self.driver.delete_rule(policy_id, rule['id']) + + return self.driver.delete_action_data(policy_id=policy_id, category_id=category_id, + data_id=data_id) + + @enforce("read", "assignments") + def get_subject_assignments(self, moon_user_id, policy_id, subject_id=None, category_id=None): + return self.driver.get_subject_assignments(policy_id=policy_id, subject_id=subject_id, + category_id=category_id) + + @enforce(("read", "write"), "assignments") + def add_subject_assignment(self, moon_user_id, policy_id, subject_id, category_id, data_id): + + logger.debug("add_subject_assignment policyID {}".format(policy_id)) + if not category_id or ( + not Managers.ModelManager.get_subject_categories(moon_user_id=moon_user_id, + category_id=category_id)): + raise exceptions.SubjectCategoryUnknown + + self.__category_dependency_validation(moon_user_id, policy_id, category_id, + 'subject_categories') + + if not subject_id or ( + not self.get_subjects(moon_user_id=moon_user_id, policy_id=policy_id, + perimeter_id=subject_id)): + raise exceptions.SubjectUnknown + subjects_data = self.get_subject_data(moon_user_id=moon_user_id, policy_id=policy_id, + data_id=data_id, category_id=category_id) + if not data_id or not subjects_data or data_id not in subjects_data[0]['data']: + raise exceptions.DataUnknown + + return self.driver.add_subject_assignment(policy_id=policy_id, subject_id=subject_id, + category_id=category_id, data_id=data_id) + + @enforce(("read", "write"), "assignments") + def delete_subject_assignment(self, moon_user_id, policy_id, subject_id, category_id, data_id): + if policy_id: + return self.driver.delete_subject_assignment( + policy_id=policy_id, subject_id=subject_id, + category_id=category_id, data_id=data_id) + raise exceptions.PolicyUnknown + + @enforce("read", "assignments") + def get_object_assignments(self, moon_user_id, policy_id, object_id=None, category_id=None): + return self.driver.get_object_assignments(policy_id=policy_id, object_id=object_id, + category_id=category_id) + + @enforce(("read", "write"), "assignments") + def add_object_assignment(self, moon_user_id, policy_id, object_id, category_id, data_id): + + logger.debug("add_object_assignment policyID {}".format(policy_id)) + if not category_id or ( + not Managers.ModelManager.get_object_categories(moon_user_id=moon_user_id, + category_id=category_id)): + raise exceptions.ObjectCategoryUnknown + + self.__category_dependency_validation(moon_user_id, policy_id, category_id, + 'object_categories') + + if not object_id or ( + not self.get_objects(moon_user_id=moon_user_id, policy_id=policy_id, + perimeter_id=object_id)): + raise exceptions.ObjectUnknown + objects_data = self.get_object_data(moon_user_id=moon_user_id, policy_id=policy_id, + data_id=data_id, category_id=category_id) + if not data_id or not objects_data or data_id not in objects_data[0]['data']: + raise exceptions.DataUnknown + + return self.driver.add_object_assignment(policy_id=policy_id, object_id=object_id, + category_id=category_id, data_id=data_id) + + @enforce(("read", "write"), "assignments") + def delete_object_assignment(self, moon_user_id, policy_id, object_id, category_id, data_id): + if policy_id: + return self.driver.delete_object_assignment(policy_id=policy_id, object_id=object_id, + category_id=category_id, data_id=data_id) + raise exceptions.PolicyUnknown + + @enforce("read", "assignments") + def get_action_assignments(self, moon_user_id, policy_id, action_id=None, category_id=None): + return self.driver.get_action_assignments(policy_id=policy_id, action_id=action_id, + category_id=category_id) + + @enforce(("read", "write"), "assignments") + def add_action_assignment(self, moon_user_id, policy_id, action_id, category_id, data_id): + + logger.debug("add_action_assignment policyID {}".format(policy_id)) + + if not category_id or ( + not Managers.ModelManager.get_action_categories(moon_user_id=moon_user_id, + category_id=category_id)): + raise exceptions.ActionCategoryUnknown + + self.__category_dependency_validation(moon_user_id, policy_id, category_id, + 'action_categories') + + if not action_id or ( + not self.get_actions(moon_user_id=moon_user_id, policy_id=policy_id, + perimeter_id=action_id)): + raise exceptions.ActionUnknown + actions_data = self.get_action_data(moon_user_id=moon_user_id, policy_id=policy_id, + data_id=data_id, category_id=category_id) + if not data_id or not actions_data or data_id not in actions_data[0]['data']: + raise exceptions.DataUnknown + + return self.driver.add_action_assignment(policy_id=policy_id, action_id=action_id, + category_id=category_id, data_id=data_id) + + @enforce(("read", "write"), "assignments") + def delete_action_assignment(self, moon_user_id, policy_id, action_id, category_id, data_id): + if policy_id: + return self.driver.delete_action_assignment(policy_id=policy_id, action_id=action_id, + category_id=category_id, data_id=data_id) + raise exceptions.PolicyUnknown + + @enforce("read", "rules") + def get_rules(self, moon_user_id, policy_id, meta_rule_id=None, rule_id=None): + return self.driver.get_rules(policy_id=policy_id, meta_rule_id=meta_rule_id, + rule_id=rule_id) + + @enforce(("read", "write"), "policies") + def update_rule(self, moon_user_id, rule_id, value): + if not value or 'instructions' not in value: + raise exceptions.RuleContentError + + rule_list = self.driver.get_rules(policy_id=None, rule_id=rule_id) + if not rule_id or rule_id not in rule_list: + raise exceptions.RuleUnknown + + return self.driver.update_rule(rule_id=rule_id, value=value) + + @enforce(("read", "write"), "rules") + def add_rule(self, moon_user_id, policy_id, meta_rule_id, value): + + if not meta_rule_id or ( + not self.ModelManager.get_meta_rules(moon_user_id=moon_user_id, + meta_rule_id=meta_rule_id)): + raise exceptions.MetaRuleUnknown + + if not value or 'instructions' not in value: # TODO or not value['instructions']: + raise exceptions.MetaRuleContentError + + decision_exist = False + default_instruction = {"decision": "grant"} + + for instruction in value['instructions']: + if 'decision' in instruction: + decision_exist = True + if not instruction['decision']: + instruction['decision'] = default_instruction['decision'] + elif instruction['decision'].lower() not in ['grant', 'deny', 'continue']: + raise exceptions.RuleContentError("Invalid Decision") + + if not decision_exist: + value['instructions'].append(default_instruction) + + self.__dependencies_validation(moon_user_id, policy_id, meta_rule_id) + + self.__check_existing_rule(policy_id=policy_id, meta_rule_id=meta_rule_id, + moon_user_id=moon_user_id, rule_value=value) + + return self.driver.add_rule(policy_id=policy_id, meta_rule_id=meta_rule_id, value=value) + + def __check_existing_rule(self, moon_user_id, policy_id, meta_rule_id, rule_value): + + if not meta_rule_id: + raise exceptions.MetaRuleUnknown + + meta_rule = self.ModelManager.get_meta_rules(moon_user_id=moon_user_id, + meta_rule_id=meta_rule_id) + if not meta_rule: + raise exceptions.MetaRuleUnknown + + if len(meta_rule[meta_rule_id]['subject_categories']) + len( + meta_rule[meta_rule_id]['object_categories']) \ + + len(meta_rule[meta_rule_id]['action_categories']) > len(rule_value['rule']): + raise exceptions.RuleContentError(message="Missing Data") + + if len(meta_rule[meta_rule_id]['subject_categories']) + len( + meta_rule[meta_rule_id]['object_categories']) \ + + len(meta_rule[meta_rule_id]['action_categories']) < len(rule_value['rule']): + raise exceptions.MetaRuleContentError(message="Missing Data") + + temp_rule_data = list( + rule_value['rule'][0:len(meta_rule[meta_rule_id]['subject_categories'])]) + found_data_counter = 0 + start_sub = len(meta_rule[meta_rule_id]['subject_categories']) + + for sub_cat_id in meta_rule[meta_rule_id]['subject_categories']: + subjects_data = self.get_subject_data(moon_user_id=moon_user_id, + category_id=sub_cat_id, policy_id=policy_id) + if subjects_data: + found_data_counter = self.__validate_data_id(sub_cat_id, subjects_data[0]['data'], + temp_rule_data, + "Missing Subject_category " + , found_data_counter) + + if found_data_counter != len(meta_rule[meta_rule_id]['subject_categories']): + raise exceptions.RuleContentError(message="Missing Data") + + _index = start_sub + len(meta_rule[meta_rule_id]['object_categories']) + temp_rule_data = list(rule_value['rule'][start_sub:_index]) + found_data_counter = 0 + start_sub = start_sub + len(meta_rule[meta_rule_id]['object_categories']) + + for ob_cat_id in meta_rule[meta_rule_id]['object_categories']: + object_data = self.get_object_data(moon_user_id=moon_user_id, + category_id=ob_cat_id, policy_id=policy_id) + if object_data: + found_data_counter = self.__validate_data_id(ob_cat_id, object_data[0]['data'], + temp_rule_data, + "Missing Object_category ", + found_data_counter) + + if found_data_counter != len(meta_rule[meta_rule_id]['object_categories']): + raise exceptions.RuleContentError(message="Missing Data") + + _index = start_sub + len(meta_rule[meta_rule_id]['action_categories']) + temp_rule_data = list(rule_value['rule'][start_sub:_index]) + found_data_counter = 0 + + for act_cat_id in meta_rule[meta_rule_id]['action_categories']: + action_data = self.get_action_data(moon_user_id=moon_user_id, category_id=act_cat_id, + policy_id=policy_id) + if action_data: + found_data_counter = self.__validate_data_id(act_cat_id, action_data[0]['data'], + temp_rule_data, + "Missing Action_category ", + found_data_counter) + + # Note: adding count of data linked to a global attribute + found_data_counter += len(list(filter(lambda x: "attributes:" in x, temp_rule_data))) + if found_data_counter != len(meta_rule[meta_rule_id]['action_categories']): + raise exceptions.RuleContentError(message="Missing Data") + + @staticmethod + def __validate_data_id(cat_id, data_ids, temp_rule_data, error_msg, found_data_counter): + for ID in data_ids: + if ID in temp_rule_data: + temp_rule_data.remove(ID) + found_data_counter += 1 + # if no data id found in the rule, so rule not valid + if found_data_counter < 1: + raise exceptions.RuleContentError(message=error_msg + cat_id) + return found_data_counter + + @enforce(("read", "write"), "rules") + def delete_rule(self, moon_user_id, policy_id, rule_id): + return self.driver.delete_rule(policy_id=policy_id, rule_id=rule_id) + + @enforce("read", "meta_data") + def get_available_metadata(self, moon_user_id, policy_id): + categories = { + "subject": [], + "object": [], + "action": [] + } + policy = self.driver.get_policies(policy_id=policy_id) + if not policy: + raise exceptions.PolicyUnknown + model_id = policy[policy_id]["model_id"] + model = Managers.ModelManager.get_models(moon_user_id=moon_user_id, model_id=model_id) + try: + meta_rule_list = model[model_id]["meta_rules"] + for meta_rule_id in meta_rule_list: + meta_rule = Managers.ModelManager.get_meta_rules(moon_user_id=moon_user_id, + meta_rule_id=meta_rule_id) + categories["subject"].extend(meta_rule[meta_rule_id]["subject_categories"]) + categories["object"].extend(meta_rule[meta_rule_id]["object_categories"]) + categories["action"].extend(meta_rule[meta_rule_id]["action_categories"]) + finally: + return categories + + def __dependencies_validation(self, moon_user_id, policy_id, meta_rule_id=None): + + policies = self.get_policies(moon_user_id=moon_user_id, policy_id=policy_id) + if not policy_id or (not policies): + raise exceptions.PolicyUnknown + + policy_content = policies[next(iter(policies))] + model_id = policy_content['model_id'] + models = Managers.ModelManager.get_models(moon_user_id=moon_user_id, model_id=model_id) + if not model_id or not models: + raise exceptions.ModelUnknown + + model_content = models[next(iter(models))] + if meta_rule_id: + meta_rule_exists = False + + for model_meta_rule_id in model_content['meta_rules']: + if model_meta_rule_id == meta_rule_id: + meta_rule_exists = True + break + + if not meta_rule_exists: + raise exceptions.MetaRuleNotLinkedWithPolicyModel + + meta_rule = self.ModelManager.get_meta_rules(moon_user_id=moon_user_id, + meta_rule_id=meta_rule_id) + meta_rule_content = meta_rule[next(iter(meta_rule))] + if (not meta_rule_content['subject_categories']) or \ + (not meta_rule_content['object_categories']) or \ + (not meta_rule_content['action_categories']): + raise exceptions.MetaRuleContentError + return model_content + + def __category_dependency_validation(self, moon_user_id, policy_id, category_id, category_key): + model = self.__dependencies_validation(moon_user_id=moon_user_id, policy_id=policy_id) + category_found = False + for model_meta_rule_id in model['meta_rules']: + meta_rule = self.ModelManager.get_meta_rules(moon_user_id=moon_user_id, + meta_rule_id=model_meta_rule_id) + meta_rule_content = meta_rule[next(iter(meta_rule))] + if meta_rule_content[category_key] and category_id in meta_rule_content[category_key]: + category_found = True + break + + if not category_found: + raise exceptions.CategoryNotAssignedMetaRule + + def __check_blank_model(self, model): + if 'meta_rules' not in model or not model['meta_rules']: + raise exceptions.MetaRuleUnknown + for meta_rule_id in model['meta_rules']: + self.__check_blank_meta_rule(meta_rule_id) + + def __check_blank_meta_rule(self, meta_rule_id): + meta_rule = self.driver.get_meta_rules(meta_rule_id=meta_rule_id) + if not meta_rule: + return exceptions.MetaRuleUnknown + meta_rule_content = meta_rule[next(iter(meta_rule))] + if (not meta_rule_content['subject_categories']) or ( + not meta_rule_content['object_categories']) or ( + not meta_rule_content['action_categories']): + raise exceptions.MetaRuleContentError + diff --git a/moon_manager/moon_manager/api/db/slave.py b/moon_manager/moon_manager/api/db/slave.py new file mode 100644 index 00000000..f5ac9189 --- /dev/null +++ b/moon_manager/moon_manager/api/db/slave.py @@ -0,0 +1,55 @@ +# 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 uuid import uuid4 +import logging +from moon_utilities.security_functions import enforce +from moon_manager.api.db.managers import Managers +from moon_utilities import exceptions + +logger = logging.getLogger("moon.db.api.slave") + + +class SlaveManager(Managers): + + def __init__(self, connector=None): + self.driver = connector.driver + Managers.SlaveManager = self + + @enforce(("read", "write"), "slave") + def update_slave(self, moon_user_id, slave_id, value): + if slave_id not in self.driver.get_slaves(slave_id=slave_id): + raise exceptions.SlaveNameUnknown + + return self.driver.update_slave(slave_id=slave_id, value=value) + + @enforce(("read", "write"), "slave") + def delete_slave(self, moon_user_id, slave_id): + if slave_id not in self.driver.get_slaves(slave_id=slave_id): + raise exceptions.SlaveNameUnknown + return self.driver.delete_slave(slave_id=slave_id) + + @enforce(("read", "write"), "slave") + def add_slave(self, moon_user_id, slave_id=None, value=None): + if not value or 'name' not in value or not value['name'].strip(): + raise exceptions.SlaveNameUnknown + + if slave_id in self.driver.get_slaves(slave_id=slave_id): + raise exceptions.SlaveExisting + if not slave_id: + slave_id = uuid4().hex + + return self.driver.add_slave(slave_id=slave_id, value=value) + + @enforce("read", "slave") + def get_slaves(self, moon_user_id, slave_id=None): + return self.driver.get_slaves(slave_id=slave_id) diff --git a/moon_manager/moon_manager/api/generic.py b/moon_manager/moon_manager/api/generic.py deleted file mode 100644 index 721f6213..00000000 --- a/moon_manager/moon_manager/api/generic.py +++ /dev/null @@ -1,144 +0,0 @@ -# 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'. -""" -Those API are helping API used to manage the Moon platform. -""" - -from flask_restful import Resource, request -import logging -import moon_manager.api -from python_moonutilities.security_functions import check_auth - -__version__ = "4.3.2" - -logger = logging.getLogger("moon.manager.api." + __name__) - - -class Status(Resource): - """ - Endpoint for status requests - """ - - __urls__ = ( - "/status", - "/status/", - "/status/" - ) - - def get(self, component_id=None): - """Retrieve status of all components - - :return: { - "orchestrator": { - "status": "Running" - }, - "security_router": { - "status": "Running" - } - } - """ - raise NotImplemented - - -class Logs(Resource): - """ - Endpoint for logs requests - """ - - __urls__ = ( - "/logs", - "/logs/", - "/logs/" - ) - - def get(self, component_id=None): - """Get logs from the Moon platform - - :param component_id: the ID of the component your are looking for (optional) - :return: [ - "2015-04-15-13:45:20 - "2015-04-15-13:45:21 - "2015-04-15-13:45:22 - "2015-04-15-13:45:23 - ] - """ - filter_str = request.args.get('filter', '') - from_str = request.args.get('from', '') - to_str = request.args.get('to', '') - event_number = request.args.get('event_number', '') - try: - event_number = int(event_number) - except ValueError: - event_number = None - args = dict() - args["filter"] = filter_str - args["from"] = from_str - args["to"] = to_str - args["event_number"] = event_number - - raise NotImplemented - - -class API(Resource): - """ - Endpoint for API requests - """ - - __urls__ = ( - "/api", - "/api/", - "/api/", - "/api//", - "/api//" - ) - - @check_auth - def get(self, group_id="", endpoint_id="", user_id=""): - """Retrieve all API endpoints or a specific endpoint if endpoint_id is given - - :param group_id: the name of one existing group (ie generic, ...) - :param endpoint_id: the name of one existing component (ie Logs, Status, ...) - :return: { - "group_name": { - "endpoint_name": { - "description": "a description", - "methods": { - "get": "description of the HTTP method" - }, - "urls": ('/api', '/api/', '/api/') - } - } - """ - __methods = ("get", "post", "put", "delete", "options", "patch") - api_list = filter(lambda x: "__" not in x, dir(moon_manager.api)) - api_desc = dict() - for api_name in api_list: - api_desc[api_name] = {} - group_api_obj = eval("moon_manager.api.{}".format(api_name)) - api_desc[api_name]["description"] = group_api_obj.__doc__ - if "__version__" in dir(group_api_obj): - api_desc[api_name]["version"] = group_api_obj.__version__ - object_list = list(filter(lambda x: "__" not in x, - dir(group_api_obj))) - for obj in map(lambda x: eval("moon_manager.api.{}.{}".format(api_name, x)), - object_list): - if "__urls__" in dir(obj): - api_desc[api_name][obj.__name__] = dict() - api_desc[api_name][obj.__name__]["urls"] = obj.__urls__ - api_desc[api_name][obj.__name__]["methods"] = dict() - for _method in filter(lambda x: x in __methods, dir(obj)): - docstring = eval( - "moon_manager.api.{}.{}.{}.__doc__".format(api_name, obj.__name__, - _method)) - api_desc[api_name][obj.__name__]["methods"][_method] = docstring - api_desc[api_name][obj.__name__]["description"] = str(obj.__doc__) - if group_id in api_desc: - if endpoint_id in api_desc[group_id]: - return {group_id: {endpoint_id: api_desc[group_id][endpoint_id]}} - elif len(endpoint_id) > 0: - logger.error("Unknown endpoint_id {}".format(endpoint_id)) - return {"error": "Unknown endpoint_id {}".format(endpoint_id)} - return {group_id: api_desc[group_id]} - return api_desc diff --git a/moon_manager/moon_manager/api/information/__init__.py b/moon_manager/moon_manager/api/information/__init__.py new file mode 100644 index 00000000..582be686 --- /dev/null +++ b/moon_manager/moon_manager/api/information/__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_manager/moon_manager/api/information/global_attrs.py b/moon_manager/moon_manager/api/information/global_attrs.py new file mode 100644 index 00000000..21b21445 --- /dev/null +++ b/moon_manager/moon_manager/api/information/global_attrs.py @@ -0,0 +1,145 @@ +# 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. + +""" +API to gather information from external component like OpenStack +""" + +from uuid import uuid4 +import logging +from moon_utilities.security_functions import enforce +from moon_manager.api.information.managers import Managers + +LOGGER = logging.getLogger("moon.manager.information.global_attrs") + + +class GlobalAttrsManager(Managers): + """ + Manager use to get information from external components + """ + + def __init__(self, connector=None): + self.driver = connector.driver + Managers.GlobalAttrsManager = self + + def set_auth(self, **kwargs): + """Set authorizations if necessary + + :param kwargs: arguments which are necessary to login to the server + :return: headers to use + """ + return self.driver.set_auth(**kwargs) + + def unset_auth(self, **kwargs): + """Unset the authorization is necessary + + :param kwargs: arguments which are necessary to logout to the server + :return: headers to use + """ + return self.driver.unset_auth(**kwargs) + + @enforce("read", "pip") + def get_users(self, user_id=None, **kwargs): + """List users in the server + + :param user_id: the user name or user ID + :param kwargs: all arguments necessary to list users + :return: a list of users + """ + return self.driver.get_users(user_id=user_id, **kwargs) + + @enforce("write", "pip") + def add_user(self, user_id=None, **kwargs): + """Add a user in the server + + :param user_id: the user name or user ID + :param kwargs: all arguments necessary to add a user + :return: the user added + """ + if not user_id: + user_id = uuid4().hex + return self.driver.add_user(user_id=user_id, **kwargs) + + @enforce(("read", "write"), "pip") + def update_user(self, user_id, **kwargs): + """Update a user in the server + + :param user_id: the user name or user ID + :param kwargs: all arguments necessary to update the user + :return: the user updated + """ + return self.driver.update_user(user_id=user_id, **kwargs) + + @enforce("write", "pip") + def delete_user(self, user_id, **kwargs): + """Delete a user in the server + + :param user_id: the user name or user ID + :param kwargs: all arguments necessary to delete the user + :return: True if the user has been deleted + """ + return self.driver.delete_user(user_id=user_id, **kwargs) + + @enforce("read", "pip") + def get_objects(self, object_id=None, object_type=None, **kwargs): + """List objects in the server + + :param object_id: the object name or user ID + :param object_type: the object type (project, vms, ...) + :param kwargs: all arguments necessary to list the object + :return: a list of objects + """ + return self.driver.get_objects(object_id=object_id, object_type=object_type, **kwargs) + + @enforce("read", "pip") + def get_object(self, object_type, **kwargs): + """List objects in the server + + :param object_type: the object type (project, vms, ...) + :param kwargs: all arguments necessary to list the object + :return: a list of objects + """ + return self.driver.get_object(object_type=object_type, **kwargs) + + @enforce("write", "pip") + def add_object(self, object_id=None, object_type=None, **kwargs): + """Add an object in the server + + :param object_id: the object name or user ID + :param object_type: the object type (project, vms, ...) + :param kwargs: all arguments necessary to add the object + :return: the object added + """ + if not object_id: + object_id = uuid4().hex + return self.driver.add_object(object_id=object_id, object_type=object_type, **kwargs) + + @enforce(("read", "write"), "pip") + def update_object(self, object_id, object_type=None, **kwargs): + """Update an object in the server + + :param object_id: the object name or user ID + :param object_type: the object type (project, vms, ...) + :param kwargs: all arguments necessary to update the object + :return: the object updated + """ + return self.driver.update_object(object_id=object_id, object_type=object_type, **kwargs) + + @enforce("write", "pip") + def delete_object(self, object_id=None, object_type=None, **kwargs): + """Delete an object in the server + + :param object_id: the object name or user ID + :param object_type: the object type (project, vms, ...) + :param kwargs: all arguments necessary to delete the object + :return: True if the object has been deleted + """ + return self.driver.delete_object(object_id=object_id, object_type=object_type, **kwargs) diff --git a/moon_manager/moon_manager/api/information/information.py b/moon_manager/moon_manager/api/information/information.py new file mode 100644 index 00000000..132b8bce --- /dev/null +++ b/moon_manager/moon_manager/api/information/information.py @@ -0,0 +1,106 @@ +# 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. + +""" +API to gather information from external component like OpenStack +""" + +from uuid import uuid4 +import logging +from moon_utilities.security_functions import enforce +from moon_manager.api.information.managers import Managers + +LOGGER = logging.getLogger("moon.manager.information.api.information") + + +class InformationManager(Managers): + """ + Manager use to get information from external components + """ + + def __init__(self, connector=None): + self.driver = connector.driver + Managers.InformationManager = self + + def set_auth(self, **kwargs): + """Set authorizations if necessary + + :param kwargs: arguments which are necessary to login to the server + :return: headers to use + """ + return self.driver.set_auth(**kwargs) + + def unset_auth(self, **kwargs): + """Unset the authorization is necessary + + :param kwargs: arguments which are necessary to logout to the server + :return: headers to use + """ + return self.driver.unset_auth(**kwargs) + + @enforce("read", "pip") + def get_items(self, item_id=None, **kwargs): + """List items in the server + + :param item_id: the item name or item ID + :param kwargs: all arguments necessary to list items + :return: a list of items + """ + return self.driver.get_items(item_id=item_id, **kwargs) + + @enforce("write", "pip") + def add_item(self, item_id=None, **kwargs): + """Add a item in the server + + :param item_id: the item name or item ID + :param kwargs: all arguments necessary to add a item + :return: the item added + """ + if not item_id: + item_id = uuid4().hex + return self.driver.add_item(item_id=item_id, **kwargs) + + @enforce(("read", "write"), "pip") + def update_item(self, item_id, **kwargs): + """Update a item in the server + + :param item_id: the item name or item ID + :param kwargs: all arguments necessary to update the item + :return: the item updated + """ + return self.driver.update_item(item_id=item_id, **kwargs) + + @enforce("write", "pip") + def delete_item(self, item_id, **kwargs): + """Delete a item in the server + + :param item_id: the item name or item ID + :param kwargs: all arguments necessary to delete the item + :return: True if the item has been deleted + """ + return self.driver.delete_item(item_id=item_id, **kwargs) + + @enforce("read", "pip") + def get_projects(self): + """List projects in the server + + :return: the list of projects + """ + return self.driver.get_projects() + + @enforce("write", "pip") + def create_project(self, **tenant_dict): + """Create a project in the server + + :param tenant_dict: all arguments necessary to create a project + :return: True if the item has been deleted + """ + return self.driver.create_project(**tenant_dict) diff --git a/moon_manager/moon_manager/api/information/managers.py b/moon_manager/moon_manager/api/information/managers.py new file mode 100644 index 00000000..23aff8f5 --- /dev/null +++ b/moon_manager/moon_manager/api/information/managers.py @@ -0,0 +1,19 @@ +# 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. + +""" +Group all managers for this API +""" + + +class Managers(object): + """Object that links managers together""" + InformationManager = None diff --git a/moon_manager/moon_manager/api/json_export.py b/moon_manager/moon_manager/api/json_export.py index 069e5884..06a7ba71 100644 --- a/moon_manager/moon_manager/api/json_export.py +++ b/moon_manager/moon_manager/api/json_export.py @@ -1,279 +1,37 @@ -# Copyright 2018 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'. +# Software Name: MOON -import logging -from flask_restful import Resource -from python_moonutilities.security_functions import check_auth -from python_moondb.core import PDPManager -from python_moondb.core import PolicyManager -from python_moondb.core import ModelManager -from moon_manager.api.json_utils import JsonUtils, BaseException - -__version__ = "4.5.0" - -logger = logging.getLogger("moon.manager.api." + __name__) - - -class JsonExport(Resource): - __urls__ = ( - "/export", - "/export/", - ) - - def _export_rules(self, json_content): - policies = PolicyManager.get_policies(self._user_id) - rules_array = [] - - for policy_key in policies: - rules = PolicyManager.get_rules(self._user_id, policy_key) - rules = rules["rules"] - # logger.info(rules) - for rule in rules: - rule_dict = dict() - JsonUtils.copy_field_if_exists(rule, rule_dict, "instructions", dict) - JsonUtils.copy_field_if_exists(rule, rule_dict, "enabled", True) - JsonUtils.convert_id_to_name(rule["meta_rule_id"], rule_dict, "meta_rule", - "meta_rule", ModelManager, self._user_id) - JsonUtils.convert_id_to_name(policy_key, rule_dict, "policy", "policy", - PolicyManager, self._user_id) - ids = rule["rule"] - rule_description = dict() - meta_rule = ModelManager.get_meta_rules(self._user_id, rule["meta_rule_id"]) - meta_rule = [v for v in meta_rule.values()] - meta_rule = meta_rule[0] - index_subject_data = len(meta_rule["subject_categories"]) - 1 - index_object_data = len(meta_rule["subject_categories"]) + len( - meta_rule["object_categories"]) - 1 - index_action_data = len(meta_rule["subject_categories"]) + len( - meta_rule["object_categories"]) + len(meta_rule["action_categories"]) - 1 - ids_subject_data = [ids[0]] if len(meta_rule["subject_categories"]) == 1 else ids[ - 0:index_subject_data] - ids_object_data = [ids[index_object_data]] if len( - meta_rule["object_categories"]) == 1 else ids[ - index_subject_data + 1:index_object_data] - ids_action_date = [ids[index_action_data]] if len( - meta_rule["action_categories"]) == 1 else ids[ - index_object_data + 1:index_action_data] - JsonUtils.convert_ids_to_names(ids_subject_data, rule_description, "subject_data", - "subject_data", PolicyManager, self._user_id, - policy_key) - JsonUtils.convert_ids_to_names(ids_object_data, rule_description, "object_data", - "object_data", PolicyManager, self._user_id, - policy_key) - JsonUtils.convert_ids_to_names(ids_action_date, rule_description, "action_data", - "action_data", PolicyManager, self._user_id, - policy_key) - rule_dict["rule"] = rule_description - rules_array.append(rule_dict) - - if len(rules_array) > 0: - json_content['rules'] = rules_array - - def _export_meta_rules(self, json_content): - meta_rules = ModelManager.get_meta_rules(self._user_id) - meta_rules_array = [] - # logger.info(meta_rules) - for meta_rule_key in meta_rules: - # logger.info(meta_rules[meta_rule_key]) - meta_rule_dict = dict() - JsonUtils.copy_field_if_exists(meta_rules[meta_rule_key], meta_rule_dict, "name", str) - JsonUtils.copy_field_if_exists(meta_rules[meta_rule_key], meta_rule_dict, "description", - str) - JsonUtils.convert_ids_to_names(meta_rules[meta_rule_key]["subject_categories"], - meta_rule_dict, "subject_categories", "subject_category", - ModelManager, self._user_id) - JsonUtils.convert_ids_to_names(meta_rules[meta_rule_key]["object_categories"], - meta_rule_dict, "object_categories", "object_category", - ModelManager, self._user_id) - JsonUtils.convert_ids_to_names(meta_rules[meta_rule_key]["action_categories"], - meta_rule_dict, "action_categories", "action_category", - ModelManager, self._user_id) - logger.info("Exporting meta rule {}".format(meta_rule_dict)) - meta_rules_array.append(meta_rule_dict) - if len(meta_rules_array) > 0: - json_content['meta_rules'] = meta_rules_array +# Version: 5.4 - def _export_subject_object_action_assignments(self, type_element, json_content): - export_method_data = getattr(PolicyManager, 'get_' + type_element + '_assignments') - policies = PolicyManager.get_policies(self._user_id) - element_assignments_array = [] - for policy_key in policies: - assignments = export_method_data(self._user_id, policy_key) - # logger.info(assignments) - for assignment_key in assignments: - assignment_dict = dict() - JsonUtils.convert_id_to_name(assignments[assignment_key][type_element + "_id"], - assignment_dict, type_element, type_element, - PolicyManager, self._user_id, policy_key) - JsonUtils.convert_id_to_name(assignments[assignment_key]["category_id"], - assignment_dict, "category", - type_element + "_category", ModelManager, - self._user_id, policy_key) - JsonUtils.convert_ids_to_names(assignments[assignment_key]["assignments"], - assignment_dict, "assignments", - type_element + "_data", PolicyManager, self._user_id, - policy_key) - element_assignments_array.append(assignment_dict) - logger.info("Exporting {} assignment {}".format(type_element, assignment_dict)) - if len(element_assignments_array) > 0: - json_content[type_element + '_assignments'] = element_assignments_array +# SPDX-FileCopyrightText: Copyright (c) 2018-2020 Orange and its contributors +# SPDX-License-Identifier: Apache-2.0 - def _export_subject_object_action_datas(self, type_element, json_content): - export_method_data = getattr(PolicyManager, 'get_' + type_element + '_data') - policies = PolicyManager.get_policies(self._user_id) - element_datas_array = [] - for policy_key in policies: - datas = export_method_data(self._user_id, policy_key) - # logger.info("data found : {}".format(datas)) - for data_group in datas: - policy_id = data_group["policy_id"] - category_id = data_group["category_id"] - # logger.info(data_group["data"]) - for data_key in data_group["data"]: - data_dict = dict() - if type_element == 'subject': - JsonUtils.copy_field_if_exists(data_group["data"][data_key], data_dict, - "name", str) - JsonUtils.copy_field_if_exists(data_group["data"][data_key], data_dict, - "description", str) - else: - JsonUtils.copy_field_if_exists(data_group["data"][data_key], data_dict, - "name", str) - JsonUtils.copy_field_if_exists(data_group["data"][data_key], data_dict, - "description", str) +# 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. - JsonUtils.convert_id_to_name(policy_id, data_dict, "policy", "policy", - PolicyManager, self._user_id) - JsonUtils.convert_id_to_name(category_id, data_dict, "category", - type_element + "_category", ModelManager, - self._user_id, policy_key) - logger.info("Exporting {} data {}".format(type_element, data_dict)) - element_datas_array.append(data_dict) - if len(element_datas_array) > 0: - json_content[type_element + '_data'] = element_datas_array - - def _export_subject_object_action_categories(self, type_element, json_content): - export_method = getattr(ModelManager, 'get_' + type_element + '_categories') - element_categories = export_method(self._user_id) - element_categories_array = [] - for element_category_key in element_categories: - element_category = dict() - JsonUtils.copy_field_if_exists(element_categories[element_category_key], - element_category, "name", str) - JsonUtils.copy_field_if_exists(element_categories[element_category_key], - element_category, "description", str) - element_categories_array.append(element_category) - logger.info("Exporting {} category {}".format(type_element, element_category)) - if len(element_categories_array) > 0: - json_content[type_element + '_categories'] = element_categories_array - - def _export_subject_object_action(self, type_element, json_content): - export_method = getattr(PolicyManager, 'get_' + type_element + 's') - policies = PolicyManager.get_policies(self._user_id) - element_dict = dict() - elements_array = [] - for policy_key in policies: - elements = export_method(self._user_id, policy_key) - for element_key in elements: - # logger.info("Exporting {}".format(elements[element_key])) - element = dict() - JsonUtils.copy_field_if_exists(elements[element_key], element, "name", str) - JsonUtils.copy_field_if_exists(elements[element_key], element, "description", str) - JsonUtils.copy_field_if_exists(elements[element_key], element, "extra", dict) - if element["name"] not in element_dict: - element["policies"] = [] - element_dict[element["name"]] = element - current_element = element_dict[element["name"]] - current_element["policies"].append({"name": JsonUtils.convert_id_to_name_string( - policy_key, "policy", PolicyManager, self._user_id)}) - - for key in element_dict: - logger.info("Exporting {} {}".format(type_element, element_dict[key])) - elements_array.append(element_dict[key]) - - if len(elements_array) > 0: - json_content[type_element + 's'] = elements_array - - def _export_policies(self, json_content): - policies = PolicyManager.get_policies(self._user_id) - policies_array = [] - for policy_key in policies: - policy = dict() - JsonUtils.copy_field_if_exists(policies[policy_key], policy, "name", str) - JsonUtils.copy_field_if_exists(policies[policy_key], policy, "genre", str) - JsonUtils.copy_field_if_exists(policies[policy_key], policy, "description", str) - JsonUtils.convert_id_to_name(policies[policy_key]["model_id"], policy, "model", "model", - ModelManager, self._user_id) - logger.info("Exporting policy {}".format(policy)) - policies_array.append(policy) - if len(policies_array) > 0: - json_content["policies"] = policies_array - - def _export_models(self, json_content): - models = ModelManager.get_models(self._user_id) - models_array = [] - for model_key in models: - model = dict() - JsonUtils.copy_field_if_exists(models[model_key], model, "name", str) - JsonUtils.copy_field_if_exists(models[model_key], model, "description", str) - # logger.info(models[model_key]["meta_rules"]) - JsonUtils.convert_ids_to_names(models[model_key]["meta_rules"], model, "meta_rules", - "meta_rule", ModelManager, self._user_id) - logger.info("Exporting model {}".format(model)) - models_array.append(model) - if len(models_array) > 0: - json_content["models"] = models_array - - def _export_pdps(self, json_content): - pdps = PDPManager.get_pdp(self._user_id) - pdps_array = [] - for pdp_key in pdps: - logger.info("Exporting pdp {}".format(pdps[pdp_key])) - pdps_array.append(pdps[pdp_key]) - if len(pdps_array) > 0: - json_content["pdps"] = pdps_array +import hug +import logging +from moon_manager import db_driver as driver +from moon_utilities.auth_functions import api_key_authentication +from moon_utilities.json_utils import JsonExport - def _export_json(self, user_id): - self._user_id = user_id - json_content = dict() +logger = logging.getLogger("moon.manager.api." + __name__) - logger.info("Exporting pdps...") - self._export_pdps(json_content) - logger.info("Exporting policies...") - self._export_policies(json_content) - logger.info("Exporting models...") - self._export_models(json_content) - # export subjects, subject_data, subject_categories, subject_assignements idem for object and action - list_element = [{"key": "subject"}, {"key": "object"}, {"key": "action"}] - for elt in list_element: - logger.info("Exporting {}s...".format(elt["key"])) - self._export_subject_object_action(elt["key"], json_content) - logger.info("Exporting {} categories...".format(elt["key"])) - self._export_subject_object_action_categories(elt["key"], json_content) - logger.info("Exporting {} data...".format(elt["key"])) - self._export_subject_object_action_datas(elt["key"], json_content) - logger.info("Exporting {} assignments...".format(elt["key"])) - self._export_subject_object_action_assignments(elt["key"], json_content) - logger.info("Exporting meta rules...") - self._export_meta_rules(json_content) - logger.info("Exporting rules...") - self._export_rules(json_content) - return json_content +class Export(object): - @check_auth - def get(self, user_id=None): + @staticmethod + @hug.get("/export", requires=api_key_authentication) + def get(authed_user: hug.directives.user = None): """Import file. - :param user_id: user ID who do the request + :param authed_user: user ID who do the request :return: { } :internal_api: """ - json_file = self._export_json(user_id) - logger.info(json_file) + json_file = JsonExport(driver_name="db", driver=driver).export_json( + moon_user_id=authed_user) return {"content": json_file} diff --git a/moon_manager/moon_manager/api/json_import.py b/moon_manager/moon_manager/api/json_import.py index 05f4a0c0..922c5c2c 100644 --- a/moon_manager/moon_manager/api/json_import.py +++ b/moon_manager/moon_manager/api/json_import.py @@ -1,27 +1,23 @@ -# 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'. +# Software Name: MOON -from flask import request -from flask_restful import Resource -import flask_restful -from flask import abort +# 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 python_moonutilities.security_functions import check_auth -from python_moonutilities import exceptions -import logging -import json -from moon_manager.api.base_exception import BaseException -from moon_manager.api.json_utils import JsonUtils, UnknownName -from python_moondb.core import PDPManager -from python_moondb.core import PolicyManager -from python_moondb.core import ModelManager +import hug +import logging +from moon_manager import db_driver as driver +from moon_utilities.json_utils import JsonImport +from moon_utilities.auth_functions import api_key_authentication -__version__ = "4.5.0" -logger = logging.getLogger("moon.manager.api." + __name__) +LOGGER = logging.getLogger("moon.manager.api." + __name__) INST_CALLBACK = 0 DATA_CALLBACK = 1 @@ -29,556 +25,20 @@ ASSIGNMENT_CALLBACK = 2 CATEGORIES_CALLBACK = 3 -class ForbiddenOverride(BaseException): - def __init__(self, message): - # Call the base class constructor with the parameters it needs - super(ForbiddenOverride, self).__init__(message) - - -class UnknownPolicy(BaseException): - def __init__(self, message): - # Call the base class constructor with the parameters it needs - super(UnknownPolicy, self).__init__(message) - - -class UnknownModel(BaseException): - def __init__(self, message): - # Call the base class constructor with the parameters it needs - super(UnknownModel, self).__init__(message) - - -class UnknownData(BaseException): - def __init__(self, message): - # Call the base class constructor with the parameters it needs - super(UnknownData, self).__init__(message) - - -class MissingPolicy(BaseException): - def __init__(self, message): - # Call the base class constructor with the parameters it needs - super(MissingPolicy, self).__init__(message) - - -class InvalidJson(BaseException): - def __init__(self, message): - # Call the base class constructor with the parameters it needs - super(InvalidJson, self).__init__(message) - - -class JsonImport(Resource): - __urls__ = ( - "/import", - "/import/", - ) - - def _reorder_rules_ids(self, rule, ordered_perimeter_categories_ids, json_data_ids, policy_id, - get_function): - ordered_json_ids = [None] * len(ordered_perimeter_categories_ids) - for json_id in json_data_ids: - data = get_function(self._user_id, policy_id, data_id=json_id) - data = data[0] - if data["category_id"] not in ordered_perimeter_categories_ids: - raise InvalidJson( - "The category id {} of the rule {} does not match the meta rule".format( - data["category_id"], rule)) - if ordered_json_ids[ - ordered_perimeter_categories_ids.index(data["category_id"])] is not None: - raise InvalidJson( - "The category id {} of the rule {} shall not be used twice in the same rule".format( - data["category_id"], rule)) - ordered_json_ids[ordered_perimeter_categories_ids.index(data["category_id"])] = json_id - logger.info(ordered_json_ids) - return ordered_json_ids - - def _import_rules(self, json_rules): - if not isinstance(json_rules, list): - raise InvalidJson("rules shall be a list!") - - for json_rule in json_rules: - json_to_use = dict() - JsonUtils.copy_field_if_exists(json_rule, json_to_use, "instructions", str) - JsonUtils.copy_field_if_exists(json_rule, json_to_use, "enabled", bool, - default_value=True) - - json_ids = dict() - JsonUtils.convert_name_to_id(json_rule, json_ids, "policy", "policy_id", "policy", - PolicyManager, self._user_id) - JsonUtils.convert_name_to_id(json_rule, json_to_use, "meta_rule", "meta_rule_id", - "meta_rule", ModelManager, self._user_id) - json_subject_ids = dict() - json_object_ids = dict() - json_action_ids = dict() - JsonUtils.convert_names_to_ids(json_rule["rule"], json_subject_ids, "subject_data", - "subject", "subject_data", PolicyManager, self._user_id, - json_ids["policy_id"]) - JsonUtils.convert_names_to_ids(json_rule["rule"], json_object_ids, "object_data", - "object", "object_data", PolicyManager, self._user_id, - json_ids["policy_id"]) - JsonUtils.convert_names_to_ids(json_rule["rule"], json_action_ids, "action_data", - "action", "action_data", PolicyManager, self._user_id, - json_ids["policy_id"]) - - meta_rule = ModelManager.get_meta_rules(self._user_id, json_to_use["meta_rule_id"]) - meta_rule = [v for v in meta_rule.values()] - meta_rule = meta_rule[0] - - json_to_use_rule = self._reorder_rules_ids(json_rule, meta_rule["subject_categories"], - json_subject_ids["subject"], - json_ids["policy_id"], - PolicyManager.get_subject_data) - json_to_use_rule = json_to_use_rule + self._reorder_rules_ids(json_rule, meta_rule[ - "object_categories"], json_object_ids["object"], json_ids["policy_id"], - PolicyManager.get_object_data) - json_to_use_rule = json_to_use_rule + self._reorder_rules_ids(json_rule, meta_rule[ - "action_categories"], json_action_ids["action"], json_ids["policy_id"], - PolicyManager.get_action_data) - json_to_use["rule"] = json_to_use_rule - try: - logger.debug("Adding / updating a rule from json {}".format(json_to_use)) - PolicyManager.add_rule(self._user_id, json_ids["policy_id"], - json_to_use["meta_rule_id"], json_to_use) - except exceptions.RuleExisting: - pass - except exceptions.PolicyUnknown: - raise UnknownPolicy("Unknown policy with id {}".format(json_ids["policy_id"])) - - def _import_meta_rules(self, json_meta_rules): - logger.info("Input meta rules : {}".format(json_meta_rules)) - for json_meta_rule in json_meta_rules: - json_to_use = dict() - JsonUtils.copy_field_if_exists(json_meta_rule, json_to_use, "name", str) - JsonUtils.copy_field_if_exists(json_meta_rule, json_to_use, "description", str) - JsonUtils.convert_names_to_ids(json_meta_rule, json_to_use, "subject_categories", - "subject_categories", "subject_category", ModelManager, - self._user_id) - JsonUtils.convert_names_to_ids(json_meta_rule, json_to_use, "object_categories", - "object_categories", "object_category", ModelManager, - self._user_id) - JsonUtils.convert_names_to_ids(json_meta_rule, json_to_use, "action_categories", - "action_categories", "action_category", ModelManager, - self._user_id) - logger.debug("Adding / updating a metarule from json {}".format(json_meta_rule)) - meta_rule = ModelManager.add_meta_rule(self._user_id, meta_rule_id=None, - value=json_to_use) - logger.debug("Added / updated meta rule : {}".format(meta_rule)) - - def _import_subject_object_action_assignments(self, json_item_assignments, type_element): - import_method = getattr(PolicyManager, 'add_' + type_element + '_assignment') - get_method = getattr(PolicyManager, 'get_' + type_element + '_data') - - if not isinstance(json_item_assignments, list): - raise InvalidJson(type_element + " assignments shall be a list!") - - # get the policy id related to the user - policies = PolicyManager.get_policies(self._user_id) - - for json_item_assignment in json_item_assignments: - item_override = JsonUtils.get_override(json_item_assignment) - if item_override is True: - raise ForbiddenOverride( - "{} assignments do not support override flag !".format(type_element)) - - json_assignment = dict() - JsonUtils.convert_name_to_id(json_item_assignment, json_assignment, "category", - "category_id", type_element + "_category", ModelManager, - self._user_id) - - has_found_data = False - # loop over policies - for policy_id in policies: - json_data = dict() - try: - JsonUtils.convert_name_to_id(json_item_assignment, json_assignment, - type_element, "id", type_element, PolicyManager, - self._user_id, policy_id) - JsonUtils.convert_names_to_ids(json_item_assignment, json_data, "assignments", - "data_id", type_element + "_data", PolicyManager, - self._user_id, policy_id, - json_assignment["category_id"]) - has_found_data = True - except UnknownName: - # the category or data has not been found in this policy : we look into the next one - continue - for data_id in json_data["data_id"]: - # find the policy related to the current data - data = get_method(self._user_id, policy_id, data_id, - json_assignment["category_id"]) - if data is not None and len(data) == 1: - logger.debug( - "Adding / updating a {} assignment from json {}".format(type_element, - json_assignment)) - import_method(self._user_id, policy_id, json_assignment["id"], - json_assignment["category_id"], - data_id) - else: - raise UnknownData("Unknown data with id {}".format(data_id)) - - # case the data has not been found in any policies - if has_found_data is False: - raise InvalidJson("The json contains unknown {} data or category : {}".format( - type_element, - json_item_assignment)) - - def _import_subject_object_action_datas(self, json_items_data, mandatory_policy_ids, - type_element): - if type_element == "subject": - import_method = getattr(PolicyManager, 'set_' + type_element + '_data') - else: - import_method = getattr(PolicyManager, 'add_' + type_element + '_data') - # get_method = getattr(PolicyManager, 'get_' + type_element + '_data') - - if not isinstance(json_items_data, list): - raise InvalidJson(type_element + " data shall be a list!") - - for json_item_data in json_items_data: - item_override = JsonUtils.get_override(json_items_data) - if item_override is True: - raise ForbiddenOverride( - "{} datas do not support override flag !".format(type_element)) - json_to_use = dict() - JsonUtils.copy_field_if_exists(json_item_data, json_to_use, "name", str) - JsonUtils.copy_field_if_exists(json_item_data, json_to_use, "description", str) - json_policy = dict() - # field_mandatory : not mandatory if there is some mandatory policies - JsonUtils.convert_names_to_ids(json_item_data, json_policy, "policies", "policy_id", - "policy", - PolicyManager, self._user_id, - field_mandatory=len(mandatory_policy_ids) == 0) - json_category = dict() - JsonUtils.convert_name_to_id(json_item_data, json_category, "category", "category_id", - type_element + "_category", - ModelManager, self._user_id) - policy_ids = [] - if "policy_id" in json_policy: - policy_ids = json_policy["policy_id"] - - for policy_id in policy_ids: - if policy_id is not None and policy_id not in mandatory_policy_ids: - mandatory_policy_ids.append(policy_id) - - if len(mandatory_policy_ids) == 0: - raise InvalidJson("Invalid data, the policy shall be set when importing {}".format( - json_item_data)) - category_id = None - if "category_id" in json_category: - category_id = json_category["category_id"] - if category_id is None: - raise InvalidJson( - "Invalid data, the category shall be set when importing {}".format( - json_item_data)) - - for policy_id in mandatory_policy_ids: - try: - data = import_method(self._user_id, policy_id, category_id=category_id, - value=json_to_use) - except exceptions.PolicyUnknown: - raise UnknownPolicy("Unknown policy with id {}".format(policy_id)) - except Exception as e: - logger.exception(str(e)) - raise e - - def _import_subject_object_action_categories(self, json_item_categories, type_element): - import_method = getattr(ModelManager, 'add_' + type_element + '_category') - get_method = getattr(ModelManager, 'get_' + type_element + '_categories') - - categories = get_method(self._user_id) - - if not isinstance(json_item_categories, list): - raise InvalidJson(type_element + " categories shall be a list!") - - for json_item_category in json_item_categories: - json_to_use = dict() - JsonUtils.copy_field_if_exists(json_item_category, json_to_use, "name", str) - - # check if category with the same name exists : do this in moondb ? - existing_id = None - for category_key in categories: - if categories[category_key]["name"] == json_to_use["name"]: - existing_id = category_key +class JsonImportAPI(object): - JsonUtils.copy_field_if_exists(json_item_category, json_to_use, "description", str) - item_override = JsonUtils.get_override(json_item_category) - if item_override is True: - raise ForbiddenOverride( - "{} categories do not support override flag !".format(type_element)) - - try: - category = import_method(self._user_id, existing_id, json_to_use) - except (exceptions.SubjectCategoryExisting, exceptions.ObjectCategoryExisting, - exceptions.ActionCategoryExisting): - # it already exists: do nothing - logger.warning("Ignored {} category with name {} is already in the database".format( - type_element, json_to_use["name"])) - except Exception as e: - logger.warning("Error while importing the category : {}".format(str(e))) - logger.exception(str(e)) - raise e - - def _import_subject_object_action(self, json_items, mandatory_policy_ids, type_element): - import_method = getattr(PolicyManager, 'add_' + type_element) - get_method = getattr(PolicyManager, 'get_' + type_element + 's') - - if not isinstance(json_items, list): - raise InvalidJson(type_element + " items shall be a list!") - - for json_item in json_items: - json_without_policy_name = dict() - JsonUtils.copy_field_if_exists(json_item, json_without_policy_name, "name", str) - JsonUtils.copy_field_if_exists(json_item, json_without_policy_name, "description", str) - JsonUtils.copy_field_if_exists(json_item, json_without_policy_name, "extra", dict) - JsonUtils.convert_names_to_ids(json_item, json_without_policy_name, "policies", - "policy_list", "policy", PolicyManager, self._user_id, - field_mandatory=False) - policy_ids = json_without_policy_name["policy_list"] - for mandatory_policy_id in mandatory_policy_ids: - if mandatory_policy_id not in policy_ids: - policy_ids.append(mandatory_policy_id) - # policy_ids and json_without_policy_name are references to the same array... - # json_without_policy_name["policy_list"].append(mandatory_policy_id) - - item_override = JsonUtils.get_override(json_item) - if item_override is True: - raise ForbiddenOverride("{} does not support override flag !".format(type_element)) - - if len(policy_ids) == 0: - raise MissingPolicy( - "a {} needs at least one policy to be created or updated : {}".format( - type_element, json.dumps(json_item))) - - for policy_id in policy_ids: - try: - items_in_db = get_method(self._user_id, policy_id) - key = None - for key_in_db in items_in_db: - if items_in_db[key_in_db]["name"] == json_without_policy_name["name"]: - key = key_in_db - break - element = import_method(self._user_id, policy_id, perimeter_id=key, - value=json_without_policy_name) - logger.debug("Added / updated {} : {}".format(type_element, element)) - - except exceptions.PolicyUnknown: - raise UnknownPolicy("Unknown policy when adding a {}!".format(type_element)) - except Exception as e: - logger.exception(str(e)) - raise BaseException(str(e)) - - def _import_policies(self, json_policies): - policy_mandatory_ids = [] - - if not isinstance(json_policies, list): - raise InvalidJson("policies shall be a list!") - - for json_policy in json_policies: - # TODO put this in moondb - # policy_in_db = PolicyManager.get_policies_by_name(json_without_model_name["name"]) - policies = PolicyManager.get_policies(self._user_id) - policy_in_db = None - policy_id = None - for policy_key in policies: - if policies[policy_key]["name"] == json_policy["name"]: - policy_in_db = policies[policy_key] - policy_id = policy_key - # end TODO - if policy_in_db is None: - policy_does_exist = False - else: - policy_does_exist = True - - policy_override = JsonUtils.get_override(json_policy) - policy_mandatory = JsonUtils.get_mandatory(json_policy) - - if policy_override is False and policy_does_exist: - if policy_id: - policy_mandatory_ids.append(policy_id) - logger.warning( - "Existing policy not updated because of the override option is not set !") - continue - - json_without_model_name = dict() - JsonUtils.copy_field_if_exists(json_policy, json_without_model_name, "name", str) - JsonUtils.copy_field_if_exists(json_policy, json_without_model_name, "description", str) - JsonUtils.copy_field_if_exists(json_policy, json_without_model_name, "genre", str) - JsonUtils.convert_name_to_id(json_policy, json_without_model_name, "model", "model_id", - "model", ModelManager, self._user_id, - field_mandatory=False) - - if not policy_does_exist: - logger.debug("Creating policy {} ".format(json_without_model_name)) - added_policy = PolicyManager.add_policy(self._user_id, None, - json_without_model_name) - if policy_mandatory is True: - keys = list(added_policy.keys()) - policy_mandatory_ids.append(keys[0]) - elif policy_override is True: - logger.debug("Updating policy {} ".format(json_without_model_name)) - updated_policy = PolicyManager.update_policy(self._user_id, policy_id, - json_without_model_name) - if policy_mandatory is True: - policy_mandatory_ids.append(policy_id) - return policy_mandatory_ids - - def _import_models_with_new_meta_rules(self, json_models): - if not isinstance(json_models, list): - raise InvalidJson("models shall be a list!") - - for json_model in json_models: - logger.debug("json_model {}".format(json_model)) - models = ModelManager.get_models(self._user_id) - model_in_db = None - model_id = None - for model_key in models: - if ("id" in json_model and model_key == json_model["id"]) or ( - "name" in json_model and models[model_key]["name"] == json_model["name"]): - model_in_db = models[model_key] - model_id = model_key - - # this should not occur as the model has been put in db previously in _import_models_without_new_meta_rules - if model_in_db is None: - raise UnknownModel("Unknown model ") - - json_key = dict() - JsonUtils.convert_names_to_ids(json_model, json_key, "meta_rules", "meta_rule_id", - "meta_rule", ModelManager, self._user_id) - for meta_rule_id in json_key["meta_rule_id"]: - if meta_rule_id not in model_in_db["meta_rules"]: - model_in_db["meta_rules"].append(meta_rule_id) - - ModelManager.update_model(self._user_id, model_id, model_in_db) - - def _import_models_without_new_meta_rules(self, json_models): - if not isinstance(json_models, list): - raise InvalidJson("models shall be a list!") - - for json_model in json_models: - json_without_new_metarules = dict() - JsonUtils.copy_field_if_exists(json_model, json_without_new_metarules, "name", str) - - # TODO put this in moondb - # model_in_db = ModelManager.get_models_by_name(json_without_new_metarules["name"]) - models = ModelManager.get_models(self._user_id) - model_in_db = None - for model_key in models: - if models[model_key]["name"] == json_without_new_metarules["name"]: - model_in_db = models[model_key] - model_id = model_key - # end TODO - - JsonUtils.copy_field_if_exists(json_model, json_without_new_metarules, "description", - str) - if model_in_db is None: - model_does_exist = False - else: - json_without_new_metarules["meta_rules"] = model_in_db["meta_rules"] - model_does_exist = True - model_override = JsonUtils.get_override(json_model) - if not model_does_exist: - logger.debug("Creating model {} ".format(json_without_new_metarules)) - ModelManager.add_model(self._user_id, None, json_without_new_metarules) - elif model_override is True: - logger.debug( - "Updating model with id {} : {} ".format(model_id, json_without_new_metarules)) - ModelManager.update_model(self._user_id, model_id, json_without_new_metarules) - - def _import_pdps(self, json_pdps): - if not isinstance(json_pdps, list): - raise InvalidJson("pdps shall be a list!") - - for json_pdp in json_pdps: - json_to_use = dict() - JsonUtils.copy_field_if_exists(json_pdp, json_to_use, "name", str) - JsonUtils.copy_field_if_exists(json_pdp, json_to_use, "keystone_project_id", str) - JsonUtils.copy_field_if_exists(json_pdp, json_to_use, "security_pipeline", list) - JsonUtils.copy_field_if_exists(json_pdp, json_to_use, "description", str) - - pdps = PDPManager.get_pdp(self._user_id) - exists = False - for pdp_key in pdps: - if pdps[pdp_key]["name"] == json_to_use["name"]: - PDPManager.update_pdp(self._user_id, pdp_id=pdp_key, value=json_to_use) - exists = True - if exists is False: - PDPManager.add_pdp(self._user_id, value=json_to_use) - - def _import_json(self, user_id): - self._user_id = user_id - if 'file' in request.files: - file = request.files['file'] - logger.debug("Importing {} file...".format(file)) - json_content = json.load(file) - else: - json_content = request.json - logger.debug("Importing content: {} ...".format(json_content)) - - # first import the models without the meta rules as they are not yet defined - if "models" in json_content: - logger.info("Importing models...") - self._import_models_without_new_meta_rules(json_content["models"]) - - # import the policies that depends on the models - mandatory_policy_ids = [] - if "policies" in json_content: - logger.info("Importing policies...") - mandatory_policy_ids = self._import_policies(json_content["policies"]) - - # import subjects, subject_data, subject_categories, idem for object and action - list_element = [{"key": "subject"}, {"key": "object"}, {"key": "action"}] - for elt in list_element: - in_key = elt["key"] - key = in_key + "s" - if key in json_content: - logger.info("Importing {}...".format(key)) - self._import_subject_object_action(json_content[key], mandatory_policy_ids, in_key) - key = in_key + "_categories" - if key in json_content: - logger.info("Importing {}...".format(key)) - self._import_subject_object_action_categories(json_content[key], in_key) - - # import meta rules - if "meta_rules" in json_content: - logger.info("Importing meta rules...") - self._import_meta_rules(json_content["meta_rules"]) - - # add the metarule to model - if "models" in json_content: - logger.info("Updating models with meta rules...") - self._import_models_with_new_meta_rules(json_content["models"]) - - for elt in list_element: - in_key = elt["key"] - key = in_key + "_data" - if key in json_content: - logger.info("Importing {}...".format(key)) - self._import_subject_object_action_datas(json_content[key], mandatory_policy_ids, - in_key) - - # import subjects assignments, idem for object and action - for elt in list_element: - in_key = elt["key"] - key = in_key + "_assignments" - if key in json_content: - logger.info("Importing {}...".format(key)) - self._import_subject_object_action_assignments(json_content[key], in_key) - - # import rules - if "rules" in json_content: - logger.info("Importing rules...") - self._import_rules(json_content["rules"]) - - # import pdps - if "pdps" in json_content: - logger.info("Importing pdps...") - self._import_pdps(json_content["pdps"]) - - @check_auth - def post(self, user_id=None): - """Import file. - - :param user_id: user ID who do the request - :return: { - - } - :internal_api: + @staticmethod + @hug.post("/import", requires=api_key_authentication) + def post(request, body, authed_user: hug.directives.user = None): + """ + Import data inside the database + :param request: the request send by the user + :param body: the content of the request + :param authed_user: the name of the authenticated user + :return: "Import ok !" (if import is OK) + :raises multiple exceptions depending on the context """ - self._import_json(user_id) - return "Import ok !" + json_import_ob = JsonImport(driver_name="db", driver=driver) + imported_data = json_import_ob.import_json(moon_user_id=authed_user, request=request, body=body) + LOGGER.info('Imported data: {}'.format(imported_data)) + return imported_data diff --git a/moon_manager/moon_manager/api/json_utils.py b/moon_manager/moon_manager/api/json_utils.py deleted file mode 100644 index 6a5830f1..00000000 --- a/moon_manager/moon_manager/api/json_utils.py +++ /dev/null @@ -1,282 +0,0 @@ -import logging -from moon_manager.api.base_exception import BaseException - -logger = logging.getLogger("moon.manager.api." + __name__) - - -class UnknownName(BaseException): - def __init__(self, message): - # Call the base class constructor with the parameters it needs - super(UnknownName, self).__init__(message) - - -class UnknownId(BaseException): - def __init__(self, message): - # Call the base class constructor with the parameters it needs - super(UnknownId, self).__init__(message) - - -class MissingIdOrName(BaseException): - def __init__(self, message): - # Call the base class constructor with the parameters it needs - super(MissingIdOrName, self).__init__(message) - - -class UnknownField(BaseException): - def __init__(self, message): - # Call the base class constructor with the parameters it needs - super(UnknownField, self).__init__(message) - - -class JsonUtils: - @staticmethod - def get_override(json_content): - if "override" in json_content: - return json_content["override"] - return False - - @staticmethod - def get_mandatory(json_content): - if "mandatory" in json_content: - return json_content["mandatory"] - return False - - @staticmethod - def copy_field_if_exists(json_in, json_out, field_name, type_field, default_value=None): - if field_name in json_in: - json_out[field_name] = json_in[field_name] - else: - if type_field is bool: - if default_value is None: - default_value = False - json_out[field_name] = default_value - if type_field is str: - if default_value is None: - default_value = "" - json_out[field_name] = default_value - if type_field is dict: - json_out[field_name] = dict() - if type_field is list: - json_out[field_name] = [] - - @staticmethod - def _get_element_in_db_from_id(element_type, element_id, user_id, policy_id, category_id, - meta_rule_id, manager): - # the item is supposed to be in the db, we check it exists! - if element_type == "model": - data_db = manager.get_models(user_id, model_id=element_id) - elif element_type == "policy": - data_db = manager.get_policies(user_id, policy_id=element_id) - elif element_type == "subject": - data_db = manager.get_subjects(user_id, policy_id, perimeter_id=element_id) - elif element_type == "object": - data_db = manager.get_objects(user_id, policy_id, perimeter_id=element_id) - elif element_type == "action": - data_db = manager.get_actions(user_id, policy_id, perimeter_id=element_id) - elif element_type == "subject_category": - data_db = manager.get_subject_categories(user_id, category_id=element_id) - elif element_type == "object_category": - data_db = manager.get_object_categories(user_id, category_id=element_id) - elif element_type == "action_category": - data_db = manager.get_action_categories(user_id, category_id=element_id) - elif element_type == "meta_rule": - data_db = manager.get_meta_rules(user_id, meta_rule_id=element_id) - elif element_type == "subject_data": - data_db = manager.get_subject_data(user_id, policy_id, data_id=element_id, - category_id=category_id) - elif element_type == "object_data": - data_db = manager.get_object_data(user_id, policy_id, data_id=element_id, - category_id=category_id) - elif element_type == "action_data": - data_db = manager.get_action_data(user_id, policy_id, data_id=element_id, - category_id=category_id) - elif element_type == "meta_rule": - data_db = manager.get_meta_rules(user_id, meta_rule_id=meta_rule_id) - else: - raise Exception("Conversion of {} not implemented yet!".format(element_type)) - - # logger.info(data_db) - - # do some post processing ... the result should be {key : { .... .... } } - if element_type == "subject_data" or element_type == "object_data" or element_type == "action_data": - if data_db is not None and isinstance(data_db, list): - # TODO remove comments after fixing the bug on moondb when adding metarule : we can have several identical entries ! - # if len(data_db) > 1: - # raise Exception("Several {} with the same id : {}".format(element_type, data_db)) - data_db = data_db[0] - - if data_db is not None and data_db["data"] is not None and isinstance(data_db["data"], - dict): - # TODO remove comments after fixing the bug on moondb when adding metarule : we can have several identical entries ! - # if len(data_db["data"].values()) != 1: - # raise Exception("Several {} with the same id : {}".format(element_type, data_db)) - # data_db = data_db["data"] - # TODO remove these two lines after fixing the bug on moondb when adding metarule : we can have several identical entries ! - list_values = list(data_db["data"].values()) - data_db = list_values[0] - # logger.info("subject data after postprocessing {}".format(data_db)) - return data_db - - @staticmethod - def _get_element_id_in_db_from_name(element_type, element_name, user_id, policy_id, category_id, - meta_rule_id, manager): - if element_type == "model": - data_db = manager.get_models(user_id) - elif element_type == "policy": - data_db = manager.get_policies(user_id) - elif element_type == "subject": - data_db = manager.get_subjects(user_id, policy_id) - elif element_type == "object": - data_db = manager.get_objects(user_id, policy_id) - elif element_type == "action": - data_db = manager.get_actions(user_id, policy_id) - elif element_type == "subject_category": - data_db = manager.get_subject_categories(user_id) - elif element_type == "object_category": - data_db = manager.get_object_categories(user_id) - elif element_type == "action_category": - data_db = manager.get_action_categories(user_id) - elif element_type == "meta_rule": - data_db = manager.get_meta_rules(user_id) - elif element_type == "subject_data": - data_db = manager.get_subject_data(user_id, policy_id, category_id=category_id) - elif element_type == "object_data": - data_db = manager.get_object_data(user_id, policy_id, category_id=category_id) - elif element_type == "action_data": - data_db = manager.get_action_data(user_id, policy_id, category_id=category_id) - elif element_type == "meta_rule": - data_db = manager.get_meta_rules(user_id) - elif element_type == "rule": - data_db = manager.get_rules(user_id, policy_id) - else: - raise BaseException("Conversion of {} not implemented yet!".format(element_type)) - - if isinstance(data_db, dict): - for key_id in data_db: - if isinstance(data_db[key_id], dict) and "name" in data_db[key_id]: - if data_db[key_id]["name"] == element_name: - return key_id - else: - for elt in data_db: - if isinstance(elt, - dict) and "data" in elt: # we handle here subject_data, object_data and action_data... - for data_key in elt["data"]: - # logger.info("data from the db {} ".format(elt["data"][data_key])) - data = elt["data"][data_key] - if "name" in data and data["name"] == element_name: - return data_key - if "value" in data and data["value"]["name"] == element_name: - return data_key - return None - - @staticmethod - def convert_name_to_id(json_in, json_out, field_name_in, field_name_out, element_type, manager, - user_id, policy_id=None, category_id=None, meta_rule_id=None, - field_mandatory=True): - if field_name_in not in json_in: - raise UnknownField("The field {} is not in the input json".format(field_name_in)) - - if "id" in json_in[field_name_in]: - data_db = JsonUtils._get_element_in_db_from_id(element_type, - json_in[field_name_in]["id"], user_id, - policy_id, category_id, meta_rule_id, - manager) - if data_db is None: - raise UnknownId("No {} with id {} found in database".format(element_type, - json_in[field_name_in]["id"])) - json_out[field_name_out] = json_in[field_name_in]["id"] - - elif "name" in json_in[field_name_in]: - id_in_db = JsonUtils._get_element_id_in_db_from_name(element_type, - json_in[field_name_in]["name"], - user_id, policy_id, category_id, - meta_rule_id, manager) - if id_in_db is None: - raise UnknownName( - "No {} with name {} found in database".format(element_type, - json_in[field_name_in]["name"])) - json_out[field_name_out] = id_in_db - elif field_mandatory is True: - raise MissingIdOrName("No id or name found in the input json {}".format(json_in)) - - @staticmethod - def convert_id_to_name(id_, json_out, field_name_out, element_type, manager, user_id, - policy_id=None, category_id=None, meta_rule_id=None): - json_out[field_name_out] = { - "name": JsonUtils.convert_id_to_name_string(id_, element_type, manager, user_id, - policy_id, category_id, meta_rule_id)} - - @staticmethod - def __convert_results_to_element(element): - if isinstance(element, dict) and "name" not in element and "value" not in element: - list_values = [v for v in element.values()] - elif isinstance(element, list): - list_values = element - else: - list_values = [] - list_values.append(element) - return list_values[0] - - @staticmethod - def convert_id_to_name_string(id_, element_type, manager, user_id, - policy_id=None, category_id=None, meta_rule_id=None): - - element = JsonUtils._get_element_in_db_from_id(element_type, id_, user_id, policy_id, - category_id, meta_rule_id, manager) - # logger.info(element) - if element is None: - raise UnknownId("No {} with id {} found in database".format(element_type, id_)) - res = JsonUtils.__convert_results_to_element(element) - # logger.info(res) - if "name" in res: - return res["name"] - if "value" in res and "name" in res["value"]: - return res["value"]["name"] - return None - - @staticmethod - def convert_names_to_ids(json_in, json_out, field_name_in, field_name_out, element_type, - manager, user_id, policy_id=None, category_id=None, meta_rule_id=None, - field_mandatory=True): - ids = [] - if field_name_in not in json_in: - raise UnknownField("The field {} is not in the input json".format(field_name_in)) - - for elt in json_in[field_name_in]: - if "id" in elt: - data_db = JsonUtils._get_element_in_db_from_id(element_type, elt["id"], user_id, - policy_id, category_id, - meta_rule_id, manager) - if data_db is None: - raise UnknownId( - "No {} with id {} found in database".format(element_type, elt["id"])) - ids.append(elt["id"]) - elif "name" in elt: - id_in_db = JsonUtils._get_element_id_in_db_from_name(element_type, elt["name"], - user_id, policy_id, - category_id, meta_rule_id, - manager) - if id_in_db is None: - raise UnknownName( - "No {} with name {} found in database".format(element_type, elt["name"])) - ids.append(id_in_db) - elif field_mandatory is True: - raise MissingIdOrName("No id or name found in the input json {}".format(elt)) - json_out[field_name_out] = ids - - @staticmethod - def convert_ids_to_names(ids, json_out, field_name_out, element_type, manager, user_id, - policy_id=None, category_id=None, meta_rule_id=None): - res_array = [] - for id_ in ids: - element = JsonUtils._get_element_in_db_from_id(element_type, id_, user_id, policy_id, - category_id, meta_rule_id, manager) - if element is None: - raise UnknownId("No {} with id {} found in database".format(element_type, id_)) - res = JsonUtils.__convert_results_to_element(element) - # logger.info(res) - if "name" in res: - res_array.append({"name": res["name"]}) - if "value" in res and "name" in res["value"]: - res_array.append({"name": res["value"]["name"]}) - json_out[field_name_out] = res_array diff --git a/moon_manager/moon_manager/api/logs.py b/moon_manager/moon_manager/api/logs.py new file mode 100644 index 00000000..49b39b11 --- /dev/null +++ b/moon_manager/moon_manager/api/logs.py @@ -0,0 +1,25 @@ +# 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. + + +"""Test hug API (local, command-line, and HTTP access)""" +import hug + + +@hug.local() +@hug.get("/logs/") +def list(): + """ + List logs + :return: JSON status output + """ + + return {"logs": []} diff --git a/moon_manager/moon_manager/api/meta_data.py b/moon_manager/moon_manager/api/meta_data.py index b0b86d10..828124df 100644 --- a/moon_manager/moon_manager/api/meta_data.py +++ b/moon_manager/moon_manager/api/meta_data.py @@ -1,42 +1,53 @@ -# 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'. -""" -Meta Data are elements used to create Meta data (skeleton of security policies) +# 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. + +""" +Meta Data are elements used to create Meta data (skeleton of security policies)subject_categories +* Subjects are the source of an action on an object + (examples : users, virtual machines) +* Objects are the destination of an action + (examples virtual machines, virtual Routers) +* Actions are what subject wants to do on an object """ -from flask import request -from flask_restful import Resource +import hug import logging -from python_moonutilities.security_functions import check_auth -from python_moondb.core import ModelManager -from python_moonutilities.security_functions import validate_input +import requests +from moon_manager import db_driver as driver +from moon_utilities.security_functions import validate_input +from moon_utilities.auth_functions import api_key_authentication, connect_from_env +from moon_utilities.invalided_functions import invalidate_meta_data_in_slaves +from moon_manager.api import slave as slave_class +from moon_manager.api import configuration -__version__ = "4.3.2" +# from moon_manager.server import handle_exception, handle_custom_exceptions -logger = logging.getLogger("moon.manager.api." + __name__) +LOGGER = logging.getLogger("moon.manager.api." + __name__) -class SubjectCategories(Resource): +class SubjectCategories(object): """ Endpoint for subject categories requests """ - __urls__ = ( - "/subject_categories", - "/subject_categories/", - "/subject_categories/", - ) - - @validate_input("get", kwargs_state=[False, False]) - @check_auth - def get(self, category_id=None, user_id=None): + @staticmethod + @hug.local() + @hug.get("/subject_categories", requires=api_key_authentication) + @hug.get("/subject_categories/{category_id}", requires=api_key_authentication) + def get(category_id: hug.types.text = None, authed_user: hug.directives.user = None): """Retrieve all subject categories or a specific one :param category_id: uuid of the subject category - :param user_id: user ID who do the request + :param authed_user: user ID who do the request :return: { "subject_category_id": { "name": "name of the category", @@ -45,18 +56,19 @@ class SubjectCategories(Resource): } :internal_api: get_subject_categories """ - data = ModelManager.get_subject_categories( - user_id=user_id, category_id=category_id) + data = driver.ModelManager.get_subject_categories(moon_user_id=authed_user, + category_id=category_id) return {"subject_categories": data} - @validate_input("post", body_state={"name": True}) - @check_auth - def post(self, category_id=None, user_id=None): + @staticmethod + @hug.local() + @hug.post("/subject_categories", requires=api_key_authentication) + def post(body: validate_input("name"), authed_user: hug.directives.user = None): """Create or update a subject category. - :param category_id: must not be used here - :param user_id: user ID who do the request + :param body: body of the request + :param authed_user: user ID who do the request :request body: { "name": "name of the category (mandatory)", "description": "description of the category (optional)" @@ -69,18 +81,19 @@ class SubjectCategories(Resource): } :internal_api: add_subject_category """ - data = ModelManager.add_subject_category( - user_id=user_id, value=request.json) + data = driver.ModelManager.add_subject_category(moon_user_id=authed_user, value=body) + return {"subject_categories": data} - @validate_input("delete", kwargs_state=[True, False]) - @check_auth - def delete(self, category_id=None, user_id=None): + @staticmethod + @hug.local() + @hug.delete("/subject_categories/{category_id}", requires=api_key_authentication) + def delete(category_id: hug.types.text = None, authed_user: hug.directives.user = None): """Delete a subject category :param category_id: uuid of the subject category to delete - :param user_id: user ID who do the request + :param authed_user: user ID who do the request :return: { "result": "True or False", "message": "optional message (optional)" @@ -88,30 +101,29 @@ class SubjectCategories(Resource): :internal_api: delete_subject_category """ - data = ModelManager.delete_subject_category( - user_id=user_id, category_id=category_id) + driver.ModelManager.delete_subject_category(moon_user_id=authed_user, + category_id=category_id) + + slaves = slave_class.Slaves.get().get("slaves") + invalidate_meta_data_in_slaves(slaves=slaves, category_id=category_id, type='subject') return {"result": True} -class ObjectCategories(Resource): +class ObjectCategories(object): """ Endpoint for object categories requests """ - __urls__ = ( - "/object_categories", - "/object_categories/", - "/object_categories/", - ) - - @validate_input("get", kwargs_state=[False, False]) - @check_auth - def get(self, category_id=None, user_id=None): + @staticmethod + @hug.local() + @hug.get("/object_categories", requires=api_key_authentication) + @hug.get("/object_categories/{category_id}", requires=api_key_authentication) + def get(category_id: hug.types.text = None, authed_user: hug.directives.user = None): """Retrieve all object categories or a specific one :param category_id: uuid of the object category - :param user_id: user ID who do the request + :param authed_user: user ID who do the request :return: { "object_category_id": { "name": "name of the category", @@ -120,18 +132,19 @@ class ObjectCategories(Resource): } :internal_api: get_object_categories """ - data = ModelManager.get_object_categories( - user_id=user_id, category_id=category_id) + data = driver.ModelManager.get_object_categories(moon_user_id=authed_user, + category_id=category_id) return {"object_categories": data} - @validate_input("post", body_state={"name": True}) - @check_auth - def post(self, category_id=None, user_id=None): + @staticmethod + @hug.local() + @hug.post("/object_categories", requires=api_key_authentication) + def post(body: validate_input("name"), authed_user: hug.directives.user = None): """Create or update a object category. - :param category_id: must not be used here - :param user_id: user ID who do the request + :param body: body of the request + :param authed_user: user ID who do the request :request body: { "name": "name of the category (mandatory)", "description": "description of the category (optional)" @@ -144,19 +157,18 @@ class ObjectCategories(Resource): } :internal_api: add_object_category """ - - data = ModelManager.add_object_category( - user_id=user_id, value=request.json) + data = driver.ModelManager.add_object_category(moon_user_id=authed_user, value=body) return {"object_categories": data} - @validate_input("delete", kwargs_state=[True, False]) - @check_auth - def delete(self, category_id=None, user_id=None): + @staticmethod + @hug.local() + @hug.delete("/object_categories/{category_id}", requires=api_key_authentication) + def delete(category_id: hug.types.text = None, authed_user: hug.directives.user = None): """Delete an object category :param category_id: uuid of the object category to delete - :param user_id: user ID who do the request + :param authed_user: user ID who do the request :return: { "result": "True or False", "message": "optional message (optional)" @@ -164,30 +176,29 @@ class ObjectCategories(Resource): :internal_api: delete_object_category """ - data = ModelManager.delete_object_category( - user_id=user_id, category_id=category_id) + driver.ModelManager.delete_object_category(moon_user_id=authed_user, + category_id=category_id) + + slaves = slave_class.Slaves.get().get("slaves") + invalidate_meta_data_in_slaves(slaves=slaves, category_id=category_id, type='object') return {"result": True} -class ActionCategories(Resource): +class ActionCategories(object): """ Endpoint for action categories requests """ - __urls__ = ( - "/action_categories", - "/action_categories/", - "/action_categories/", - ) - - @validate_input("get", kwargs_state=[False, False]) - @check_auth - def get(self, category_id=None, user_id=None): + @staticmethod + @hug.local() + @hug.get("/action_categories", requires=api_key_authentication) + @hug.get("/action_categories/{category_id}", requires=api_key_authentication) + def get(category_id: hug.types.text = None, authed_user: hug.directives.user = None): """Retrieve all action categories or a specific one :param category_id: uuid of the action category - :param user_id: user ID who do the request + :param authed_user: user ID who do the request :return: { "action_category_id": { "name": "name of the category", @@ -196,19 +207,18 @@ class ActionCategories(Resource): } :internal_api: get_action_categories """ - - data = ModelManager.get_action_categories( - user_id=user_id, category_id=category_id) - + data = driver.ModelManager.get_action_categories(moon_user_id=authed_user, + category_id=category_id) return {"action_categories": data} - @validate_input("post", body_state={"name": True}) - @check_auth - def post(self, category_id=None, user_id=None): + @staticmethod + @hug.local() + @hug.post("/action_categories", requires=api_key_authentication) + def post(body: validate_input("name"), authed_user: hug.directives.user = None): """Create or update an action category. - :param category_id: must not be used here - :param user_id: user ID who do the request + :param body: body of the request + :param authed_user: user ID who do the request :request body: { "name": "name of the category (mandatory)", "description": "description of the category (optional)" @@ -222,25 +232,353 @@ class ActionCategories(Resource): :internal_api: add_action_category """ - data = ModelManager.add_action_category( - user_id=user_id, value=request.json) + data = driver.ModelManager.add_action_category(moon_user_id=authed_user, value=body) return {"action_categories": data} - @validate_input("delete", kwargs_state=[True, False]) - @check_auth - def delete(self, category_id=None, user_id=None): + @staticmethod + @hug.local() + @hug.delete("/action_categories/{category_id}", requires=api_key_authentication) + def delete(category_id: hug.types.text = None, authed_user: hug.directives.user = None): """Delete an action :param category_id: uuid of the action category to delete - :param user_id: user ID who do the request + :param authed_user: user ID who do the request :return: { "result": "True or False", "message": "optional message (optional)" } :internal_api: delete_action_category """ - data = ModelManager.delete_action_category( - user_id=user_id, category_id=category_id) + driver.ModelManager.delete_action_category(moon_user_id=authed_user, + category_id=category_id) + + slaves = slave_class.Slaves.get().get("slaves") + invalidate_meta_data_in_slaves(slaves=slaves, category_id=category_id, type='action') return {"result": True} + + +SubjectCategoriesAPI = hug.API(name='subject_categories', doc=SubjectCategories.__doc__) + + +@hug.object(name='subject_categories', version='1.0.0', api=SubjectCategoriesAPI) +class SubjectCategoriesCLI(object): + """An example of command like calls via an Object""" + + @staticmethod + @hug.object.cli + def list(name_or_id="", human: bool = False): + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + _subject_categories_req = requests.get("{}/subject_categories".format(db_conf.get("url")), + headers={"x-api-key": manager_api_key} + ) + if _subject_categories_req.status_code == 200: + if name_or_id: + _subject_categories = None + if name_or_id in _subject_categories_req.json().get("subject_categories"): + _subject_categories = _subject_categories_req.json().get("subject_categories")\ + .get(name_or_id) + else: + for _subject_categories_key in _subject_categories_req.json()\ + .get("subject_categories"): + _name = _subject_categories_req.json().get("subject_categories")\ + .get(_subject_categories_key).get("name") + if _name == name_or_id: + _subject_categories = _subject_categories_req.json()\ + .get("subject_categories").get(_subject_categories_key) + name_or_id = _subject_categories_key + break + if not _subject_categories: + raise Exception("Cannot find SubjectCategories with name or ID {}".format( + name_or_id)) + result = {"subject_categories": {name_or_id: _subject_categories}} + else: + result = _subject_categories_req.json() + + if human: + return SubjectCategoriesCLI.human_display(result) + else: + return result + LOGGER.error('Cannot list SubjectCategories {}'.format(_subject_categories_req.status_code)) + + @staticmethod + @hug.object.cli + def add(name, description="", human: bool = False): + """ + Add subject category in database + :return: JSON status output + """ + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + _url = "{}/subject_categories".format(db_conf.get("url")) + _subject_categories = requests.post( + _url, + json={ + "name": name, + "description": description, + }, + headers={ + "x-api-key": manager_api_key, + "Content-Type": "application/json" + } + ) + if _subject_categories.status_code == 200: + LOGGER.warning('Create {}'.format(_subject_categories.content)) + if human: + return SubjectCategoriesCLI.human_display(_subject_categories.json()) + else: + return _subject_categories.json() + LOGGER.error('Cannot create {}'.format(name, _subject_categories.content[:40])) + + @staticmethod + @hug.object.cli + def delete(name_or_id): + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + _subject_categories = SubjectCategoriesCLI.list() + for _perimeter_id, _perimeter_value in _subject_categories.get("subject_categories")\ + .items(): + if _perimeter_value.get("name") == name_or_id: + _url = "{}/subject_categories/{}".format(db_conf.get("url"), _perimeter_id) + req = requests.delete( + _url, + headers={"x-api-key": manager_api_key} + ) + break + else: + LOGGER.error("Cannot find SubjectCategories with name {}".format(name_or_id)) + return False + if req.status_code == 200: + LOGGER.warning('Deleted {}'.format(name_or_id)) + return True + LOGGER.error("Cannot delete SubjectCategories with name {}".format(name_or_id)) + return False + + @staticmethod + def human_display(subject_categories_json): + human_result = "Subject Categories" + for subject_category in subject_categories_json.get("subject_categories"): + human_result += "\n" + subject_categories_json.get("subject_categories").get(subject_category).get("name") + "\n" + human_result += "\tid : " + subject_categories_json.get("subject_categories").get(subject_category).get("id") + "\n" + human_result += "\tname : " + subject_categories_json.get("subject_categories").get(subject_category).get("name") + "\n" + human_result += "\tdescription : " + subject_categories_json.get("subject_categories").get(subject_category).get("description") + "\n" + return human_result + + +ObjectCategoriesAPI = hug.API(name='object_categories', doc=ObjectCategories.__doc__) + + +@hug.object(name='object_categories', version='1.0.0', api=ObjectCategoriesAPI) +class ObjectCategoriesCLI(object): + """An example of command like calls via an Object""" + + @staticmethod + @hug.object.cli + def list(name_or_id="", human: bool = False): + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + _object_categories_req = requests.get("{}/object_categories".format(db_conf.get("url")), + headers={"x-api-key": manager_api_key} + ) + if _object_categories_req.status_code == 200: + if name_or_id: + _object_categories = None + if name_or_id in _object_categories_req.json().get("object_categories"): + _object_categories = _object_categories_req.json().get("object_categories")\ + .get(name_or_id) + else: + for _object_categories_key in _object_categories_req.json()\ + .get("object_categories"): + _name = _object_categories_req.json().get("object_categories")\ + .get(_object_categories_key).get("name") + if _name == name_or_id: + _object_categories = _object_categories_req.json()\ + .get("object_categories").get(_object_categories_key) + name_or_id = _object_categories_key + break + if not _object_categories: + raise Exception("Cannot find ObjectCategories with name or ID {}".format( + name_or_id)) + result = {"object_categories": {name_or_id: _object_categories}} + else: + result = _object_categories_req.json() + + if human: + return ObjectCategoriesCLI.human_display(result) + else: + return result + LOGGER.error('Cannot list ObjectCategories {}'.format(_object_categories_req.status_code)) + + @staticmethod + @hug.object.cli + def add(name, description="", human: bool = False): + """ + Add object category in database + :return: JSON status output + """ + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + _url = "{}/object_categories".format(db_conf.get("url")) + _object_categories = requests.post( + _url, + json={ + "name": name, + "description": description, + }, + headers={ + "x-api-key": manager_api_key, + "Content-Type": "application/json" + } + ) + if _object_categories.status_code == 200: + LOGGER.warning('Create {}'.format(_object_categories.content)) + if human: + return ObjectCategoriesCLI.human_display(_object_categories.json()) + else: + return _object_categories.json() + LOGGER.error('Cannot create {}'.format(name, _object_categories.content[:40])) + + @staticmethod + @hug.object.cli + def delete(name_or_id): + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + _object_categories = ObjectCategoriesCLI.list() + for _perimeter_id, _perimeter_value in _object_categories.get("object_categories").items(): + if _perimeter_value.get("name") == name_or_id: + _url = "{}/object_categories/{}".format(db_conf.get("url"), _perimeter_id) + req = requests.delete( + _url, + headers={"x-api-key": manager_api_key} + ) + break + else: + LOGGER.error("Cannot find ObjectCategories with name {}".format(name_or_id)) + return False + if req.status_code == 200: + LOGGER.warning('Deleted {}'.format(name_or_id)) + return True + LOGGER.error("Cannot delete ObjectCategories with name {}".format(name_or_id)) + return False + + @staticmethod + def human_display(object_categories_json): + human_result = "Object Categories" + for object_category in object_categories_json.get("object_categories"): + human_result += "\n" + object_categories_json.get("object_categories").get(object_category).get("name") + "\n" + human_result += "\tid : " + object_categories_json.get("object_categories").get(object_category).get("id") + "\n" + human_result += "\tname : " + object_categories_json.get("object_categories").get(object_category).get("name") + "\n" + human_result += "\tdescription : " + object_categories_json.get("object_categories").get(object_category).get("description") + "\n" + return human_result + +ActionCategoriesAPI = hug.API(name='action_categories', doc=ActionCategories.__doc__) + + +@hug.object(name='action_categories', version='1.0.0', api=ActionCategoriesAPI) +class ActionCategoriesCLI(object): + """An example of command like calls via an Object""" + + @staticmethod + @hug.object.cli + def list(name_or_id="", human: bool = False): + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + _action_categories_req = requests.get("{}/action_categories".format(db_conf.get("url")), + headers={"x-api-key": manager_api_key} + ) + if _action_categories_req.status_code == 200: + if name_or_id: + _action_categories = None + if name_or_id in _action_categories_req.json().get("action_categories"): + _action_categories = _action_categories_req.json().get("action_categories")\ + .get(name_or_id) + else: + for _action_categories_key in _action_categories_req.json()\ + .get("action_categories"): + _name = _action_categories_req.json().get("action_categories")\ + .get(_action_categories_key).get("name") + if _name == name_or_id: + _action_categories = _action_categories_req.json()\ + .get("action_categories").get(_action_categories_key) + name_or_id = _action_categories_key + break + if not _action_categories: + raise Exception("Cannot find ActionCategories with name or ID {}".format( + name_or_id)) + result = {"action_categories": {name_or_id: _action_categories}} + else: + result = _action_categories_req.json() + + if human: + return ActionCategoriesCLI.human_display(result) + else: + return result + LOGGER.error('Cannot list ActionCategories {}'.format(_action_categories_req.status_code)) + + @staticmethod + @hug.object.cli + def add(name, description="",human: bool = False): + """ + Add action category in database + :return: JSON status output + """ + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + _url = "{}/action_categories".format(db_conf.get("url")) + _action_categories = requests.post( + _url, + json={ + "name": name, + "description": description, + }, + headers={ + "x-api-key": manager_api_key, + "Content-Type": "application/json" + } + ) + if _action_categories.status_code == 200: + LOGGER.warning('Create {}'.format(_action_categories.content)) + if human: + return ActionCategoriesCLI.human_display(_action_categories.json()) + else: + return _action_categories.json() + LOGGER.error('Cannot create {}'.format(name, _action_categories.content[:40])) + + @staticmethod + @hug.object.cli + def delete(name_or_id): + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + _action_categories = ActionCategoriesCLI.list() + for _perimeter_id, _perimeter_value in _action_categories.get("action_categories").items(): + if _perimeter_value.get("name") == name_or_id: + _url = "{}/action_categories/{}".format(db_conf.get("url"), _perimeter_id) + req = requests.delete( + _url, + headers={"x-api-key": manager_api_key} + ) + break + else: + LOGGER.error("Cannot find ActionCategories with name {}".format(name_or_id)) + return False + if req.status_code == 200: + LOGGER.warning('Deleted {}'.format(name_or_id)) + return True + LOGGER.error("Cannot delete ActionCategories with name {}".format(name_or_id)) + return False + + @staticmethod + def human_display(action_categories_json): + human_result = "Action Categories" + for action_category in action_categories_json.get("action_categories"): + human_result += "\n" + action_categories_json.get("action_categories").get(action_category).get( + "name") + "\n" + human_result += "\tid : " + action_categories_json.get("action_categories").get(action_category).get( + "id") + "\n" + human_result += "\tname : " + action_categories_json.get("action_categories").get(action_category).get( + "name") + "\n" + human_result += "\tdescription : " + action_categories_json.get("action_categories").get( + action_category).get("description") + "\n" + return human_result diff --git a/moon_manager/moon_manager/api/meta_rules.py b/moon_manager/moon_manager/api/meta_rules.py index 738aad71..fbdfc2e5 100644 --- a/moon_manager/moon_manager/api/meta_rules.py +++ b/moon_manager/moon_manager/api/meta_rules.py @@ -1,39 +1,45 @@ -# 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'. +# 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. + + """ Meta rules are skeleton for security policies """ - -from flask import request -from flask_restful import Resource +import hug import logging -from python_moonutilities.security_functions import check_auth -from python_moondb.core import ModelManager -from python_moonutilities.security_functions import validate_input +import requests +from moon_manager import db_driver as driver +from moon_utilities.security_functions import validate_input +from moon_utilities.auth_functions import api_key_authentication, connect_from_env +from moon_utilities.invalided_functions import invalidate_meta_rule_in_slaves +from moon_manager.api import slave as slave_class +from moon_manager.api import configuration +from moon_manager.api import meta_data -__version__ = "4.3.2" +# from moon_manager.server import handle_exception, handle_custom_exceptions -logger = logging.getLogger("moon.manager.api." + __name__) +LOGGER = logging.getLogger("moon.manager.api." + __name__) -class MetaRules(Resource): +class MetaRules(object): """ Endpoint for meta rules requests """ - __urls__ = ( - "/meta_rules", - "/meta_rules/", - "/meta_rules/", - "/meta_rules//" - ) - - @validate_input("get", kwargs_state=[False, False]) - @check_auth - def get(self, meta_rule_id=None, user_id=None): + @staticmethod + @hug.local() + @hug.get("/meta_rules/", requires=api_key_authentication) + @hug.get("/meta_rules/{meta_rule_id}", requires=api_key_authentication) + def get(meta_rule_id: hug.types.text = None, authed_user: hug.directives.user = None): """Retrieve all sub meta rules :param meta_rule_id: Meta rule algorithm ID @@ -52,19 +58,23 @@ class MetaRules(Resource): :internal_api: get_meta_rules """ - data = ModelManager.get_meta_rules( - user_id=user_id, meta_rule_id=meta_rule_id) + data = driver.ModelManager.get_meta_rules( + moon_user_id=authed_user, meta_rule_id=meta_rule_id) return {"meta_rules": data} - @validate_input("post", body_state={"name": True, "subject_categories": False, - "object_categories": False, "action_categories": False}) - @check_auth - def post(self, meta_rule_id=None, user_id=None): + @staticmethod + @hug.local() + @hug.post("/meta_rules/", requires=api_key_authentication) + # @validate_input("post", body_state={"name": True, "subject_categories": False, + # "object_categories": False, "action_categories": False}) + def post( + body: validate_input("name", "subject_categories", "object_categories", + "action_categories"), authed_user: hug.directives.user = None): """Add a meta rule - :param meta_rule_id: Meta rule ID (not used here) - :param user_id: user ID who do the request + :param body: body of the request + :param authed_user: user ID who do the request :request body: post = { "name": "name of the meta rule (mandatory)", "subject_categories": ["subject_category_id1 (mandatory)", @@ -86,20 +96,23 @@ class MetaRules(Resource): :internal_api: add_meta_rules """ - data = ModelManager.add_meta_rule( - user_id=user_id, meta_rule_id=None, value=request.json) + data = driver.ModelManager.add_meta_rule( + moon_user_id=authed_user, meta_rule_id=None, value=body) return {"meta_rules": data} - @validate_input("patch", kwargs_state=[True, False], - body_state={"name": True, "subject_categories": False, - "object_categories": False, "action_categories": False}) - @check_auth - def patch(self, meta_rule_id=None, user_id=None): + @staticmethod + @hug.local() + @hug.patch("/meta_rules/", requires=api_key_authentication) + @hug.patch("/meta_rules/{meta_rule_id}", requires=api_key_authentication) + def patch(body: validate_input("name", "subject_categories", "object_categories", + "action_categories"), meta_rule_id: hug.types.text = None, + authed_user: hug.directives.user = None): """Update a meta rule - :param meta_rule_id: Meta rule ID - :param user_id: user ID who do the request + :param body: body of the request + :param meta_rule_id: ID of the Meta Rule + :param authed_user: user ID who do the request :request body: patch = { "name": "name of the meta rule", "subject_categories": ["subject_category_id1", @@ -120,18 +133,24 @@ class MetaRules(Resource): } :internal_api: set_meta_rules """ - data = ModelManager.update_meta_rule( - user_id=user_id, meta_rule_id=meta_rule_id, value=request.json) + data = driver.ModelManager.update_meta_rule( + moon_user_id=authed_user, meta_rule_id=meta_rule_id, value=body) + + slaves = slave_class.Slaves.get().get("slaves") + invalidate_meta_rule_in_slaves(slaves=slaves, meta_rule_id=meta_rule_id,is_delete=False, + data=data) return {"meta_rules": data} - @validate_input("delete", kwargs_state=[True, False]) - @check_auth - def delete(self, meta_rule_id=None, user_id=None): + @staticmethod + @hug.local() + @hug.delete("/meta_rules/", requires=api_key_authentication) + @hug.delete("/meta_rules/{meta_rule_id}", requires=api_key_authentication) + def delete(meta_rule_id: hug.types.text = None, authed_user: hug.directives.user = None): """Delete a meta rule :param meta_rule_id: Meta rule ID - :param user_id: user ID who do the request + :param authed_user: user ID who do the request :return: { "meta_rules": { "meta_rule_id1": { @@ -146,7 +165,145 @@ class MetaRules(Resource): :internal_api: delete_meta_rules """ - data = ModelManager.delete_meta_rule( - user_id=user_id, meta_rule_id=meta_rule_id) + driver.ModelManager.delete_meta_rule( + moon_user_id=authed_user, meta_rule_id=meta_rule_id) + + slaves = slave_class.Slaves.get().get("slaves") + invalidate_meta_rule_in_slaves(slaves=slaves, meta_rule_id=meta_rule_id) return {"result": True} + + +MetaRulesAPI = hug.API(name='meta_rules', doc=MetaRules.__doc__) + + +@hug.object(name='meta_rules', version='1.0.0', api=MetaRulesAPI) +class MetaRulesCLI(object): + """An example of command like calls via an Object""" + + @staticmethod + @hug.object.cli + def list(name_or_id="", human: bool = False): + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + _meta_rules_req = requests.get("{}/meta_rules".format(db_conf.get("url")), + headers={"x-api-key": manager_api_key} + ) + if _meta_rules_req.status_code == 200: + if name_or_id: + _meta_rules = None + if name_or_id in _meta_rules_req.json().get("meta_rules"): + _meta_rules = _meta_rules_req.json().get("meta_rules").get(name_or_id) + else: + for _key in _meta_rules_req.json().get("meta_rules"): + _name = _meta_rules_req.json().get("meta_rules").get(_key).get("name") + if _name == name_or_id: + _meta_rules = _meta_rules_req.json().get("meta_rules").get(_key) + name_or_id = _key + break + if not _meta_rules: + raise Exception("Cannot find meta_rules with name or ID {}".format( + name_or_id)) + result = {"meta_rules": {name_or_id: _meta_rules}} + else: + result = _meta_rules_req.json() + + if human: + return MetaRulesCLI.human_display(result) + else: + return result + LOGGER.error('Cannot list meta_rules {}'.format(_meta_rules_req.status_code)) + + @staticmethod + @hug.object.cli + def add(name, subject_categories, object_categories, action_categories, human: bool = False): + """ + Add a meta rule in database + :return: JSON status output + """ + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + subject_categories_ids = [] + for cat in subject_categories.split(","): + _cat_dict = meta_data.SubjectCategoriesCLI.list(cat).get("subject_categories") + if cat in _cat_dict: + subject_categories_ids.append(cat) + else: + subject_categories_ids.append(list(_cat_dict.keys())[0]) + object_categories_ids = [] + for cat in object_categories.split(","): + _cat_dict = meta_data.ObjectCategoriesCLI.list(cat).get("object_categories") + if cat in _cat_dict: + object_categories_ids.append(cat) + else: + object_categories_ids.append(list(_cat_dict.keys())[0]) + action_categories_ids = [] + for cat in action_categories.split(","): + _cat_dict = meta_data.ActionCategoriesCLI.list(cat).get("action_categories") + if cat in _cat_dict: + action_categories_ids.append(cat) + else: + action_categories_ids.append(list(_cat_dict.keys())[0]) + _url = "{}/meta_rules".format(db_conf.get("url")) + req = requests.post( + _url, + json={ + "name": name, + "subject_categories": subject_categories_ids, + "object_categories": object_categories_ids, + "action_categories": action_categories_ids, + }, + headers={ + "x-api-key": manager_api_key, + "Content-Type": "application/json" + } + ) + if req.status_code == 200: + LOGGER.warning('Create {}'.format(req.content)) + if human: + return MetaRulesCLI.human_display(req.json()) + else: + return req.json() + LOGGER.error('Cannot create {}'.format(name, req.content[:40])) + + @staticmethod + @hug.object.cli + def delete(name_or_id): + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + _meta_rules = MetaRulesCLI.list() + for _id, _value in _meta_rules.get("meta_rules").items(): + if _id == name_or_id or _value.get("name") == name_or_id: + _url = "{}/meta_rules/{}".format(db_conf.get("url"), _id) + req = requests.delete( + _url, + headers={"x-api-key": manager_api_key} + ) + break + else: + LOGGER.error("Cannot find meta_rules with name {}".format(name_or_id)) + return False + if req.status_code == 200: + LOGGER.warning('Deleted {}'.format(name_or_id)) + return True + LOGGER.error("Cannot delete meta_rules with name {}".format(name_or_id)) + return False + + @staticmethod + def human_display(meta_rules_json): + human_result = "Meta Rules" + for metarule in meta_rules_json.get("meta_rules"): + human_result += "\n" + meta_rules_json.get("meta_rules").get(metarule).get("name") + "\n" + human_result += "\tid : " + metarule + "\n" + human_result += "\tname : " + meta_rules_json.get("meta_rules").get(metarule).get("name") + "\n" + human_result += "\tdescription : " + meta_rules_json.get("meta_rules").get(metarule).get("description") + "\n" + human_result += "\tsubject_categories :\n" + for subject_category in meta_rules_json.get("meta_rules").get(metarule).get("subject_categories"): + human_result += "\t\t" + subject_category + "\n" + human_result += "\tobject_categories :\n" + for object_category in meta_rules_json.get("meta_rules").get(metarule).get("object_categories"): + human_result += "\t\t" + object_category + "\n" + human_result += "\taction_categories :\n" + for action_category in meta_rules_json.get("meta_rules").get(metarule).get("action_categories"): + human_result += "\t\t" + action_category + "\n" + return human_result diff --git a/moon_manager/moon_manager/api/models.py b/moon_manager/moon_manager/api/models.py index c72396cf..e6f3dc2d 100644 --- a/moon_manager/moon_manager/api/models.py +++ b/moon_manager/moon_manager/api/models.py @@ -1,42 +1,47 @@ -# 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'. +# 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. + """ Models aggregate multiple meta rules """ -from flask import request -from flask_restful import Resource +import hug +import json import logging -from python_moonutilities.security_functions import check_auth -from python_moondb.core import ModelManager -from python_moonutilities.security_functions import validate_input +import requests +from moon_manager import db_driver as driver +from moon_utilities.security_functions import validate_input +from moon_utilities.auth_functions import api_key_authentication, connect_from_env +from moon_utilities.invalided_functions import invalidate_model_in_slaves +from moon_manager.api import slave as slave_class +from moon_manager.api import configuration -__version__ = "4.3.2" -logger = logging.getLogger("moon.manager.api." + __name__) +LOGGER = logging.getLogger("moon.manager.api." + __name__) -class Models(Resource): +class Models(object): """ Endpoint for model requests """ - __urls__ = ( - "/models", - "/models/", - "/models/", - "/models//", - ) - - @validate_input("get", kwargs_state=[False, False]) - @check_auth - def get(self, uuid=None, user_id=None): + @staticmethod + @hug.local() + @hug.get("/models", requires=api_key_authentication) + @hug.get("/models/{model_id}", requires=api_key_authentication) + def get(model_id: hug.types.text = None, moon_user_id=None): """Retrieve all models - :param uuid: uuid of the model - :param user_id: user ID who do the request + :param model_id: uuid of the model + :param moon_user_id: user ID who do the request :return: { "model_id1": { "name": "...", @@ -46,17 +51,17 @@ class Models(Resource): } :internal_api: get_models """ - data = ModelManager.get_models(user_id=user_id, model_id=uuid) - + data = driver.ModelManager.get_models(moon_user_id=moon_user_id, model_id=model_id) return {"models": data} - @validate_input("post", body_state={"name": True, "meta_rules": False}) - @check_auth - def post(self, uuid=None, user_id=None): + @staticmethod + @hug.local() + @hug.post("/models", requires=api_key_authentication) + def post(body: validate_input("name"), moon_user_id=None): """Create model. - :param uuid: uuid of the model (not used here) - :param user_id: user ID who do the request + :param body: body of the request + :param moon_user_id: user ID who do the request :request body: { "name": "name of the model (mandatory)", "description": "description of the model (optional)", @@ -71,18 +76,19 @@ class Models(Resource): } :internal_api: add_model """ - data = ModelManager.add_model( - user_id=user_id, model_id=uuid, value=request.json) + data = driver.ModelManager.add_model( + moon_user_id=moon_user_id, value=body) return {"models": data} - @validate_input("delete", kwargs_state=[True, False]) - @check_auth - def delete(self, uuid=None, user_id=None): + @staticmethod + @hug.local() + @hug.delete("/models/{model_id}", requires=api_key_authentication) + def delete(model_id: hug.types.text, moon_user_id=None): """Delete a model - :param uuid: uuid of the model to delete - :param user_id: user ID who do the request + :param model_id: uuid of the model to delete + :param moon_user_id: user ID who do the request :return: { "result": "True or False", "message": "optional message (optional)" @@ -90,18 +96,22 @@ class Models(Resource): :internal_api: delete_model """ - data = ModelManager.delete_model(user_id=user_id, model_id=uuid) + driver.ModelManager.delete_model(moon_user_id=moon_user_id, model_id=model_id) + + slaves = slave_class.Slaves.get().get("slaves") + invalidate_model_in_slaves(slaves=slaves, model_id=model_id) return {"result": True} - @validate_input("patch", kwargs_state=[True, False], - body_state={"name": True, "meta_rules": False}) - @check_auth - def patch(self, uuid=None, user_id=None): + @staticmethod + @hug.local() + @hug.patch("/models/{model_id}", requires=api_key_authentication) + def patch(body: validate_input("name"), model_id: hug.types.text, moon_user_id=None): """Update a model - :param uuid: uuid of the model to update - :param user_id: user ID who do the request + :param body: body of the request + :param model_id: uuid of the model to update + :param moon_user_id: user ID who do the request :return: { "model_id1": { "name": "name of the model", @@ -111,7 +121,136 @@ class Models(Resource): } :internal_api: update_model """ - data = ModelManager.update_model( - user_id=user_id, model_id=uuid, value=request.json) + data = driver.ModelManager.update_model( + moon_user_id=moon_user_id, model_id=model_id, value=body) + + slaves = slave_class.Slaves.get().get("slaves") + invalidate_model_in_slaves(slaves=slaves, model_id=model_id, is_delete=False, + data=data[model_id]) return {"models": data} + + +ModelsAPI = hug.API(name='models', doc=Models.__doc__) + + +@hug.object(name='models', version='1.0.0', api=ModelsAPI) +class ModelsCLI(object): + """An example of command like calls via an Object""" + + @staticmethod + @hug.object.cli + def list(name_or_id="", human: bool = False): + """ + List models from the database + :return: JSON status output + """ + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + _models_req = requests.get("{}/models".format(db_conf.get("url")), + headers={"x-api-key": manager_api_key} + ) + if _models_req.status_code == 200: + if name_or_id: + _models = None + if name_or_id in _models_req.json().get("models"): + _models = _models_req.json().get("models").get(name_or_id) + else: + for _models_key in _models_req.json().get("models"): + _name = _models_req.json().get("models").get(_models_key).get("name") + if _name == name_or_id: + _models = _models_req.json().get("models").get(_models_key) + name_or_id = _models_key + break + if not _models: + raise Exception("Cannot find model with name or ID {}".format(name_or_id)) + else: + if human: + result = {"models": {name_or_id: _models}} + else: + result = {"models": [{name_or_id: _models}]} + else: + result = _models_req.json() + + if human: + return ModelsCLI.human_display(result) + else: + return result + LOGGER.error('Cannot list Models {}'.format(_models_req.status_code)) + + + @staticmethod + @hug.object.cli + def add(name, meta_rule, description="", human: bool = False): + """ + Add model in the database + :return: JSON status output + """ + from moon_manager.api import meta_rules + + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + + _meta_rules = meta_rules.MetaRules.get()["meta_rules"] + _meta_rules_by_name = {_meta_rules[i]["name"]: i for i in _meta_rules} + + if meta_rule in _meta_rules_by_name: + meta_rule_id = _meta_rules_by_name[meta_rule] + elif meta_rule in _meta_rules: + meta_rule_id = meta_rule + else: + raise Exception("Cannot find meta_rule with name or ID {}".format(meta_rule)) + + _models = requests.post( + "{}/models".format(db_conf.get("url")), + json={ + "name": name, + "description": description, + "meta_rules": [meta_rule_id] + }, + headers={ + "x-api-key": manager_api_key, + "Content-Type": "application/json" + } + ) + if _models.status_code == 200: + LOGGER.warning('Create {}'.format(_models.content)) + if human: + return ModelsCLI.human_display(_models.json()) + else: + return _models.json() + LOGGER.error('Cannot create {}'.format(name, _models.content)) + + @staticmethod + @hug.object.cli + def delete(name='default'): + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + _models = ModelsCLI.list() + for _slave_id, _slave_value in _models.get("models").items(): + if _slave_value.get("name") == name: + req = requests.delete( + "{}/models/{}".format(db_conf.get("url"), _slave_id), + headers={"x-api-key": manager_api_key} + ) + break + else: + LOGGER.error("Cannot find model with name {}".format(name)) + return + if req.status_code == 200: + LOGGER.warning('Deleted {}'.format(name)) + return True + LOGGER.error("Cannot delete model with name {}".format(name)) + + @staticmethod + def human_display(models_json): + human_result = "Models" + for model in models_json.get("models"): + human_result += "\n" + models_json.get("models").get(model).get("name") + " : \n" + human_result += "\tname : " + models_json.get("models").get(model).get("name") + "\n" + human_result += "\tid : " + model + "\n" + human_result += "\tdescription : " + models_json.get("models").get(model).get("description") + "\n" + human_result += "\tmeta_rules : \n" + for meta_rule in models_json.get("models").get(model).get("meta_rules"): + human_result += "\t\tid : " + meta_rule + "\n" + return human_result diff --git a/moon_manager/moon_manager/api/orchestration/__init__.py b/moon_manager/moon_manager/api/orchestration/__init__.py new file mode 100644 index 00000000..1856aa2c --- /dev/null +++ b/moon_manager/moon_manager/api/orchestration/__init__.py @@ -0,0 +1,12 @@ +# 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_manager/moon_manager/api/orchestration/managers.py b/moon_manager/moon_manager/api/orchestration/managers.py new file mode 100644 index 00000000..b4776ec2 --- /dev/null +++ b/moon_manager/moon_manager/api/orchestration/managers.py @@ -0,0 +1,21 @@ +# 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 logging + +logger = logging.getLogger("moon.api.orchestration.managers") + + +class Managers(object): + """Object that links managers together""" + SlaveManager = None + PipelineManager = None diff --git a/moon_manager/moon_manager/api/orchestration/pipeline.py b/moon_manager/moon_manager/api/orchestration/pipeline.py new file mode 100644 index 00000000..32d8a1f9 --- /dev/null +++ b/moon_manager/moon_manager/api/orchestration/pipeline.py @@ -0,0 +1,44 @@ +# 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 uuid import uuid4 +import logging +from moon_utilities import exceptions +from moon_utilities.security_functions import enforce +from moon_manager.api.orchestration.managers import Managers + +logger = logging.getLogger("moon.manager.api.orchestration.pod") + + +class PipelineManager(Managers): + + def __init__(self, connector=None): + self.driver = connector.driver + Managers.PipelineManager = self + + @enforce(("read", "write"), "pipelines") + def update_pipeline(self, moon_user_id, pipeline_id, data): + return self.driver.update_pipeline(pipeline_id=pipeline_id, data=data) + + @enforce("write", "pipelines") + def delete_pipeline(self, moon_user_id, pipeline_id): + return self.driver.delete_pipeline(pipeline_id=pipeline_id) + + @enforce("write", "pipelines") + def add_pipeline(self, moon_user_id, pipeline_id=None, data=None): + if not pipeline_id: + pipeline_id = uuid4().hex + return self.driver.add_pipeline(pipeline_id=pipeline_id, data=data) + + @enforce("read", "pipelines") + def get_pipelines(self, moon_user_id, pipeline_id=None): + return self.driver.get_pipelines(pipeline_id=pipeline_id) diff --git a/moon_manager/moon_manager/api/orchestration/slave.py b/moon_manager/moon_manager/api/orchestration/slave.py new file mode 100644 index 00000000..200dd3d6 --- /dev/null +++ b/moon_manager/moon_manager/api/orchestration/slave.py @@ -0,0 +1,44 @@ +# 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 uuid import uuid4 +import logging +from moon_utilities import exceptions +from moon_utilities.security_functions import enforce +from moon_manager.api.orchestration.managers import Managers + +logger = logging.getLogger("moon.manager.api.orchestration.pod") + + +class SlaveManager(Managers): + + def __init__(self, connector=None): + self.driver = connector.driver + Managers.SlaveManager = self + + @enforce(("read", "write"), "slaves") + def update_slave(self, moon_user_id, slave_id, value): + return self.driver.update_slave(slave_id=slave_id, value=value) + + @enforce("write", "slaves") + def delete_slave(self, moon_user_id, slave_id): + self.driver.delete_slave(slave_id=slave_id) + + @enforce("write", "slaves") + def add_slave(self, moon_user_id, slave_id=None, data=None): + if not slave_id: + slave_id = uuid4().hex + return self.driver.add_slave(slave_id=slave_id, data=data) + + @enforce("read", "slaves") + def get_slaves(self, moon_user_id, slave_id=None): + return self.driver.get_slaves(slave_id=slave_id) diff --git a/moon_manager/moon_manager/api/pdp.py b/moon_manager/moon_manager/api/pdp.py index 65a6a5f1..6f0b5214 100644 --- a/moon_manager/moon_manager/api/pdp.py +++ b/moon_manager/moon_manager/api/pdp.py @@ -1,214 +1,398 @@ -# 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'. +# 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. + """ -PDP are Policy Decision Point. +PDP are Policy Decision Points. """ -from flask import request -from flask_restful import Resource +import hug +import json import logging import requests -import time -from python_moonutilities.security_functions import check_auth -from python_moondb.core import PDPManager -from python_moondb.core import PolicyManager -from python_moondb.core import ModelManager -from python_moonutilities import configuration, exceptions -from python_moonutilities.security_functions import validate_input - -__version__ = "4.3.2" - -logger = logging.getLogger("moon.manager.api." + __name__) - - -def delete_pod(uuid): - conf = configuration.get_configuration("components/orchestrator") - hostname = conf["components/orchestrator"].get("hostname", "orchestrator") - port = conf["components/orchestrator"].get("port", 80) - proto = conf["components/orchestrator"].get("protocol", "http") - # while True: - # try: - url = "{}://{}:{}/pods".format(proto, hostname, port) - req = requests.get(url) - # except requests.exceptions.ConnectionError: - # logger.warning("Orchestrator is not ready, standby... {}".format(url)) - # time.sleep(1) - # else: - # break - for pod_key, pod_list in req.json().get("pods", {}).items(): - for pod_value in pod_list: - if "pdp_id" in pod_value: - if pod_value["pdp_id"] == uuid: - req = requests.delete( - "{}://{}:{}/pods/{}".format(proto, hostname, port, pod_key)) - if req.status_code != 200: - logger.warning( - "Cannot delete pod {} - {}".format(pod_key, pod_value['name'])) - logger.debug(req.content) - # Note (Asteroide): no need to go further if one match - break - - -def add_pod(uuid, data): - if not data.get("keystone_project_id"): - return - logger.info("Add a new pod {}".format(data)) - if "pdp_id" not in data: - data["pdp_id"] = uuid - data['policies'] = PolicyManager.get_policies(user_id="admin") - data['models'] = ModelManager.get_models(user_id="admin") - conf = configuration.get_configuration("components/orchestrator") - hostname = conf["components/orchestrator"].get("hostname", "orchestrator") - port = conf["components/orchestrator"].get("port", 80) - proto = conf["components/orchestrator"].get("protocol", "http") - while True: - try: - req = requests.post( - "{}://{}:{}/pods".format(proto, hostname, port), - json=data, - headers={"content-type": "application/json"}) - except requests.exceptions.ConnectionError as e: - logger.warning("add_pod: Orchestrator is not ready, standby...") - logger.exception(e) - time.sleep(1) - else: - break - logger.info("Pod add request answer : {}".format(req.text)) +from moon_manager.api import ERROR_CODE +from moon_manager import db_driver +from moon_utilities.auth_functions import api_key_authentication, connect_from_env +from moon_manager import orchestration_driver +from moon_utilities import exceptions +from moon_utilities.security_functions import validate_input +from moon_utilities.invalided_functions import invalidate_pdp_in_slaves +from moon_manager.api import slave as slave_class +from moon_manager.api import configuration - -def check_keystone_pid(k_pid): - data = PDPManager.get_pdp(user_id="admin") - for pdp_key, pdp_value in data.items(): - logger.info("pdp={}".format(pdp_value)) - if pdp_value["keystone_project_id"] == k_pid: - return True +LOGGER = logging.getLogger("moon.manager.api." + __name__) -class PDP(Resource): +class PDP(object): """ Endpoint for pdp requests """ - __urls__ = ( - "/pdp", - "/pdp/", - "/pdp/", - "/pdp//", - ) - - @validate_input("get", kwargs_state=[False, False]) - @check_auth - def get(self, uuid=None, user_id=None): + @staticmethod + @hug.local() + @hug.get("/pdp/", requires=api_key_authentication) + @hug.get("/pdp/{uuid}", requires=api_key_authentication) + def get(uuid: hug.types.uuid = None, authed_user: hug.directives.user = None): """Retrieve all pdp :param uuid: uuid of the pdp - :param user_id: user ID who do the request + :param authed_user: the name of the authenticated user :return: { "pdp_id1": { "name": "...", "security_pipeline": [...], - "keystone_project_id": "keystone_project_id1", + "vim_project_id": "vim_project_id1", "description": "... (optional)", } } :internal_api: get_pdp """ - - data = PDPManager.get_pdp(user_id=user_id, pdp_id=uuid) + if uuid: + uuid = str(uuid).replace("-", "") + data = db_driver.PDPManager.get_pdp(moon_user_id=authed_user, pdp_id=uuid) return {"pdps": data} - @validate_input("post", body_state={"name": True, "security_pipeline": True, - "keystone_project_id": True}) - @check_auth - def post(self, uuid=None, user_id=None): + @staticmethod + @hug.local() + @hug.post("/pdp/", requires=api_key_authentication) + def post(body: validate_input("name"), response, authed_user: hug.directives.user = None): """Create pdp. - :param uuid: uuid of the pdp (not used here) - :param user_id: user ID who do the request + :param body: preformed body from Hug + :param response: preformed response from Hug + :param authed_user: the name of the authenticated user :request body: { "name": "name of the PDP (mandatory)", "security_pipeline": ["may be empty"], - "keystone_project_id": "keystone_project_id1 (may be empty)", + "vim_project_id": "vim_project_id1 (may be empty)", "description": "description of the PDP (optional)", } :return: { "pdp_id1": { "name": "...", "security_pipeline": [...], - "keystone_project_id": "keystone_project_id1", + "vim_project_id": "vim_project_id1", "description": "... (optional)", } } :internal_api: add_pdp """ - - data = dict(request.json) - if not data.get("keystone_project_id"): - data["keystone_project_id"] = None - else: - if check_keystone_pid(data.get("keystone_project_id")): - raise exceptions.PdpKeystoneMappingConflict - data = PDPManager.add_pdp( - user_id=user_id, pdp_id=None, value=request.json) + if not body.get("security_pipeline"): + body["security_pipeline"] = [] + if not body.get("vim_project_id"): + body["vim_project_id"] = None + data = db_driver.PDPManager.add_pdp( + moon_user_id="admin", pdp_id=None, value=body) uuid = list(data.keys())[0] - logger.debug("data={}".format(data)) - logger.debug("uuid={}".format(uuid)) - add_pod(uuid=uuid, data=data[uuid]) - - return {"pdps": data} + if body["vim_project_id"] and body["security_pipeline"]: + orchestration_driver.PipelineManager.add_pipeline( + moon_user_id=authed_user, pipeline_id=uuid, data=data[uuid]) + return {"pdps": db_driver.PDPManager.get_pdp(moon_user_id=authed_user, pdp_id=uuid)} - @validate_input("delete", kwargs_state=[True, False]) - @check_auth - def delete(self, uuid, user_id=None): + @staticmethod + @hug.local() + @hug.delete("/pdp/{uuid}", requires=api_key_authentication) + def delete(uuid: hug.types.uuid, response=None, authed_user: hug.directives.user = None): """Delete a pdp :param uuid: uuid of the pdp to delete - :param user_id: user ID who do the request + :param response: preformed response from Hug + :param authed_user: the name of the authenticated user :return: { "result": "True or False", "message": "optional message (optional)" } :internal_api: delete_pdp """ - data = PDPManager.delete_pdp(user_id=user_id, pdp_id=uuid) - delete_pod(uuid) + uuid = str(uuid).replace("-", "") + data = db_driver.PDPManager.delete_pdp(moon_user_id=authed_user, pdp_id=uuid) + LOGGER.info(data) + + orchestration_driver.PipelineManager.delete_pipeline(moon_user_id=authed_user, pipeline_id=uuid) + slaves = slave_class.Slaves.get().get("slaves") + invalidate_pdp_in_slaves(slaves=slaves, pdp_id=uuid) return {"result": True} - @validate_input("patch", kwargs_state=[True, False], - body_state={"name": True, "security_pipeline": True, - "keystone_project_id": True}) - @check_auth - def patch(self, uuid, user_id=None): + @staticmethod + @hug.local() + @hug.patch("/pdp/{uuid}", requires=api_key_authentication) + def patch(uuid: hug.types.uuid, body: validate_input("name"), response, + authed_user: hug.directives.user = None): """Update a pdp - :param uuid: uuid of the pdp to update - :param user_id: user ID who do the request + :param uuid: uuid of the pdp to delete + :param body: preformed body from Hug + :param response: preformed response from Hug + :param authed_user: the name of the authenticated user :return: { "pdp_id1": { "name": "name of the PDP", "security_pipeline": ["may be empty"], - "keystone_project_id": "keystone_project_id1 (may be empty)", + "vim_project_id": "vim_project_id1 (may be empty)", "description": "description of the PDP (optional)", } } :internal_api: update_pdp """ - _data = dict(request.json) - if not _data.get("keystone_project_id"): - _data["keystone_project_id"] = None + uuid = str(uuid).replace("-", "") + prev_data = db_driver.PDPManager.get_pdp(moon_user_id=authed_user, pdp_id=uuid) + if not prev_data: + response.status = ERROR_CODE[400] + return {"message": "The PDP is unknown."} + + data = db_driver.PDPManager.update_pdp(moon_user_id=authed_user, pdp_id=uuid, value=body).get(uuid) + + orchestration_driver.PipelineManager.update_pipeline(moon_user_id=authed_user, pipeline_id=uuid, data=data) + slaves = slave_class.Slaves.get().get("slaves") + invalidate_pdp_in_slaves(slaves=slaves, pdp_id=uuid, is_delete=False, data=data) + + return {"pdps": db_driver.PDPManager.get_pdp(moon_user_id=authed_user, pdp_id=uuid)} + + +PDPAPI = hug.API(name='pdps', doc=PDP.__doc__) + + +@hug.object(name='pdps', version='1.0.0', api=PDPAPI) +class PDPCLI(object): + """An example of command like calls via an Object""" + + @staticmethod + @hug.object.cli + def list(name_or_id="", human: bool = False): + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + _pdps = requests.get("{}/pdp".format(db_conf.get("url")), + headers={"x-api-key": manager_api_key} + ) + if _pdps.status_code == 200: + if name_or_id: + _pdp = None + if name_or_id in _pdps.json().get("pdps"): + _pdp = _pdps.json().get("pdps").get(name_or_id) + else: + for _pdp_key in _pdps.json().get("pdps"): + if _pdps.json().get("pdps").get(_pdp_key).get("name") == name_or_id: + _pdp = _pdps.json().get("pdps").get(_pdp_key) + name_or_id = _pdp_key + break + if not _pdp: + raise Exception("Cannot find PDP with name or ID {}".format(name_or_id)) + else: + if human: + result = {"pdps": {name_or_id: _pdp}} + else: + result = {"pdps": [{name_or_id: _pdp}]} + else: + result = _pdps.json() + + if human: + return PDPCLI.human_display(result) + else: + return result + LOGGER.error('Cannot list PDP {}'.format(_pdps.status_code)) + + @staticmethod + @hug.object.cli + def add(name, description="", security_pipeline="", vim_project_id="", human: bool = False): + """ + Add pdp in the database + :return: JSON status output + """ + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + security_pipeline = security_pipeline.split(",") + _pdps = requests.post( + "{}/pdp".format(db_conf.get("url")), + json={ + "name": name, + "security_pipeline": security_pipeline, + "vim_project_id": vim_project_id, + "description": description, + }, + headers={ + "x-api-key": manager_api_key, + "Content-Type": "application/json" + } + ) + if _pdps.status_code == 200: + LOGGER.warning('Create {}'.format(_pdps.content)) + if human: + return PDPCLI.human_display(_pdps.json()) + else: + return _pdps.json() + LOGGER.error('Cannot create {}'.format(name, _pdps.content[:40])) + + @staticmethod + @hug.object.cli + def delete(name='default'): + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + _pdps = PDPCLI.list() + for _slave_id, _slave_value in _pdps.get("pdps").items(): + if _slave_value.get("name") == name: + req = requests.delete( + "{}/pdp/{}".format(db_conf.get("url"), _slave_id), + headers={"x-api-key": manager_api_key} + ) + break else: - if check_keystone_pid(_data.get("keystone_project_id")): - raise exceptions.PdpKeystoneMappingConflict - data = PDPManager.update_pdp( - user_id=user_id, pdp_id=uuid, value=_data) - logger.debug("data={}".format(data)) - logger.debug("uuid={}".format(uuid)) - add_pod(uuid=uuid, data=data[uuid]) + LOGGER.error("Cannot find PDP with name {}".format(name)) + return False + if req.status_code == 200: + LOGGER.warning('Deleted {}'.format(name)) + return True + LOGGER.error("Cannot delete PDP with name {}".format(name)) + return False - return {"pdps": data} + @staticmethod + @hug.object.cli + def update(name, description=None, security_pipeline=None, vim_project_id=None): + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + _pdps = PDPCLI.list() + + for _slave_id, _slave_value in _pdps.get("pdps").items(): + if _slave_value.get("name") == name: + description_updated = _slave_value.get("description") + security_pipeline_updated = _slave_value.get("security_pipeline") + vim_project_id_updated = _slave_value.get("vim_project_id") + + if description is not None: + description_updated = description + if security_pipeline is not None: + if security_pipeline == "": + LOGGER.error(f"Policy given to update the PDP {name} is unknown") + return + else: + security_pipeline_updated = security_pipeline.split(",") + if vim_project_id is not None: + vim_project_id_updated = vim_project_id + + req = requests.patch( + "{}/pdp/{}".format(db_conf.get("url"), _slave_id), + json={ + "name": name, + "security_pipeline": security_pipeline_updated, + "vim_project_id": vim_project_id_updated, + "description": description_updated, + }, + headers={ + "x-api-key": manager_api_key, + "Content-Type": "application/json" + } + ) + + if req.status_code == 200: + LOGGER.warning('Updated {}'.format(name)) + return True + LOGGER.error('Cannot update PDP {}'.format(req.status_code)) + return False + + @staticmethod + def human_display(pdps_json): + human_result = "PDPs" + for pdp in pdps_json.get("pdps"): + human_result += "\n" + pdps_json.get("pdps").get(pdp).get("name") + " : \n" + human_result += "\tname : " + pdps_json.get("pdps").get(pdp).get("name") + "\n" + human_result += "\tid : " + pdp + "\n" + human_result += "\tdescription : " + pdps_json.get("pdps").get(pdp).get("description") + "\n" + human_result += "\tvim_project_id : " + pdps_json.get("pdps").get(pdp).get("vim_project_id") + "\n" + human_result += "\tsecurity_pipeline : \n" + for security_pipeline in pdps_json.get("pdps").get(pdp).get("security_pipeline"): + human_result += "\t\t" + security_pipeline + "\n" + return human_result + + # FIXME: not tested + # @staticmethod + # @hug.object.cli + # def set_project(pdp_name, project_id): + # db_conf = configuration.get_configuration(key='management') + # manager_api_key = configuration.get_api_key_for_user("admin") + # _pdp = PDPCLI.get(pdp_name) + # _pdp_id = list(_pdp.get("pdps")[0].keys())[0] + # _pdp_name = _pdp.get("pdps")[0].get(_pdp_id).get("name") + # _pdps = requests.patch( + # "{}/pdp/{}".format(db_conf.get("url"), _pdp_id), + # json={ + # "name": _pdp_name, + # "vim_project_id": project_id, + # }, + # headers={ + # "x-api-key": manager_api_key, + # "Content-Type": "application/json" + # } + # ) + # if _pdps.status_code == 200: + # LOGGER.warning('Set project {}'.format(_pdps.content)) + # return _pdps.json() + # LOGGER.error('Cannot set project {} (error: {})'.format(project_id, _pdps.status_code)) + # return 'Cannot set project {} (error: {})'.format(project_id, _pdps.status_code) + # + # @staticmethod + # @hug.object.cli + # def add_pipeline(pdp_name, pipeline_id): + # db_conf = configuration.get_configuration(key='management') + # manager_api_key = configuration.get_api_key_for_user("admin") + # _pdp = PDPCLI.get(pdp_name) + # _pdp_id = list(_pdp.get("pdps")[0].keys())[0] + # _pdp_name = _pdp.get("pdps")[0].get(_pdp_id).get("name") + # _pdp_pipelines = _pdp.get("pdps")[0].get(_pdp_id).get("security_pipeline", []) + # # TODO check if pipeline exists + # _pdp_pipelines.append(pipeline_id) + # _pdps = requests.patch( + # "{}/pdp/{}".format(db_conf.get("url"), _pdp_id), + # json={ + # "name": _pdp_name, + # "security_pipeline": _pdp_pipelines, + # }, + # headers={ + # "x-api-key": manager_api_key, + # "Content-Type": "application/json" + # } + # ) + # if _pdps.status_code == 200: + # LOGGER.warning('Set project {}'.format(_pdps.content)) + # return _pdps.json() + # LOGGER.error('Cannot add security pipeline {} (error: {})'.format(pipeline_id, + # _pdps.status_code)) + # return 'Cannot add security pipeline {} (error: {})'.format(pipeline_id, _pdps.content) + # + # @staticmethod + # @hug.object.cli + # def delete_pipeline(pdp_name, pipeline_id): + # db_conf = configuration.get_configuration(key='management') + # manager_api_key = configuration.get_api_key_for_user("admin") + # _pdp = PDPCLI.get(pdp_name) + # _pdp_id = list(_pdp.get("pdps")[0].keys())[0] + # _pdp_name = _pdp.get("pdps")[0].get(_pdp_id).get("name") + # _pdp_pipelines = _pdp.get("pdps")[0].get(_pdp_id).get("security_pipeline") + # # TODO check if pipeline exists + # _pdp_pipelines.remove(pipeline_id) + # _pdps = requests.patch( + # "{}/pdp/{}".format(db_conf.get("url"), _pdp_id), + # json={ + # "name": _pdp_name, + # "security_pipeline": _pdp_pipelines, + # }, + # headers={ + # "x-api-key": manager_api_key, + # "Content-Type": "application/json" + # } + # ) + # if _pdps.status_code == 200: + # LOGGER.warning('Set project {}'.format(_pdps.content)) + # return _pdps.json() + # LOGGER.error('Cannot add security pipeline {} (error: {})'.format(pipeline_id, + # _pdps.status_code)) + # return 'Cannot add security pipeline {} (error: {})'.format(pipeline_id, _pdps.status_code) diff --git a/moon_manager/moon_manager/api/perimeter.py b/moon_manager/moon_manager/api/perimeter.py index a0fda4ad..98bb2769 100644 --- a/moon_manager/moon_manager/api/perimeter.py +++ b/moon_manager/moon_manager/api/perimeter.py @@ -1,7 +1,15 @@ -# 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'. +# 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. + + """ * Subjects are the source of an action on an object (examples : users, virtual machines) @@ -9,42 +17,40 @@ (examples virtual machines, virtual Routers) * Actions are what subject wants to do on an object """ - -from flask import request -from flask_restful import Resource +import hug import logging -from python_moonutilities.security_functions import check_auth -from python_moondb.core import PolicyManager -from python_moonutilities.security_functions import validate_input +import requests +from moon_manager import db_driver as driver +from moon_utilities import exceptions +from moon_utilities.security_functions import validate_input +from moon_utilities.auth_functions import api_key_authentication, connect_from_env +from moon_utilities.invalided_functions import invalidate_perimeter_in_slaves +from moon_manager.api import slave as slave_class +from moon_manager.api import configuration +from moon_manager.api import policy -__version__ = "4.3.2" +LOGGER = logging.getLogger("moon.manager.api." + __name__) -logger = logging.getLogger("moon.manager.api." + __name__) - -class Subjects(Resource): +class Subjects(object): """ Endpoint for subjects requests """ - __urls__ = ( - "/subjects", - "/subjects/", - "/subjects/", - "/policies//subjects", - "/policies//subjects/", - "/policies//subjects/", - ) - - @validate_input("get", kwargs_state=[False, False, False]) - @check_auth - def get(self, uuid=None, perimeter_id=None, user_id=None): + @staticmethod + @hug.local() + @hug.get("/subjects/", requires=api_key_authentication) + @hug.get("/subjects/{perimeter_id}", requires=api_key_authentication) + @hug.get("/policies/{uuid}/subjects/", requires=api_key_authentication) + @hug.get("/policies/{uuid}/subjects/{perimeter_id}", requires=api_key_authentication) + def get(uuid: hug.types.text = None, perimeter_id: hug.types.text = None, + authed_user: hug.directives.user = None): """Retrieve all subjects or a specific one if perimeter_id is given for a given policy :param uuid: uuid of the policy :param perimeter_id: uuid of the subject - :param user_id: user ID who do the request + :param authed_user: user ID who do the request :return: { "subject_id": { "name": "name of the subject", @@ -55,22 +61,29 @@ class Subjects(Resource): :internal_api: get_subjects """ - data = PolicyManager.get_subjects( - user_id=user_id, + # data = {"policy_id": str(uuid), "perimeter_id": str(perimeter_id)} + data = driver.PolicyManager.get_subjects( + moon_user_id=authed_user, policy_id=uuid, perimeter_id=perimeter_id ) - + # logger.info(db_driver.PolicyManager.get_subjects(policy_id=str(uuid))) return {"subjects": data} - @validate_input("post", body_state={"name": True}) - @check_auth - def post(self, uuid=None, perimeter_id=None, user_id=None): + @staticmethod + @hug.local() + @hug.post("/newsubject", requires=api_key_authentication) + @hug.post("/subjects/{perimeter_id}", requires=api_key_authentication) + @hug.post("/policies/{uuid}/subjects/", requires=api_key_authentication) + @hug.post("/policies/{uuid}/subjects/{perimeter_id}", requires=api_key_authentication) + def post(body: validate_input("name"), uuid: hug.types.text = None, perimeter_id: + hug.types.text = None, authed_user: hug.directives.user = None): """Create or update a subject. + :param body: body of the request :param uuid: uuid of the policy :param perimeter_id: must not be used here - :param user_id: user ID who do the request + :param authed_user: user ID who do the request :request body: { "name": "name of the subject (mandatory)", "description": "description of the subject (optional)", @@ -89,20 +102,25 @@ class Subjects(Resource): :internal_api: set_subject """ - data = PolicyManager.add_subject( - user_id=user_id, policy_id=uuid, - perimeter_id=perimeter_id, value=request.json) + if 'policy_list' in body: + raise exceptions.PerimeterContentError("body should not contain policy_list") + # data = {"policy_id": uuid, "perimeter_id": perimeter_id} + data = driver.PolicyManager.add_subject(moon_user_id=authed_user, + policy_id=uuid, + perimeter_id=perimeter_id, + value=body) return {"subjects": data} - @validate_input("patch", kwargs_state=[False, True, False]) - @check_auth - def patch(self, uuid=None, perimeter_id=None, user_id=None): + @staticmethod + @hug.local() + @hug.patch("/subjects/{perimeter_id}", requires=api_key_authentication) + def patch(body, perimeter_id: hug.types.text = None, authed_user: hug.directives.user = None): """Create or update a subject. - :param uuid: uuid of the policy + :param body: body of the request :param perimeter_id: must not be used here - :param user_id: user ID who do the request + :param authed_user: user ID who do the request :request body: { "name": "name of the subject", "description": "description of the subject (optional)", @@ -120,13 +138,24 @@ class Subjects(Resource): } :internal_api: set_subject """ - data = PolicyManager.update_subject(user_id=user_id, perimeter_id=perimeter_id, - value=request.json) + # data = {"policy_id": uuid, "perimeter_id": perimeter_id} + data = driver.PolicyManager.update_subject(moon_user_id=authed_user, + perimeter_id=perimeter_id, + value=body) + + slaves = slave_class.Slaves.get().get("slaves") + invalidate_perimeter_in_slaves(slaves=slaves, policy_id=None, perimeter_id=perimeter_id, + type="subject", data=data[perimeter_id], is_delete=False) + return {"subjects": data} - @validate_input("delete", kwargs_state=[False, True, False]) - @check_auth - def delete(self, uuid=None, perimeter_id=None, user_id=None): + @staticmethod + @hug.local() + @hug.delete("/subjects/", requires=api_key_authentication) + @hug.delete("/subjects/{perimeter_id}", requires=api_key_authentication) + @hug.delete("/policies/{uuid}/subjects/{perimeter_id}", requires=api_key_authentication) + def delete(uuid: hug.types.text = None, perimeter_id: hug.types.text = None, + authed_user: hug.directives.user = None): """Delete a subject for a given policy :param uuid: uuid of the policy (mandatory if perimeter_id is not set) @@ -144,29 +173,30 @@ class Subjects(Resource): :internal_api: delete_subject """ - data = PolicyManager.delete_subject( - user_id=user_id, policy_id=uuid, perimeter_id=perimeter_id) + # data = {"policy_id": uuid, "perimeter_id": perimeter_id} + data = driver.PolicyManager.delete_subject( + moon_user_id=authed_user, policy_id=uuid, perimeter_id=perimeter_id) + + slaves = slave_class.Slaves.get().get("slaves") + invalidate_perimeter_in_slaves(slaves=slaves, policy_id=uuid, perimeter_id=perimeter_id, + type="subject" ) return {"result": True} -class Objects(Resource): +class Objects(object): """ Endpoint for objects requests """ - __urls__ = ( - "/objects", - "/objects/", - "/objects/", - "/policies//objects", - "/policies//objects/", - "/policies//objects/", - ) - - @validate_input("get", kwargs_state=[False, False, False]) - @check_auth - def get(self, uuid=None, perimeter_id=None, user_id=None): + @staticmethod + @hug.local() + @hug.get("/objects/", requires=api_key_authentication) + @hug.get("/objects/{perimeter_id}", requires=api_key_authentication) + @hug.get("/policies/{uuid}/objects/", requires=api_key_authentication) + @hug.get("/policies/{uuid}/objects/{perimeter_id}", requires=api_key_authentication) + def get(uuid: hug.types.text = None, perimeter_id: hug.types.text = None, + authed_user: hug.directives.user = None): """Retrieve all objects or a specific one if perimeter_id is given for a given policy @@ -182,17 +212,22 @@ class Objects(Resource): :internal_api: get_objects """ - data = PolicyManager.get_objects( - user_id=user_id, - policy_id=uuid, - perimeter_id=perimeter_id - ) + data = driver.PolicyManager.get_objects(moon_user_id=authed_user, + policy_id=uuid, + perimeter_id=perimeter_id + ) return {"objects": data} - @validate_input("post", body_state={"name": True}) - @check_auth - def post(self, uuid=None, perimeter_id=None, user_id=None): + # + @staticmethod + @hug.local() + @hug.post("/newobject", requires=api_key_authentication) + @hug.post("/objects/{perimeter_id}", requires=api_key_authentication) + @hug.post("/policies/{uuid}/objects/", requires=api_key_authentication) + @hug.post("/policies/{uuid}/objects/{perimeter_id}", requires=api_key_authentication) + def post(body: validate_input("name"), uuid: hug.types.text = None, perimeter_id: + hug.types.text = None, authed_user: hug.directives.user = None): """Create or update a object. :param uuid: uuid of the policy @@ -210,15 +245,16 @@ class Objects(Resource): } :internal_api: set_object """ - data = PolicyManager.add_object( - user_id=user_id, policy_id=uuid, - perimeter_id=perimeter_id, value=request.json) + data = driver.PolicyManager.add_object(moon_user_id=authed_user, + policy_id=uuid, + perimeter_id=perimeter_id, value=body) return {"objects": data} - @validate_input("patch", kwargs_state=[False, True, False]) - @check_auth - def patch(self, uuid=None, perimeter_id=None, user_id=None): + @staticmethod + @hug.local() + @hug.patch("/objects/{perimeter_id}", requires=api_key_authentication) + def patch(body, perimeter_id: hug.types.text = None, authed_user: hug.directives.user = None): """Create or update a object. :param uuid: uuid of the policy @@ -236,14 +272,23 @@ class Objects(Resource): } :internal_api: set_object """ - data = PolicyManager.update_object(user_id=user_id, perimeter_id=perimeter_id, - value=request.json) + data = driver.PolicyManager.update_object(moon_user_id=authed_user, + perimeter_id=perimeter_id, + value=body) + + slaves = slave_class.Slaves.get().get("slaves") + invalidate_perimeter_in_slaves(slaves=slaves, policy_id=None, perimeter_id=perimeter_id, + type="object", data=data[perimeter_id], is_delete=False) return {"objects": data} - @validate_input("delete", kwargs_state=[False, True, False]) - @check_auth - def delete(self, uuid=None, perimeter_id=None, user_id=None): + @staticmethod + @hug.local() + @hug.delete("/objects/", requires=api_key_authentication) + @hug.delete("/objects/{perimeter_id}", requires=api_key_authentication) + @hug.delete("/policies/{uuid}/objects/{perimeter_id}", requires=api_key_authentication) + def delete(uuid: hug.types.text = None, perimeter_id: hug.types.text = None, authed_user: + hug.directives.user = None): """Delete a object for a given policy :param uuid: uuid of the policy (mandatory if perimeter_id is not set) @@ -258,29 +303,29 @@ class Objects(Resource): :internal_api: delete_object """ - data = PolicyManager.delete_object( - user_id=user_id, policy_id=uuid, perimeter_id=perimeter_id) + data = driver.PolicyManager.delete_object( + moon_user_id=authed_user, policy_id=uuid, perimeter_id=perimeter_id) + + slaves = slave_class.Slaves.get().get("slaves") + invalidate_perimeter_in_slaves(slaves=slaves, policy_id=uuid, perimeter_id=perimeter_id, + type="object") return {"result": True} -class Actions(Resource): +class Actions(object): """ Endpoint for actions requests """ - __urls__ = ( - "/actions", - "/actions/", - "/actions/", - "/policies//actions", - "/policies//actions/", - "/policies//actions/", - ) - - @validate_input("get", kwargs_state=[False, False, False]) - @check_auth - def get(self, uuid=None, perimeter_id=None, user_id=None): + @staticmethod + @hug.local() + @hug.get("/actions/", requires=api_key_authentication) + @hug.get("/actions/{perimeter_id}", requires=api_key_authentication) + @hug.get("/policies/{uuid}/actions/", requires=api_key_authentication) + @hug.get("/policies/{uuid}/actions/{perimeter_id}", requires=api_key_authentication) + def get(uuid: hug.types.text = None, perimeter_id: hug.types.text = None, + authed_user: hug.directives.user = None): """Retrieve all actions or a specific one if perimeter_id is given for a given policy @@ -296,14 +341,19 @@ class Actions(Resource): :internal_api: get_actions """ - data = PolicyManager.get_actions( - user_id=user_id, policy_id=uuid, perimeter_id=perimeter_id) + data = driver.PolicyManager.get_actions( + moon_user_id=authed_user, policy_id=uuid, perimeter_id=perimeter_id) return {"actions": data} - @validate_input("post", body_state={"name": True}) - @check_auth - def post(self, uuid=None, perimeter_id=None, user_id=None): + @staticmethod + @hug.local() + @hug.post("/newaction", requires=api_key_authentication) + @hug.post("/actions/{perimeter_id}", requires=api_key_authentication) + @hug.post("/policies/{uuid}/actions/", requires=api_key_authentication) + @hug.post("/policies/{uuid}/actions/{perimeter_id}", requires=api_key_authentication) + def post(body: validate_input("name"), uuid: hug.types.text = None, perimeter_id: + hug.types.text = None, authed_user: hug.directives.user = None): """Create or update a action. :param uuid: uuid of the policy @@ -321,15 +371,15 @@ class Actions(Resource): } :internal_api: set_action """ - data = PolicyManager.add_action( - user_id=user_id, policy_id=uuid, - perimeter_id=perimeter_id, value=request.json) + data = driver.PolicyManager.add_action( + moon_user_id=authed_user, policy_id=uuid, perimeter_id=perimeter_id, value=body) return {"actions": data} - @validate_input("patch", kwargs_state=[False, True, False]) - @check_auth - def patch(self, uuid=None, perimeter_id=None, user_id=None): + @staticmethod + @hug.local() + @hug.patch("/actions/{perimeter_id}", requires=api_key_authentication) + def patch(body, perimeter_id: hug.types.text = None, authed_user: hug.directives.user = None): """Create or update a action. :param uuid: uuid of the policy @@ -347,14 +397,22 @@ class Actions(Resource): } :internal_api: set_action """ - data = PolicyManager.update_action(user_id=user_id, perimeter_id=perimeter_id, - value=request.json) + data = driver.PolicyManager.update_action( + moon_user_id=authed_user, perimeter_id=perimeter_id, value=body) + + slaves = slave_class.Slaves.get().get("slaves") + invalidate_perimeter_in_slaves(slaves=slaves, policy_id=None, perimeter_id=perimeter_id, + type="action", data=data[perimeter_id], is_delete=False) return {"actions": data} - @validate_input("delete", kwargs_state=[False, True, False]) - @check_auth - def delete(self, uuid=None, perimeter_id=None, user_id=None): + @staticmethod + @hug.local() + @hug.delete("/actions/", requires=api_key_authentication) + @hug.delete("/actions/{perimeter_id}", requires=api_key_authentication) + @hug.delete("/policies/{uuid}/actions/{perimeter_id}", requires=api_key_authentication) + def delete(uuid: hug.types.text = None, perimeter_id: hug.types.text = None, authed_user: + hug.directives.user = None): """Delete a action for a given policy :param uuid: uuid of the policy (mandatory if perimeter_id is not set) @@ -369,7 +427,559 @@ class Actions(Resource): :internal_api: delete_action """ - data = PolicyManager.delete_action( - user_id=user_id, policy_id=uuid, perimeter_id=perimeter_id) + data = driver.PolicyManager.delete_action( + moon_user_id=authed_user, policy_id=uuid, perimeter_id=perimeter_id) + + slaves = slave_class.Slaves.get().get("slaves") + invalidate_perimeter_in_slaves(slaves=slaves, policy_id=uuid, perimeter_id=perimeter_id, + type="action") return {"result": True} + + +SubjectsAPI = hug.API(name='subjects', doc=Subjects.__doc__) + + +def filter_dict(data, filter_args): + for item in data: + output = [] + for arg in filter_args: + if arg.strip() in data.get(item): + output.append(str(data.get(item).get(arg.strip()))) + yield output + + +@hug.object(name='subjects', version='1.0.0', api=SubjectsAPI) +class SubjectsCLI(object): + """An example of command like calls via an Object""" + + @staticmethod + @hug.object.cli + def list(name_or_id="", filter=None, human: bool = False): + """ + List subjects from the database + :return: JSON status output + """ + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + _subjects_req = requests.get("{}/subjects".format(db_conf.get("url")), + headers={"x-api-key": manager_api_key} + ) + if _subjects_req.status_code == 200: + if name_or_id: + _subjects = None + if name_or_id in _subjects_req.json().get("subjects"): + _subjects = _subjects_req.json().get("subjects").get(name_or_id) + else: + for _subjects_key in _subjects_req.json().get("subjects"): + _name = _subjects_req.json().get("subjects").get(_subjects_key).get("name") + if _name == name_or_id: + _subjects = _subjects_req.json().get("subjects").get(_subjects_key) + name_or_id = _subjects_key + break + if not _subjects: + raise Exception("Cannot find Subjects with name or ID {}".format(name_or_id)) + else: + if human: + result = {"subjects": {name_or_id: _subjects}} + else: + result = {"subjects": [{name_or_id: _subjects}]} + elif filter: + return "\n".join( + ["\t".join(_t) for _t in filter_dict(_subjects_req.json().get("subjects"), + filter.split(","))] + ) + else: + result = _subjects_req.json() + + if human: + return SubjectsCLI.human_display(result) + else: + return result + LOGGER.error('Cannot list Subjects {}'.format(_subjects_req.status_code)) + + @staticmethod + @hug.object.cli + def add(name, description="", policy_name_or_id="", human: bool = False): + """ + Add subject in the database + :return: JSON status output + """ + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + _url = "{}/newsubject".format(db_conf.get("url")) + if policy_name_or_id: + policy_id = list(policy.PoliciesCLI.list(policy_name_or_id).get("policies").keys())[0] + _url = "{}/policies/{}/subjects".format(db_conf.get("url"), policy_id) + _subjects = requests.post( + _url, + json={ + "name": name, + "description": description, + }, + headers={ + "x-api-key": manager_api_key, + "Content-Type": "application/json" + } + ) + if _subjects.status_code == 200: + LOGGER.warning('Create {}'.format(_subjects.content)) + if human: + return SubjectsCLI.human_display(_subjects.json()) + else: + return _subjects.json() + LOGGER.error('Cannot create {}'.format(name, _subjects.content[:40])) + + @staticmethod + @hug.object.cli + def delete(name_or_id, policy_name_or_id): + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + _subjects = SubjectsCLI.list() + for _perimeter_id, _perimeter_value in _subjects.get("subjects").items(): + if _perimeter_value.get("name") == name_or_id: + _url = "{}/subjects/{}".format(db_conf.get("url"), _perimeter_id) + if policy_name_or_id: + policy_id = list( + policy.PoliciesCLI.list(policy_name_or_id).get("policies").keys())[0] + _url = "{}/policies/{}/subjects/{}".format(db_conf.get("url"), policy_id, + _perimeter_id) + req = requests.delete( + _url, + headers={"x-api-key": manager_api_key} + ) + break + else: + LOGGER.error("Cannot find Subjects with name {}".format(name_or_id)) + return False + if req.status_code == 200: + LOGGER.warning('Deleted {}'.format(name_or_id)) + return True + LOGGER.error("Cannot delete Subjects with name {}".format(name_or_id)) + return False + + @staticmethod + @hug.object.cli + def update(name_or_id, description=None, extra=None, email=None, new_name=None): + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + _subjects = SubjectsCLI.list() + for _perimeter_id, _perimeter_value in _subjects.get("subjects").items(): + if _perimeter_id == name_or_id or _perimeter_value.get("name") == name_or_id: + updated_name = _perimeter_value.get("name") + updated_id = _perimeter_value.get("id") + updated_description = _perimeter_value.get("description") + updated_extra = _perimeter_value.get("extra") + updated_email = _perimeter_value.get("email") + _url = "{}/subjects/{}".format(db_conf.get("url"), _perimeter_id) + + if new_name is not None: + updated_name = new_name + if description is not None: + updated_description = description + if extra is not None: + if extra == "": + updated_extra = {} + else: + updated_extra.update(dict((k, v) for k, v in (item.split(':') for item in extra.split(',')))) + if email is not None: + updated_email = email + + req = requests.patch( + _url, + json={ + "name": updated_name, + "id": updated_id, + "description": updated_description, + "extra": updated_extra, + "email": updated_email + }, + headers={"x-api-key": manager_api_key} + ) + break + else: + LOGGER.error("Cannot find Subjects with name {}".format(name_or_id)) + return False + if req.status_code == 200: + LOGGER.warning('Updated {}'.format(name_or_id)) + return True + LOGGER.error("Cannot update Subjects with name {}".format(name_or_id)) + return False + + @staticmethod + def human_display(subjects_json): + human_result = "Subjects" + for subject in subjects_json.get("subjects"): + human_result += "\n" + subjects_json.get("subjects").get(subject).get("name") + " : \n" + human_result += "\tname : " + subjects_json.get("subjects").get(subject).get("name") + "\n" + human_result += "\tid : " + subjects_json.get("subjects").get(subject).get("id") + "\n" + human_result += "\tdescription : " + subjects_json.get("subjects").get(subject).get("description") + "\n" + human_result += "\temail : " + subjects_json.get("subjects").get(subject).get("email") + "\n" + human_result += "\textra : \n" + for extra in subjects_json.get("subjects").get(subject).get("extra"): + human_result += "\t\t : " + extra + "\n" + human_result += "\tpolicies : \n" + for policy in subjects_json.get("subjects").get(subject).get("policy_list"): + human_result += "\t\tid : " + policy + "\n" + return human_result + + +ObjectsAPI = hug.API(name='objects', doc=Objects.__doc__) + + +@hug.object(name='objects', version='1.0.0', api=ObjectsAPI) +class ObjectsCLI(object): + """An example of command like calls via an Object""" + + @staticmethod + @hug.object.cli + def list(name_or_id="", filter=None, human: bool = False): + """ + List objects from the database + :return: JSON status output + """ + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + _objects_req = requests.get("{}/objects".format(db_conf.get("url")), + headers={"x-api-key": manager_api_key} + ) + if _objects_req.status_code == 200: + if name_or_id: + _objects = None + if name_or_id in _objects_req.json().get("objects"): + _objects = _objects_req.json().get("objects").get(name_or_id) + else: + for _objects_key in _objects_req.json().get("objects"): + _name = _objects_req.json().get("objects").get(_objects_key).get("name") + if _name == name_or_id: + _objects = _objects_req.json().get("objects").get(_objects_key) + name_or_id = _objects_key + break + if not _objects: + raise Exception("Cannot find Objects with name or ID {}".format(name_or_id)) + else: + if human: + result = {"objects": {name_or_id: _objects}} + else: + result = {"objects": [{name_or_id: _objects}]} + elif filter: + return "\n".join( + ["\t".join(_t) for _t in filter_dict(_objects_req.json().get("objects"), + filter.split(","))] + ) + else: + result =_objects_req.json() + + if human: + return ObjectsCLI.human_display(result) + else: + return result + LOGGER.error('Cannot list Objects {}'.format(_objects_req.status_code)) + + @staticmethod + @hug.object.cli + def add(name, description="", policy_name_or_id="", human: bool = False): + """ + Add object in the database + :return: JSON status output + """ + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + _url = "{}/newobject".format(db_conf.get("url")) + if policy_name_or_id: + policy_id = list(policy.PoliciesCLI.list(policy_name_or_id).get("policies").keys())[0] + _url = "{}/policies/{}/objects".format(db_conf.get("url"), policy_id) + _objects = requests.post( + _url, + json={ + "name": name, + "description": description, + }, + headers={ + "x-api-key": manager_api_key, + "Content-Type": "application/json" + } + ) + if _objects.status_code == 200: + LOGGER.warning('Create {}'.format(_objects.content)) + if human: + return ObjectsCLI.human_display(_objects.json()) + else: + return _objects.json() + LOGGER.error('Cannot create {}'.format(name, _objects.content[:40])) + + @staticmethod + @hug.object.cli + def update(name_or_id, description=None, extra=None, email=None, new_name=None): + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + _objects = ObjectsCLI.list() + for _perimeter_id, _perimeter_value in _objects.get("objects").items(): + if _perimeter_id == name_or_id or _perimeter_value.get("name") == name_or_id: + updated_name = _perimeter_value.get("name") + updated_id = _perimeter_value.get("id") + updated_description = _perimeter_value.get("description") + updated_extra = _perimeter_value.get("extra") + updated_email = _perimeter_value.get("email") + _url = "{}/objects/{}".format(db_conf.get("url"), _perimeter_id) + + if new_name is not None: + updated_name = new_name + if description is not None: + updated_description = description + if extra is not None: + if extra == "": + updated_extra = {} + else: + updated_extra.update(dict((k, v) for k, v in (item.split(':') for item in extra.split(',')))) + if email is not None: + updated_email = email + + req = requests.patch( + _url, + json={ + "name": updated_name, + "id": updated_id, + "description": updated_description, + "extra": updated_extra, + "email": updated_email + }, + headers={"x-api-key": manager_api_key} + ) + break + else: + LOGGER.error("Cannot find object with name {}".format(name_or_id)) + return False + if req.status_code == 200: + LOGGER.warning('Updated {}'.format(name_or_id)) + return True + LOGGER.error("Cannot update object with name {}".format(name_or_id)) + return False + + @staticmethod + @hug.object.cli + def delete(name_or_id, policy_name_or_id): + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + _objects = ObjectsCLI.list() + for _perimeter_id, _perimeter_value in _objects.get("objects").items(): + if _perimeter_value.get("name") == name_or_id: + _url = "{}/objects/{}".format(db_conf.get("url"), _perimeter_id) + if policy_name_or_id: + policy_id = list( + policy.PoliciesCLI.list(policy_name_or_id).get("policies").keys())[0] + _url = "{}/policies/{}/objects/{}".format(db_conf.get("url"), policy_id, + _perimeter_id) + req = requests.delete( + _url, + headers={"x-api-key": manager_api_key} + ) + break + else: + LOGGER.error("Cannot find Objects with name {}".format(name_or_id)) + return False + if req.status_code == 200: + LOGGER.warning('Deleted {}'.format(name_or_id)) + return True + LOGGER.error("Cannot delete Objects with name {}".format(name_or_id)) + return False + + @staticmethod + def human_display(objects_json): + human_result = "Objects" + for object in objects_json.get("objects"): + human_result += "\n" + objects_json.get("objects").get(object).get("name") + " : \n" + human_result += "\tname : " + objects_json.get("objects").get(object).get("name") + "\n" + human_result += "\tid : " + object + "\n" + human_result += "\tdescription : " + objects_json.get("objects").get(object).get("description") + "\n" + human_result += "\temail : " + objects_json.get("objects").get(object).get("email") + "\n" + human_result += "\textra : \n" + if objects_json.get("objects").get(object).get("extra").get("component") != None: + human_result += "\t\tcomponent : " + objects_json.get("objects").get(object).get("extra").get( + "component") + "\n" + else: + human_result + "\t\t\n" + human_result += "\tpolicies : \n" + for policy in objects_json.get("objects").get(object).get("policy_list"): + human_result += "\t\tid : " + policy + "\n" + return human_result + +ActionsAPI = hug.API(name='actions', doc=Actions.__doc__) + + +@hug.object(name='actions', version='1.0.0', api=ActionsAPI) +class ActionsCLI(object): + """An example of command like calls via an Object""" + + @staticmethod + @hug.object.cli + def list(name_or_id="", filter="", human: bool = False): + """ + List actions from the database + :return: JSON status output + """ + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + _actions_req = requests.get("{}/actions".format(db_conf.get("url")), + headers={"x-api-key": manager_api_key} + ) + if _actions_req.status_code == 200: + if name_or_id: + _actions = None + if name_or_id in _actions_req.json().get("actions"): + _actions = _actions_req.json().get("actions").get(name_or_id) + else: + for _actions_key in _actions_req.json().get("actions"): + _name = _actions_req.json().get("actions").get(_actions_key).get("name") + if _name == name_or_id: + _actions = _actions_req.json().get("actions").get(_actions_key) + name_or_id = _actions_key + break + if not _actions: + raise Exception("Cannot find Actions with name or ID {}".format(name_or_id)) + else: + if human: + result = {"actions": {name_or_id: _actions}} + else: + result = {"actions": [{name_or_id: _actions}]} + elif filter: + return "\n".join( + ["\t".join(_t) for _t in filter_dict(_actions_req.json().get("actions"), + filter.split(","))] + ) + else: + result = _actions_req.json() + + if human: + return ActionsCLI.human_display(result) + else: + return result + + LOGGER.error('Cannot list Actions {}'.format(_actions_req.status_code)) + + @staticmethod + @hug.object.cli + def add(name, description="", policy_name_or_id="", human: bool = False): + """ + Add action in the database + :return: JSON status output + """ + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + _url = "{}/newaction".format(db_conf.get("url")) + if policy_name_or_id: + policy_id = list(policy.PoliciesCLI.list(policy_name_or_id).get("policies").keys())[0] + _url = "{}/policies/{}/actions".format(db_conf.get("url"), policy_id) + _actions = requests.post( + _url, + json={ + "name": name, + "description": description, + }, + headers={ + "x-api-key": manager_api_key, + "Content-Type": "application/json" + } + ) + if _actions.status_code == 200: + LOGGER.warning('Create {}'.format(_actions.content)) + if human: + return ActionsCLI.human_display(_actions.json()) + else: + return _actions.json() + LOGGER.error('Cannot create {}'.format(name, _actions.content[:40])) + + @staticmethod + @hug.object.cli + def update(name_or_id, description=None, extra=None, email=None, new_name=None): + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + _actions = ActionsCLI.list() + for _perimeter_id, _perimeter_value in _actions.get("actions").items(): + if _perimeter_id == name_or_id or _perimeter_value.get("name") == name_or_id: + updated_name = _perimeter_value.get("name") + updated_id = _perimeter_value.get("id") + updated_description = _perimeter_value.get("description") + updated_extra = _perimeter_value.get("extra") + updated_email = _perimeter_value.get("email") + _url = "{}/actions/{}".format(db_conf.get("url"), _perimeter_id) + + if new_name is not None: + updated_name = new_name + if description is not None: + updated_description = description + if extra is not None: + if extra == "": + updated_extra = {} + else: + updated_extra.update(dict((k, v) for k, v in (item.split(':') for item in extra.split(',')))) + if email is not None: + updated_email = email + + req = requests.patch( + _url, + json={ + "name": updated_name, + "id": updated_id, + "description": updated_description, + "extra": updated_extra, + "email": updated_email + }, + headers={"x-api-key": manager_api_key} + ) + break + else: + LOGGER.error("Cannot find action with name {}".format(name_or_id)) + return False + if req.status_code == 200: + LOGGER.warning('Updated {}'.format(name_or_id)) + return True + LOGGER.error("Cannot update action with name {}".format(name_or_id)) + return False + + @staticmethod + @hug.object.cli + def delete(name_or_id, policy_name_or_id): + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + _actions = ActionsCLI.list() + for _perimeter_id, _perimeter_value in _actions.get("actions").items(): + if _perimeter_value.get("name") == name_or_id: + _url = "{}/actions/{}".format(db_conf.get("url"), _perimeter_id) + if policy_name_or_id: + policy_id = list( + policy.PoliciesCLI.list(policy_name_or_id).get("policies").keys())[0] + _url = "{}/policies/{}/actions/{}".format(db_conf.get("url"), policy_id, + _perimeter_id) + req = requests.delete( + _url, + headers={"x-api-key": manager_api_key} + ) + break + else: + LOGGER.error("Cannot find Actions with name {}".format(name_or_id)) + return False + if req.status_code == 200: + LOGGER.warning('Deleted {}'.format(name_or_id)) + return True + LOGGER.error("Cannot delete Actions with name {}".format(name_or_id)) + return False + + @staticmethod + def human_display(actions_json): + human_result = "Actions" + for action in actions_json.get("actions"): + human_result += "\n" + actions_json.get("actions").get(action).get("name") + " : \n" + human_result += "\tname : " + actions_json.get("actions").get(action).get("name") + "\n" + human_result += "\tid : " + actions_json.get("actions").get(action).get("id") + "\n" + human_result += "\tdescription : " + actions_json.get("actions").get(action).get("description") + "\n" + human_result += "\temail : " + actions_json.get("actions").get(action).get("email") + "\n" + human_result += "\textra : \n" + if actions_json.get("actions").get(action).get("extra").get("component") != None: + human_result += "\t\tcomponent : " + actions_json.get("actions").get(action).get("extra").get("component") + "\n" + else: + human_result + "\t\t\n" + human_result += "\tpolicies : \n" + for policy in actions_json.get("actions").get(action).get("policy_list"): + human_result += "\t\tid : " + policy + "\n" + return human_result diff --git a/moon_manager/moon_manager/api/policies.py b/moon_manager/moon_manager/api/policies.py deleted file mode 100644 index 3264e8e0..00000000 --- a/moon_manager/moon_manager/api/policies.py +++ /dev/null @@ -1,125 +0,0 @@ -# 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'. -""" -Policies are instances of security models and implement security policies - -""" - -from flask import request -from flask_restful import Resource -import logging -from python_moonutilities.security_functions import check_auth -from python_moondb.core import PolicyManager -from python_moonutilities.security_functions import validate_input - -__version__ = "4.3.2" - -logger = logging.getLogger("moon.manager.api." + __name__) - - -class Policies(Resource): - """ - Endpoint for policy requests - """ - - __urls__ = ( - "/policies", - "/policies/", - "/policies/", - "/policies//", - ) - - @validate_input("get", kwargs_state=[False, False]) - @check_auth - def get(self, uuid=None, user_id=None): - """Retrieve all policies - - :param uuid: uuid of the policy - :param user_id: user ID who do the request - :return: { - "policy_id1": { - "name": "name of the policy (mandatory)", - "model_id": "ID of the model linked to this policy", - "genre": "authz of admin (optional, default to authz)", - "description": "description of the policy (optional)", - } - } - :internal_api: get_policies - """ - - data = PolicyManager.get_policies(user_id=user_id, policy_id=uuid) - - return {"policies": data} - - @validate_input("post", body_state={"name": True, "model_id": False}) - @check_auth - def post(self, uuid=None, user_id=None): - """Create policy. - - :param uuid: uuid of the policy (not used here if a new policy is created) - :param user_id: user ID who do the request - :request body: { - "name": "name of the policy (mandatory)", - "model_id": "ID of the model linked to this policy", - "genre": "authz of admin (optional, default to authz)", - "description": "description of the policy (optional)", - } - :return: { - "policy_id1": { - "name": "name of the policy (mandatory)", - "model_id": "ID of the model linked to this policy", - "genre": "authz of admin (optional, default to authz)", - "description": "description of the policy (optional)", - } - } - :internal_api: add_policy - """ - - data = PolicyManager.add_policy( - user_id=user_id, policy_id=uuid, value=request.json) - - return {"policies": data} - - @validate_input("delete", kwargs_state=[True, False]) - @check_auth - def delete(self, uuid=None, user_id=None): - """Delete a policy - - :param uuid: uuid of the policy to delete - :param user_id: user ID who do the request - :return: { - "result": "True or False", - "message": "optional message (optional)" - } - :internal_api: delete_policy - """ - - data = PolicyManager.delete_policy(user_id=user_id, policy_id=uuid) - - return {"result": True} - - @validate_input("patch", kwargs_state=[True, False], - body_state={"name": True, "model_id": False}) - @check_auth - def patch(self, uuid=None, user_id=None): - """Update a policy - - :param uuid: uuid of the policy to update - :param user_id: user ID who do the request - :return: { - "policy_id1": { - "name": "name of the policy (mandatory)", - "model_id": "ID of the model linked to this policy", - "genre": "authz of admin (optional, default to authz)", - "description": "description of the policy (optional)", - } - } - :internal_api: update_policy - """ - - data = PolicyManager.update_policy( - user_id=user_id, policy_id=uuid, value=request.json) - - return {"policies": data} diff --git a/moon_manager/moon_manager/api/policy.py b/moon_manager/moon_manager/api/policy.py new file mode 100644 index 00000000..727fceb6 --- /dev/null +++ b/moon_manager/moon_manager/api/policy.py @@ -0,0 +1,293 @@ +# 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. + +""" +Policies are instances of security models and implement security policies + +""" + +import hug +import logging +import requests +from moon_manager.api import ERROR_CODE +from moon_utilities.auth_functions import api_key_authentication, connect_from_env +from moon_manager import db_driver +from moon_utilities import exceptions +from moon_utilities.security_functions import validate_input +from moon_utilities.invalided_functions import invalidate_policy_in_slaves +from moon_manager.api import slave as slave_class +from moon_manager.api import configuration + +LOGGER = logging.getLogger("moon.manager.api." + __name__) + + +class Policies(object): + """ + Endpoint for policy requests + """ + + @staticmethod + @hug.local() + @hug.get("/policies/", requires=api_key_authentication) + @hug.get("/policies/{uuid}", requires=api_key_authentication) + def get(uuid: hug.types.uuid = None, authed_user: hug.directives.user = None): + """Retrieve all policies + + :param uuid: uuid of the policy + :param authed_user: the name of the authenticated user + :return: { + "policy_id1": { + "name": "name of the policy (mandatory)", + "model_id": "ID of the model linked to this policy", + "genre": "authz of admin (optional, default to authz)", + "description": "description of the policy (optional)", + } + } + """ + if uuid: + uuid = str(uuid).replace("-", "") + data = db_driver.PolicyManager.get_policies(moon_user_id=authed_user, policy_id=uuid) + + return {"policies": data} + + @staticmethod + @hug.local() + @hug.post("/policies/", requires=api_key_authentication) + def post(body: validate_input("name"), response, authed_user: hug.directives.user = None): + """Create policy. + + :param body: preformed body from Hug + :param response: preformed response from Hug + :param authed_user: the name of the authenticated user + :request body: { + "name": "name of the policy (mandatory)", + "model_id": "ID of the model linked to this policy", + "genre": "authz of admin (optional, default to authz)", + "description": "description of the policy (optional)", + } + :return: { + "policy_id1": { + "name": "name of the policy (mandatory)", + "model_id": "ID of the model linked to this policy", + "genre": "authz of admin (optional, default to authz)", + "description": "description of the policy (optional)", + } + } + """ + data = db_driver.PolicyManager.add_policy( + moon_user_id=authed_user, policy_id=None, value=body) + + return {"policies": data} + + @staticmethod + @hug.local() + @hug.delete("/policies/{uuid}", requires=api_key_authentication) + def delete(uuid: hug.types.text, response=None, authed_user: hug.directives.user = None): + """Delete a policy + + :param uuid: uuid of the policy to delete + :param response: preformed response from Hug + :param authed_user: the name of the authenticated user + :return: { + "result": "True or False", + "message": "optional message (optional)" + } + """ + uuid = str(uuid).replace("-", "") + db_driver.PolicyManager.delete_policy( + moon_user_id=authed_user, policy_id=uuid) + slaves = slave_class.Slaves.get().get("slaves") + invalidate_policy_in_slaves(slaves=slaves, policy_id=uuid) + + return {"result": True} + + @staticmethod + @hug.local() + @hug.patch("/policies/{uuid}", requires=api_key_authentication) + def patch(uuid: hug.types.uuid, body: validate_input("name"), response, + authed_user: hug.directives.user = None): + """Update a policy + + :param uuid: uuid of the policy to update + :param body: preformed body from Hug + :param response: preformed response from Hug + :param authed_user: the name of the authenticated user + :return: { + "policy_id1": { + "name": "name of the policy (mandatory)", + "model_id": "ID of the model linked to this policy", + "genre": "authz of admin (optional, default to authz)", + "description": "description of the policy (optional)", + } + } + """ + + uuid = str(uuid).replace("-", "") + prev_data = db_driver.PolicyManager.get_policies(moon_user_id=authed_user, policy_id=uuid) + if not prev_data: + response.status = ERROR_CODE[400] + return {"message": "The policy is unknown."} + data = db_driver.PolicyManager.update_policy( + moon_user_id=authed_user, policy_id=uuid, value=body).get(uuid) + slaves = slave_class.Slaves.get().get("slaves") + invalidate_policy_in_slaves(slaves=slaves, policy_id=uuid, data=data, is_delete=False) + + return {"policies": db_driver.PolicyManager.get_policies(moon_user_id=authed_user, + policy_id=uuid)} + + +PoliciesAPI = hug.API(name='policies', doc=Policies.__doc__) + + +@hug.object(name='policies', version='1.0.0', api=PoliciesAPI) +class PoliciesCLI(object): + """An example of command like calls via an Object""" + + @staticmethod + @hug.object.cli + def list(name_or_id="", human: bool = False): + """ + List policies from the database + :return: JSON status output + """ + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + _policies_req = requests.get("{}/policies".format(db_conf.get("url")), + headers={"x-api-key": manager_api_key} + ) + if _policies_req.status_code == 200: + if name_or_id: + _policies = None + if name_or_id in _policies_req.json().get("policies"): + _policies = _policies_req.json().get("policies").get(name_or_id) + else: + for _policies_key in _policies_req.json().get("policies"): + _name = _policies_req.json().get("policies").get(_policies_key).get("name") + if _name == name_or_id : + _policies = _policies_req.json().get("policies").get(_policies_key) + name_or_id = _policies_key + break + if not _policies: + raise Exception("Cannot find policy with name {}".format(name_or_id)) + else: + result = {"policies": {name_or_id: _policies}} + else: + result = _policies_req.json() + + if human: + return PoliciesCLI.human_display(result); + else: + return result + + @staticmethod + @hug.object.cli + def add(name, model, description="", genre="authz", human: bool = False): + """ + Add a new policy from the database + :return: JSON policies output + """ + from moon_manager.api import models + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + _model = models.ModelsCLI.list(model).get("models")[0] + _policies = requests.post( + "{}/policies".format(db_conf.get("url")), + json={ + "name": name, + "model_id": list(_model.keys())[0], + "genre": genre, + "description": description, + }, + headers={ + "x-api-key": manager_api_key, + "Content-Type": "application/json" + } + ) + if _policies.status_code == 200: + LOGGER.warning('Create {}'.format(_policies.content)) + if human: + return PoliciesCLI.list('', True) + else: + return _policies.json() + LOGGER.error('Cannot create {} ({})'.format(name, _policies.content)) + + @staticmethod + @hug.object.cli + def delete(name_or_id): + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + _policies = PoliciesCLI.list() + for _id, _value in _policies.get("policies").items(): + if _id == name_or_id or _value.get("name") == name_or_id: + req = requests.delete( + "{}/policies/{}".format(db_conf.get("url"), _id), + headers={"x-api-key": manager_api_key} + ) + break + else: + LOGGER.error("Cannot find policy with name {}".format(name_or_id)) + return False + if req.status_code == 200: + LOGGER.warning('Deleted {}'.format(name_or_id)) + return True + LOGGER.error("Cannot delete policy with name {}".format(name_or_id)) + return False + + @staticmethod + @hug.object.cli + def update(name_or_id, model_id=None, description=None, genre=None): + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + _policies = PoliciesCLI.list() + for _id, _value in _policies.get("policies").items(): + if _id == name_or_id or _value.get("name") == name_or_id: + updated_model_id = _value.get("model_id") + updated_genre = _value.get("genre") + updated_description = _value.get("description") + + if model_id is not None: + updated_model_id = model_id + if description is not None: + updated_description = description + if genre is not None: + updated_genre = genre + + req = requests.patch( + "{}/policies/{}".format(db_conf.get("url"), _id), + json={ + "name": _value.get("name"), + "model_id": updated_model_id, + "genre": updated_genre, + "description": updated_description, + }, + headers={"x-api-key": manager_api_key} + ) + break + else: + LOGGER.error("Cannot find policy with name {}".format(name_or_id)) + return False + if req.status_code == 200: + LOGGER.warning('Updated {}'.format(name_or_id)) + return True + LOGGER.error("Cannot update policy with name {}".format(name_or_id)) + return False + + @staticmethod + def human_display(policies_json): + human_result = "Policies" + for policy in policies_json.get("policies"): + human_result += "\n" + policies_json.get("policies").get(policy).get("name") + " : \n" + human_result += "\tname : " + policies_json.get("policies").get(policy).get("name") + "\n" + human_result += "\tdescription : " + policies_json.get("policies").get(policy).get("description") + "\n" + human_result += "\tgenre : " + policies_json.get("policies").get(policy).get("genre") + "\n" + human_result += "\tmodel_id : " + policies_json.get("policies").get(policy).get("model_id") + "\n" + return human_result + diff --git a/moon_manager/moon_manager/api/rules.py b/moon_manager/moon_manager/api/rules.py index cbd39969..e984ee93 100644 --- a/moon_manager/moon_manager/api/rules.py +++ b/moon_manager/moon_manager/api/rules.py @@ -1,42 +1,63 @@ -# 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'. +# 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. + """ -Rules (TODO) +Rules """ -from flask import request -from flask_restful import Resource +import hug +import json import logging -from python_moonutilities.security_functions import check_auth -from python_moondb.core import PolicyManager -from python_moonutilities.security_functions import validate_input +import requests +from moon_manager import db_driver as driver +from moon_utilities.security_functions import validate_input +from moon_utilities.invalided_functions import invalidate_rules_in_slaves +from moon_manager.api import configuration +from moon_utilities.auth_functions import init_db, api_key_authentication, connect_from_env +from moon_manager.api import slave as slave_class +from moon_manager.api import policy, meta_rules, data -__version__ = "4.3.2" +LOGGER = logging.getLogger("moon.manager.api." + __name__) -logger = logging.getLogger("moon.manager.api." + __name__) - -class Rules(Resource): +class Rules(object): """ Endpoint for rules requests """ - __urls__ = ("/policies//rules", - "/policies//rules/", - "/policies//rules/", - "/policies//rules//", - ) + @staticmethod + def _get_data_name(user, rule_data, policy_id): + global_data = driver.PolicyManager.get_subject_data(moon_user_id=user, policy_id=policy_id) +\ + driver.PolicyManager.get_object_data(moon_user_id=user, policy_id=policy_id) +\ + driver.PolicyManager.get_action_data(moon_user_id=user, policy_id=policy_id) + _rule_names = list() + for rule_id in rule_data: + for _data in global_data: + if rule_id in _data.get("data"): + _rule_names.append(_data.get("data")[rule_id].get("name")) + break + else: + _rule_names.append(rule_id) + return _rule_names - @validate_input("get", kwargs_state=[False, False, False]) - @check_auth - def get(self, uuid=None, rule_id=None, user_id=None): + @staticmethod + @hug.local() + @hug.get("/policies/{uuid}/rules", requires=api_key_authentication) + @hug.get("/policies/{uuid}/rules/{rule_id}", requires=api_key_authentication) + def get(uuid: hug.types.text, rule_id: hug.types.text = None, moon_user_id: hug.directives.user = None): """Retrieve all rules or a specific one :param uuid: policy ID :param rule_id: rule ID - :param user_id: user ID who do the request + :param moon_user_id: user ID who do the request :return: { "rules": [ "policy_id": "policy_id1", @@ -50,27 +71,28 @@ class Rules(Resource): :internal_api: get_rules """ - data = PolicyManager.get_rules(user_id=user_id, - policy_id=uuid, - rule_id=rule_id) + data = driver.PolicyManager.get_rules(moon_user_id=moon_user_id, policy_id=uuid, + rule_id=rule_id) return {"rules": data} - @validate_input("post", kwargs_state=[True, False, False], - body_state={"meta_rule_id": True, "rule": True, "instructions": True}) - @check_auth - def post(self, uuid=None, rule_id=None, user_id=None): + @staticmethod + @hug.local() + @hug.post("/policies/{uuid}/rules", requires=api_key_authentication) + def post(body: validate_input("meta_rule_id", "rule", "instructions"), + uuid: hug.types.text, + moon_user_id: hug.directives.user = None): """Add a rule to a meta rule :param uuid: policy ID - :param rule_id: rule ID (not used here) - :param user_id: user ID who do the request + :param body: body of the request + :param moon_user_id: user ID who do the request :request body: post = { "meta_rule_id": "meta_rule_id1", # mandatory "rule": ["subject_data_id2", "object_data_id2", "action_data_id2"], # mandatory - "instructions": ( # mandatory + "instructions": [ # mandatory {"decision": "grant"}, - ) + ] "enabled": True } :return: { @@ -80,18 +102,18 @@ class Rules(Resource): "rule": ["subject_data_id1", "object_data_id1", "action_data_id1"], - "instructions": ( + "instructions": [ {"decision": "grant"}, # "grant" to immediately exit, # "continue" to wait for the result of next policy # "deny" to deny the request - ) + ] } "rule_id2": { "rule": ["subject_data_id2", "object_data_id2", "action_data_id2"], - "instructions": ( + "instructions": [ { "update": { "operation": "add", @@ -102,34 +124,233 @@ class Rules(Resource): }, {"chain": {"name": "rbac"}} # chain with the policy named rbac - ) + ] } ] } :internal_api: add_rule """ - args = request.json - data = PolicyManager.add_rule(user_id=user_id, - policy_id=uuid, - meta_rule_id=args['meta_rule_id'], - value=args) + data = driver.PolicyManager.add_rule(moon_user_id=moon_user_id, policy_id=uuid, + meta_rule_id=body['meta_rule_id'], value=body) + + subject, object_, *action = Rules._get_data_name(moon_user_id, body['rule'], uuid) + instruction = body['instructions'][0]['decision'] + LOGGER.info(f"The user <{moon_user_id}> added the rule <{subject}, {object_}, {action}: {instruction}>") + + slaves = slave_class.Slaves.get().get("slaves") + invalidate_rules_in_slaves(slaves=slaves, policy_id=uuid, rule_id=None) return {"rules": data} - @validate_input("delete", kwargs_state=[True, True, False]) - @check_auth - def delete(self, uuid=None, rule_id=None, user_id=None): + @staticmethod + @hug.local() + @hug.patch("/policies/{uuid}/rules/{rule_id}", requires=api_key_authentication) + def patch(uuid: hug.types.text, rule_id: hug.types.text, body: validate_input("instructions"), + moon_user_id: hug.directives.user = None): + """Updates a rule (only its instructions) + + :param uuid: policy ID + :param body: body of the request + :param rule_id: the id of the rule to patch + :param moon_user_id: user ID who do the request + :request body: patch = { + "instructions": [ # mandatory + {"decision": "grant"}, + ] + } + :return: { + "rules": [ + "meta_rule_id": "meta_rule_id1", + "rule_id1": { + "rule": ["subject_data_id1", + "object_data_id1", + "action_data_id1"], + "instructions": [ + {"decision": "grant"}, + # "grant" to immediately exit, + # "continue" to wait for the result of next policy (not yet supported) + # "deny" to deny the request + ] + } + ] + } + :internal_api: update_rule + """ + prev_data = driver.PolicyManager.get_rules(moon_user_id=moon_user_id, policy_id=uuid, rule_id=rule_id)[rule_id] + subject, object_, *action = Rules._get_data_name(moon_user_id, prev_data['rule'], prev_data['policy_id']) + prev_inst = prev_data['instructions'][0]['decision'] + + data = driver.PolicyManager.update_rule(moon_user_id=moon_user_id, rule_id=rule_id, value=body) + + new_inst = data[rule_id]['instructions'][0]['decision'] + LOGGER.info(f"The user <{moon_user_id}> updated the rule <{subject}, {object_}, {action}> from {prev_inst} to {new_inst}") + + slaves = slave_class.Slaves.get().get("slaves") + invalidate_rules_in_slaves(slaves=slaves, policy_id=uuid, rule_id=rule_id) + + return {"rules": data} + + @staticmethod + @hug.local() + @hug.delete("/policies/{uuid}/rules/{rule_id}", requires=api_key_authentication) + def delete(uuid: hug.types.text, rule_id: hug.types.text, moon_user_id: hug.directives.user = None): """Delete one rule linked to a specific sub meta rule :param uuid: policy ID :param rule_id: rule ID - :param user_id: user ID who do the request + :param moon_user_id: user ID who do the request :return: { "result": true } :internal_api: delete_rule """ + prev_data = driver.PolicyManager.get_rules(moon_user_id=moon_user_id, policy_id=uuid, rule_id=rule_id)[rule_id] + subject, object_, *action = Rules._get_data_name(moon_user_id, prev_data['rule'], prev_data['policy_id']) + prev_inst = prev_data['instructions'][0]['decision'] + + driver.PolicyManager.delete_rule(moon_user_id=moon_user_id, policy_id=uuid, rule_id=rule_id) - data = PolicyManager.delete_rule( - user_id=user_id, policy_id=uuid, rule_id=rule_id) + LOGGER.info(f"The user <{moon_user_id}> deleted the rule <{subject}, {object_}, {action}: {prev_inst}>") + + slaves = slave_class.Slaves.get().get("slaves") + invalidate_rules_in_slaves(slaves=slaves, policy_id=uuid, rule_id=rule_id) return {"result": True} + + +RulesAPI = hug.API(name='rules', doc=Rules.__doc__) +db_conf = configuration.get_configuration(key='management') +init_db(db_conf.get("token_file")) + + +@hug.object(name='rules', version='1.0.0', api=RulesAPI) +class RulesCLI(object): + """An example of command like calls via an Object""" + + __global_data = None + + @staticmethod + def get_data_name(rule_data, policy_id): + if not RulesCLI.__global_data: + RulesCLI.__global_data = data.SubjectDataCLI.list(policy_id).get("subject_data") + \ + data.ObjectDataCLI.list(policy_id).get("object_data") + \ + data.ActionDataCLI.list(policy_id).get("action_data") + _rule_names = list() + for rule_id in rule_data: + for _data in RulesCLI.__global_data: + if rule_id in _data.get("data"): + _rule_names.append(_data.get("data")[rule_id].get("name")) + break + else: + _rule_names.append(rule_id) + return _rule_names + + @staticmethod + def get_data_id(rule_data, policy_id): + _global_data = data.SubjectDataCLI.list(policy_id).get("subject_data") + \ + data.ObjectDataCLI.list(policy_id).get("object_data") + \ + data.ActionDataCLI.list(policy_id).get("action_data") + _rule_ids = list() + for rule_id_or_name in rule_data: + _id = None + for _data in _global_data: + if rule_id_or_name in _data.get("data"): + _id = _data.get("data")[rule_id_or_name].get("name") + break + else: + for _data_key in _data.get("data"): + if _data.get("data")[_data_key]['name'] == rule_id_or_name: + _id = _data_key + break + if _id: + _rule_ids.append(_id) + break + else: + raise Exception("Cannot find data for {}".format(rule_id_or_name)) + return _rule_ids + + @staticmethod + @hug.object.cli + def list(policy_name_or_id, human: bool = False, instructions: bool = False): + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + policy_id = list(policy.PoliciesCLI.list(policy_name_or_id).get("policies").keys())[0] + _rules = requests.get("{}/policies/{}/rules".format(db_conf.get("url"), policy_id), + headers={"x-api-key": manager_api_key} + ) + if _rules.status_code == 200: + if human: + for value in _rules.json().get("rules", {}).get("rules"): + if value.get("enabled"): + _rule_names = RulesCLI.get_data_name(value.get("rule"), policy_id) + output = value.get("id") + " | " + output += "{:30}".format(" ".join(_rule_names)) + if instructions: + output += " " + json.dumps(value.get("instructions")) + print(output) + else: + return _rules.json() + else: + raise Exception("Got a {} response ({})".format(_rules.status_code, _rules.text)) + + @staticmethod + @hug.object.cli + def add(policy_name_or_id, meta_rule_id_or_name, rule_items, + instructions: hug.types.one_of(("grant", "deny")) = None, enabled: bool = True): + if not instructions: + instructions = [{'decision': 'grant'}] + else: + instructions = [{'decision': instructions}] + rules_list = [] + for item in rule_items.split(","): + rules_list.append(item.strip()) + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + policy_id = list(policy.PoliciesCLI.list(policy_name_or_id).get("policies").keys())[0] + meta_rule_id = list(meta_rules.MetaRulesCLI.list(meta_rule_id_or_name). + get("meta_rules").keys())[0] + + _rules = requests.post("{}/policies/{}/rules".format(db_conf.get("url"), policy_id), + headers={"x-api-key": manager_api_key}, + json={ + "meta_rule_id": meta_rule_id, + "rule": RulesCLI.get_data_id(rules_list, policy_id), + "instructions": instructions, + "enabled": enabled + }) + if _rules.status_code == 200: + return _rules.json() + else: + raise Exception("Got a {} response ({})".format(_rules.status_code, _rules.text)) + + @staticmethod + @hug.object.cli + def update(policy_name_or_id, rule_id, instructions: hug.types.one_of(('grant', 'deny'))): + instructions = [{'decision': instructions}] + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + policy_id = list(policy.PoliciesCLI.list(policy_name_or_id).get("policies").keys())[0] + + _rules = requests.patch("{}/policies/{}/rules/{}".format(db_conf.get("url"), policy_id, rule_id), + headers={"x-api-key": manager_api_key}, + json={ + "instructions": instructions + }) + if _rules.status_code == 200: + return _rules.json() + else: + raise Exception("Got a {} response ({})".format(_rules.status_code, _rules.text)) + + @staticmethod + @hug.object.cli + def delete(policy_name_or_id, rule_id): + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + policy_id = list(policy.PoliciesCLI.list(policy_name_or_id).get("policies").keys())[0] + + _rules = requests.delete("{}/policies/{}/rules/{}".format( + db_conf.get("url"), policy_id, rule_id), + headers={"x-api-key": manager_api_key}) + if _rules.status_code == 200: + return _rules.json() + else: + raise Exception("Got a {} response ({})".format(_rules.status_code, _rules.text)) diff --git a/moon_manager/moon_manager/api/slave.py b/moon_manager/moon_manager/api/slave.py new file mode 100644 index 00000000..a0201bdb --- /dev/null +++ b/moon_manager/moon_manager/api/slave.py @@ -0,0 +1,341 @@ +# 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. + +""" +Slaves are endpoint for external connectors like OpenStack + +""" + +import logging +import hug +import os +import requests +from moon_manager.api import ERROR_CODE +from moon_manager import db_driver +from moon_manager import orchestration_driver +from moon_manager.api import configuration +from moon_utilities import exceptions +from moon_utilities.auth_functions import init_db, api_key_authentication, connect_from_env + +LOGGER = logging.getLogger("moon.manager.api." + __name__) + + +class Slaves(object): + """ + Endpoint for slave requests + """ + + @staticmethod + @hug.local() + @hug.get("/slaves/", requires=api_key_authentication) + @hug.get("/slaves/{uuid}", requires=api_key_authentication) + def get(uuid: hug.types.uuid = None, authed_user: hug.directives.user = None): + """Retrieve all slaves + + :param uuid: uuid of the pdp + :return: { + "slaves": { + "XXX": { + "name": "...", + "address": "..." + }, + "YYY": { + "name": "...", + "address": "..." + } + } + } + """ + if uuid: + uuid = str(uuid).replace("-", "") + data = db_driver.SlaveManager.get_slaves(moon_user_id=authed_user) + + return {"slaves": data} + + @staticmethod + @hug.local() + @hug.post("/slave/", requires=api_key_authentication) + def post(body, response, authed_user: hug.directives.user = None): + """Create a slave. + + :request body: { + "name": "name of the slave (mandatory)", + "address": "local_or_ssh://a.b.c.d", + "description": "description of the slave (optional)", + } + :return: { + "slaves": { + "XXX": { + "name": "...", + "address": "..." + }, + "YYY": { + "name": "...", + "address": "..." + } + } + } + """ + try: + # Create the DB item + data = db_driver.SlaveManager.add_slave( + moon_user_id=authed_user, slave_id=None, value=body) + + uuid = list(data.keys())[0] + # Build and run the process + new_data = orchestration_driver.SlaveManager.add_slave(moon_user_id=authed_user, + slave_id=uuid, data=data[uuid]) + + # Update the DB item with the information from the process (port, ...) + data = db_driver.SlaveManager.update_slave( + moon_user_id=authed_user, slave_id=uuid, value=new_data) + + except AttributeError as e: + response.status = ERROR_CODE[400] + LOGGER.exception(e) + except exceptions.MoonError as e: + response.status = ERROR_CODE[e.code] + return {"slaves": data} + + @staticmethod + @hug.local() + @hug.delete("/slave/{uuid}", requires=api_key_authentication) + def delete(uuid: hug.types.uuid, response=None, authed_user: hug.directives.user = None): + """Delete a slave + + :param uuid: uuid of the slave to delete + :param authed_user: authenticated user name + :param response: response initialized by Hug + :return: { + "result": "True or False", + "message": "optional message (optional)" + } + """ + uuid = str(uuid).replace("-", "") + try: + db_driver.SlaveManager.delete_slave( + moon_user_id=authed_user, slave_id=uuid) + + orchestration_driver.SlaveManager.delete_slave( + moon_user_id=authed_user, slave_id=uuid) + + except exceptions.MoonError as e: + response.status = ERROR_CODE[e.code] + return {"result": False, "description": str(e)} + except Exception as e: + LOGGER.exception(e) + return {"result": False, "description": str(e)} + return {"result": True} + + @staticmethod + @hug.local() + @hug.patch("/slave/{uuid}", requires=api_key_authentication) + def patch(uuid: hug.types.uuid, body, response, authed_user: hug.directives.user = None): + """Update a slave + + :param uuid: uuid of the slave to delete + :param body: body content of the Hug request + :param authed_user: authenticated user name + :param response: response initialized by Hug + :return: { + "pdp_id1": { + "name": "name of the PDP", + "address": "local_or_ssh://a.b.c.d", + "description": "description of the slave (optional)", + } + } + """ + + uuid = str(uuid).replace("-", "") + prev_data = db_driver.SlaveManager.get_slaves(moon_user_id=authed_user, slave_id=uuid) + if not prev_data: + response.status = ERROR_CODE[400] + return {"message": "The slave is unknown."} + try: + data = db_driver.SlaveManager.update_slave( + moon_user_id=authed_user, slave_id=uuid, value=body) + + + #TODO kill the server using orchestration_driver + + except AttributeError as e: + response.status = ERROR_CODE[400] + LOGGER.exception(e) + return {"message": str(e)} + except exceptions.MoonError as e: + response.status = ERROR_CODE[e.code] + return {"message": str(e)} + + orchestration_driver.SlaveManager.update_slave(moon_user_id=authed_user, slave_id=uuid, value=body) + + return { + "slaves": db_driver.SlaveManager.get_slaves(moon_user_id=authed_user, slave_id=uuid) + } + + +SlavesAPI = hug.API(name='slaves', doc=Slaves.__doc__) +db_conf = configuration.get_configuration(key='management') +init_db(db_conf.get("token_file")) + + +@hug.object(name='slaves', version='1.0.0', api=SlavesAPI) +class SlavesCLI(object): + """An example of command like calls via an Object""" + + @staticmethod + @hug.object.cli + def list(human: bool = False): + """ + List slaves from the database + :return: JSON status output + """ + db_conf = configuration.get_configuration(key='management') + + manager_api_key = connect_from_env() + _slaves = requests.get("{}/slaves".format(db_conf.get("url")), + headers={"x-api-key": manager_api_key} + ) + if _slaves.status_code == 200: + result = _slaves.json() + + if human: + return SlavesCLI.human_display(result) + else: + return result + LOGGER.error('Cannot list Slave Data {}'.format(_slaves.status_code)) + + @staticmethod + @hug.object.cli + def add(name='default', address="local", description="", grant_if_unknown_project: bool = False, human: bool = False): + """ + Add slave in the database + :return: JSON status output + """ + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + _slaves = requests.post( + "{}/slave".format(db_conf.get("url")), + json={ + "name": name, + "address": address, + "description": description, + "grant_if_unknown_project": grant_if_unknown_project + }, + headers={ + "x-api-key": manager_api_key, + "Content-Type": "application/json" + } + ) + if _slaves.status_code == 200: + LOGGER.warning('Create {}'.format(_slaves.content)) + if human: + return SlavesCLI.human_display(_slaves.json()) + else: + return _slaves.json() + LOGGER.error('Cannot create {}'.format(name, _slaves.content)) + + @staticmethod + @hug.object.cli + def delete(name='default'): + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + _slaves = SlavesCLI.list() + for _slave_id, _slave_value in _slaves.get("slaves").items(): + if _slave_value.get("name") == name: + req = requests.delete( + "{}/slave/{}".format(db_conf.get("url"), _slave_id), + headers={"x-api-key": manager_api_key} + ) + break + else: + LOGGER.error("Cannot find slave with name {}".format(name)) + return False + if req.status_code == 200: + LOGGER.warning('Deleted {}'.format(name)) + return True + LOGGER.error("Cannot delete slave with name {}".format(name)) + return False + + @staticmethod + @hug.object.cli + def update(name='default', address=None, description=None, + grant_if_unknown_project: hug.types.one_of(("y", "n")) = None): + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + _slaves = SlavesCLI.list() + + for _slave_id, _slave_value in _slaves.get("slaves").items(): + if _slave_value.get("name") == name: + address_updated = _slave_value.get("address") + description_updated = _slave_value.get("description") + grant_if_unknown_project_updated = _slave_value.get("grant_if_unknown_project") + + if address is not None: + address_updated = address + if description is not None: + description_updated = description + if grant_if_unknown_project is not None: + grant_if_unknown_project_updated = True if grant_if_unknown_project in ("y", "true", "1") else False + + req = requests.patch( + "{}/slave/{}".format(db_conf.get("url"), _slave_id), + json={ + "name": name, + "address": address_updated, + "description": description_updated, + "grant_if_unknown_project": grant_if_unknown_project_updated, + }, + headers={ + "x-api-key": manager_api_key, + "Content-Type": "application/json" + } + ) + if req.status_code == 200: + LOGGER.warning('Updated {}'.format(name)) + return True + else: + LOGGER.error('Cannot update {}'.format(name)) + return False + + LOGGER.error('Cannot find {}'.format(name)) + return False + + @staticmethod + def human_display(slaves_json): + human_result = "Slaves" + for slave in slaves_json.get("slaves"): + human_result += "\n" + slaves_json.get("slaves").get(slave).get("name") + " : \n" + human_result += "\tname : " + slaves_json.get("slaves").get(slave).get("name") + "\n" + human_result += "\tid : " + slave + "\n" + human_result += "\tdescription : " + slaves_json.get("slaves").get(slave).get("description") + "\n" + human_result += "\taddress : " + slaves_json.get("slaves").get(slave).get("address") + "\n" + human_result += "\tgrant_if_unknown_project : " + str(slaves_json.get("slaves").get(slave).get("grant_if_unknown_project")) + "\n" + human_result += "\tprocess : " + slaves_json.get("slaves").get(slave).get("process") + "\n" + human_result += "\tlog : " + slaves_json.get("slaves").get(slave).get("log") + "\n" + human_result += "\tapi_key : " + slaves_json.get("slaves").get(slave).get("api_key") + "\n" + human_result += SlavesCLI.human_display_extra(slaves_json.get("slaves").get(slave).get("extra")) + return human_result + + @staticmethod + def human_display_extra(extra_json): + human_result = "\textra" + human_result += "\n" + human_result += "\t\tdescription : " + extra_json.get("description") + "\n" + human_result += "\t\tstarttime : " + str(extra_json.get("starttime")) + "\n" + human_result += "\t\tport : " + str(extra_json.get("port")) + "\n" + human_result += "\t\tserver_ip : " + str(extra_json.get("server_ip")) + "\n" + human_result += "\t\tstatus : " + extra_json.get("status") + "\n" + human_result += "\t\tprocess : " + extra_json.get("process") + "\n" + human_result += "\t\tlog : " + extra_json.get("log") + "\n" + human_result += "\t\tapi_key : " + extra_json.get("api_key") + "\n" + return human_result + + diff --git a/moon_manager/moon_manager/api/slaves.py b/moon_manager/moon_manager/api/slaves.py deleted file mode 100644 index e2928de0..00000000 --- a/moon_manager/moon_manager/api/slaves.py +++ /dev/null @@ -1,111 +0,0 @@ -# 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'. -""" -PDP are Policy Decision Point. - -""" - -from flask import request -from flask_restful import Resource -import logging -import requests -from python_moonutilities.security_functions import check_auth - -from python_moonutilities import configuration -from python_moonutilities.security_functions import validate_input - -__version__ = "4.3.0" - -logger = logging.getLogger("moon.manager.api." + __name__) - - -class Slaves(Resource): - """ - Endpoint for pdp requests - """ - - __urls__ = ( - "/slaves", - "/slaves/", - "/slaves/", - "/slaves//", - ) - - def __init__(self, **kwargs): - conf = configuration.get_configuration("components/orchestrator") - self.orchestrator_hostname = conf["components/orchestrator"].get("hostname", - "orchestrator") - self.orchestrator_port = conf["components/orchestrator"].get("port", - 80) - - @validate_input("get", kwargs_state=[False, False]) - @check_auth - def get(self, uuid=None, user_id=None): - """Retrieve all slaves - - :param uuid: uuid of the slave - :param user_id: user ID who do the request - :return: { - "slaves": { - "XXX": { - "name": "...", - "installed": True - }, - "YYY": { - "name": "...", - "installed": False - } - } - } - """ - req = requests.get("http://{}:{}/slaves".format( - self.orchestrator_hostname, self.orchestrator_port - )) - return {"slaves": req.json().get("slaves", dict())} - - @validate_input("patch", kwargs_state=[False, False], - body_state={"op": True, "variable": True, "value": True}) - @check_auth - def patch(self, uuid=None, user_id=None): - """Update a slave - - :param uuid: uuid of the slave to update - :param user_id: user ID who do the request - :request body: { - "op": "replace", - "variable": "configured", - "value": True, - } - :return: 204 - :internal_api: add_pdp - """ - logger.info("Will made a request for {}".format(uuid)) - if request.json.get("op") == "replace" \ - and request.json.get("variable") == "configured" \ - and request.json.get("value"): - req = requests.post("http://{}:{}/pods".format( - self.orchestrator_hostname, self.orchestrator_port, - ), - json={"slave_name": uuid} - ) - if req.status_code != 200: - logger.warning("Get error from Orchestrator {} {}".format( - req.reason, req.status_code - )) - return "Orchestrator: " + str(req.reason), req.status_code - elif request.json.get("op") == "replace" \ - and request.json.get("variable") == "configured" \ - and not request.json.get("value"): - req = requests.delete("http://{}:{}/pods/{}".format( - self.orchestrator_hostname, self.orchestrator_port, uuid - )) - if req.status_code != 200: - logger.warning("Get error from Orchestrator {} {}".format( - req.reason, req.status_code - )) - return "Orchestrator: " + str(req.reason), req.status_code - else: - return "Malformed request", 400 - return {"slaves": req.json()} diff --git a/moon_manager/moon_manager/api/status.py b/moon_manager/moon_manager/api/status.py new file mode 100644 index 00000000..b299db49 --- /dev/null +++ b/moon_manager/moon_manager/api/status.py @@ -0,0 +1,127 @@ +# 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. + + +"""Status API""" +import hug +import logging +import requests +from moon_utilities.auth_functions import api_key_authentication, connect_from_env +from moon_manager import orchestration_driver +from moon_manager.api import configuration +from datetime import datetime + +logger = logging.getLogger("moon.manager.api.status") + + +class Status(object): + """ + Endpoint for status requests + """ + @staticmethod + @hug.local() + @hug.get("/status/", requires=api_key_authentication) + def list_status(authed_user: hug.directives.user=None): + """ + List statuses + :return: JSON status output + """ + pipelines = orchestration_driver.PipelineManager.get_pipelines(moon_user_id=authed_user) + slaves = orchestration_driver.SlaveManager.get_slaves(moon_user_id=authed_user) + + config = configuration.search_config_file("moon.yaml") + log_file = config["logging"]["handlers"]["file"]["filename"] + + result = {"status": { + "manager": {"name": "manager", "status": "up", "log": log_file}, + }} + + for slave in slaves: + result["status"][slave] = slaves[slave] + for slave in pipelines: + result["status"][slave].update(pipelines[slave]) + + web_port = config["dashboard"]["port"] + url = ":".join(config["management"]["url"].split(":")[:-1]) + + result["status"]["web_GUI"] = {"name": "web GUI", "status": "down"} + try: + req = requests.get(f"{url}:{web_port}") + except requests.exceptions.ConnectionError: + req = None + + if req and req.status_code == 200: + result["status"]["web_GUI"].update({"status": "up", "port": web_port}) + + return result + + +StatusAPI = hug.API(name='status', doc=Status.__doc__) + + +@hug.cli("status") +def status(quiet: bool = False, human: bool = False): + """ + CLI Parameter to get status + :param quiet: + :param human: + :return: JSON status output + """ + db_conf = configuration.get_configuration(key='management') + manager_api_key = connect_from_env() + _status = requests.get("{}/status".format(db_conf.get("url")), + headers={"x-api-key": manager_api_key} + ) + + if _status.status_code == 200: + if human: + result = "Status" + statuses = _status.json()["status"].items() + for uuid, values in statuses: + # for humans, it's best to call the servers by their name + # instead of their uuid + result += f"\n{values['name']} :" + if quiet: + result += " OK" if values["status"] == "up" else " KO" + pipelines = values.get("pipelines") + if pipelines is not None: + for pipeline_uuid in pipelines: + result += f"\n\t{pipeline_uuid} :" + result += " OK" if pipelines[pipeline_uuid]["status"] == "up" else " KO" + else: + result += "\n" # not quiet mode : newline needed + result += f"\tuuid : {uuid}\n" + for k2, v2 in values.items(): + if k2 == "pipelines": + result += StatusCLI.format_pipelines_for_status(values["pipelines"]) + elif k2 == "starttime": + result += f"\t{k2} : {datetime.fromtimestamp(v2).strftime('%d/%m/%Y %H:%M:%S')}\n" + elif k2 != "name": + result += f"\t{k2} : {v2}\n" + return result + else: + return _status.json() + + +@hug.object(name='status', version='1.0.0', api=StatusAPI) +class StatusCLI(object): + """An example of command like calls via an Object""" + @staticmethod + def format_pipelines_for_status(pipelines): + result = "" + for pipeline in pipelines: + result += f"\n\t{pipeline} :\n" + for k,v in pipelines[pipeline].items(): + if k == "starttime": + result += f"\t\t{k} : {datetime.fromtimestamp(v).strftime('%d/%m/%Y %H:%M:%S')}\n" + else: + result += f"\t\t{k} : {v}\n" + return result diff --git a/moon_manager/moon_manager/api/users.py b/moon_manager/moon_manager/api/users.py new file mode 100644 index 00000000..9de78ff3 --- /dev/null +++ b/moon_manager/moon_manager/api/users.py @@ -0,0 +1,95 @@ +# 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. + +""" +Users +""" +import hug +import logging +import getpass +from tinydb import Query +from moon_utilities.auth_functions import db, init_db, add_user, get_api_key, change_password + +LOGGER = logging.getLogger("moon.manager.api." + __name__) + +UsersAPI = hug.API('users') + + +@hug.object(name='users', version='1.0.0', api=UsersAPI) +class UsersCLI(object): + """An example of command like calls via an Object""" + + @staticmethod # nosec + @hug.object.cli + def add(username, password: hug.types.text = ""): + """ + Add a user to the database + """ + return add_user(username, password) + + @staticmethod # nosec + @hug.object.cli + def change_password(username, password: hug.types.text = "", new_password: hug.types.text = ""): + """ + Authenticate a username and password against our database + """ + result = change_password(username, password, new_password) + if not result: + return "Wrong password" + return result + + @staticmethod # nosec + @hug.object.cli + def key(username, password: hug.types.text = ""): + """ + Authenticate a username and password against our database + """ + if password == "": + password = getpass.getpass() + return get_api_key(username, password) + + @staticmethod + @hug.object.cli + def list(human: bool = False): + """ + List users from the database + """ + global db + if db is None: + init_db() + user_model = Query() + users = db.search(user_model.username.matches('.*')) + if human: + result = "Users" + if users: + for user in users: + result += f"\n{user['username']} : \n" + result += f"\tusername : {user['username']}\n" + result += f"\tapi_key : {user['api_key']}" + else: + result += f"\nNo user" + return result + else: + result = [] + if users: + for user in users: + result.append({ + 'username': user['username'], + 'api_key': user['api_key'] + }) + return {'users': result} + + + + + + + -- cgit 1.2.3-korg