From edff5fbc8a8596fb2dfd74abc456f2b7f2c05a74 Mon Sep 17 00:00:00 2001 From: Tom Bosmans Date: Fri, 29 Nov 2024 15:40:22 +0100 Subject: [PATCH 1/9] obsolete: set personal certificate as default --- .../ssl_certificates/personal_certificate.py | 68 +++++++++++-------- 1 file changed, 41 insertions(+), 27 deletions(-) diff --git a/ibmsecurity/isam/base/ssl_certificates/personal_certificate.py b/ibmsecurity/isam/base/ssl_certificates/personal_certificate.py index 6c69fb71..5d7f1913 100644 --- a/ibmsecurity/isam/base/ssl_certificates/personal_certificate.py +++ b/ibmsecurity/isam/base/ssl_certificates/personal_certificate.py @@ -10,16 +10,20 @@ def get_all(isamAppliance, kdb_id, check_mode=False, force=False): """ Retrieving personal certificate names and details in a certificate database """ - return isamAppliance.invoke_get("Retrieving personal certificate names and details in a certificate database", - "/isam/ssl_certificates/{0}/personal_cert".format(kdb_id)) + return isamAppliance.invoke_get( + "Retrieving personal certificate names and details in a certificate database", + "/isam/ssl_certificates/{0}/personal_cert".format(kdb_id) + ) def get(isamAppliance, kdb_id, cert_id, check_mode=False, force=False): """ Retrieving a personal certificate from a certificate database """ - return isamAppliance.invoke_get("Retrieving a personal certificate from a certificate database", - "/isam/ssl_certificates/{0}/personal_cert/{1}".format(kdb_id, cert_id)) + return isamAppliance.invoke_get( + "Retrieving a personal certificate from a certificate database", + "/isam/ssl_certificates/{0}/personal_cert/{1}".format(kdb_id, cert_id) + ) def generate(isamAppliance, kdb_id, label, dn, expire='365', default='no', size='2048', signature_algorithm='', @@ -69,19 +73,28 @@ def generate(isamAppliance, kdb_id, label, dn, expire='365', default='no', size= def set(isamAppliance, kdb_id, cert_id, default='no', check_mode=False, force=False): """ Setting a personal certificate as default in a certificate database + + Obsolete since 10.0.3 """ - if force is True or _check_default(isamAppliance, kdb_id, cert_id, default) is True: - if check_mode is True: - return isamAppliance.create_return_object(changed=True) - else: - return isamAppliance.invoke_put( - "Setting a personal certificate as default in a certificate database", - "/isam/ssl_certificates/{0}/personal_cert/{1}".format(kdb_id, cert_id), - { - 'default': default - }) + warnings = [] - return isamAppliance.create_return_object() + if ibmsecurity.utilities.tools.version_compare(isamAppliance.facts["version"], "10.0.3.0") > 0: + warnings.append( + f"Appliance is at version: {isamAppliance.facts['version']}. Setting certificates as default is no longer supported." + ) + else: + if force is True or _check_default(isamAppliance, kdb_id, cert_id, default) is True: + if check_mode is True: + return isamAppliance.create_return_object(changed=True) + else: + return isamAppliance.invoke_put( + "Setting a personal certificate as default in a certificate database", + "/isam/ssl_certificates/{0}/personal_cert/{1}".format(kdb_id, cert_id), + { + 'default': default + }) + + return isamAppliance.create_return_object(warnings=warnings) def receive(isamAppliance, kdb_id, label, cert, default='no', check_mode=False, force=False): @@ -149,19 +162,20 @@ def import_cert(isamAppliance, kdb_id, cert, label=None, password=None, check_mo return isamAppliance.create_return_object(changed=True) else: return isamAppliance.invoke_post_files( - "Importing a personal certificate into a certificate database", - "/isam/ssl_certificates/{0}/personal_cert".format(kdb_id), - [ - { - 'file_formfield': 'cert', - 'filename': cert, - 'mimetype': 'application/octet-stream' - } - ], + "Importing a personal certificate into a certificate database", + "/isam/ssl_certificates/{0}/personal_cert".format(kdb_id), + [ { - 'password': password, - 'operation': 'import' - }) + 'file_formfield': 'cert', + 'filename': cert, + 'mimetype': 'application/octet-stream' + } + ], + { + 'password': password, + 'operation': 'import' + } + ) return isamAppliance.create_return_object() From a6ef29153d9cc190aae54659f7ed90d74cf61435 Mon Sep 17 00:00:00 2001 From: Tom Bosmans Date: Fri, 29 Nov 2024 15:47:08 +0100 Subject: [PATCH 2/9] feature: rename personal certificate --- .../ssl_certificates/personal_certificate.py | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/ibmsecurity/isam/base/ssl_certificates/personal_certificate.py b/ibmsecurity/isam/base/ssl_certificates/personal_certificate.py index 5d7f1913..12ee5cec 100644 --- a/ibmsecurity/isam/base/ssl_certificates/personal_certificate.py +++ b/ibmsecurity/isam/base/ssl_certificates/personal_certificate.py @@ -70,6 +70,30 @@ def generate(isamAppliance, kdb_id, label, dn, expire='365', default='no', size= return isamAppliance.create_return_object(changed=False, warnings=warnings) +def rename(isamAppliance, kdb_id, cert_id, new_id, + check_mode=False, force=False): + """ + Rename a personal certificate. New in 10.0.7 + """ + warnings = [] + + if ibmsecurity.utilities.tools.version_compare(isamAppliance.facts["version"], "10.0.7.0") < 0: + warnings.append( + f"Appliance is at version: {isamAppliance.facts['version']}. Renaming a certificate requires at least 10.0.7.0" + ) + else: + if force: + if check_mode: + return isamAppliance.create_return_object(changed=True) + else: + return isamAppliance.invoke_put( + "Renaming a personal certificate as default in a certificate database", + f"/isam/ssl_certificates/{kdb_id}/personal_cert/{cert_id}", + { + 'new_id': new_id + }) + + def set(isamAppliance, kdb_id, cert_id, default='no', check_mode=False, force=False): """ Setting a personal certificate as default in a certificate database From fb516cb2f76a5db024abe34a978b7fd089aa28a7 Mon Sep 17 00:00:00 2001 From: Tom Bosmans Date: Fri, 29 Nov 2024 16:20:30 +0100 Subject: [PATCH 3/9] feature: improvements to rename personal certificate --- .../isam/base/ssl_certificates/personal_certificate.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ibmsecurity/isam/base/ssl_certificates/personal_certificate.py b/ibmsecurity/isam/base/ssl_certificates/personal_certificate.py index 12ee5cec..74469eb6 100644 --- a/ibmsecurity/isam/base/ssl_certificates/personal_certificate.py +++ b/ibmsecurity/isam/base/ssl_certificates/personal_certificate.py @@ -82,7 +82,7 @@ def rename(isamAppliance, kdb_id, cert_id, new_id, f"Appliance is at version: {isamAppliance.facts['version']}. Renaming a certificate requires at least 10.0.7.0" ) else: - if force: + if force or (_check(isamAppliance, kdb_id, cert_id) and not _check(isamAppliance, kdb_id, new_id)): if check_mode: return isamAppliance.create_return_object(changed=True) else: @@ -107,7 +107,7 @@ def set(isamAppliance, kdb_id, cert_id, default='no', check_mode=False, force=Fa f"Appliance is at version: {isamAppliance.facts['version']}. Setting certificates as default is no longer supported." ) else: - if force is True or _check_default(isamAppliance, kdb_id, cert_id, default) is True: + if force is True or _check_default(isamAppliance, kdb_id, cert_id, default): if check_mode is True: return isamAppliance.create_return_object(changed=True) else: From a19990714b500ff6cbfea40ea111b8ac4458fcb2 Mon Sep 17 00:00:00 2001 From: Tom Bosmans Date: Fri, 29 Nov 2024 17:10:13 +0100 Subject: [PATCH 4/9] deprecate --- ibmsecurity/isam/base/audit_configuration.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/ibmsecurity/isam/base/audit_configuration.py b/ibmsecurity/isam/base/audit_configuration.py index 688b33bb..23d21bea 100644 --- a/ibmsecurity/isam/base/audit_configuration.py +++ b/ibmsecurity/isam/base/audit_configuration.py @@ -13,6 +13,7 @@ comp_uri = uri + "/components" requires_modules = ["mga", "federation"] requires_version = None +warnings = ['This module (audit_configuration) is deprecated, use audit.configuration instead'] def get(isamAppliance, check_mode=False, force=False): @@ -20,14 +21,7 @@ def get(isamAppliance, check_mode=False, force=False): Retrieve audit configuration """ return isamAppliance.invoke_get("Retrieve audit configuration", uri, requires_modules=requires_modules, - requires_version=requires_version) - -def getComponents(isamAppliance, check_mode=False, force=False): - """ - Retrieve audit configuration components - """ - return isamAppliance.invoke_get("Retrieve audit configuration components", comp_uri, requires_modules=requires_modules, - requires_version=requires_version) + requires_version=requires_version, warnings=warnings) def set(isamAppliance, id, config, enabled=True, type='Syslog', verbose=True, check_mode=False, force=False, use_json=False, components=None): @@ -165,12 +159,12 @@ def set(isamAppliance, id, config, enabled=True, type='Syslog', verbose=True, ch if force is True or update_required is True: if check_mode is True: - return isamAppliance.create_return_object(changed=True) + return isamAppliance.create_return_object(changed=True, warnings=warnings) else: return isamAppliance.invoke_put( "Update Audit Configuration", "{0}/{1}".format(uri, id), json_data, requires_modules=requires_modules, - requires_version=requires_version) + requires_version=requires_version, warnings=warnings) return isamAppliance.create_return_object() From f666b97e6210251e140474286f17de1d8acc2fd1 Mon Sep 17 00:00:00 2001 From: Tom Bosmans Date: Fri, 29 Nov 2024 17:11:35 +0100 Subject: [PATCH 5/9] feature: refactor audit code to accomodate new functions --- ibmsecurity/isam/base/audit/__init__.py | 0 ibmsecurity/isam/base/audit/components.py | 250 +++++++++++++++++++ ibmsecurity/isam/base/audit/configuration.py | 242 ++++++++++++++++++ 3 files changed, 492 insertions(+) create mode 100644 ibmsecurity/isam/base/audit/__init__.py create mode 100644 ibmsecurity/isam/base/audit/components.py create mode 100644 ibmsecurity/isam/base/audit/configuration.py diff --git a/ibmsecurity/isam/base/audit/__init__.py b/ibmsecurity/isam/base/audit/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/ibmsecurity/isam/base/audit/components.py b/ibmsecurity/isam/base/audit/components.py new file mode 100644 index 00000000..89762252 --- /dev/null +++ b/ibmsecurity/isam/base/audit/components.py @@ -0,0 +1,250 @@ +import logging +from ibmsecurity.utilities import tools + +try: + basestring +except NameError: + basestring = (str, bytes) + +logger = logging.getLogger(__name__) + +# URI for this module +uri = "/iam/access/v8/audit/components" +requires_modules = ["mga", "federation"] +requires_version = None + + +def get_all(isamAppliance, check_mode=False, force=False): + """ + Retrieve audit configuration + """ + return isamAppliance.invoke_get("Retrieve all audit component configuration", uri, requires_modules=requires_modules, + requires_version=requires_version) + + +def get(isamAppliance, check_mode=False, force=False): + """ + Retrieve audit configuration components + """ + return isamAppliance.invoke_get("Retrieve audit configuration component", uri, requires_modules=requires_modules, + requires_version=requires_version) + + +def set(isamAppliance, id, config, enabled=True, type='Syslog', verbose=True, check_mode=False, force=False, use_json=False, components=None): + """ + Update Audit Configuration + + Sample data for Audit Configuration: + In JSON Format: + { u'config': [ { u'datatype': u'String', + u'key': u'ISAM.Audit.syslogclient.SSL_TRUST_STORE', + u'sensitive': False, + u'validValues': [], + u'value': u''}, + { u'datatype': u'String', + u'key': u'ISAM.Audit.syslogclient.CLIENT_AUTH_KEY', + u'sensitive': False, + u'validValues': [], + u'value': u''}, + { u'datatype': u'Boolean', + u'key': u'ISAM.Audit.syslogclient.FAILOVER_TO_DISK', + u'sensitive': False, + u'validValues': [], + u'value': u'false'}, + { u'datatype': u'Integer', + u'key': u'ISAM.Audit.syslogclient.NUM_RETRY', + u'sensitive': False, + u'validValues': [], + u'value': u'2'}, + { u'datatype': u'Integer', + u'key': u'ISAM.Audit.syslogclient.NUM_SENDER_THREADS', + u'sensitive': False, + u'validValues': [], + u'value': u'1'}, + { u'datatype': u'Boolean', + u'key': u'ISAM.Audit.syslogclient.CLIENT_CERT_AUTH_REQUIRED', + u'sensitive': False, + u'validValues': [], + u'value': u'false'}, + { u'datatype': u'Integer', + u'key': u'ISAM.Audit.syslogclient.SERVER_PORT', + u'sensitive': False, + u'validValues': [], + u'value': u'514'}, + { u'datatype': u'Hostname', + u'key': u'ISAM.Audit.syslogclient.SERVER_HOST', + u'sensitive': False, + u'validValues': [], + u'value': u'127.0.0.1'}, + { u'datatype': u'String', + u'key': u'ISAM.Audit.syslogclient.TRANSPORT', + u'sensitive': False, + u'validValues': [], + u'value': u'TRANSPORT_UDP'}, + { u'datatype': u'Integer', + u'key': u'ISAM.Audit.syslogclient.QUEUE_FULL_TIMEOUT', + u'sensitive': False, + u'validValues': [], + u'value': u'-1'}, + { u'datatype': u'Integer', + u'key': u'ISAM.Audit.syslogclient.MAX_QUEUE_SIZE', + u'sensitive': False, + u'validValues': [], + u'value': u'1000'}], + u'enabled': False, + u'id': u'1', + u'type': u'Syslog', + u'verbose': False} + + In YAML Format: + config: + - datatype: String + key: ISAM.Audit.syslogclient.SSL_TRUST_STORE + sensitive: false + validValues: [] + value: '' + - datatype: String + key: ISAM.Audit.syslogclient.CLIENT_AUTH_KEY + sensitive: false + validValues: [] + value: '' + - datatype: Boolean + key: ISAM.Audit.syslogclient.FAILOVER_TO_DISK + sensitive: false + validValues: [] + value: 'false' + - datatype: Integer + key: ISAM.Audit.syslogclient.NUM_RETRY + sensitive: false + validValues: [] + value: '2' + - datatype: Integer + key: ISAM.Audit.syslogclient.NUM_SENDER_THREADS + sensitive: false + validValues: [] + value: '1' + - datatype: Boolean + key: ISAM.Audit.syslogclient.CLIENT_CERT_AUTH_REQUIRED + sensitive: false + validValues: [] + value: 'false' + - datatype: Integer + key: ISAM.Audit.syslogclient.SERVER_PORT + sensitive: false + validValues: [] + value: '514' + - datatype: Hostname + key: ISAM.Audit.syslogclient.SERVER_HOST + sensitive: false + validValues: [] + value: 127.0.0.1 + - datatype: String + key: ISAM.Audit.syslogclient.TRANSPORT + sensitive: false + validValues: [] + value: TRANSPORT_UDP + - datatype: Integer + key: ISAM.Audit.syslogclient.QUEUE_FULL_TIMEOUT + sensitive: false + validValues: [] + value: '-1' + - datatype: Integer + key: ISAM.Audit.syslogclient.MAX_QUEUE_SIZE + sensitive: false + validValues: [] + value: '1000' + enabled: false + id: '1' + type: Syslog + verbose: false + """ + pol_id, update_required, json_data = _check(isamAppliance, id, config, enabled, type, verbose, use_json, components) + if pol_id is None: + from ibmsecurity.appliance.ibmappliance import IBMError + raise IBMError("999", "Cannot update data for unknown Audit Configuration ID: {0}".format(id)) + + if force is True or update_required is True: + if check_mode is True: + return isamAppliance.create_return_object(changed=True) + else: + return isamAppliance.invoke_put( + "Update Audit Configuration", + "{0}/{1}".format(uri, id), json_data, requires_modules=requires_modules, + requires_version=requires_version) + + return isamAppliance.create_return_object() + + +def _check(isamAppliance, id, config, enabled, type, verbose, use_json=False, components=None): + """ + Check and return True if update needed + """ + update_required = False + pol_id = None + # convert all values into string - any other type causes issues + for cfg in config: + if isinstance(cfg['value'], bool): + cfg['value'] = str(cfg['value']).lower() + else: + cfg['value'] = str(cfg['value']) + # Ensure boolean variables are set correctly + if isinstance(verbose, str): + if verbose.lower() == "true": + verbose = True + else: + verbose = False + if isinstance(enabled, str): + if enabled.lower() == "true": + enabled = True + else: + enabled = False + if isinstance(use_json, str): + if use_json.lower() == "true": + use_json = True + else: + use_json = False + json_data = { + "id": id, + "config": config, + "enabled": enabled, + "type": type, + "verbose": verbose, + "useJSONFormat": use_json + } + ret_obj = get(isamAppliance) + for aud_cfg in ret_obj['data']: + if id == aud_cfg['id']: + pol_id = id + break + if pol_id is None: + logger.warning("Audit Configuration not found, returning no update required.") + return pol_id, update_required, json_data + elif components is not None: + json_data["components"] = components + update_required = True + else: + import ibmsecurity.utilities.tools + sorted_json_data = ibmsecurity.utilities.tools.json_sort(json_data) + logger.debug("Sorted input: {0}".format(sorted_json_data)) + sorted_ret_obj = ibmsecurity.utilities.tools.json_sort(aud_cfg) + logger.debug("Sorted existing data: {0}".format(sorted_ret_obj)) + if sorted_ret_obj != sorted_json_data: + logger.info("Changes detected, update needed.") + update_required = True + + return pol_id, update_required, json_data + + +def compare(isamAppliance1, isamAppliance2): + """ + Compare Audit Configuration between two appliances + """ + ret_obj1 = get(isamAppliance1) + ret_obj2 = get(isamAppliance2) + + for obj in ret_obj1['data']: + del obj['id'] + for obj in ret_obj2['data']: + del obj['id'] + + return tools.json_compare(ret_obj1, ret_obj2, deleted_keys=['id']) diff --git a/ibmsecurity/isam/base/audit/configuration.py b/ibmsecurity/isam/base/audit/configuration.py new file mode 100644 index 00000000..d8d53081 --- /dev/null +++ b/ibmsecurity/isam/base/audit/configuration.py @@ -0,0 +1,242 @@ +import logging +from ibmsecurity.utilities import tools + +try: + basestring +except NameError: + basestring = (str, bytes) + +logger = logging.getLogger(__name__) + +# URI for this module +uri = "/iam/access/v8/audit" +requires_modules = ["mga", "federation"] +requires_version = None + + +def get(isamAppliance, check_mode=False, force=False): + """ + Retrieve audit configuration + """ + return isamAppliance.invoke_get("Retrieve audit configuration", uri, requires_modules=requires_modules, + requires_version=requires_version) + + +def set(isamAppliance, id, config, enabled=True, type='Syslog', verbose=True, check_mode=False, force=False, use_json=False, components=None): + """ + Update Audit Configuration + + Sample data for Audit Configuration: + In JSON Format: + { u'config': [ { u'datatype': u'String', + u'key': u'ISAM.Audit.syslogclient.SSL_TRUST_STORE', + u'sensitive': False, + u'validValues': [], + u'value': u''}, + { u'datatype': u'String', + u'key': u'ISAM.Audit.syslogclient.CLIENT_AUTH_KEY', + u'sensitive': False, + u'validValues': [], + u'value': u''}, + { u'datatype': u'Boolean', + u'key': u'ISAM.Audit.syslogclient.FAILOVER_TO_DISK', + u'sensitive': False, + u'validValues': [], + u'value': u'false'}, + { u'datatype': u'Integer', + u'key': u'ISAM.Audit.syslogclient.NUM_RETRY', + u'sensitive': False, + u'validValues': [], + u'value': u'2'}, + { u'datatype': u'Integer', + u'key': u'ISAM.Audit.syslogclient.NUM_SENDER_THREADS', + u'sensitive': False, + u'validValues': [], + u'value': u'1'}, + { u'datatype': u'Boolean', + u'key': u'ISAM.Audit.syslogclient.CLIENT_CERT_AUTH_REQUIRED', + u'sensitive': False, + u'validValues': [], + u'value': u'false'}, + { u'datatype': u'Integer', + u'key': u'ISAM.Audit.syslogclient.SERVER_PORT', + u'sensitive': False, + u'validValues': [], + u'value': u'514'}, + { u'datatype': u'Hostname', + u'key': u'ISAM.Audit.syslogclient.SERVER_HOST', + u'sensitive': False, + u'validValues': [], + u'value': u'127.0.0.1'}, + { u'datatype': u'String', + u'key': u'ISAM.Audit.syslogclient.TRANSPORT', + u'sensitive': False, + u'validValues': [], + u'value': u'TRANSPORT_UDP'}, + { u'datatype': u'Integer', + u'key': u'ISAM.Audit.syslogclient.QUEUE_FULL_TIMEOUT', + u'sensitive': False, + u'validValues': [], + u'value': u'-1'}, + { u'datatype': u'Integer', + u'key': u'ISAM.Audit.syslogclient.MAX_QUEUE_SIZE', + u'sensitive': False, + u'validValues': [], + u'value': u'1000'}], + u'enabled': False, + u'id': u'1', + u'type': u'Syslog', + u'verbose': False} + + In YAML Format: + config: + - datatype: String + key: ISAM.Audit.syslogclient.SSL_TRUST_STORE + sensitive: false + validValues: [] + value: '' + - datatype: String + key: ISAM.Audit.syslogclient.CLIENT_AUTH_KEY + sensitive: false + validValues: [] + value: '' + - datatype: Boolean + key: ISAM.Audit.syslogclient.FAILOVER_TO_DISK + sensitive: false + validValues: [] + value: 'false' + - datatype: Integer + key: ISAM.Audit.syslogclient.NUM_RETRY + sensitive: false + validValues: [] + value: '2' + - datatype: Integer + key: ISAM.Audit.syslogclient.NUM_SENDER_THREADS + sensitive: false + validValues: [] + value: '1' + - datatype: Boolean + key: ISAM.Audit.syslogclient.CLIENT_CERT_AUTH_REQUIRED + sensitive: false + validValues: [] + value: 'false' + - datatype: Integer + key: ISAM.Audit.syslogclient.SERVER_PORT + sensitive: false + validValues: [] + value: '514' + - datatype: Hostname + key: ISAM.Audit.syslogclient.SERVER_HOST + sensitive: false + validValues: [] + value: 127.0.0.1 + - datatype: String + key: ISAM.Audit.syslogclient.TRANSPORT + sensitive: false + validValues: [] + value: TRANSPORT_UDP + - datatype: Integer + key: ISAM.Audit.syslogclient.QUEUE_FULL_TIMEOUT + sensitive: false + validValues: [] + value: '-1' + - datatype: Integer + key: ISAM.Audit.syslogclient.MAX_QUEUE_SIZE + sensitive: false + validValues: [] + value: '1000' + enabled: false + id: '1' + type: Syslog + verbose: false + """ + pol_id, update_required, json_data = _check(isamAppliance, id, config, enabled, type, verbose, use_json, components) + if pol_id is None: + from ibmsecurity.appliance.ibmappliance import IBMError + raise IBMError("999", "Cannot update data for unknown Audit Configuration ID: {0}".format(id)) + + if force is True or update_required is True: + if check_mode is True: + return isamAppliance.create_return_object(changed=True) + else: + return isamAppliance.invoke_put( + "Update Audit Configuration", + "{0}/{1}".format(uri, id), json_data, requires_modules=requires_modules, + requires_version=requires_version) + + return isamAppliance.create_return_object() + + +def _check(isamAppliance, id, config, enabled, type, verbose, use_json=False, components=None): + """ + Check and return True if update needed + """ + update_required = False + pol_id = None + # convert all values into string - any other type causes issues + for cfg in config: + if isinstance(cfg['value'], bool): + cfg['value'] = str(cfg['value']).lower() + else: + cfg['value'] = str(cfg['value']) + # Ensure boolean variables are set correctly + if isinstance(verbose, str): + if verbose.lower() == "true": + verbose = True + else: + verbose = False + if isinstance(enabled, str): + if enabled.lower() == "true": + enabled = True + else: + enabled = False + if isinstance(use_json, str): + if use_json.lower() == "true": + use_json = True + else: + use_json = False + json_data = { + "id": id, + "config": config, + "enabled": enabled, + "type": type, + "verbose": verbose, + "useJSONFormat": use_json + } + ret_obj = get(isamAppliance) + for aud_cfg in ret_obj['data']: + if id == aud_cfg['id']: + pol_id = id + break + if pol_id is None: + logger.warning("Audit Configuration not found, returning no update required.") + return pol_id, update_required, json_data + elif components is not None: + json_data["components"] = components + update_required = True + else: + import ibmsecurity.utilities.tools + sorted_json_data = ibmsecurity.utilities.tools.json_sort(json_data) + logger.debug("Sorted input: {0}".format(sorted_json_data)) + sorted_ret_obj = ibmsecurity.utilities.tools.json_sort(aud_cfg) + logger.debug("Sorted existing data: {0}".format(sorted_ret_obj)) + if sorted_ret_obj != sorted_json_data: + logger.info("Changes detected, update needed.") + update_required = True + + return pol_id, update_required, json_data + + +def compare(isamAppliance1, isamAppliance2): + """ + Compare Audit Configuration between two appliances + """ + ret_obj1 = get(isamAppliance1) + ret_obj2 = get(isamAppliance2) + + for obj in ret_obj1['data']: + del obj['id'] + for obj in ret_obj2['data']: + del obj['id'] + + return tools.json_compare(ret_obj1, ret_obj2, deleted_keys=['id']) From ed403b9241bb86ac4d9ab713a049479d73f3c53e Mon Sep 17 00:00:00 2001 From: Tom Bosmans Date: Fri, 6 Dec 2024 16:50:46 +0100 Subject: [PATCH 6/9] feature: implement audit components settings --- ibmsecurity/isam/base/audit/components.py | 274 +++++++--------------- 1 file changed, 80 insertions(+), 194 deletions(-) diff --git a/ibmsecurity/isam/base/audit/components.py b/ibmsecurity/isam/base/audit/components.py index 89762252..d56bf96f 100644 --- a/ibmsecurity/isam/base/audit/components.py +++ b/ibmsecurity/isam/base/audit/components.py @@ -1,4 +1,5 @@ import logging + from ibmsecurity.utilities import tools try: @@ -22,225 +23,110 @@ def get_all(isamAppliance, check_mode=False, force=False): requires_version=requires_version) -def get(isamAppliance, check_mode=False, force=False): +def search(isamAppliance, component_name: str, check_mode=False, force=False): """ - Retrieve audit configuration components + Get the id for the component by (group) name """ - return isamAppliance.invoke_get("Retrieve audit configuration component", uri, requires_modules=requires_modules, - requires_version=requires_version) + ret_obj = None + + ret_obj = get_all(isamAppliance) + if ret_obj.get("data", None): + for obj in ret_obj.get("data"): + if obj['group'] == component_name: + logger.info(f"Found name {component_name} id: {obj['id']}") + return obj['id'] + return None + else: + return None -def set(isamAppliance, id, config, enabled=True, type='Syslog', verbose=True, check_mode=False, force=False, use_json=False, components=None): +def get(isamAppliance, group_name: str = None, component_id: str = None, type_id: str = None, check_mode=False, force=False): """ - Update Audit Configuration - - Sample data for Audit Configuration: - In JSON Format: - { u'config': [ { u'datatype': u'String', - u'key': u'ISAM.Audit.syslogclient.SSL_TRUST_STORE', - u'sensitive': False, - u'validValues': [], - u'value': u''}, - { u'datatype': u'String', - u'key': u'ISAM.Audit.syslogclient.CLIENT_AUTH_KEY', - u'sensitive': False, - u'validValues': [], - u'value': u''}, - { u'datatype': u'Boolean', - u'key': u'ISAM.Audit.syslogclient.FAILOVER_TO_DISK', - u'sensitive': False, - u'validValues': [], - u'value': u'false'}, - { u'datatype': u'Integer', - u'key': u'ISAM.Audit.syslogclient.NUM_RETRY', - u'sensitive': False, - u'validValues': [], - u'value': u'2'}, - { u'datatype': u'Integer', - u'key': u'ISAM.Audit.syslogclient.NUM_SENDER_THREADS', - u'sensitive': False, - u'validValues': [], - u'value': u'1'}, - { u'datatype': u'Boolean', - u'key': u'ISAM.Audit.syslogclient.CLIENT_CERT_AUTH_REQUIRED', - u'sensitive': False, - u'validValues': [], - u'value': u'false'}, - { u'datatype': u'Integer', - u'key': u'ISAM.Audit.syslogclient.SERVER_PORT', - u'sensitive': False, - u'validValues': [], - u'value': u'514'}, - { u'datatype': u'Hostname', - u'key': u'ISAM.Audit.syslogclient.SERVER_HOST', - u'sensitive': False, - u'validValues': [], - u'value': u'127.0.0.1'}, - { u'datatype': u'String', - u'key': u'ISAM.Audit.syslogclient.TRANSPORT', - u'sensitive': False, - u'validValues': [], - u'value': u'TRANSPORT_UDP'}, - { u'datatype': u'Integer', - u'key': u'ISAM.Audit.syslogclient.QUEUE_FULL_TIMEOUT', - u'sensitive': False, - u'validValues': [], - u'value': u'-1'}, - { u'datatype': u'Integer', - u'key': u'ISAM.Audit.syslogclient.MAX_QUEUE_SIZE', - u'sensitive': False, - u'validValues': [], - u'value': u'1000'}], - u'enabled': False, - u'id': u'1', - u'type': u'Syslog', - u'verbose': False} - - In YAML Format: - config: - - datatype: String - key: ISAM.Audit.syslogclient.SSL_TRUST_STORE - sensitive: false - validValues: [] - value: '' - - datatype: String - key: ISAM.Audit.syslogclient.CLIENT_AUTH_KEY - sensitive: false - validValues: [] - value: '' - - datatype: Boolean - key: ISAM.Audit.syslogclient.FAILOVER_TO_DISK - sensitive: false - validValues: [] - value: 'false' - - datatype: Integer - key: ISAM.Audit.syslogclient.NUM_RETRY - sensitive: false - validValues: [] - value: '2' - - datatype: Integer - key: ISAM.Audit.syslogclient.NUM_SENDER_THREADS - sensitive: false - validValues: [] - value: '1' - - datatype: Boolean - key: ISAM.Audit.syslogclient.CLIENT_CERT_AUTH_REQUIRED - sensitive: false - validValues: [] - value: 'false' - - datatype: Integer - key: ISAM.Audit.syslogclient.SERVER_PORT - sensitive: false - validValues: [] - value: '514' - - datatype: Hostname - key: ISAM.Audit.syslogclient.SERVER_HOST - sensitive: false - validValues: [] - value: 127.0.0.1 - - datatype: String - key: ISAM.Audit.syslogclient.TRANSPORT - sensitive: false - validValues: [] - value: TRANSPORT_UDP - - datatype: Integer - key: ISAM.Audit.syslogclient.QUEUE_FULL_TIMEOUT - sensitive: false - validValues: [] - value: '-1' - - datatype: Integer - key: ISAM.Audit.syslogclient.MAX_QUEUE_SIZE - sensitive: false - validValues: [] - value: '1000' - enabled: false - id: '1' - type: Syslog - verbose: false + Retrieve specific audit configuration component group by component_id, by type or by group name """ - pol_id, update_required, json_data = _check(isamAppliance, id, config, enabled, type, verbose, use_json, components) - if pol_id is None: - from ibmsecurity.appliance.ibmappliance import IBMError - raise IBMError("999", "Cannot update data for unknown Audit Configuration ID: {0}".format(id)) + requires_version = None + warnings = [] + if component_id is None and type_id is None and group_name is None: + warnings = ['No group_name, component_id nor type_id passed'] + return isamAppliance.create_return_object(warnings) + elif group_name: + # translate name to component_id + component_id = search(isamAppliance, component_name=group_name) + if component_id: + uri_part = component_id + else: + warnings = [f"Cannot find group by name of: {group_name}"] + return isamAppliance.create_return_object(warnings) + elif component_id: + # Ignore type_id in this case + uri_part = component_id + else: + # valid values are runtime or management + if type_id in ['runtime', 'management']: + requires_version = "10.0.7.0" + uri_part = type_id + else: + warnings = [f"Invalid type_id passed to function: {type_id}"] + return isamAppliance.create_return_object(warnings) + return isamAppliance.invoke_get("Retrieve audit configuration component ", f"{uri}/{uri_part}", requires_modules=requires_modules, + requires_version=requires_version, warnings=warnings) - if force is True or update_required is True: - if check_mode is True: + +def set(isamAppliance, component_id: str, enabled=True, check_mode=False, force=False): + """ + Update Audit Configuration Component by id + This simply enables or disables the group. + """ + if isinstance(enabled, str): + if enabled.upper() in ['TRUE', 'YES']: + enabled = True + else: + enabled = False + update_required = _check(isamAppliance, component_id, enabled) + if enabled: + json_data = { + 'enabled': True + } + else: + json_data = { + 'enabled': False + } + if force or update_required: + if check_mode: return isamAppliance.create_return_object(changed=True) else: return isamAppliance.invoke_put( - "Update Audit Configuration", - "{0}/{1}".format(uri, id), json_data, requires_modules=requires_modules, + "Update Audit Configuration Component", + f"{uri}/{component_id}", + json_data, + requires_modules=requires_modules, requires_version=requires_version) - return isamAppliance.create_return_object() - -def _check(isamAppliance, id, config, enabled, type, verbose, use_json=False, components=None): +def _check(isamAppliance, component_id: str, enabled: bool): """ Check and return True if update needed """ update_required = False - pol_id = None - # convert all values into string - any other type causes issues - for cfg in config: - if isinstance(cfg['value'], bool): - cfg['value'] = str(cfg['value']).lower() - else: - cfg['value'] = str(cfg['value']) - # Ensure boolean variables are set correctly - if isinstance(verbose, str): - if verbose.lower() == "true": - verbose = True - else: - verbose = False - if isinstance(enabled, str): - if enabled.lower() == "true": - enabled = True - else: - enabled = False - if isinstance(use_json, str): - if use_json.lower() == "true": - use_json = True - else: - use_json = False - json_data = { - "id": id, - "config": config, - "enabled": enabled, - "type": type, - "verbose": verbose, - "useJSONFormat": use_json - } - ret_obj = get(isamAppliance) - for aud_cfg in ret_obj['data']: - if id == aud_cfg['id']: - pol_id = id - break - if pol_id is None: - logger.warning("Audit Configuration not found, returning no update required.") - return pol_id, update_required, json_data - elif components is not None: - json_data["components"] = components + + ret_obj = get(isamAppliance, component_id=component_id) + cmp_cfg = ret_obj.get("data", None) + logger.debug(f"\n\n{cmp_cfg}\n\n") + if cmp_cfg is not None and str(cmp_cfg.get('enabled', "frottekop")) != str(enabled): update_required = True + logger.debug(f"\n\nAudit Configuration Component requires an update {cmp_cfg.get('enabled')} <> {enabled}") else: - import ibmsecurity.utilities.tools - sorted_json_data = ibmsecurity.utilities.tools.json_sort(json_data) - logger.debug("Sorted input: {0}".format(sorted_json_data)) - sorted_ret_obj = ibmsecurity.utilities.tools.json_sort(aud_cfg) - logger.debug("Sorted existing data: {0}".format(sorted_ret_obj)) - if sorted_ret_obj != sorted_json_data: - logger.info("Changes detected, update needed.") - update_required = True + logger.warning("Audit Configuration Component does not need an update or does not exist.") - return pol_id, update_required, json_data + return update_required def compare(isamAppliance1, isamAppliance2): """ - Compare Audit Configuration between two appliances + Compare Audit Configuration Components between two appliances """ - ret_obj1 = get(isamAppliance1) - ret_obj2 = get(isamAppliance2) + ret_obj1 = get_all(isamAppliance1) + ret_obj2 = get_all(isamAppliance2) for obj in ret_obj1['data']: del obj['id'] From 6e5ddfddaf33c06d7ccf14adeff9decb6ad9d910 Mon Sep 17 00:00:00 2001 From: Tom Bosmans Date: Fri, 6 Dec 2024 16:52:09 +0100 Subject: [PATCH 7/9] feature: add getComponents function --- ibmsecurity/isam/base/audit_configuration.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ibmsecurity/isam/base/audit_configuration.py b/ibmsecurity/isam/base/audit_configuration.py index 23d21bea..f8bb4a91 100644 --- a/ibmsecurity/isam/base/audit_configuration.py +++ b/ibmsecurity/isam/base/audit_configuration.py @@ -24,6 +24,14 @@ def get(isamAppliance, check_mode=False, force=False): requires_version=requires_version, warnings=warnings) +def getComponents(isamAppliance, check_mode=False, force=False): + """ + Retrieve audit configuration components + """ + return isamAppliance.invoke_get("Retrieve audit configuration components", comp_uri, requires_modules=requires_modules, + requires_version=requires_version) + + def set(isamAppliance, id, config, enabled=True, type='Syslog', verbose=True, check_mode=False, force=False, use_json=False, components=None): """ Update Audit Configuration From 328040e173dc6757d427b85d8b38851becf10a9e Mon Sep 17 00:00:00 2001 From: Tom Bosmans Date: Fri, 6 Dec 2024 16:53:02 +0100 Subject: [PATCH 8/9] documentation: add todo --- ibmsecurity/isam/base/audit/components.py | 1 + 1 file changed, 1 insertion(+) diff --git a/ibmsecurity/isam/base/audit/components.py b/ibmsecurity/isam/base/audit/components.py index d56bf96f..e9383530 100644 --- a/ibmsecurity/isam/base/audit/components.py +++ b/ibmsecurity/isam/base/audit/components.py @@ -76,6 +76,7 @@ def set(isamAppliance, component_id: str, enabled=True, check_mode=False, force= """ Update Audit Configuration Component by id This simply enables or disables the group. + TODO: Add set by type and set_all """ if isinstance(enabled, str): if enabled.upper() in ['TRUE', 'YES']: From 37e6bbcf44c2687d4554e46e316eb92ccb2b7658 Mon Sep 17 00:00:00 2001 From: Tom Bosmans Date: Fri, 6 Dec 2024 16:57:17 +0100 Subject: [PATCH 9/9] documentation: update version --- changelog.md | 9 ++++++++- pyproject.toml | 2 +- setup.py | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/changelog.md b/changelog.md index b8b829e2..e0531215 100644 --- a/changelog.md +++ b/changelog.md @@ -2,6 +2,13 @@ ## Unreleased +## 2024.12.6.0 + +- deprecated: set personal certificate as default +- deprecated: `base/audit_configuration.py`. Use `base/audit/configuration.py` instead +- feature: base/audit/configuration.py added +- feature: base/audit/components.py added + ## 2024.10.11.0 - fix: corrections in test script @@ -85,7 +92,7 @@ ## 2023.4.25.0 - fix: add id parameter to ibmsecurity/isam/aac/fido2/relying_parties.py (#377) -- fix: add __init__.py in ibmsecurity/isvg sub folders (#380) +- fix: add `__init__.py` in ibmsecurity/isvg sub folders (#380) ### Build & Deploy diff --git a/pyproject.toml b/pyproject.toml index 8fadf66e..dcf50210 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta" [project] name = "ibmsecurity" -version = "2024.10.11.0" +version = "2024.12.6.0" authors = [ { name="IBM", email="secorch@wwpdl.vnet.ibm.com" }, ] diff --git a/setup.py b/setup.py index c0e05e29..c0bcf74b 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ packages=find_packages(), # Date of release used for version - please be sure to use YYYY.MM.DD.seq#, MM and DD should be two digits e.g. 2017.02.05.0 # seq# will be zero unless there are multiple release on a given day - then increment by one for additional release for that date - version="2024.10.11.0", + version="2024.12.6.0", description="Idempotent functions for IBM Security Appliance REST APIs", author="IBM", author_email="secorch@wwpdl.vnet.ibm.com",