diff options
Diffstat (limited to 'moon_utilities/moon_utilities')
-rw-r--r-- | moon_utilities/moon_utilities/__init__.py | 13 | ||||
-rw-r--r-- | moon_utilities/moon_utilities/auth_functions.py | 307 | ||||
-rw-r--r-- | moon_utilities/moon_utilities/exceptions.py | 903 | ||||
-rw-r--r-- | moon_utilities/moon_utilities/generate_opst_policy.py | 503 | ||||
-rw-r--r-- | moon_utilities/moon_utilities/install.py | 155 | ||||
-rw-r--r-- | moon_utilities/moon_utilities/invalided_functions.py | 440 | ||||
-rw-r--r-- | moon_utilities/moon_utilities/json_utils.py | 2076 | ||||
-rw-r--r-- | moon_utilities/moon_utilities/security_functions.py | 83 | ||||
-rw-r--r-- | moon_utilities/moon_utilities/update_opst_policies.py | 85 |
9 files changed, 4565 insertions, 0 deletions
diff --git a/moon_utilities/moon_utilities/__init__.py b/moon_utilities/moon_utilities/__init__.py new file mode 100644 index 00000000..d1e99258 --- /dev/null +++ b/moon_utilities/moon_utilities/__init__.py @@ -0,0 +1,13 @@ +# 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. + + +__version__ = "0.8" diff --git a/moon_utilities/moon_utilities/auth_functions.py b/moon_utilities/moon_utilities/auth_functions.py new file mode 100644 index 00000000..08a0ec22 --- /dev/null +++ b/moon_utilities/moon_utilities/auth_functions.py @@ -0,0 +1,307 @@ +# 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 binascii +import hashlib +import os +import hug +import logging +import os +import getpass +from tinydb import TinyDB, Query +from moon_utilities import exceptions + +LOGGER = logging.getLogger("moon.utilities.auth_functions") +db = None + + +def init_db(db_filename="db.json"): + global db + db = TinyDB(db_filename) + + +def xor_encode(data, key): + """ + Encode data with the given key + + :param data: the data to encode + :param key: the key ie password + :return: a xor-ed version of the 2 strings + """ + if not data: + return "" + if not key: + raise exceptions.EncryptError + return binascii.hexlify( + ''.join(chr(ord(a) ^ ord(b)) for a, b in zip(data, key)).encode("utf-8")).decode("utf-8") + + +def xor_decode(data, key): + """ + Decode data with the given key + + :param data: the data to decode + :param key: the key ie password + :return: a xor-ed version of the 2 strings + """ + if not data: + return "" + if not key: + raise exceptions.DecryptError + data = binascii.a2b_hex(data.encode("utf-8")).decode("utf-8") + return ''.join(chr(ord(a) ^ ord(b)) for a, b in zip(data, key)) + + +# From https://github.com/timothycrosley/hug/blob/develop/examples/secure_auth_with_db_example.py + +def hash_password(password, salt): + """ + Securely hash a password using a provided salt + :param password: + :param salt: + :return: Hex encoded SHA512 hash of provided password + """ + password = str(password).encode('utf-8') + salt = str(salt).encode('utf-8') + return hashlib.sha512(password + salt).hexdigest() + + +def gen_api_key(username): + """ + Create a random API key for a user + :param username: + :return: Hex encoded SHA512 random string + """ + salt = str(os.urandom(64)).encode('utf-8') + return hash_password(username, salt) + + +def get_api_key_for_user(username): + """ + Return the API key for a particular user + :param username: + :return: API key + """ + global db + if db is None: + init_db() + user_model = Query() + user = db.get(user_model.username == username) + + if not user: + LOGGER.warning("User %s not found", username) + return False + + return user['api_key'] + + +def del_api_key_for_user(username): + """ + Delete the API key for a particular user + :param username: + :return: API key + """ + global db + if db is None: + init_db() + user_model = Query() + users = db.search(user_model.username == username) + + if not users: + LOGGER.warning("User %s not found", username) + return False + try: + for user in users: + user['api_key'] = None + db.write_back(users) + return True + except Exception as e: + LOGGER.exception(e) + return False + + +def connect_from_env(): + try: + user = os.environ["MOON_USERNAME"] + pw = os.environ["MOON_PASSWORD"] + except KeyError: + LOGGER.error("Set your credentials with moonrc") + exit(-1) + + return get_api_key(user, pw) + + +@hug.cli("get_key") +def get_api_key(username, password): + """ + Authenticate a username and password against our database + :param username: + :param password: + :return: authenticated username + """ + global db + if db is None: + init_db() + user_model = Query() + user = db.get(user_model.username == username) + + if not user: + LOGGER.warning("User %s not found", username) + return False + + if user['password'] == hash_password(password, user.get('salt')): + return user['api_key'] + + return False + + +@hug.cli() +def authenticate_user(username, password): + """ + Authenticate a username and password against our database + :param username: + :param password: + :return: authenticated username + """ + global db + if db is None: + init_db() + user_model = Query() + users = db.search(user_model.username == username) + + if not users: + LOGGER.warning("User %s not found", username) + return False + + for user in users: + # Note: will only update the first item + if user['password'] == hash_password(password, user.get('salt')): + if not user['api_key']: + api_key = gen_api_key(username) + user['api_key'] = api_key + db.write_back(users) + return user['username'] + LOGGER.warning("Wrong password for user %s", username) + return False + + +@hug.cli() +def change_password(username, current_password, new_password): + """ + Change the password of the user in the database + :param username: + :param current_password: + :param new_password: + :return: True or False + """ + + if current_password == "": # nosec (not a hardcoded password) + current_password = getpass.getpass() + + is_password_ok = authenticate_user(username, current_password) + if not is_password_ok: + return False + + if new_password == "": # nosec (not a hardcoded password) + new_password = getpass.getpass() + + global db + if db is None: + init_db() + user_model = Query() + user = db.search(user_model.username == username)[0] + + salt = user['salt'] + password = hash_password(new_password, salt) + api_key = gen_api_key(username) + + user_id = db.update({'password': password, 'api_key': api_key}, doc_ids=[user.doc_id]) + + return { + 'result': 'success', + 'eid': user_id, + 'user_created': user, + 'api_key': api_key + } + + +@hug.cli() +def authenticate_key(api_key): + """ + Authenticate an API key against our database + :param api_key: + :return: authenticated username + """ + global db + if db is None: + init_db() + try: + if not api_key: + return False + user_model = Query() + user = db.search(user_model.api_key == api_key)[0] + if user: + return user['username'] + except Exception as e: + LOGGER.exception(e) + LOGGER.error("Cannot retrieve user for this authentication key {}".format(api_key)) + return False + + +""" + API Methods start here +""" + +api_key_authentication = hug.authentication.api_key(authenticate_key) +basic_authentication = hug.authentication.basic(authenticate_user) + + +@hug.cli("add_user") # nosec (not a hardcoded password) +def add_user(username, password=""): + """ + CLI Parameter to add a user to the database + :param username: + :param password: if not given, a password prompt is displayed + :return: JSON status output + """ + global db + if db is None: + init_db() + user_model = Query() + if db.search(user_model.username == username): + return { + 'error': 'User {0} already exists'.format(username) + } + + if password == "": # nosec (not a hardcoded password) + password = getpass.getpass() + + salt = hashlib.sha512(str(os.urandom(64)).encode('utf-8')).hexdigest() + password = hash_password(password, salt) + api_key = gen_api_key(username) + + user = { + 'username': username, + 'password': password, + 'salt': salt, + 'api_key': api_key + } + user_id = db.insert(user) + + return { + 'result': 'success', + 'eid': user_id, + 'user_created': user, + 'api_key': api_key + } diff --git a/moon_utilities/moon_utilities/exceptions.py b/moon_utilities/moon_utilities/exceptions.py new file mode 100644 index 00000000..4e016e70 --- /dev/null +++ b/moon_utilities/moon_utilities/exceptions.py @@ -0,0 +1,903 @@ +# 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.utilities.exceptions") +_ = str + + +class MoonErrorMetaClass(type): + + def __init__(cls, name, bases, dct): + super(MoonErrorMetaClass, cls).__init__(name, bases, dct) + cls.hierarchy += "/" + str(name) + + +class MoonError(BaseException): + __metaclass__ = MoonErrorMetaClass + hierarchy = "" + description = _("There is an error requesting the Moon platform.") + code = 400 + title = 'Moon Error' + logger = "ERROR" + + def __init__(self, message="", status_code=None, payload=""): + if message: + self.description = message + if status_code: + self.code = status_code + self.payload = payload + super(MoonError, self).__init__() + + def __str__(self): + return "{}: {}".format(self.code, self.title) + + def __del__(self): + message = "{} ({}) {}".format(self.hierarchy, self.description, self.payload) + if self.logger == "ERROR": + try: + logger.error(message) + except AttributeError: + logger.error(message) + elif self.logger == "WARNING": + try: + logger.warning(message) + except AttributeError: + logger.warning(message) + elif self.logger == "CRITICAL": + try: + logger.critical(message) + except AttributeError: + logger.critical(message) + elif self.logger == "AUTHZ": + try: + logger.error(message) + except AttributeError: + logger.error(message) + else: + try: + logger.info(message) + except AttributeError: + logger.info(message) + + # def to_dict(self): + # rv = dict(self.payload or ()) + # rv['message'] = "{} ({})".format(self.hierarchy, self.description) + # rv['title'] = self.title + # rv['code'] = self.code + # return rv + + +# Exceptions for Tenant + +class TenantException(MoonError): + description = _("There is an error requesting this tenant.") + code = 400 + title = 'Tenant Error' + logger = "ERROR" + + +class TenantUnknown(TenantException): + description = _("The tenant is unknown.") + code = 400 + title = 'Tenant Unknown' + logger = "ERROR" + + +class TenantAddedNameExisting(TenantException): + description = _("The tenant name is existing.") + code = 400 + title = 'Added Tenant Name Existing' + logger = "ERROR" + + +class TenantNoIntraExtension(TenantException): + description = _("The tenant has not intra_extension.") + code = 400 + title = 'Tenant No Intra_Extension' + logger = "ERROR" + + +class TenantNoIntraAuthzExtension(TenantNoIntraExtension): + description = _("The tenant has not intra_admin_extension.") + code = 400 + title = 'Tenant No Intra_Admin_Extension' + logger = "ERROR" + + +# Exceptions for IntraExtension + + +class IntraExtensionException(MoonError): + description = _("There is an error requesting this IntraExtension.") + code = 400 + title = 'Extension Error' + + +class IntraExtensionUnknown(IntraExtensionException): + description = _("The intra_extension is unknown.") + code = 400 + title = 'Intra Extension Unknown' + logger = "Error" + + +class ModelUnknown(MoonError): + description = _("The model is unknown.") + code = 400 + title = 'Model Unknown' + logger = "Error" + + +class ModelContentError(MoonError): + description = _("The model content is invalid.") + code = 400 + title = 'Model Unknown' + logger = "Error" + + +class ModelExisting(MoonError): + description = _("The model already exists.") + code = 409 + title = 'Model Error' + logger = "Error" + + +# Authz exceptions + +class AuthzException(MoonError): + description = _("There is an authorization error requesting this IntraExtension.") + code = 403 + title = 'Authz Exception' + logger = "AUTHZ" + + +# Auth exceptions + +class AuthException(MoonError): + description = _("There is an authentication error requesting this API. " + "You must provide a valid token from Keystone.") + code = 401 + title = 'Auth Exception' + logger = "AUTHZ" + + +# Admin exceptions + +class AdminException(MoonError): + description = _("There is an error requesting this Authz IntraExtension.") + code = 400 + title = 'Authz Exception' + logger = "AUTHZ" + + +class AdminMetaData(AdminException): + code = 400 + title = 'Metadata Exception' + + +class AdminPerimeter(AdminException): + code = 400 + title = 'Perimeter Exception' + + +class AdminScope(AdminException): + code = 400 + title = 'Scope Exception' + + +class AdminAssignment(AdminException): + code = 400 + title = 'Assignment Exception' + + +class AdminMetaRule(AdminException): + code = 400 + title = 'Aggregation Algorithm Exception' + + +class AdminRule(AdminException): + code = 400 + title = 'Rule Exception' + + +class CategoryNameInvalid(AdminMetaData): + description = _("The given category name is invalid.") + code = 400 + title = 'Category Name Invalid' + logger = "ERROR" + + +class SubjectCategoryExisting(AdminMetaData): + description = _("The given subject category already exists.") + code = 409 + title = 'Subject Category Existing' + logger = "ERROR" + + +class ObjectCategoryExisting(AdminMetaData): + description = _("The given object category already exists.") + code = 409 + title = 'Object Category Existing' + logger = "ERROR" + + +class ActionCategoryExisting(AdminMetaData): + description = _("The given action category already exists.") + code = 409 + title = 'Action Category Existing' + logger = "ERROR" + + +class SubjectCategoryUnknown(AdminMetaData): + description = _("The given subject category is unknown.") + code = 400 + title = 'Subject Category Unknown' + logger = "ERROR" + + +class DeleteSubjectCategoryWithMetaRule(MoonError): + description = _("Cannot delete subject category used in meta rule ") + code = 400 + title = 'Subject Category With Meta Rule Error' + logger = "Error" + + +class DeleteObjectCategoryWithMetaRule(MoonError): + description = _("Cannot delete Object category used in meta rule ") + code = 400 + title = 'Object Category With Meta Rule Error' + logger = "Error" + + +class ObjectCategoryUnknown(AdminMetaData): + description = _("The given object category is unknown.") + code = 400 + title = 'Object Category Unknown' + logger = "ERROR" + + +class DeleteActionCategoryWithMetaRule(MoonError): + description = _("Cannot delete Action category used in meta rule ") + code = 400 + title = 'Action Category With Meta Rule Error' + logger = "Error" + + +class ActionCategoryUnknown(AdminMetaData): + description = _("The given action category is unknown.") + code = 400 + title = 'Action Category Unknown' + logger = "ERROR" + + +class PerimeterContentError(AdminPerimeter): + description = _("Perimeter content is invalid.") + code = 400 + title = 'Perimeter content is invalid.' + logger = "ERROR" + + +class DeletePerimeterWithAssignment(MoonError): + description = _("Cannot delete perimeter with assignment") + code = 400 + title = 'Perimeter With Assignment Error' + logger = "Error" + + +class SubjectUnknown(AdminPerimeter): + description = _("The given subject is unknown.") + code = 400 + title = 'Subject Unknown' + logger = "ERROR" + + +class ObjectUnknown(AdminPerimeter): + description = _("The given object is unknown.") + code = 400 + title = 'Object Unknown' + logger = "ERROR" + + +class ActionUnknown(AdminPerimeter): + description = _("The given action is unknown.") + code = 400 + title = 'Action Unknown' + logger = "ERROR" + + +class SubjectExisting(AdminPerimeter): + description = _("The given subject is existing.") + code = 409 + title = 'Subject Existing' + logger = "ERROR" + + +class ObjectExisting(AdminPerimeter): + description = _("The given object is existing.") + code = 409 + title = 'Object Existing' + logger = "ERROR" + + +class ActionExisting(AdminPerimeter): + description = _("The given action is existing.") + code = 409 + title = 'Action Existing' + logger = "ERROR" + + +class SubjectNameExisting(AdminPerimeter): + description = _("The given subject name is existing.") + code = 409 + title = 'Subject Name Existing' + logger = "ERROR" + + +class ObjectNameExisting(AdminPerimeter): + description = _("The given object name is existing.") + code = 409 + title = 'Object Name Existing' + logger = "ERROR" + + +class ActionNameExisting(AdminPerimeter): + description = _("The given action name is existing.") + code = 409 + title = 'Action Name Existing' + logger = "ERROR" + + +class ObjectsWriteNoAuthorized(AdminPerimeter): + description = _("The modification on Objects is not authorized.") + code = 400 + title = 'Objects Write No Authorized' + logger = "AUTHZ" + + +class ActionsWriteNoAuthorized(AdminPerimeter): + description = _("The modification on Actions is not authorized.") + code = 400 + title = 'Actions Write No Authorized' + logger = "AUTHZ" + + +class SubjectScopeUnknown(AdminScope): + description = _("The given subject scope is unknown.") + code = 400 + title = 'Subject Scope Unknown' + logger = "ERROR" + + +class ObjectScopeUnknown(AdminScope): + description = _("The given object scope is unknown.") + code = 400 + title = 'Object Scope Unknown' + logger = "ERROR" + + +class ActionScopeUnknown(AdminScope): + description = _("The given action scope is unknown.") + code = 400 + title = 'Action Scope Unknown' + logger = "ERROR" + + +class SubjectScopeExisting(AdminScope): + description = _("The given subject scope is existing.") + code = 409 + title = 'Subject Scope Existing' + logger = "ERROR" + + +class ObjectScopeExisting(AdminScope): + description = _("The given object scope is existing.") + code = 409 + title = 'Object Scope Existing' + logger = "ERROR" + + +class ActionScopeExisting(AdminScope): + description = _("The given action scope is existing.") + code = 409 + title = 'Action Scope Existing' + logger = "ERROR" + + +class SubjectScopeNameExisting(AdminScope): + description = _("The given subject scope name is existing.") + code = 409 + title = 'Subject Scope Name Existing' + logger = "ERROR" + + +class ObjectScopeNameExisting(AdminScope): + description = _("The given object scope name is existing.") + code = 409 + title = 'Object Scope Name Existing' + logger = "ERROR" + + +class ActionScopeNameExisting(AdminScope): + description = _("The given action scope name is existing.") + code = 409 + title = 'Action Scope Name Existing' + logger = "ERROR" + + +class SubjectAssignmentUnknown(AdminAssignment): + description = _("The given subject assignment value is unknown.") + code = 400 + title = 'Subject Assignment Unknown' + logger = "ERROR" + + +class ObjectAssignmentUnknown(AdminAssignment): + description = _("The given object assignment value is unknown.") + code = 400 + title = 'Object Assignment Unknown' + logger = "ERROR" + + +class ActionAssignmentUnknown(AdminAssignment): + description = _("The given action assignment value is unknown.") + code = 400 + title = 'Action Assignment Unknown' + logger = "ERROR" + + +class SubjectAssignmentExisting(AdminAssignment): + description = _("The given subject assignment value is existing.") + code = 409 + title = 'Subject Assignment Existing' + logger = "ERROR" + + +class ObjectAssignmentExisting(AdminAssignment): + description = _("The given object assignment value is existing.") + code = 409 + title = 'Object Assignment Existing' + logger = "ERROR" + + +class ActionAssignmentExisting(AdminAssignment): + description = _("The given action assignment value is existing.") + code = 409 + title = 'Action Assignment Existing' + logger = "ERROR" + + +class AggregationAlgorithmNotExisting(AdminMetaRule): + description = _("The given aggregation algorithm is not existing.") + code = 400 + title = 'Aggregation Algorithm Not Existing' + logger = "ERROR" + + +class AggregationAlgorithmUnknown(AdminMetaRule): + description = _("The given aggregation algorithm is unknown.") + code = 400 + title = 'Aggregation Algorithm Unknown' + logger = "ERROR" + + +class SubMetaRuleAlgorithmNotExisting(AdminMetaRule): + description = _("The given sub_meta_rule algorithm is unknown.") + code = 400 + title = 'Sub_meta_rule Algorithm Unknown' + logger = "ERROR" + + +class MetaRuleUnknown(AdminMetaRule): + description = _("The given meta rule is unknown.") + code = 400 + title = 'Meta Rule Unknown' + logger = "ERROR" + + +class MetaRuleNotLinkedWithPolicyModel(MoonError): + description = _("The meta rule is not found in the model attached to the policy.") + code = 400 + title = 'MetaRule Not Linked With Model - Policy' + logger = "Error" + + +class CategoryNotAssignedMetaRule(MoonError): + description = _("The category is not found in the meta rules attached to the policy.") + code = 400 + title = 'Category Not Linked With Meta Rule - Policy' + logger = "Error" + + +class SubMetaRuleNameExisting(AdminMetaRule): + description = _("The sub meta rule name already exists.") + code = 409 + title = 'Sub Meta Rule Name Existing' + logger = "ERROR" + + +class MetaRuleExisting(AdminMetaRule): + description = _("The meta rule already exists.") + code = 409 + title = 'Meta Rule Existing' + logger = "ERROR" + + +class MetaRuleContentError(AdminMetaRule): + description = _("Invalid content of meta rule.") + code = 400 + title = 'Meta Rule Error' + logger = "ERROR" + + +class MetaRuleUpdateError(AdminMetaRule): + description = _("Meta_rule is used in Rule.") + code = 400 + title = 'Meta_Rule Update Error' + logger = "ERROR" + + +class RuleExisting(AdminRule): + description = _("The rule already exists.") + code = 409 + title = 'Rule Existing' + logger = "ERROR" + + +class RuleContentError(AdminRule): + description = _("Invalid content of rule.") + code = 400 + title = 'Rule Error' + logger = "ERROR" + + +class RuleUnknown(AdminRule): + description = _("The rule for that request doesn't exist.") + code = 400 + title = 'Rule Unknown' + logger = "ERROR" + + +# Consul exceptions + + +class ConsulError(MoonError): + description = _("There is an error connecting to Consul.") + code = 400 + title = 'Consul error' + logger = "ERROR" + + +class ConsulComponentNotFound(ConsulError): + description = _("The component do not exist in Consul database.") + code = 500 + title = 'Consul error' + logger = "WARNING" + + +class ConsulComponentContentError(ConsulError): + description = _("invalid content of component .") + code = 500 + title = 'Consul Content error' + logger = "WARNING" + + +# Containers exceptions + + +class DockerError(MoonError): + description = _("There is an error with Docker.") + code = 400 + title = 'Docker error' + logger = "ERROR" + + +class ContainerMissing(DockerError): + description = _("Some containers are missing.") + code = 400 + title = 'Container missing' + logger = "ERROR" + + +class WrapperConflict(MoonError): + description = _("A Wrapper already exist for the specified slave.") + code = 409 + title = 'Wrapper conflict' + logger = "ERROR" + + +class PipelineConflict(MoonError): + description = _("A Pipeline already exist for the specified slave.") + code = 409 + title = 'Pipeline conflict' + logger = "ERROR" + + +class PipelineUnknown(MoonError): + description = _("This Pipeline is unknown from the system.") + code = 400 + title = 'Pipeline Unknown' + logger = "ERROR" + + +class WrapperUnknown(MoonError): + description = _("This Wrapper is unknown from the system.") + code = 400 + title = 'Wrapper Unknown' + logger = "ERROR" + + +class SlaveNameUnknown(MoonError): + description = _("The slave is unknown.") + code = 400 + title = 'Slave Unknown' + logger = "Error" + + +class SlaveExisting(MoonError): + description = _("The slave already exists.") + code = 409 + title = 'Slave Error' + logger = "Error" + + +class PdpUnknown(MoonError): + description = _("The pdp is unknown.") + code = 400 + title = 'Pdp Unknown' + logger = "Error" + + +class PdpExisting(MoonError): + description = _("The pdp already exists.") + code = 409 + title = 'Pdp Error' + logger = "Error" + + +class PdpContentError(MoonError): + description = _("Invalid content of pdp.") + code = 400 + title = 'Pdp Error' + logger = "Error" + + +class PdpInUse(MoonError): + description = _("The pdp is inuse.") + code = 400 + title = 'Pdp Inuse' + logger = "Error" + + +class PdpKeystoneMappingConflict(MoonError): + description = _("A pdp is already mapped to that Keystone project.") + code = 409 + title = 'Pdp Mapping Error' + logger = "Error" + + +class PolicyUnknown(MoonError): + description = _("The policy is unknown.") + code = 400 + title = 'Policy Unknown' + logger = "Error" + + +class PolicyContentError(MoonError): + description = _("The policy content is invalid.") + code = 400 + title = 'Policy Content Error' + logger = "Error" + + +class PolicyExisting(MoonError): + description = _("The policy already exists.") + code = 409 + title = 'Policy Already Exists' + logger = "Error" + + +class PolicyUpdateError(MoonError): + description = _("The policy data is used.") + code = 400 + title = 'Policy update error' + logger = "Error" + + +class DeleteData(MoonError): + description = _("Cannot delete data with assignment") + code = 400 + title = 'Data Error' + logger = "Error" + + +class DeleteCategoryWithData(MoonError): + description = _("Cannot delete category with data") + code = 400 + title = 'Category With Data Error' + logger = "Error" + + +class DeleteCategoryWithMetaRule(MoonError): + description = _("Cannot delete category with meta rule") + code = 400 + title = 'Category With MetaRule Error' + logger = "Error" + + +class DeleteCategoryWithAssignment(MoonError): + description = _("Cannot delete category with assignment ") + code = 400 + title = 'Category With Assignment Error' + logger = "Error" + + +class DeleteModelWithPolicy(MoonError): + description = _("Cannot delete model with policy") + code = 400 + title = 'Model With Policy Error' + logger = "Error" + + +class DeletePolicyWithPdp(MoonError): + description = _("Cannot delete policy with pdp") + code = 400 + title = 'Policy With PDP Error' + logger = "Error" + + +class DeletePolicyWithPerimeter(MoonError): + description = _("Cannot delete policy with perimeter") + code = 400 + title = 'Policy With Perimeter Error' + logger = "Error" + + +class DeletePolicyWithData(MoonError): + description = _("Cannot delete policy with data") + code = 400 + title = 'Policy With Data Error' + logger = "Error" + + +class DeletePolicyWithRules(MoonError): + description = _("Cannot delete policy with rules") + code = 400 + title = 'Policy With Rule Error' + logger = "Error" + + +class DeleteMetaRuleWithModel(MoonError): + description = _("Cannot delete meta rule with model") + code = 400 + title = 'Meta rule With Model Error' + logger = "Error" + + +class DeleteMetaRuleWithRule(MoonError): + description = _("Cannot delete meta rule with rule") + code = 400 + title = 'Meta rule With Model Error' + logger = "Error" + + +class DataContentError(MoonError): + description = _("The data Content Error.") + code = 400 + title = 'Data Content Error' + logger = "Error" + + +class DataUnknown(MoonError): + description = _("The data unknown.") + code = 400 + title = 'Data Unknown' + logger = "Error" + + +class ValidationContentError(MoonError): + description = _("The Content validation incorrect.") + code = 400 + title = 'Invalid Content' + logger = "Error" + + def __init__(self, message=""): + self.message = message + super().__init__(message) + + def __str__(self): + return self.message + + +class ValidationKeyError(MoonError): + description = _("The Key validation incorrect.") + code = 400 + title = 'Invalid Key' + logger = "Error" + + def __init__(self, message=""): + self.message = message + super().__init__(message) + + def __str__(self): + return self.message + + +class ForbiddenOverride(MoonError): + description = _("Forbidden override flag.") + code = 500 + title = 'Forbidden override.' + logger = "Error" + + +class InvalidJson(MoonError): + description = _("Invalid Json") + code = 400 + title = 'Invalid Json.' + logger = "Error" + + +class UnknownName(MoonError): + description = _("Name is Unknown") + code = 400 + title = 'Unknown Name.' + logger = "Error" + + +class UnknownId(MoonError): + description = _("ID is Unknown") + code = 400 + title = 'Unknown ID.' + logger = "Error" + + +class MissingIdOrName(MoonError): + description = _("Name or ID is missing") + code = 400 + title = 'Missing ID or Name.' + logger = "Error" + + +class UnknownField(MoonError): + description = _("Field is Unknown") + code = 400 + title = 'Unknown Field.' + logger = "Error" + + +class DecryptError(MoonError): + description = _("Cannot decrypt API key") + code = 401 + title = 'API Key Error.' + logger = "Error" + + +class EncryptError(MoonError): + description = _("Cannot encrypt API key") + code = 401 + title = 'API Key Error.' + logger = "Error" + + +class AttributeUnknownError(MoonError): + description = _("Cannot find attribute") + code = 401 + title = 'Attribute Error.' + logger = "Error" + + +class AttributeValueUnknownError(MoonError): + description = _("Cannot find value for this attribute") + code = 401 + title = 'Attribute Value Error.' + logger = "Error" + diff --git a/moon_utilities/moon_utilities/generate_opst_policy.py b/moon_utilities/moon_utilities/generate_opst_policy.py new file mode 100644 index 00000000..4e357911 --- /dev/null +++ b/moon_utilities/moon_utilities/generate_opst_policy.py @@ -0,0 +1,503 @@ +# 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. + +""" +Generate a policy template from a list of OpenStack policy.json file +""" +import argparse +import json +import logging +import os +import re +import glob +import copy + + +FILES = [ + "cinder.policy.json", + "glance.policy.json", + "keystone.policy.json", + "neutron.policy.json", + "nova.policy.json", +] +policy_in = { + "pdps": [ + { + "name": "external_pdp", + "keystone_project_id": "", + "description": "", + "policies": [{"name": "OpenStack RBAC Policy"}] + } + ], + + "policies": [ + { + "name": "OpenStack RBAC Policy", + "genre": "authz", + "description": "A RBAC policy similar of what you can find through policy.json files", + "model": {"name": "OPST_RBAC"}, "mandatory": True, "override": True + } + ], + + "models": [ + { + "name": "OPST_RBAC", + "description": "", + "meta_rules": [{"name": "rbac"}], + "override": True + } + ], + + "subjects": [ + {"name": "admin", "description": "", "extra": {}, + "policies": [{"name": "OpenStack RBAC Policy"}]} + ], + + "subject_categories": [{"name": "role", "description": "a role in OpenStack"}], + + "subject_data": [ + {"name": "admin", "description": "the admin role", + "policies": [], "category": {"name": "role"}}, + {"name": "member", "description": "the member role", + "policies": [], "category": {"name": "role"}} + ], + + "subject_assignments": [ + {"subject": {"name": "admin"}, "category": {"name": "role"}, + "assignments": [{"name": "admin"}, {"name": "member"}]}, + ], + + "objects": [ + {"name": "all", "description": "describe all element of a project", "extra": {}, + "policies": [{"name": "OpenStack RBAC Policy"}]}, + ], + + "object_categories": [{"name": "id", "description": "the UID of each virtual machine"}], + + "object_data": [ + { + "name": "all", + "description": "represents all virtual machines in this project", + "policies": [], + "category": {"name": "id"}}, + ], + + "object_assignments": [ + {"object": {"name": "all"}, "category": {"name": "id"}, "assignments": [{"name": "all"}]} + ], + + "actions": [], + + "action_categories": [{"name": "action_id", "description": ""}], + + "action_data": [], + + "action_assignments": [], + + "meta_rules": [ + { + "name": "rbac", "description": "", + "subject_categories": [{"name": "role"}], + "object_categories": [{"name": "id"}], + "action_categories": [{"name": "action_id"}] + } + ], + + "rules": [], + +} + +policy_out = copy.deepcopy(policy_in) + +AUTO_EXCLUDE_KEYS = """ +context_is_admin +admin_or_owner +admin_api +default +owner +context_is_advsvc +admin_or_network_owner +admin_owner_or_network_owner +admin_only +regular_user +admin_or_data_plane_int +shared +shared_subnetpools +shared_address_scopes +external +admin_required +cloud_admin +service_role +service_or_admin +admin_and_matching_domain_id +service_admin_or_owner +""" + +logger = logging.getLogger(__name__) +__rules = [] + + +def init(): + """ + Initialize the application + :return: argument given in the command line + """ + global policy_in, policy_out + parser = argparse.ArgumentParser() + parser.add_argument("--verbose", '-v', action='store_true', help='verbose mode') + parser.add_argument("--debug", '-d', action='store_true', help='debug mode') + parser.add_argument("--dir", + help='directory containing policy files, defaults to ./policy.json.d', + default="./policy.json.d") + parser.add_argument("--template", "-t", + help='use a specific template file, defaults to internal template', + default="") + parser.add_argument("--indent", '-i', help='indent the output (default:None)', type=int, + default=None) + parser.add_argument("--output", '-o', help='output name, defaults to opst_default_policy.json', + type=str, default="opst_default_policy.json") + parser.add_argument("--exclude", "-x", + help="Exclude some attributes in output " + "(example: \"actions,*_categories\")", + default="") + args = parser.parse_args() + logging_format = "%(levelname)s: %(message)s" + if args.verbose: + logging.basicConfig(level=logging.INFO, format=logging_format) + if args.debug: + logging.basicConfig(level=logging.DEBUG, format=logging_format) + else: + logging.basicConfig(format=logging_format) + if args.template: + try: + policy_in = json.loads(open(args.template).read()) + policy_out = copy.deepcopy(policy_in) + logger.info("Using template {}".format(args.template)) + policy_out.pop('rules') + policy_out['rules'] = [] + for cpt, item in enumerate(policy_in['rules']): + if "templates" not in item: + policy_out['rules'].append(item) + policy_out.pop('action_assignments') + policy_out['action_assignments'] = [] + for cpt, item in enumerate(policy_in['action_assignments']): + if "templates" not in item: + policy_out['action_assignments'].append(item) + + except json.decoder.JSONDecodeError as e: + logger.error("Cannot decode template file {}".format(args.template)) + if args.debug: + logger.exception(e) + + return args + + +def get_json(filename): + """ + retrieve rule from a JSON file + :param filename: url of the file + :return: list of rules in this file + """ + _json_file = json.loads(open(filename).read()) + keys = list(_json_file.keys()) + values = list(_json_file.values()) + for value in values: + if value in keys: + keys.remove(value) + return keys + + +def get_flat(filename): + """ + retrieve rule from a flat text file + :param filename: url of the file + :return: list of rules in this file + """ + results = [] + for line in open(filename): + key = line.split('"')[1] + results.append(key) + return results + + +def get_opst_rules(args): + """ + Get all rules in each policy.json + :param args: arguments given in the command line + :return: all rules in a dict + """ + results = {} + for filename in glob.glob(os.path.join(args.dir, "**/*.json"), recursive=True): + logger.info("Reading {}".format(filename)) + component = filename.replace("policy.json", "").strip("/") + component = os.path.basename(component).split(".")[0] + try: + keys = get_json(filename) + except json.decoder.JSONDecodeError: + keys = get_flat(filename) + for _key in AUTO_EXCLUDE_KEYS.splitlines(): + if _key in keys: + keys.remove(_key) + results[component] = keys + logger.info("Adding {} definitions in {}".format(len(keys), component)) + return results + + +def get_policy_name(): + """ + Retrieve the policy name from the policy dict + Useful if the policy data comes from a template + """ + for _policy in policy_in.get("policies"): + return _policy.get("name") + + +def get_meta_rule_name(): + """ + Retrieve the policy name from the policy dict + Useful if the policy data comes from a template + """ + for _policy in policy_in.get("meta_rules"): + return _policy.get("name") + + +def get_default_data(data, category): + """ + Find the default value from a list of data, return the first one if no default found + :param data: the data to search in + :param category: the category of the data + :return: one name contained in data + """ + for _data in data: + if _data.get("category").get("name") == category: + if _data.get("extra", {}).get("action") == "default": + return _data.get("name") + # default: return the first one + for _data in data: + if _data.get("category").get("name") == category: + return _data.get("name") + + +def get_meta_rule(meta_rule_name=None): + for meta_rule in policy_in['meta_rules']: + if meta_rule_name == meta_rule.get('name'): + return meta_rule + else: + return policy_in['meta_rules'][0] + + +def build_actions(opst_rule, component): + _output = { + "name": opst_rule, + "description": "{} action for {}".format(opst_rule, component), + "extra": {"component": component}, + "policies": [] + } + policy_out['actions'].append(_output) + + +def build_action_data(opst_rule, component): + _output = { + "name": opst_rule, + "description": "{} action for {}".format(opst_rule, component), + "policies": [], + "category": {"name": "action_id"} + } + policy_out['action_data'].append(_output) + + +def build_action_assignments_with_templates(opst_rule): + for assignment in policy_in['action_assignments']: + for template in assignment.get('templates', []): + name = template.get('action', {}).get('name') + new = None + if "filter:" in name: + if "*" == name.split(":")[-1].strip(): + new = copy.deepcopy(template) + new['action']['name'] = opst_rule + else: + for _str in name.split(":")[-1].strip().split(","): + if _str.strip() in opst_rule: + new = copy.deepcopy(template) + new['action']['name'] = opst_rule + if new: + policy_out['action_assignments'].append(new) + + +def build_action_assignments(opst_rule): + _output = { + "action": {"name": opst_rule}, + "category": {"name": "action_id"}, + "assignments": [{"name": opst_rule}, ]} + policy_out['action_assignments'].append(_output) + build_action_assignments_with_templates(opst_rule) + + +def add_rule(rule): + # TODO: check rule before adding it + if rule in __rules: + # note: don't add the rule if already added + return + __rules.append(rule) + _raw_rule = { + "subject_data": [], + "object_data": [], + "action_data": [] + } + cpt = 0 + for _ in get_meta_rule().get("subject_categories"): + _raw_rule["subject_data"].append({"name": rule[cpt]}) + cpt += 1 + for _ in get_meta_rule().get("object_categories"): + _raw_rule["object_data"].append({"name": rule[cpt]}) + cpt += 1 + for _ in get_meta_rule().get("action_categories"): + _raw_rule["action_data"].append({"name": rule[cpt]}) + cpt += 1 + + _output = { + "meta_rule": {"name": get_meta_rule_name()}, + "rule": _raw_rule, + "policy": {"name": get_policy_name()}, + "instructions": [{"decision": "grant"}], + "enabled": True + } + policy_out['rules'].append(_output) + + +def check_filter_on_template(data_list, opst_rule): + for cpt, _data in enumerate(data_list): + if "filter:" in _data: + filter_str = _data.partition(":")[-1] + if filter_str == "*": + data_list[cpt] = opst_rule + add_rule(data_list) + else: + for _str in filter_str.split(","): + if _str.strip() in opst_rule: + data_list[cpt] = opst_rule + add_rule(data_list) + break + + +def build_rules_with_templates(rule, opst_rule): + meta_rule = get_meta_rule() + for template in rule.get("templates", []): + data_list = [] + for cat in meta_rule.get("subject_categories"): + for item in template.get("subject"): + if item.get("category") == cat.get('name'): + data_list.append(item.get("name")) + break + for cat in meta_rule.get("object_categories"): + for item in template.get("object"): + if item.get("category") == cat.get('name'): + data_list.append(item.get("name")) + break + for cat in meta_rule.get("action_categories"): + for item in template.get("action"): + if item.get("category") == cat.get('name'): + data_list.append(item.get("name")) + break + check_filter_on_template(data_list, opst_rule) + + +def build_rules(opst_rule): + rules = policy_in.get("rules") + for rule in rules: + if isinstance(rule, dict): + build_rules_with_templates(rule, opst_rule) + else: + add_rule(rule) + + +def build_dict(results): + """ + Build the dictionary given the actions found in the policy.json files + :param results: list of rule for each component + :return: nothing + """ + policy_out["rules"] = [] + for component in results: + for opst_rule in results[component]: + build_actions(opst_rule, component) + build_action_data(opst_rule, component) + build_action_assignments(opst_rule) + build_rules(opst_rule) + + +def exclude_attrs(args): + """ + Exclude attributes from the output JSON file + :param args: arguments given in the command line + :return: nothing + """ + attrs_to_exclude = [] + if not args.exclude: + return + for excl_item in args.exclude.split(","): + excl_item = excl_item.replace("*", ".*").strip() + logger.debug("excl_item=%s", excl_item) + for attr in policy_in: + logger.debug("attr=%s", attr) + if re.match(excl_item, attr): + attrs_to_exclude.append(attr) + for attr in attrs_to_exclude: + logger.info("Deleting %s", attr) + policy_out.pop(attr) + + +def write_tests(rules): + if "admin" not in map(lambda x: x.get("name"), policy_in.get("subjects")): + logger.warning("Don't write tests in output because, there is no 'admin' user") + return + if "all" not in map(lambda x: x.get("name"), policy_in.get("objects")): + logger.warning("Don't write tests in output because, there is no 'all' object") + return + if "checks" not in policy_in: + policy_out["checks"] = {"granted": [], "denied": []} + if "granted" not in policy_in["checks"]: + policy_out["checks"]["granted"] = [] + if "denied" not in policy_in["checks"]: + policy_out["checks"]["denied"] = [] + for component in rules: + for rule in rules.get(component): + policy_out["checks"]["granted"].append(("admin", "all", rule)) + if "test_user" in map(lambda x: x.get("name"), policy_in.get("subjects")): + for component in rules: + for rule in rules.get(component): + policy_out["checks"]["denied"].append(("test_user", "all", rule)) + + +def write_dict(args): + """ + Write the dictionary in the output filename given in command line + :param args: arguments given in the command line + :return: nothing + """ + json.dump(policy_out, open(args.output, "w"), indent=args.indent) + + +def main(): + """ + Main end point + :return: nothing + """ + args = init() + rules = get_opst_rules(args) + build_dict(rules) + write_tests(rules) + exclude_attrs(args) + write_dict(args) + + +if __name__ == "__main__": + main() diff --git a/moon_utilities/moon_utilities/install.py b/moon_utilities/moon_utilities/install.py new file mode 100644 index 00000000..236dfe06 --- /dev/null +++ b/moon_utilities/moon_utilities/install.py @@ -0,0 +1,155 @@ +# 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. + +""" +Install the Moon platform +""" + +import argparse +import logging +import os +import subprocess # nosec +import sys +import getpass + +try: + import pytest +except ModuleNotFoundError: + subprocess.call([sys.executable, "-m", "pip", "install", "pytest", "--upgrade"]) # nosec + subprocess.call([sys.executable, "-m", "pip", "install", "pytest-cov", "--upgrade"]) # nosec + subprocess.call([sys.executable, "-m", "pip", "install", "cliff", "--upgrade"]) # nosec + subprocess.call([sys.executable, "-m", "pip", "install", "requests_mock", "--upgrade"]) # nosec + import pytest +try: + from git import Repo +except ModuleNotFoundError: + subprocess.call([sys.executable, "-m", "pip", "install", "GitPython", "--upgrade"]) # nosec + from git import Repo + +COMPONENTS = { + "moon_utilities": [], + "moon_cache": [], + "moon_manager": [], + "moon_engine": [], +} + +logger = logging.getLogger(__name__) + + +def init(): + """ + Initialize the application + :return: argument given in the command line + """ + parser = argparse.ArgumentParser() + parser.add_argument("--verbose", '-v', action='store_true', help='verbose mode') + parser.add_argument("--debug", '-d', action='store_true', help='debug mode') + parser.add_argument("--pre", '-p', action='store_true', help='install packages in dev mode') + parser.add_argument("--git", '-g', action='store_true', help='install packages from source tree') + parser.add_argument("--tests", '-t', action='store_true', help='run tests on each package') + parser.add_argument("--username", '-u', help='set the username for the Gitlab server') + parser.add_argument("--password", '-pa', help='set the password for the Gitlab server') + parser.add_argument("--password-file", '-pf', + help='set the filename of the file containing all passwords for the ' + 'Gitlab server') + parser.add_argument("--do-not-clean", '-dnc', action='store_true', + help='do not clean the dev environment') + args = parser.parse_args() + logging_format = "%(levelname)s: %(message)s" + if args.verbose: + logging.basicConfig(level=logging.INFO, format=logging_format) + if args.debug: + logging.basicConfig(level=logging.DEBUG, format=logging_format) + else: + logging.basicConfig(format=logging_format) + + if args.password_file: + logger.info("Using {} as password file".format(args.password_file)) + for line in open(args.password_file): + try: + comp, user, password = line.split(":") + if comp in COMPONENTS: + COMPONENTS[comp] = [user, password.strip()] + else: + logger.error("Unknown component {} in password file".format(comp)) + except ValueError: + # empty line + pass + return args + + +def install_from_pkg(package, args): + logger.info(f"Installing from pkg {package}") + command = [sys.executable, "-m", "pip", "install", package, "--upgrade"] + if args.pre: + command.append("--pre") + subprocess.call(command) # nosec + + +def install_from_src(package, args): + logger.info(f"Installing {package} from source...") + if os.path.isdir(os.path.join("src", package)): + repo = Repo("src/" + package) + repo.remote().pull() + else: + if args.password_file: + Repo.clone_from("https://{}:{}@gitlab.forge.orange-labs.fr/moon/{}.git".format( + COMPONENTS[package][0], COMPONENTS[package][1], package), + to_path=os.path.join(os.getcwd(), "src", package)) + elif args.username: + logger.info(f"installing with {args.username}") + Repo.clone_from("https://{}:{}@gitlab.forge.orange-labs.fr/moon/{}.git".format( + args.username, args.password, package), + to_path=os.path.join(os.getcwd(), "src", package)) + else: + Repo.clone_from("https://gitlab.forge.orange-labs.fr/moon/{}.git".format(package), + to_path=os.path.join(os.getcwd(), "src", package)) + + # logger.info(f"Installing from source {package}") + cur_dir = os.getcwd() + os.chdir(os.path.join("src", package)) + command = [sys.executable, "-m", "pip", "install", "-r", "requirements.txt"] + subprocess.call(command) # nosec + command = [sys.executable, "-m", "pip", "install", "."] + subprocess.call(command) # nosec + + if args.tests: + pytest.main(["tests/unit_python"]) + + os.chdir(cur_dir) + + +def clean_git(): + subprocess.call(["rm", "-rf", "src"]) # nosec + + +def main(): + args = init() + if not args.git: + for component in COMPONENTS: + install_from_pkg(component, args) + else: + try: + try: + os.mkdir("src") + except FileExistsError: + pass + if args.username and not args.password: + args.password = getpass.getpass(f"Give the password for {args.username} Gitlab") + for component in COMPONENTS: + install_from_src(component, args) + finally: + if not args.do_not_clean: + clean_git() + + +if __name__ == "__main__": + main() diff --git a/moon_utilities/moon_utilities/invalided_functions.py b/moon_utilities/moon_utilities/invalided_functions.py new file mode 100644 index 00000000..1c24d3f4 --- /dev/null +++ b/moon_utilities/moon_utilities/invalided_functions.py @@ -0,0 +1,440 @@ +# 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 +import requests + +logger = logging.getLogger("moon.utilities." + __name__) + + +def invalidate_assignment_in_slaves(slaves, policy_id, perimeter_id, category_id, data_id, type): + """ + Send a request to one or more slaves to invalidate specific assignments + :param slaves: list of slaves + :param policy_id: the ID of the concerned policy + :param perimeter_id: the ID of the concerned perimeter + :param category_id: the ID of the concerned category + :param data_id: the ID of the concerned data + :return: None + """ + + hostname, port = "", "" + uri = "update/assignment" + result = [] + for key, value in slaves.get('slaves', {}).items(): + if value.get("extra", {}).get("status") != "up": + continue + try: + hostname = value.get("extra", {}).get("server_ip") + port = value.get("extra", {}).get("port") + if policy_id and perimeter_id and category_id and data_id: + update = requests.delete("http://{}:{}/{}/{}/{}/{}/{}/{}".format( + hostname, port, uri, policy_id, type, perimeter_id, category_id, data_id), + timeout=1 + ) + elif policy_id and perimeter_id and category_id: + update = requests.delete("http://{}:{}/{}/{}/{}/{}/{}".format( + hostname, port, uri, policy_id, type, perimeter_id, category_id), + timeout=1 + ) + elif policy_id and perimeter_id: + update = requests.delete("http://{}:{}/{}/{}/{}/{}".format( + hostname, port, uri, policy_id, type, perimeter_id), + timeout=1 + ) + elif policy_id: + update = requests.delete("http://{}:{}/{}/{}/{}".format( + hostname, port, uri, policy_id, type), + timeout=1 + ) + + logger.debug("result {} {}:{} = {}".format( + update.status_code, + hostname, + port, + update.text)) + result.append(value.get("name")) + except requests.exceptions.ConnectionError: + logger.warning( + "Cannot reach {}:{}".format(hostname, port)) + except requests.models.InvalidURL: + logger.warning( + "Invalid URL {}:{}".format(hostname, port)) + return result + + +def invalidate_data_in_slaves(slaves, policy_id, category_id, data_id, type): + """ + Send a request to one or more slaves to invalidate specific data + :param slaves: list of slaves + :param policy_id: the ID of the concerned policy + :param category_id: the ID of the concerned category + :param data_id: the ID of the concerned data + :return: None + """ + + hostname, port = "", "" + uri = "update/data" + result = [] + for key, value in slaves.get('slaves', {}).items(): + if value.get("extra", {}).get("status") != "up": + continue + try: + hostname = value.get("extra", {}).get("server_ip") + port = value.get("extra", {}).get("port") + update = requests.delete("http://{}:{}/{}/{}/{}".format( + hostname, port, uri, data_id, type), + timeout=1 + ) + logger.debug("result {} {}:{} = {}".format( + update.status_code, + hostname, + port, + update.text)) + result.append(value.get("name")) + except requests.exceptions.ConnectionError: + logger.warning( + "Cannot reach {}:{}".format(hostname, port)) + except requests.models.InvalidURL: + logger.warning( + "Invalid URL {}:{}".format(hostname, port)) + return result + + +def invalidate_perimeter_in_slaves(slaves, policy_id, perimeter_id, type, data=None, + is_delete=True): + """ + Send a request to one or more slaves to invalidate specific perimeter + :param slaves: list of slaves + :param policy_id: the ID of the concerned policy + :param perimeter_id: the ID of the concerned perimeter + :return: None + """ + + hostname, port = "", "" + uri = "update/perimeter" + result = [] + for key, value in slaves.get('slaves', {}).items(): + if value.get("extra", {}).get("status") != "up": + continue + try: + hostname = value.get("extra", {}).get("server_ip") + port = value.get("extra", {}).get("port") + if is_delete: + update = requests.delete("http://{}:{}/{}/{}/{}/{}".format( + hostname, port, uri, perimeter_id, policy_id, type), + timeout=1 + ) + else: + update = requests.put("http://{}:{}/{}/{}/{}/{}".format( + hostname, port, uri, perimeter_id, policy_id, type), + data=data, + timeout=1 + ) + logger.debug("result {} {}:{} = {}".format( + update.status_code, + hostname, + port, + update.text)) + result.append(value.get("name")) + except requests.exceptions.ConnectionError: + logger.warning( + "Cannot reach {}:{}".format(hostname, port)) + except requests.models.InvalidURL: + logger.warning( + "Invalid URL {}:{}".format(hostname, port)) + return result + + +def invalidate_pdp_in_slaves(slaves, pdp_id, is_delete=True, data=None): + """ + Send a request to one or more slaves to invalidate specific PDPs + :param slaves: list of slaves + :param pdp_id: the ID of the concerned PDP + :return: None + """ + + hostname, port = "", "" + uri = "update/pdp" + result = [] + for key, value in slaves.get('slaves', {}).items(): + if value.get("extra", {}).get("status") != "up": + continue + try: + hostname = value.get("extra", {}).get("server_ip") + port = value.get("extra", {}).get("port") + if is_delete: + update = requests.delete("http://{}:{}/{}/{}".format( + hostname, port, uri, pdp_id), + timeout=1 + ) + else: + update = requests.put("http://{}:{}/{}/{}".format( + hostname, port, uri, pdp_id), + data=data, + timeout=1 + ) + logger.debug("result {} {}:{} = {}".format( + update.status_code, + hostname, + port, + update.text)) + result.append(value.get("name")) + except requests.exceptions.ConnectionError: + logger.warning( + "Cannot reach {}:{}".format(hostname, port)) + except requests.models.InvalidURL: + logger.warning( + "Invalid URL {}:{}".format(hostname, port)) + return result + + +def invalidate_policy_in_slaves(slaves, policy_id, is_delete=True, data=None): + """ + Send a request to one or more slaves to invalidate specific policies + :param slaves: list of slaves + :param policy_id: the ID of the concerned policy + :return: None + """ + + hostname, port = "", "" + uri = "update/policy" + result = [] + for key, value in slaves.get('slaves', {}).items(): + if value.get("extra", {}).get("status") != "up": + continue + try: + hostname = value.get("extra", {}).get("server_ip") + port = value.get("extra", {}).get("port") + if is_delete: + update = requests.delete("http://{}:{}/{}/{}".format( + hostname, port, uri, policy_id), + timeout=1 + ) + else: + update = requests.put("http://{}:{}/{}/{}".format( + hostname, port, uri, policy_id), + data=data, + timeout=1 + ) + + logger.debug("result {} {}:{} = {}".format( + update.status_code, + hostname, + port, + update.text)) + result.append(value.get("name")) + except requests.exceptions.ConnectionError: + logger.warning( + "Cannot reach {}:{}".format(hostname, port)) + except requests.models.InvalidURL: + logger.warning( + "Invalid URL {}:{}".format(hostname, port)) + return result + + +def invalidate_rules_in_slaves(slaves, policy_id, rule_id): + """ + Send a request to one or more slaves to invalidate specific rules + :param slaves: list of slaves + :param policy_id: the ID of the concerned policy + :param rule_id: the ID of the concerned rule + :return: None + """ + + hostname, port = "", "" + uri = "update/rule" + result = [] + for key, value in slaves.get('slaves', {}).items(): + if value.get("extra", {}).get("status") != "up": + continue + try: + hostname = value.get("extra", {}).get("server_ip") + port = value.get("extra", {}).get("port") + update = requests.delete("http://{}:{}/{}/{}/{}".format( + hostname, port, uri, policy_id, rule_id), + timeout=1 + ) + logger.debug("result {} {}:{} = {}".format( + update.status_code, + hostname, + port, + update.text)) + result.append(value.get("name")) + except requests.exceptions.ConnectionError: + logger.warning( + "Cannot reach {}:{}".format(hostname, port)) + except requests.models.InvalidURL: + logger.warning( + "Invalid URL {}:{}".format(hostname, port)) + return result + + +def invalidate_model_in_slaves(slaves, model_id, is_delete=True, data=None): + """ + Send a request to one or more slaves to invalidate specific models + :param slaves: list of slaves + :param model_id: the ID of the concerned model + :return: None + """ + + hostname, port = "", "" + uri = "update/model" + result = [] + for key, value in slaves.get('slaves', {}).items(): + if value.get("extra", {}).get("status") != "up": + continue + try: + hostname = value.get("extra", {}).get("server_ip") + port = value.get("extra", {}).get("port") + if is_delete: + update = requests.delete("http://{}:{}/{}/{}".format( + hostname, port, uri, model_id), + timeout=1 + ) + else: + update = requests.put("http://{}:{}/{}/{}".format( + hostname, port, uri, model_id), + data=data, + timeout=1 + ) + logger.debug("result {} {}:{} = {}".format( + update.status_code, + hostname, + port, + update.text)) + result.append(value.get("name")) + except requests.exceptions.ConnectionError: + logger.warning( + "Cannot reach {}:{}".format(hostname, port)) + except requests.models.InvalidURL: + logger.warning( + "Invalid URL {}:{}".format(hostname, port)) + return result + + +def invalidate_meta_data_in_slaves(slaves, category_id, type): + """ + Send a request to one or more slaves to invalidate specific meta data + :param slaves: list of slaves + :param category_id: the ID of the concerned category + :return: None + """ + + hostname, port = "", "" + uri = "update/meta_data" + result = [] + for key, value in slaves.get('slaves', {}).items(): + if value.get("extra", {}).get("status") != "up": + continue + try: + hostname = value.get("extra", {}).get("server_ip") + port = value.get("extra", {}).get("port") + update = requests.delete("http://{}:{}/{}/{}/{}".format( + hostname, port, uri, category_id, type), + data={ + "category_id": category_id + }, + timeout=1 + ) + logger.debug("result {} {}:{} = {}".format( + update.status_code, + hostname, + port, + update.text)) + result.append(value.get("name")) + except requests.exceptions.ConnectionError: + logger.warning( + "Cannot reach {}:{}".format(hostname, port)) + except requests.models.InvalidURL: + logger.warning( + "Invalid URL {}:{}".format(hostname, port)) + return result + + +def invalidate_meta_rule_in_slaves(slaves, meta_rule_id, is_delete=True, data=None): + """ + Send a request to one or more slaves to invalidate specific meta rules + :param slaves: list of slaves + :param meta_rule_id: the ID of the concerned policy + :return: None + """ + + hostname, port = "", "" + uri = "update/meta_rule" + result = [] + for key, value in slaves.get('slaves', {}).items(): + if value.get("extra", {}).get("status") != "up": + continue + try: + hostname = value.get("extra", {}).get("server_ip") + port = value.get("extra", {}).get("port") + if is_delete: + update = requests.delete("http://{}:{}/{}/{}".format( + hostname, port, uri, meta_rule_id), + timeout=1 + ) + else: + update = requests.put("http://{}:{}/{}/{}".format( + hostname, port, uri, meta_rule_id), + data=data, + timeout=1 + ) + + logger.debug("result {} {}:{} = {}".format( + update.status_code, + hostname, + port, + update.text)) + result.append(value.get("name")) + except requests.exceptions.ConnectionError: + logger.warning( + "Cannot reach {}:{}".format(hostname, port)) + except requests.models.InvalidURL: + logger.warning( + "Invalid URL {}:{}".format(hostname, port)) + + +def invalidate_attributes_in_slaves(slaves, name, value=None): + """ + Send a request to one or more slaves to invalidate specific data + :param slaves: list of slaves + :param name: the name of the attribute to invalidate + :param value: the value that has changed + :return: a list of updated slaves + """ + + hostname, port = "", "" + uri = "update/attributes" + result = [] + for key, value in slaves.items(): + try: + hostname = value.get("extra", {}).get("server_ip") + port = value.get("extra", {}).get("port") + update = requests.delete("http://{}:{}/{}/{}".format( + hostname, port, uri, name), + headers={"x-api-key": value.get("extra", {}).get("api_key")}, + timeout=1 + ) + if update.status_code in (200, 202, 206, 208): + result.append(value.get("name")) + else: + logger.warning("Error when updating {} ({})".format(key, update.status_code)) + except requests.exceptions.ConnectionError: + logger.warning( + "Cannot reach {}:{}".format(hostname, port)) + except requests.models.InvalidURL: + logger.warning( + "Invalid URL {}:{}".format(hostname, port)) + return result + + diff --git a/moon_utilities/moon_utilities/json_utils.py b/moon_utilities/moon_utilities/json_utils.py new file mode 100644 index 00000000..52ab5be4 --- /dev/null +++ b/moon_utilities/moon_utilities/json_utils.py @@ -0,0 +1,2076 @@ +# 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 html +import json +import logging + +from moon_utilities import exceptions + +LOGGER = logging.getLogger("moon.utilities." + __name__) + + +class Manager: + __policy_manager = None + __model_manager = None + __pdp_manager = None + + def get_meta_rules(self, **kwargs): + raise NotImplementedError # pragma: no cover + + def set_subject_data(self, **kwargs): + raise NotImplementedError # pragma: no cover + + def set_object_data(self, **kwargs): + raise NotImplementedError # pragma: no cover + + def set_action_data(self, **kwargs): + raise NotImplementedError # pragma: no cover + + def get_subject_data(self, **kwargs): + raise NotImplementedError # pragma: no cover + + def get_object_data(self, **kwargs): + raise NotImplementedError # pragma: no cover + + def get_action_data(self, **kwargs): + raise NotImplementedError # pragma: no cover + + def add_rule(self, **kwargs): + raise NotImplementedError # pragma: no cover + + def add_meta_rule(self, **kwargs): + raise NotImplementedError # pragma: no cover + + def add_subject_assignment(self, **kwargs): + raise NotImplementedError # pragma: no cover + + def add_object_assignment(self, **kwargs): + raise NotImplementedError # pragma: no cover + + def add_action_assignment(self, **kwargs): + raise NotImplementedError # pragma: no cover + + def get_subject_assignments(self, **kwargs): + raise NotImplementedError # pragma: no cover + + def get_object_assignments(self, **kwargs): + raise NotImplementedError # pragma: no cover + + def get_action_assignments(self, **kwargs): + raise NotImplementedError # pragma: no cover + + def get_policies(self, **kwargs): + raise NotImplementedError # pragma: no cover + + def add_subject_category(self, **kwargs): + raise NotImplementedError # pragma: no cover + + def add_object_category(self, **kwargs): + raise NotImplementedError # pragma: no cover + + def add_action_category(self, **kwargs): + raise NotImplementedError # pragma: no cover + + def get_subject_categories(self, **kwargs): + raise NotImplementedError # pragma: no cover + + def get_object_categories(self, **kwargs): + raise NotImplementedError # pragma: no cover + + def get_action_categories(self, **kwargs): + raise NotImplementedError # pragma: no cover + + def add_subject(self, **kwargs): + raise NotImplementedError # pragma: no cover + + def add_object(self, **kwargs): + raise NotImplementedError # pragma: no cover + + def add_action(self, **kwargs): + raise NotImplementedError # pragma: no cover + + def get_subjects(self, **kwargs): + raise NotImplementedError # pragma: no cover + + def get_objects(self, **kwargs): + raise NotImplementedError # pragma: no cover + + def get_actions(self, **kwargs): + raise NotImplementedError # pragma: no cover + + def add_policy(self, **kwargs): + raise NotImplementedError # pragma: no cover + + def update_policy(self, **kwargs): + raise NotImplementedError # pragma: no cover + + def get_models(self, **kwargs): + raise NotImplementedError # pragma: no cover + + def update_model(self, **kwargs): + raise NotImplementedError # pragma: no cover + + def add_model(self, **kwargs): + raise NotImplementedError # pragma: no cover + + def get_pdp(self, **kwargs): + raise NotImplementedError # pragma: no cover + + def update_pdp(self, **kwargs): + raise NotImplementedError # pragma: no cover + + def add_pdp(self, **kwargs): + raise NotImplementedError # pragma: no cover + + def get_rules(self, **kwargs): + raise NotImplementedError # pragma: no cover + + +class CacheManager(Manager): + __policy_manager = None + __model_manager = None + __pdp_manager = None + + def __init__(self, cache): + self.__cache = cache + + def get_meta_rules(self, **kwargs): + return self.__cache.meta_rules + + def set_subject_data(self, **kwargs): + value = kwargs.get("value", {}) + value["category_id"] = kwargs.get("category_id", "") + value["policy_id"] = kwargs.get("policy_id", "") + return self.__cache.add_subject_data( + policy_id=kwargs.get("policy_id", ""), + category_id=kwargs.get("category_id", ""), + data=value + ) + + def set_object_data(self, **kwargs): + value = kwargs.get("value", {}) + value["category_id"] = kwargs.get("category_id", "") + value["policy_id"] = kwargs.get("policy_id", "") + return self.__cache.add_object_data( + policy_id=kwargs.get("policy_id", ""), + category_id=kwargs.get("category_id", ""), + data=value + ) + + def set_action_data(self, **kwargs): + value = kwargs.get("value", {}) + value["category_id"] = kwargs.get("category_id", "") + value["policy_id"] = kwargs.get("policy_id", "") + return self.__cache.add_action_data( + policy_id=kwargs.get("policy_id", ""), + category_id=kwargs.get("category_id", ""), + data=value + ) + + def get_subject_data(self, **kwargs): + if "policy_id" in kwargs: + results = [] + for data in self.__cache.subject_data: + if data.get("policy_id") == kwargs["policy_id"]: + if data.get("category_id") == kwargs.get("category_id"): + results.append(data) + elif not kwargs.get("category_id"): + results.append(data) + if "data_id" in kwargs: + for res in results: + if kwargs.get("data_id") in res.get("data"): + return [res, ] + else: + return results + else: + return self.__cache.subject_data + + def get_object_data(self, **kwargs): + if "policy_id" in kwargs: + results = [] + for data in self.__cache.object_data: + if data.get("policy_id") == kwargs["policy_id"]: + if data.get("category_id") == kwargs.get("category_id"): + results.append(data) + elif not kwargs.get("category_id"): + results.append(data) + return results + else: + return self.__cache.object_data + + def get_action_data(self, **kwargs): + if "policy_id" in kwargs: + results = [] + for data in self.__cache.action_data: + if data.get("policy_id") == kwargs["policy_id"]: + if data.get("category_id") == kwargs.get("category_id"): + results.append(data) + elif not kwargs.get("category_id"): + results.append(data) + return results + else: + return self.__cache.action_data + + def add_rule(self, **kwargs): + value = { + 'policy_id': kwargs.get('policy_id'), + 'meta_rule_id': kwargs.get('meta_rule_id'), + 'value': kwargs.get('value') + } + return self.__cache.add_rule(value) + + def add_meta_rule(self, **kwargs): + return self.__cache.add_meta_rule(kwargs.get("value")) + + def add_subject_assignment(self, **kwargs): + return self.__cache.add_subject_assignment( + policy_id=kwargs.get("policy_id"), + perimeter_id=kwargs.get("subject_id"), + category_id=kwargs.get("category_id"), + data_id=kwargs.get("data_id"), + ) + + def add_object_assignment(self, **kwargs): + return self.__cache.add_object_assignment( + policy_id=kwargs.get("policy_id"), + perimeter_id=kwargs.get("object_id"), + category_id=kwargs.get("category_id"), + data_id=kwargs.get("data_id"), + ) + + def add_action_assignment(self, **kwargs): + return self.__cache.add_action_assignment( + policy_id=kwargs.get("policy_id"), + perimeter_id=kwargs.get("action_id"), + category_id=kwargs.get("category_id"), + data_id=kwargs.get("data_id"), + ) + + def get_subject_assignments(self, **kwargs): + return self.__cache.subject_assignments + + def get_object_assignments(self, **kwargs): + return self.__cache.object_assignments + + def get_action_assignments(self, **kwargs): + return self.__cache.action_assignments + + def get_policies(self, **kwargs): + return self.__cache.policies + + def add_subject_category(self, **kwargs): + if kwargs.get("value", {}).get("name") != html.escape(kwargs.get("value", {}).get("name")): + raise exceptions.ValidationContentError('Forbidden characters in string') + if kwargs.get("category_id"): + if kwargs.get("category_id") in self.__cache.subject_categories: + raise exceptions.SubjectCategoryExisting + self.__cache.add_subject_category(kwargs.get("value")) + + def add_object_category(self, **kwargs): + if kwargs.get("value", {}).get("name") != html.escape(kwargs.get("value", {}).get("name")): + raise exceptions.ValidationContentError('Forbidden characters in string') + if kwargs.get("category_id"): + if kwargs.get("category_id") in self.__cache.object_categories: + raise exceptions.ObjectCategoryExisting + self.__cache.add_object_category(kwargs.get("value")) + + def add_action_category(self, **kwargs): + if kwargs.get("value", {}).get("name") != html.escape(kwargs.get("value", {}).get("name")): + raise exceptions.ValidationContentError('Forbidden characters in string') + if kwargs.get("category_id"): + if kwargs.get("category_id") in self.__cache.action_categories: + raise exceptions.ActionCategoryExisting + self.__cache.add_action_category(kwargs.get("value")) + + def get_subject_categories(self, **kwargs): + return self.__cache.subject_categories + + def get_object_categories(self, **kwargs): + return self.__cache.object_categories + + def get_action_categories(self, **kwargs): + return self.__cache.action_categories + + def add_subject(self, **kwargs): + if kwargs.get("value", {}).get("name") != html.escape(kwargs.get("value", {}).get("name")): + raise exceptions.ValidationContentError('Forbidden characters in string') + value = kwargs.get("value") + if kwargs.get("policy_id"): + value["policy_list"] = [kwargs.get("policy_id")] + if kwargs.get("perimeter_id"): + return self.__cache.update_subject(kwargs.get("perimeter_id"), value) + else: + return self.__cache.add_subject(value) + + def add_object(self, **kwargs): + if kwargs.get("value", {}).get("name") != html.escape(kwargs.get("value", {}).get("name")): + raise exceptions.ValidationContentError('Forbidden characters in string') + value = kwargs.get("value") + if kwargs.get("policy_id"): + value["policy_list"] = [kwargs.get("policy_id")] + if kwargs.get("perimeter_id"): + return self.__cache.update_object(kwargs.get("perimeter_id"), value) + else: + return self.__cache.add_object(value) + + def add_action(self, **kwargs): + if kwargs.get("value", {}).get("name") != html.escape(kwargs.get("value", {}).get("name")): + raise exceptions.ValidationContentError('Forbidden characters in string') + value = kwargs.get("value") + if kwargs.get("policy_id"): + value["policy_list"] = [kwargs.get("policy_id")] + if kwargs.get("perimeter_id"): + return self.__cache.update_action(kwargs.get("perimeter_id"), value) + else: + return self.__cache.add_action(value) + + def get_subjects(self, **kwargs): + results = {} + subjects = self.__cache.subjects + for policy_id in subjects: + results.update(subjects[policy_id]) + return results + + def get_objects(self, **kwargs): + results = {} + objects = self.__cache.objects + for policy_id in objects: + results.update(objects[policy_id]) + return results + + def get_actions(self, **kwargs): + results = {} + actions = self.__cache.actions + for policy_id in actions: + results.update(actions[policy_id]) + return results + + def add_policy(self, **kwargs): + return self.__cache.add_policy(kwargs.get("value")) + + def update_policy(self, **kwargs): + self.__cache.update_policy(kwargs.get("policy_id"), kwargs.get("value")) + + def get_models(self, **kwargs): + return self.__cache.models + + def update_model(self, **kwargs): + self.__cache.update_model(kwargs.get("model_id"), kwargs.get("value")) + + def add_model(self, **kwargs): + if kwargs.get("value", {}).get("name") != html.escape(kwargs.get("value", {}).get("name")): + raise exceptions.ValidationContentError('Forbidden characters in string') + self.__cache.add_model(kwargs.get("value")) + + def get_pdp(self, **kwargs): + return self.__cache.pdp + + def update_pdp(self, **kwargs): + self.__cache.add_pdp(pdp_id=kwargs.get("pdp_id"), data=kwargs.get("value")) + + def add_pdp(self, **kwargs): + self.__cache.add_pdp(data=kwargs.get("value")) + + def get_rules(self, **kwargs): + return self.__cache.rules + + +class DBManager(Manager): + __policy_manager = None + __model_manager = None + __pdp_manager = None + + def __init__(self, driver): # pragma: no cover + self.__policy_manager = driver.PolicyManager + self.__model_manager = driver.ModelManager + self.__pdp_manager = driver.PDPManager + + @property + def meta_rules(self): + return self.get_meta_rules() + + def get_meta_rules(self, **kwargs): + return self.__model_manager.get_meta_rules( + moon_user_id=kwargs.get("moon_user_id"), + meta_rule_id=kwargs.get("meta_rule_id") + ) + + def set_subject_data(self, **kwargs): + value = self.__policy_manager.set_subject_data( + moon_user_id=kwargs.get("moon_user_id"), + policy_id=kwargs.get("policy_id"), + category_id=kwargs.get("category_id"), + value=kwargs.get("value") + ) + return value + + def set_object_data(self, **kwargs): + return self.__policy_manager.add_object_data( + moon_user_id=kwargs.get("moon_user_id"), + policy_id=kwargs.get("policy_id"), + category_id=kwargs.get("category_id"), + value=kwargs.get("value") + ) + + def set_action_data(self, **kwargs): + return self.__policy_manager.add_action_data( + moon_user_id=kwargs.get("moon_user_id"), + policy_id=kwargs.get("policy_id"), + category_id=kwargs.get("category_id"), + value=kwargs.get("value") + ) + + @property + def subject_data(self): + _data = [] + for policy in self.__policy_manager.get_policies(moon_user_id="admin"): + for category in self.__model_manager.get_subject_categories(moon_user_id="admin"): + _data += self.__policy_manager.get_subject_data(moon_user_id="admin", + policy_id=policy, + category_id=category) + return _data + + def get_subject_data(self, **kwargs): + return self.__policy_manager.get_subject_data( + moon_user_id=kwargs.get("moon_user_id"), + policy_id=kwargs.get("policy_id"), + data_id=kwargs.get("data_id"), + category_id=kwargs.get("category_id") + ) + + @property + def object_data(self): + _data = [] + for policy in self.__policy_manager.get_policies(moon_user_id="admin"): + _data += self.__policy_manager.get_object_data(moon_user_id="admin", + policy_id=policy) + return _data + + def get_object_data(self, **kwargs): + return self.__policy_manager.get_object_data( + moon_user_id=kwargs.get("moon_user_id"), + policy_id=kwargs.get("policy_id"), + data_id=kwargs.get("data_id"), + category_id=kwargs.get("category_id") + ) + + @property + def action_data(self): + _data = [] + for policy in self.__policy_manager.get_policies(moon_user_id="admin"): + _data += self.__policy_manager.get_action_data(moon_user_id="admin", + policy_id=policy) + return _data + + def get_action_data(self, **kwargs): + return self.__policy_manager.get_action_data( + moon_user_id=kwargs.get("moon_user_id"), + policy_id=kwargs.get("policy_id"), + data_id=kwargs.get("data_id"), + category_id=kwargs.get("category_id") + ) + + def add_rule(self, **kwargs): + return self.__policy_manager.add_rule( + moon_user_id=kwargs.get("moon_user_id"), + policy_id=kwargs.get("policy_id"), + meta_rule_id=kwargs.get("meta_rule_id"), + value=kwargs.get("value") + ) + + def add_meta_rule(self, **kwargs): + return self.__model_manager.add_meta_rule( + moon_user_id=kwargs.get("moon_user_id"), + meta_rule_id=kwargs.get("meta_rule_id"), + value=kwargs.get("value") + ) + + @property + def subject_assignments(self): + _assignments = {} + for policy in self.__policy_manager.get_policies(moon_user_id="admin"): + _assignments.update(self.__policy_manager.get_subject_assignments( + moon_user_id="admin", + policy_id=policy)) + return _assignments + + def add_subject_assignment(self, **kwargs): + return self.__policy_manager.add_subject_assignment( + moon_user_id=kwargs.get("moon_user_id"), + policy_id=kwargs.get("policy_id"), + subject_id=kwargs.get("subject_id"), + category_id=kwargs.get("category_id"), + data_id=kwargs.get("data_id") + ) + + @property + def object_assignments(self): + _assignments = {} + for policy in self.__policy_manager.get_policies(moon_user_id="admin"): + _assignments.update(self.__policy_manager.get_object_assignments( + moon_user_id="admin", + policy_id=policy)) + return _assignments + + def add_object_assignment(self, **kwargs): + return self.__policy_manager.add_object_assignment( + moon_user_id=kwargs.get("moon_user_id"), + policy_id=kwargs.get("policy_id"), + object_id=kwargs.get("object_id"), + category_id=kwargs.get("category_id"), + data_id=kwargs.get("data_id") + ) + + @property + def action_assignments(self): + _assignments = {} + for policy in self.__policy_manager.get_policies(moon_user_id="admin"): + _assignments.update(self.__policy_manager.get_action_assignments( + moon_user_id="admin", + policy_id=policy)) + return _assignments + + def add_action_assignment(self, **kwargs): + return self.__policy_manager.add_action_assignment( + moon_user_id=kwargs.get("moon_user_id"), + policy_id=kwargs.get("policy_id"), + action_id=kwargs.get("action_id"), + category_id=kwargs.get("category_id"), + data_id=kwargs.get("data_id") + ) + + def get_subject_assignments(self, **kwargs): + return self.__policy_manager.get_subject_assignments( + moon_user_id=kwargs.get("moon_user_id"), + policy_id=kwargs.get("policy_id"), + category_id=kwargs.get("category_id") + ) + + def get_object_assignments(self, **kwargs): + return self.__policy_manager.get_object_assignments( + moon_user_id=kwargs.get("moon_user_id"), + policy_id=kwargs.get("policy_id"), + category_id=kwargs.get("category_id") + ) + + def get_action_assignments(self, **kwargs): + return self.__policy_manager.get_action_assignments( + moon_user_id=kwargs.get("moon_user_id"), + policy_id=kwargs.get("policy_id"), + category_id=kwargs.get("category_id") + ) + + def get_policies(self, **kwargs): + return self.__policy_manager.get_policies(moon_user_id=kwargs.get("moon_user_id")) + + @property + def subject_categories(self): + return self.__model_manager.get_subject_categories(moon_user_id="admin") + + def add_subject_category(self, **kwargs): + return self.__model_manager.add_subject_category( + moon_user_id=kwargs.get("moon_user_id"), + category_id=kwargs.get("category_id"), + value=kwargs.get("value") + ) + + @property + def object_categories(self): + return self.__model_manager.get_object_categories(moon_user_id="admin") + + def add_object_category(self, **kwargs): + return self.__model_manager.add_object_category( + moon_user_id=kwargs.get("moon_user_id"), + category_id=kwargs.get("category_id"), + value=kwargs.get("value") + ) + + @property + def action_categories(self): + return self.__model_manager.get_action_categories(moon_user_id="admin") + + def add_action_category(self, **kwargs): + return self.__model_manager.add_action_category( + moon_user_id=kwargs.get("moon_user_id"), + category_id=kwargs.get("category_id"), + value=kwargs.get("value") + ) + + def get_subject_categories(self, **kwargs): + return self.__model_manager.get_subject_categories( + moon_user_id=kwargs.get("moon_user_id") + ) + + def get_object_categories(self, **kwargs): + return self.__model_manager.get_object_categories( + moon_user_id=kwargs.get("moon_user_id") + ) + + def get_action_categories(self, **kwargs): + return self.__model_manager.get_action_categories( + moon_user_id=kwargs.get("moon_user_id") + ) + + @property + def subjects(self): + _perimeter = {} + for policy in self.__policy_manager.get_policies(moon_user_id="admin"): + _perimeter.update(self.__policy_manager.get_subjects(moon_user_id="admin", + policy_id=policy)) + return _perimeter + + def add_subject(self, **kwargs): + if kwargs.get("perimeter_id"): + return self.__policy_manager.update_subject( + moon_user_id=kwargs.get("moon_user_id"), + perimeter_id=kwargs.get("perimeter_id"), + value=kwargs.get("value") + ) + else: + return self.__policy_manager.add_subject( + moon_user_id=kwargs.get("moon_user_id"), + policy_id=kwargs.get("policy_id"), + value=kwargs.get("value") + ) + + @property + def objects(self): + _perimeter = {} + for policy in self.__policy_manager.get_policies(moon_user_id="admin"): + _perimeter.update(self.__policy_manager.get_objects(moon_user_id="admin", + policy_id=policy)) + return _perimeter + + def add_object(self, **kwargs): + if kwargs.get("perimeter_id"): + return self.__policy_manager.update_object( + moon_user_id=kwargs.get("moon_user_id"), + perimeter_id=kwargs.get("perimeter_id"), + value=kwargs.get("value") + ) + else: + return self.__policy_manager.add_object( + moon_user_id=kwargs.get("moon_user_id"), + policy_id=kwargs.get("policy_id"), + value=kwargs.get("value") + ) + + @property + def actions(self): + _perimeter = {} + for policy in self.__policy_manager.get_policies(moon_user_id="admin"): + _perimeter.update(self.__policy_manager.get_actions(moon_user_id="admin", + policy_id=policy)) + return _perimeter + + def add_action(self, **kwargs): + if kwargs.get("perimeter_id"): + return self.__policy_manager.update_action( + moon_user_id=kwargs.get("moon_user_id"), + perimeter_id=kwargs.get("perimeter_id"), + value=kwargs.get("value") + ) + else: + return self.__policy_manager.add_action( + moon_user_id=kwargs.get("moon_user_id"), + policy_id=kwargs.get("policy_id"), + value=kwargs.get("value") + ) + + def get_subjects(self, **kwargs): + return self.__policy_manager.get_subjects( + moon_user_id=kwargs.get("moon_user_id"), + policy_id=kwargs.get("policy_id") + ) + + def get_objects(self, **kwargs): + return self.__policy_manager.get_objects( + moon_user_id=kwargs.get("moon_user_id"), + policy_id=kwargs.get("policy_id") + ) + + def get_actions(self, **kwargs): + return self.__policy_manager.get_actions( + moon_user_id=kwargs.get("moon_user_id"), + policy_id=kwargs.get("policy_id") + ) + + @property + def policies(self): + return self.__policy_manager.get_policies(moon_user_id="admin") + + def add_policy(self, **kwargs): + return self.__policy_manager.add_policy( + moon_user_id=kwargs.get("moon_user_id"), + value=kwargs.get("value") + ) + + def update_policy(self, **kwargs): + return self.__policy_manager.update_policy( + moon_user_id=kwargs.get("moon_user_id"), + policy_id=kwargs.get("policy_id"), + value=kwargs.get("value")) + + @property + def models(self): + return self.get_models() + + def get_models(self, **kwargs): + return self.__model_manager.get_models(moon_user_id=kwargs.get("moon_user_id")) + + def update_model(self, **kwargs): + return self.__model_manager.update_model( + moon_user_id=kwargs.get("moon_user_id"), + model_id=kwargs.get("model_id"), + value=kwargs.get("value") + ) + + def add_model(self, **kwargs): + return self.__model_manager.add_model( + moon_user_id=kwargs.get("moon_user_id"), + value=kwargs.get("value") + ) + + @property + def pdp(self): + return self.get_pdp() + + def get_pdp(self, **kwargs): + return self.__pdp_manager.get_pdp(moon_user_id=kwargs.get("moon_user_id")) + + def update_pdp(self, **kwargs): + return self.__pdp_manager.update_pdp( + moon_user_id=kwargs.get("moon_user_id"), + pdp_id=kwargs.get("pdp_id"), + value=kwargs.get("value") + ) + + def add_pdp(self, **kwargs): + return self.__pdp_manager.add_pdp( + moon_user_id=kwargs.get("moon_user_id"), + value=kwargs.get("value") + ) + + @property + def rules(self): + _rules = [] + for policy in self.__policy_manager.get_policies(moon_user_id="admin"): + _rules.append(self.__policy_manager.get_rules(moon_user_id="admin", + policy_id=policy)) + return _rules + + def get_rules(self, **kwargs): + return self.__policy_manager.get_rules( + moon_user_id=kwargs.get("moon_user_id"), + policy_id=kwargs.get("policy_id") + ) + + +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(moon_user_id=user_id, model_id=element_id) + elif element_type == "policy": + data_db = manager.get_policies(moon_user_id=user_id, policy_id=element_id) + elif element_type == "subject": + data_db = manager.get_subjects(moon_user_id=user_id, policy_id=policy_id, perimeter_id=element_id) + elif element_type == "object": + data_db = manager.get_objects(moon_user_id=user_id, policy_id=policy_id, perimeter_id=element_id) + elif element_type == "action": + data_db = manager.get_actions(moon_user_id=user_id, policy_id=policy_id, perimeter_id=element_id) + elif element_type == "subject_category": + data_db = manager.get_subject_categories(moon_user_id=user_id, category_id=element_id) + elif element_type == "object_category": + data_db = manager.get_object_categories(moon_user_id=user_id, category_id=element_id) + elif element_type == "action_category": + data_db = manager.get_action_categories(moon_user_id=user_id, category_id=element_id) + elif element_type == "meta_rule": + data_db = manager.get_meta_rules(moon_user_id=user_id, meta_rule_id=element_id) + elif element_type == "subject_data": + data_db = manager.get_subject_data(moon_user_id=user_id, policy_id=policy_id, data_id=element_id, + category_id=category_id) + elif element_type == "object_data": + data_db = manager.get_object_data(moon_user_id=user_id, policy_id=policy_id, data_id=element_id, + category_id=category_id) + elif element_type == "action_data": + data_db = manager.get_action_data(moon_user_id=user_id, policy_id=policy_id, + data_id=element_id, + category_id=category_id) + elif element_type == "meta_rule": + data_db = manager.get_meta_rules(moon_user_id=user_id, meta_rule_id=meta_rule_id) + else: + raise Exception("Conversion of {} not implemented yet!".format(element_type)) + + # 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] + 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(moon_user_id=user_id) + elif element_type == "policy": + data_db = manager.get_policies(moon_user_id=user_id) + elif element_type == "subject": + data_db = manager.get_subjects(moon_user_id=user_id, policy_id=policy_id) + elif element_type == "object": + data_db = manager.get_objects(moon_user_id=user_id, policy_id=policy_id) + elif element_type == "action": + data_db = manager.get_actions(moon_user_id=user_id,policy_id= policy_id) + elif element_type == "subject_category": + data_db = manager.get_subject_categories(moon_user_id=user_id) + elif element_type == "object_category": + data_db = manager.get_object_categories(moon_user_id=user_id) + elif element_type == "action_category": + data_db = manager.get_action_categories(moon_user_id=user_id) + elif element_type == "meta_rule": + data_db = manager.get_meta_rules(moon_user_id=user_id) + elif element_type == "subject_data": + data_db = manager.get_subject_data(moon_user_id=user_id, policy_id=policy_id, + category_id=category_id) + elif element_type == "object_data": + data_db = manager.get_object_data(moon_user_id=user_id, policy_id=policy_id, + category_id=category_id) + elif element_type == "action_data": + data_db = manager.get_action_data(moon_user_id=user_id, policy_id=policy_id, + category_id=category_id) + elif element_type == "meta_rule": + data_db = manager.get_meta_rules(moon_user_id=user_id) + elif element_type == "rule": + data_db = manager.get_rules(moon_user_id=user_id, policy_id=policy_id) + else: + raise exceptions.MoonError("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"]: + 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 exceptions.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 exceptions.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 exceptions.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 exceptions.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) + if element is None: + raise exceptions.UnknownId("No {} with id {} found in database".format( + element_type, id_)) + res = JsonUtils.__convert_results_to_element(element) + 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 exceptions.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 exceptions.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: + LOGGER.debug("No {} with name {} found in database".format(element_type, elt["name"])) + raise exceptions.UnknownName( + "No {} with name {} found in database".format(element_type, elt["name"])) + ids.append(id_in_db) + elif "attr" in elt: + ids.append("attributes:" + elt['attr']) + elif field_mandatory is True: + raise exceptions.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 exceptions.UnknownId("No {} with id {} found in database".format( + element_type, id_)) + res = JsonUtils.__convert_results_to_element(element) + 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 + + +class JsonImport(object): + __user_id = None + __manager = None + __driver = None + + def __init__(self, driver_name="db", driver=None): + self.__driver = driver_name + if driver_name == "db": + self.__manager = DBManager(driver) + else: + self.__manager = CacheManager(driver) + + @property + def driver(self): + return self.__manager + + 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) + _exc = None + data = get_function(moon_user_id=self.__user_id, policy_id=policy_id) + for _data in data: + for item in json_data_ids: + if not _data['data']: + continue + if _data.get('data').get(item): + index = ordered_perimeter_categories_ids.index(_data["category_id"]) + if _data["category_id"] not in ordered_perimeter_categories_ids: + _exc = exceptions.InvalidJson( + "The category id {} of the rule {} does not match the meta rule".format( + _data["category_id"], rule)) + continue + if ordered_json_ids[index] is not None: + _exc = exceptions.InvalidJson( + "The category id {} of the rule {} shall not be used " + "twice in the same rule".format( + _data["category_id"], rule)) + ordered_json_ids[index] = item + if None in ordered_json_ids: + for cpt, _id in enumerate(ordered_perimeter_categories_ids): + if _id.startswith("attributes:"): + if not ordered_json_ids[cpt]: + ordered_json_ids[cpt] = json_data_ids[cpt] + + if _exc: + raise _exc + return ordered_json_ids + + def _import_rules(self, json_rules): + if not isinstance(json_rules, list): + raise exceptions.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", + self.__manager, self.__user_id) + JsonUtils.convert_name_to_id(json_rule, json_to_use, "meta_rule", "meta_rule_id", + "meta_rule", self.__manager, 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", self.__manager, + self.__user_id, + json_ids["policy_id"]) + JsonUtils.convert_names_to_ids(json_rule["rule"], json_object_ids, "object_data", + "object", "object_data", self.__manager, + self.__user_id, + json_ids["policy_id"]) + JsonUtils.convert_names_to_ids(json_rule["rule"], json_action_ids, "action_data", + "action", "action_data", self.__manager, + self.__user_id, + json_ids["policy_id"]) + + meta_rule = self.__manager.get_meta_rules( + moon_user_id=self.__user_id, + meta_rule_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"], + self.__manager.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"], + self.__manager.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"], + self.__manager.get_action_data) + json_to_use["rule"] = json_to_use_rule + try: + LOGGER.debug("Adding / updating a rule from json {}".format(json_to_use)) + self.__manager.add_rule( + moon_user_id=self.__user_id, + policy_id=json_ids["policy_id"], + meta_rule_id=json_to_use["meta_rule_id"], + value=json_to_use + ) + except exceptions.RuleExisting as e: + LOGGER.error("rule existing {}".format(e)) + except exceptions.PolicyUnknown: + raise exceptions.PolicyUnknown("Unknown policy with id {}".format( + json_ids["policy_id"])) + + def _import_meta_rules(self, json_meta_rules): + imported_mrule = [] + 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", + self.__manager, + self.__user_id) + JsonUtils.convert_names_to_ids(json_meta_rule, json_to_use, "object_categories", + "object_categories", "object_category", + self.__manager, + self.__user_id) + JsonUtils.convert_names_to_ids(json_meta_rule, json_to_use, "action_categories", + "action_categories", "action_category", + self.__manager, + self.__user_id) + LOGGER.debug("Adding / updating a metarule from json {}".format(json_meta_rule)) + try: + meta_rule = self.__manager.add_meta_rule( + moon_user_id=self.__user_id, + meta_rule_id=None, + value=json_to_use + ) + LOGGER.debug("Added / updated meta rule : {}".format(meta_rule)) + imported_mrule.append(meta_rule) + except exceptions.MetaRuleExisting: + pass + return imported_mrule + + def _import_subject_object_action_assignments(self, json_item_assignments, type_element): + import_method = None + get_method = None + if type_element == "subject": + import_method = self.__manager.add_subject_assignment + get_method = self.__manager.get_subject_data + elif type_element == "object": + import_method = self.__manager.add_object_assignment + get_method = self.__manager.get_object_data + elif type_element == "action": + import_method = self.__manager.add_action_assignment + get_method = self.__manager.get_action_data + + if not isinstance(json_item_assignments, list): + raise exceptions.InvalidJson(type_element + " assignments shall be a list!") + + # get the policy id related to the user + policies = self.__manager.get_policies(moon_user_id=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 exceptions.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", + self.__manager, + 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, + self.__manager, + self.__user_id, policy_id) + JsonUtils.convert_names_to_ids(json_item_assignment, json_data, "assignments", + "data_id", type_element + "_data", + self.__manager, + self.__user_id, policy_id, + json_assignment["category_id"]) + has_found_data = True + except exceptions.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(moon_user_id=self.__user_id, policy_id=policy_id, + data_id=data_id, + category_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)) + args = {"moon_user_id": self.__user_id, "policy_id": policy_id, + type_element + "_id": json_assignment["id"], + "category_id": json_assignment["category_id"], "data_id": data_id} + try: + import_method(**args) + except exceptions.SubjectAssignmentExisting: + pass + except exceptions.ObjectAssignmentExisting: + pass + except exceptions.ActionAssignmentExisting: + pass + else: + raise exceptions.DataUnknown("Unknown data with id {}".format(data_id)) + + # case the data has not been found in any policies + if has_found_data is False: + raise exceptions.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): + import_method = None + if type_element == "subject": + import_method = self.__manager.set_subject_data + elif type_element == "object": + import_method = self.__manager.set_object_data + elif type_element == "action": + import_method = self.__manager.set_action_data + + if not isinstance(json_items_data, list): + raise exceptions.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 exceptions.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", + self.__manager, 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", + self.__manager, 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 exceptions.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 exceptions.InvalidJson( + "Invalid data, the category shall be set when importing {}".format( + json_item_data)) + + for policy_id in mandatory_policy_ids: + try: + import_method(moon_user_id=self.__user_id, policy_id=policy_id, + category_id=category_id, + value=json_to_use) + except exceptions.PolicyUnknown: + raise exceptions.PolicyUnknown("Unknown policy with id {}".format(policy_id)) + except exceptions.SubjectScopeExisting: + pass + except exceptions.ObjectScopeExisting: + pass + except exceptions.ActionScopeExisting: + pass + except Exception as e: + LOGGER.exception(str(e)) + raise e + + def _import_subject_object_action_categories(self, json_item_categories, type_element): + imported_oac = [] + import_method = None + get_method = None + if type_element == "subject": + import_method = self.__manager.add_subject_category + get_method = self.__manager.get_subject_categories + elif type_element == "object": + import_method = self.__manager.add_object_category + get_method = self.__manager.get_object_categories + elif type_element == "action": + import_method = self.__manager.add_action_category + get_method = self.__manager.get_action_categories + + categories = get_method(moon_user_id=self.__user_id) + + if not isinstance(json_item_categories, list): + raise exceptions.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 + + 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 exceptions.ForbiddenOverride( + "{} categories do not support override flag !".format(type_element)) + + try: + import_method(moon_user_id=self.__user_id, category_id=existing_id, + value=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 + imported_oac.append({ + 'category': type_element, + 'name': json_to_use["name"]}) + return imported_oac + + def _import_subject_object_action(self, json_items, mandatory_policy_ids, type_element): + import_method = None + get_method = None + if type_element == "subject": + import_method = self.__manager.add_subject + get_method = self.__manager.get_subjects + elif type_element == "object": + import_method = self.__manager.add_object + get_method = self.__manager.get_objects + elif type_element == "action": + import_method = self.__manager.add_action + get_method = self.__manager.get_actions + + if not isinstance(json_items, list): + raise exceptions.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) + + policy_list_ids = {} + JsonUtils.convert_names_to_ids(json_item, policy_list_ids, "policies", + "policy_list", "policy", self.__manager, + self.__user_id, + field_mandatory=False) + policy_ids = policy_list_ids["policy_list"] + # json_without_policy_name["policies"] = [] + 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 exceptions.ForbiddenOverride("{} does not support override flag !".format( + type_element)) + + if len(policy_ids) == 0: + raise exceptions.PolicyUnknown( + "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(moon_user_id=self.__user_id, policy_id=policy_id) + items_in_db = items_in_db.get(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 + try: + element = import_method(moon_user_id=self.__user_id, policy_id=policy_id, + perimeter_id=key, + value=json_without_policy_name) + LOGGER.debug("Added / updated {} : {}".format(type_element, element)) + except exceptions.PolicyExisting: + pass + + except exceptions.PolicyUnknown: + LOGGER.error("Unknown policy when adding a {}!".format( + type_element)) + raise exceptions.PolicyUnknown("Unknown policy when adding a {}!".format( + type_element)) + except Exception as e: + LOGGER.exception(str(e)) + raise e + + def _import_policies(self, json_policies): + policy_mandatory_ids = [] + policy_mandatory_names = [] + + if not isinstance(json_policies, list): + raise exceptions.InvalidJson("policies shall be a list!") + + for json_policy in json_policies: + # TODO put this in moondb + # policy_in_db = + # self.driver.PolicyManager.get_policies_by_name(json_without_model_name["name"]) + policies = self.__manager.get_policies(moon_user_id=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) + policy_mandatory_names.append(json_policy["name"]) + 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", self.__manager, self.__user_id, + field_mandatory=False) + + if not policy_does_exist: + LOGGER.debug("Creating policy {} ".format(json_without_model_name)) + added_policy = self.__manager.add_policy( + moon_user_id=self.__user_id, + value=json_without_model_name) + if policy_mandatory is True: + keys = list(added_policy.keys()) + policy_mandatory_ids.append(keys[0]) + policy_mandatory_names.append(json_policy["name"]) + elif policy_override is True: + LOGGER.debug("Updating policy {} ".format(json_without_model_name)) + self.__manager.update_policy(moon_user_id=self.__user_id, + policy_id=policy_id, + value=json_without_model_name) + if policy_mandatory is True: + policy_mandatory_ids.append(policy_id) + policy_mandatory_names.append(json_policy["name"]) + return [policy_mandatory_ids, policy_mandatory_names] + + def _import_models_with_new_meta_rules(self, json_models): + if not isinstance(json_models, list): + raise exceptions.InvalidJson("models shall be a list!") + + for json_model in json_models: + models = self.__manager.get_models(moon_user_id=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 exceptions.ModelUnknown("Unknown model") + + json_key = dict() + JsonUtils.convert_names_to_ids(json_model, json_key, "meta_rules", "meta_rule_id", + "meta_rule", self.__manager, 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) + + self.__manager.update_model(moon_user_id=self.__user_id, + model_id=model_id, + value=model_in_db) + + def _import_models_without_new_meta_rules(self, json_models): + if not isinstance(json_models, list): + raise exceptions.InvalidJson("models shall be a list!") + imported_model_names = [] + for json_model in json_models: + json_without_new_metarules = dict() + JsonUtils.copy_field_if_exists(json_model, json_without_new_metarules, "name", str) + + models = self.__manager.get_models(moon_user_id=self.__user_id) + model_in_db = None + model_id = 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 + + 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)) + self.__manager.add_model(moon_user_id=self.__user_id, + value=json_without_new_metarules) + elif model_override is True: + LOGGER.debug( + "Updating model with id {} : {} ".format(model_id, json_without_new_metarules)) + self.__manager.update_model(moon_user_id=self.__user_id, + model_id=model_id, + value=json_without_new_metarules) + if "name" in json_without_new_metarules: + imported_model_names.append(json_without_new_metarules["name"]) + return imported_model_names + + def _import_pdps(self, json_pdps): + if not isinstance(json_pdps, list): + raise exceptions.InvalidJson("pdps shall be a list!") + imported_pdp_names = [] + 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, "vim_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 = self.__manager.get_pdp(moon_user_id=self.__user_id) + exists = False + for pdp_key in pdps: + if pdps[pdp_key]["name"] == json_to_use["name"]: + self.__manager.update_pdp(moon_user_id=self.__user_id, pdp_id=pdp_key, + value=json_to_use) + exists = True + if exists is False: + self.__manager.add_pdp(moon_user_id=self.__user_id, value=json_to_use) + imported_pdp_names.append(json_to_use["name"]) + + def import_json(self, **kwargs): + if self.__driver == "db": + if "body" in kwargs: + return self.__import_json_to_db(kwargs.get("body")).keys() + else: + raise Exception("Bad argument given...") + else: + if "body" in kwargs: + return self.__import_json_to_cache(kwargs.get("body")).keys() + else: + raise Exception("Bad argument given...") + + def __import_json_to_db(self, body): + imported_data = {} + + LOGGER.debug("Importing content: {} ...".format(body)) + + # first import the models without the meta rules as they are not yet defined + if "models" in body: + LOGGER.info("Importing models...") + imported_model_names = self._import_models_without_new_meta_rules(body["models"]) + imported_data['models'] = imported_model_names + + + + # import subjects, object, action_categories + list_element = [{"key": "subject"}, {"key": "object"}, {"key": "action"}] + for elt in list_element: + in_key = elt["key"] + key = in_key + "_categories" + if key in body: + LOGGER.info("Importing {}...".format(key)) + imported_oac = self._import_subject_object_action_categories(body[key], in_key) + imported_data[key] = imported_oac + + # import meta rules + if "meta_rules" in body: + LOGGER.info("Importing meta rules...") + imported_mrules = self._import_meta_rules(body["meta_rules"]) + imported_data["meta_rules"] = imported_mrules + + # add the metarule to model + if "models" in body: + LOGGER.info("Updating models with meta rules...") + self._import_models_with_new_meta_rules(body["models"]) + + # import the policies that depends on the models + mandatory_policy_ids = [] + if "policies" in body: + LOGGER.info("Importing policies...") + mandatory_policy_ids, policy_mandatory_names = self._import_policies(body["policies"]) + imported_data["policies"] = { + 'id': mandatory_policy_ids, + 'name': policy_mandatory_names} + + # import subjects, object, action + for elt in list_element: + in_key = elt["key"] + key = in_key + "s" + if key in body: + LOGGER.info("Importing {}...".format(key)) + imported_sub = self._import_subject_object_action(body[key], mandatory_policy_ids, in_key) + imported_data[key] = imported_sub + + # import subjects, object, action data + for elt in list_element: + in_key = elt["key"] + key = in_key + "_data" + if key in body: + LOGGER.info("Importing {}...".format(key)) + imported_sub =self._import_subject_object_action_datas(body[key], mandatory_policy_ids, + in_key) + imported_data[key] = imported_sub + + # import subjects assignments, idem for object and action + for elt in list_element: + in_key = elt["key"] + key = in_key + "_assignments" + if key in body: + LOGGER.info("Importing {}...".format(key)) + imported_sub = self._import_subject_object_action_assignments(body[key], in_key) + imported_data[key] = imported_sub + + # import rules + if "rules" in body: + LOGGER.info("Importing rules...") + imported_sub = self._import_rules(body["rules"]) + imported_data["rules"] = imported_sub + + # import pdps + if "pdps" in body: + LOGGER.info("Importing pdps...") + imported_sub = self._import_pdps(body["pdps"]) + imported_data["pdps"] = imported_sub + + return imported_data + + def __import_json_to_cache(self, body): + imported_data = {} + + LOGGER.debug("Importing content: {} ...".format(body)) + + # first import the models without the meta rules as they are not yet defined + if "models" in body: + LOGGER.info("Importing models...") + imported_model_names = self._import_models_without_new_meta_rules(body["models"]) + imported_data['models'] = imported_model_names + + + # import subjects, object, action_categories + list_element = [{"key": "subject"}, {"key": "object"}, {"key": "action"}] + for elt in list_element: + in_key = elt["key"] + key = in_key + "_categories" + if key in body: + LOGGER.info("Importing {}...".format(key)) + imported_oac = self._import_subject_object_action_categories(body[key], in_key) + imported_data[key] = imported_oac + + # import meta rules + if "meta_rules" in body: + LOGGER.info("Importing meta rules...") + imported_mrules = self._import_meta_rules(body["meta_rules"]) + imported_data["meta_rules"] = imported_mrules + + # add the metarule to model + if "models" in body: + LOGGER.info("Updating models with meta rules...") + self._import_models_with_new_meta_rules(body["models"]) + + # import the policies that depends on the models + mandatory_policy_ids = [] + if "policies" in body: + LOGGER.info("Importing policies...") + mandatory_policy_ids, policy_mandatory_names = self._import_policies(body["policies"]) + imported_data["policies"] = { + 'id': mandatory_policy_ids, + 'name': policy_mandatory_names} + + # import subjects, object, action + for elt in list_element: + in_key = elt["key"] + key = in_key + "s" + if key in body: + LOGGER.info("Importing {}...".format(key)) + imported_sub = self._import_subject_object_action(body[key], mandatory_policy_ids, in_key) + imported_data[key] = imported_sub + + #import subjects, object, action data + for elt in list_element: + in_key = elt["key"] + key = in_key + "_data" + if key in body: + LOGGER.info("Importing {}...".format(key)) + imported_sub= self._import_subject_object_action_datas(body[key], mandatory_policy_ids, + in_key) + imported_data[key] = imported_sub + + # import subjects assignments, idem for object and action + for elt in list_element: + in_key = elt["key"] + key = in_key + "_assignments" + if key in body: + LOGGER.info("Importing {}...".format(key)) + imported_sub = self._import_subject_object_action_assignments(body[key], in_key) + imported_data[key] = imported_sub + + # import rules + if "rules" in body: + LOGGER.info("Importing rules...") + imported_sub = self._import_rules(body["rules"]) + imported_data["rules"] = imported_sub + + # import pdps + if "pdps" in body: + LOGGER.info("Importing pdps...") + imported_sub = self._import_pdps(body["pdps"]) + imported_data["pdps"] = imported_sub + return imported_data + + +class JsonExport(object): + __user_id = None + __dirver = None + __manager = None + + def __init__(self, driver_name="db", driver=None): + self.__driver = driver_name + if driver_name == "db": + self.__manager = DBManager(driver) + else: + self.__manager = CacheManager(driver) + + def _export_rules(self, json_content): + policies = self.__manager.get_policies(moon_user_id=self.__user_id) + rules_array = [] + + for policy_key in policies: + rules = self.__manager.get_rules(moon_user_id=self.__user_id, policy_id=policy_key) + rules = rules["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", self.__manager, self.__user_id) + JsonUtils.convert_id_to_name(policy_key, rule_dict, "policy", "policy", + self.__manager, self.__user_id) + ids = rule["rule"] + rule_description = dict() + meta_rule = self.__manager.get_meta_rules(moon_user_id=self.__user_id, + meta_rule_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", self.__manager, self.__user_id, + policy_key) + JsonUtils.convert_ids_to_names(ids_object_data, rule_description, "object_data", + "object_data", self.__manager, self.__user_id, + policy_key) + JsonUtils.convert_ids_to_names(ids_action_date, rule_description, "action_data", + "action_data", self.__manager, 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 = self.__manager.get_meta_rules(moon_user_id=self.__user_id) + meta_rules_array = [] + for meta_rule_key in meta_rules: + 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", + self.__manager, self.__user_id) + JsonUtils.convert_ids_to_names(meta_rules[meta_rule_key]["object_categories"], + meta_rule_dict, "object_categories", "object_category", + self.__manager, self.__user_id) + JsonUtils.convert_ids_to_names(meta_rules[meta_rule_key]["action_categories"], + meta_rule_dict, "action_categories", "action_category", + self.__manager, self.__user_id) + meta_rules_array.append(meta_rule_dict) + if len(meta_rules_array) > 0: + json_content['meta_rules'] = meta_rules_array + + def _export_subject_object_action_assignments(self, type_element, json_content): + export_method_data = None + if type_element == "subject": + export_method_data = self.__manager.get_subject_assignments + elif type_element == "object": + export_method_data = self.__manager.get_object_assignments + if type_element == "action": + export_method_data = self.__manager.get_action_assignments + policies = self.__manager.get_policies(moon_user_id=self.__user_id) + element_assignments_array = [] + for policy_key in policies: + assignments = export_method_data(moon_user_id=self.__user_id, policy_id=policy_key) + 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, + self.__manager, self.__user_id, policy_key) + JsonUtils.convert_id_to_name(assignments[assignment_key]["category_id"], + assignment_dict, "category", + type_element + "_category", self.__manager, + self.__user_id, policy_key) + JsonUtils.convert_ids_to_names(assignments[assignment_key]["assignments"], + assignment_dict, "assignments", + type_element + "_data", self.__manager, + 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 + + def _export_subject_object_action_datas(self, type_element, json_content): + export_method_data = None + if type_element == "subject": + export_method_data = self.__manager.get_subject_data + elif type_element == "object": + export_method_data = self.__manager.get_object_data + if type_element == "action": + export_method_data = self.__manager.get_action_data + policies = self.__manager.get_policies(moon_user_id=self.__user_id) + element_datas_array = [] + for policy_key in policies: + datas = export_method_data(moon_user_id=self.__user_id, policy_id=policy_key) + for data_group in datas: + policy_id = data_group["policy_id"] + category_id = data_group["category_id"] + 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) + + JsonUtils.convert_id_to_name(policy_id, data_dict, "policy", "policy", + self.__manager, self.__user_id) + JsonUtils.convert_id_to_name(category_id, data_dict, "category", + type_element + "_category", self.__manager, + 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 = None + if type_element == "subject": + export_method = self.__manager.get_subject_categories + elif type_element == "object": + export_method = self.__manager.get_object_categories + if type_element == "action": + export_method = self.__manager.get_action_categories + element_categories = export_method(moon_user_id=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 = None + if type_element == "subject": + export_method = self.__manager.get_subjects + elif type_element == "object": + export_method = self.__manager.get_objects + if type_element == "action": + export_method = self.__manager.get_actions + policies = self.__manager.get_policies(moon_user_id=self.__user_id) + element_dict = dict() + elements_array = [] + for policy_key in policies: + elements = export_method(moon_user_id=self.__user_id, policy_id=policy_key) + for element_key in elements: + 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", self.__manager, 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 = self.__manager.get_policies(moon_user_id = 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", + self.__manager, 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 = self.__manager.get_models(moon_user_id=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) + JsonUtils.convert_ids_to_names(models[model_key]["meta_rules"], model, "meta_rules", + "meta_rule", self.__manager, 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 = self.__manager.get_pdp(moon_user_id=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 + + def export_json(self, **kwargs): + if self.__driver == "db": + if "moon_user_id" in kwargs: + return self.__export_json_from_db(kwargs.get("moon_user_id")) + else: + raise Exception("Bad argument given...") + else: + if "body" in kwargs: + return self.__export_json_from_db(kwargs.get("body")) + else: + raise Exception("Bad argument given...") + + def __export_json_from_db(self, moon_user_id=None): + self.__user_id = moon_user_id + + json_content = dict() + + 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 diff --git a/moon_utilities/moon_utilities/security_functions.py b/moon_utilities/moon_utilities/security_functions.py new file mode 100644 index 00000000..07aaa965 --- /dev/null +++ b/moon_utilities/moon_utilities/security_functions.py @@ -0,0 +1,83 @@ +# 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 moon_utilities import exceptions +import html +import hug + +LOGGER = logging.getLogger("moon.utilities." + __name__) + + +def enforce(action_names, object_name, **extra): + """Fake version of the enforce decorator""" + def wrapper_func(func): + def wrapper_args(*args, **kwargs): + # TODO: implement the enforce decorator + return func(*args, **kwargs) + return wrapper_args + return wrapper_func + + +def validate_data(data): + def __validate_string(string): + temp_str = html.escape(string) + if string != temp_str: + raise exceptions.ValidationContentError('Forbidden characters in string') + + def __validate_list_or_tuple(container): + for i in container: + validate_data(i) + + def __validate_dict(dictionary): + for key in dictionary: + validate_data(dictionary[key]) + + if isinstance(data, bool): + return True + if data is None: + data = "" + if isinstance(data, str): + __validate_string(data) + elif isinstance(data, list) or isinstance(data, tuple): + __validate_list_or_tuple(data) + elif isinstance(data, dict): + __validate_dict(data) + else: + raise exceptions.ValidationContentError('Value is Not String or Container or Dictionary: {}'.format(data)) + + +def validate_input(*validators): + """Validation only succeeds if all passed in validators return no errors""" + body_state = {"name", "id", "category_id", "data_id"} + + def validate_all_input(fields, **kwargs): + try: + for validator in validators: + # errors = validator(fields) + if validator not in fields: + raise exceptions.ValidationKeyError('Invalid Key :{} not found'.format(validator)) + + for field in body_state: + if field in fields: + try: + validate_data(fields[field]) + except exceptions.ValidationContentError as e: + raise exceptions.ValidationContentError("Key: '{}', [{}]".format(field, str(e))) + except Exception as e: + LOGGER.exception(e) + raise e + return fields + + validate_all_input.__doc__ = " and ".join(validator.__doc__ for validator in validators) + return validate_all_input diff --git a/moon_utilities/moon_utilities/update_opst_policies.py b/moon_utilities/moon_utilities/update_opst_policies.py new file mode 100644 index 00000000..4a629181 --- /dev/null +++ b/moon_utilities/moon_utilities/update_opst_policies.py @@ -0,0 +1,85 @@ +# Copyright 2019 Orange 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'. +""" +Update policy files of an OpenStack platform +""" + +import argparse +import logging +import os + + +COMPONENTS = [ + "cinder", + "glance", + "keystone", + "neutron", + "nova", +] + + +logger = logging.getLogger(__name__) + + +def init(): + """ + Initialize the application + :return: argument given in the command line + """ + global policy + parser = argparse.ArgumentParser() + parser.add_argument("--verbose", '-v', action='store_true', help='verbose mode') + parser.add_argument("--debug", '-d', action='store_true', help='debug mode') + parser.add_argument("--dir", + help='directory containing policy files, defaults to /etc', + default="/etc") + parser.add_argument("--exclude", "-x", + help="Exclude some components " + "(example: \"nova,neutron\")", + default="") + parser.add_argument("--include", "-i", + help="Only include some components " + "(example: \"nova,neutron\")", + default="") + args = parser.parse_args() + logging_format = "%(levelname)s: %(message)s" + if args.verbose: + logging.basicConfig(level=logging.INFO, format=logging_format) + if args.debug: + logging.basicConfig(level=logging.DEBUG, format=logging_format) + else: + logging.basicConfig(format=logging_format) + + return args + + +def update_component(component, args): + """ + + :param component: + :return: + """ + filename = os.path.join(args.dir, component, "policy.json") + logger.info(f"Updating {component} ({filename})") + if not os.path.isfile(filename): + logger.error(f"Cannot find {filename}") + return + + +def main(): + args = init() + if args.include: + for component in args.include.split(","): + update_component(component, args) + else: + excl_comp = args.exclude.split(",") + for component in COMPONENTS: + if component in excl_comp: + continue + update_component(component, args) + + +if __name__ == "__main__": + main() |