From cb091c7dbd797e3761a089758a8ca53a264d0ddc Mon Sep 17 00:00:00 2001 From: Sebastian-Vetter Date: Mon, 28 Jan 2019 11:32:18 +0100 Subject: [PATCH 01/20] BugFix: 2x + Feature: 1x aac\runtime_template BugFix: - runtime_template root: add check_mode and force parameter to be passed through delete call - runtime_template file: File checker should compare to None, otherwise if id=0 the check would generate a false positive Feature: Directory create with id function added --- .../isam/aac/runtime_template/directory.py | 34 +++++++++++++++++++ ibmsecurity/isam/aac/runtime_template/file.py | 2 +- ibmsecurity/isam/aac/runtime_template/root.py | 4 +-- 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/ibmsecurity/isam/aac/runtime_template/directory.py b/ibmsecurity/isam/aac/runtime_template/directory.py index f51a3161..cf60fb7a 100755 --- a/ibmsecurity/isam/aac/runtime_template/directory.py +++ b/ibmsecurity/isam/aac/runtime_template/directory.py @@ -84,6 +84,40 @@ def create(isamAppliance, path, name, check_mode=False, force=False): return isamAppliance.create_return_object(warnings=warnings) +def create(isamAppliance, id, check_mode=False, force=False): + """ + Creating a directory in the runtime template files directory + + :param isamAppliance: + :param id: + :param name: + :param check_mode: + :param force: + :return: + """ + warnings = [] + + path = os.path.dirname(id) + name = os.path.basename(id) + + check_dir = _check(isamAppliance, id) + if check_dir != None: + warnings.append("Directory {0} exists. Ignoring create.".format(id)) + + if force is True or check_dir == None: + if check_mode is True: + return isamAppliance.create_return_object(changed=True, warnings=warnings) + else: + return isamAppliance.invoke_post( + "Creating a directory in the runtime template files directory", + "/mga/template_files/{0}".format(path), + { + 'dir_name': name, + 'type': 'dir' + }) + + return isamAppliance.create_return_object(warnings=warnings) + def delete(isamAppliance, id, check_mode=False, force=False): """ diff --git a/ibmsecurity/isam/aac/runtime_template/file.py b/ibmsecurity/isam/aac/runtime_template/file.py index 572f3010..0b3fb219 100755 --- a/ibmsecurity/isam/aac/runtime_template/file.py +++ b/ibmsecurity/isam/aac/runtime_template/file.py @@ -235,7 +235,7 @@ def _check_import(isamAppliance, id, filename, check_mode=False): """ tmpdir = get_random_temp_dir() tmp_original_file = os.path.join(tmpdir, os.path.basename(id)) - if _check(isamAppliance, id): + if _check(isamAppliance, id) != None: export_file(isamAppliance, id, tmp_original_file, check_mode=False, force=True) logger.debug("file already exists on appliance") if files_same(tmp_original_file, filename): diff --git a/ibmsecurity/isam/aac/runtime_template/root.py b/ibmsecurity/isam/aac/runtime_template/root.py index ed320436..75568db1 100644 --- a/ibmsecurity/isam/aac/runtime_template/root.py +++ b/ibmsecurity/isam/aac/runtime_template/root.py @@ -79,6 +79,6 @@ def delete(isamAppliance, id, type, check_mode=False, force=False): :return: """ if(type.lower() == 'directory'): - return directory.delete(isamAppliance, id) + return directory.delete(isamAppliance, id, check_mode, force) elif(type.lower() == 'file'): - return file.delete(isamAppliance, id) + return file.delete(isamAppliance, id, check_mode, force) From 6b28c7544d512d0a94c59e5db11af5dd45699f78 Mon Sep 17 00:00:00 2001 From: Sebastian-Vetter Date: Mon, 28 Jan 2019 14:20:04 +0100 Subject: [PATCH 02/20] Feature: handle isam 9.0.5 extProperties client attributes properly --- .../isam/aac/api_protection/clients.py | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/ibmsecurity/isam/aac/api_protection/clients.py b/ibmsecurity/isam/aac/api_protection/clients.py index 682ee3c5..a3d21df5 100644 --- a/ibmsecurity/isam/aac/api_protection/clients.py +++ b/ibmsecurity/isam/aac/api_protection/clients.py @@ -76,7 +76,7 @@ def generate_client_secret(isamAppliance, check_mode=False, force=False): def add(isamAppliance, name, definitionName, companyName, redirectUri=None, companyUrl=None, contactPerson=None, contactType=None, email=None, phone=None, otherInfo=None, clientId=None, clientSecret=None, - requirePkce=None, encryptionDb=None, encryptionCert=None, jwksUri=None, check_mode=False, + requirePkce=None, encryptionDb=None, encryptionCert=None, jwksUri=None, extProperties=None, check_mode=False, force=False): """ Create an API protection definition @@ -150,6 +150,13 @@ def add(isamAppliance, name, definitionName, companyName, redirectUri=None, comp isamAppliance.facts["version"], jwksUri)) else: client_json["jwksUri"] = jwksUri + if extProperties is not None: + if tools.version_compare(isamAppliance.facts["version"], "9.0.5.0") < 0: + warnings.append( + "Appliance at version: {0}, extProperties: {1} is not supported. Needs 9.0.5.0 or higher. Ignoring extProperties for this call.".format( + isamAppliance.facts["version"], extProperties)) + else: + client_json["extProperties"] = extProperties return isamAppliance.invoke_post( "Create an API protection definition", uri, client_json, requires_modules=requires_modules, @@ -181,7 +188,7 @@ def delete(isamAppliance, name, check_mode=False, force=False): def update(isamAppliance, name, definitionName, companyName, redirectUri=None, companyUrl=None, contactPerson=None, contactType=None, email=None, phone=None, otherInfo=None, clientId=None, clientSecret=None, - requirePkce=None, encryptionDb=None, encryptionCert=None, jwksUri=None, check_mode=False, + requirePkce=None, encryptionDb=None, encryptionCert=None, jwksUri=None, extProperties=None, check_mode=False, force=False, new_name=None): """ Update a specified mapping rule @@ -290,6 +297,15 @@ def update(isamAppliance, name, definitionName, companyName, redirectUri=None, c json_data["jwksUri"] = jwksUri elif 'jwksUri' in ret_obj['data']: del ret_obj['data']['jwksUri'] + if extProperties is not None: + if tools.version_compare(isamAppliance.facts["version"], "9.0.5.0") < 0: + warnings.append( + "Appliance at version: {0}, extProperties: {1} is not supported. Needs 9.0.5.0 or higher. Ignoring extProperties for this call.".format( + isamAppliance.facts["version"], extProperties)) + else: + json_data["extProperties"] = extProperties + elif 'extProperties' in ret_obj['data']: + del ret_obj['data']['extProperties'] sorted_ret_obj = tools.json_sort(ret_obj['data']) sorted_json_data = tools.json_sort(json_data) @@ -311,7 +327,7 @@ def update(isamAppliance, name, definitionName, companyName, redirectUri=None, c def set(isamAppliance, name, definitionName, companyName, redirectUri=None, companyUrl=None, contactPerson=None, contactType=None, email=None, phone=None, otherInfo=None, clientId=None, clientSecret=None, new_name=None, - requirePkce=None, encryptionDb=None, encryptionCert=None, jwksUri=None, check_mode=False, + requirePkce=None, encryptionDb=None, encryptionCert=None, jwksUri=None, extProperties=None, check_mode=False, force=False): """ Creating or Modifying an API Protection Definition @@ -322,7 +338,7 @@ def set(isamAppliance, name, definitionName, companyName, redirectUri=None, comp return add(isamAppliance, name, definitionName, companyName, redirectUri=redirectUri, companyUrl=companyUrl, contactPerson=contactPerson, contactType=contactType, email=email, phone=phone, otherInfo=otherInfo, clientId=clientId, clientSecret=clientSecret, requirePkce=requirePkce, encryptionDb=encryptionDb, - encryptionCert=encryptionCert, jwksUri=jwksUri, check_mode=check_mode, force=True) + encryptionCert=encryptionCert, jwksUri=jwksUri, extProperties=extProperties, check_mode=check_mode, force=True) else: # Update request logger.info("Definition {0} exists, requesting to update.".format(name)) @@ -330,7 +346,7 @@ def set(isamAppliance, name, definitionName, companyName, redirectUri=None, comp contactPerson=contactPerson, contactType=contactType, email=email, phone=phone, otherInfo=otherInfo, clientId=clientId, clientSecret=clientSecret, new_name=new_name, requirePkce=requirePkce, encryptionDb=encryptionDb, encryptionCert=encryptionCert, - jwksUri=jwksUri, check_mode=check_mode, force=force) + jwksUri=jwksUri, extProperties=extProperties, check_mode=check_mode, force=force) def compare(isamAppliance1, isamAppliance2): """ From ffebd15e0661f203edd39ce6134eb0407e7ba35e Mon Sep 17 00:00:00 2001 From: Sebastian-Vetter Date: Wed, 15 May 2019 20:40:44 +0200 Subject: [PATCH 03/20] +1 Function: Compare LTPA files for idempotency on junction creation --- .../isam/web/reverse_proxy/junctions.py | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/ibmsecurity/isam/web/reverse_proxy/junctions.py b/ibmsecurity/isam/web/reverse_proxy/junctions.py index 931f9677..f89a70e2 100644 --- a/ibmsecurity/isam/web/reverse_proxy/junctions.py +++ b/ibmsecurity/isam/web/reverse_proxy/junctions.py @@ -304,7 +304,7 @@ def set(isamAppliance, reverseproxy_id, junction_point, server_hostname, server_ http2_junction=None, http2_proxy=None, sni_name=None): """ Setting a standard or virtual junction - compares with existing junction and replaces if changes are detected - TODO: Compare all the parameters in the function - LTPA, BA are some that are not being compared + TODO: Compare all the parameters in the function - BA are some that are not being compared """ warnings = [] add_required = False @@ -493,6 +493,24 @@ def set(isamAppliance, reverseproxy_id, junction_point, server_hostname, server_ sni_name = None else: jct_json['sni_name'] = sni_name + if insert_ltpa_cookies is not None: + if insert_ltpa_cookies != 'no': + jct_json['insert_ltpa_cookies'] = insert_ltpa_cookies + + if ltpa_keyfile is not None: + jct_json['ltpa_keyfile'] = ltpa_keyfile + + if version_two_cookies is not None: + jct_json['version_two_cookies'] = version_two_cookies + + if ltpa_keyfile_password is not None: + if not force: + logger.debug("Skipping ltpa_keyfile_password for idempotency.") + warnings.append("Module can not compare ltpa_keyfile_password with server. Skipping parameter for idempotency. Force update of ltpa_keyfile_password by setting force=true.") + if 'ltpa_keyfile_password' in exist_jct: + del exist_jct['ltpa_keyfile_password'] + else: + jct_json['ltpa_keyfile_password'] = ltpa_keyfile_password # TODO: Not sure of how to match following attributes! Need to revisit. # TODO: Not all function parameters are being checked - need to add! From 11657808b7aad0465ce78871b4aa765dcb97e2de Mon Sep 17 00:00:00 2001 From: Sebastian-Vetter Date: Wed, 15 May 2019 20:49:45 +0200 Subject: [PATCH 04/20] +1 function: compare zip file contents perform compare checks for zip on: - number of files - file content comparison one by one --- ibmsecurity/utilities/tools.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/ibmsecurity/utilities/tools.py b/ibmsecurity/utilities/tools.py index 4a574aa4..77183705 100644 --- a/ibmsecurity/utilities/tools.py +++ b/ibmsecurity/utilities/tools.py @@ -6,6 +6,7 @@ import hashlib import ntpath import re +import zipfile logger = logging.getLogger(__name__) @@ -171,7 +172,39 @@ def files_same(original_file, new_file): return True else: return False + +def files_same_zip_content(original_file, new_file): + identical = True + + z1 = zipfile.ZipFile(open(original_file)) + z2 = zipfile.ZipFile(open(new_file)) + + if len(z1.infolist()) != len(z2.infolist()): + logger.debug("number of archive elements differ: {} in {} vs {} from server".format(len(z1.infolist()), z1.filename, len(z2.infolist()))) + identical = False + # Can stop comparison of zip files for perfomance + return identical + for zipentry in z1.infolist(): + if zipentry.filename not in z2.namelist(): + logger.debug("no file named {} found in {}".format(zipentry.filename, z2.filename)) + identical = False + else: + with z1.open(zipentry.filename) as f: + original_file_contents = f.read() + with z2.open(zipentry.filename) as f: + new_file_contents = f.read() + hash_original_file = hashlib.sha224(original_file_contents).hexdigest() + hash_new_file = hashlib.sha224(new_file_contents).hexdigest() + if hash_original_file != hash_new_file: + identical = False + logger.debug("content for zip file {} differs.".format(zipentry.filename)) + + if identical: + logger.info("content for zip files {} and {} are the same.".format(original_file,new_file)) + else: + logger.info("content for zip files {} and {} are different.".format(original_file,new_file)) + return identical def get_random_temp_dir(): """ From eb626e4b563f84383be1ebb8e9b38e57987c86fa Mon Sep 17 00:00:00 2001 From: Sebastian-Vetter Date: Wed, 15 May 2019 20:55:57 +0200 Subject: [PATCH 05/20] +1 function: check runtime template root for idempotency adding verification check on runtime template root files between server and zip in import_file function. (makes use of new function: files_same_zip_content in ibmsecurity.utilities.tools) --- ibmsecurity/isam/aac/runtime_template/root.py | 103 +++++++++++++++--- 1 file changed, 85 insertions(+), 18 deletions(-) diff --git a/ibmsecurity/isam/aac/runtime_template/root.py b/ibmsecurity/isam/aac/runtime_template/root.py index 75568db1..a38af228 100644 --- a/ibmsecurity/isam/aac/runtime_template/root.py +++ b/ibmsecurity/isam/aac/runtime_template/root.py @@ -1,8 +1,13 @@ import logging import os.path +import ibmsecurity.utilities.tools +import zipfile +import difflib +import hashlib +import shutil from ibmsecurity.isam.aac.runtime_template import directory from ibmsecurity.isam.aac.runtime_template import file - +from ibmsecurity.utilities.tools import get_random_temp_dir, files_same_zip_content logger = logging.getLogger(__name__) uri = "/mga/template_files" @@ -24,26 +29,88 @@ def export_file(isamAppliance, filename, check_mode=False, force=False): return isamAppliance.create_return_object() -def import_file(isamAppliance, filename, check_mode=False, force=False): +def import_file(isamAppliance, filename, delete_missing=False, check_mode=False, force=False): """ - Replace all Runtime Template Files + Import all Runtime Template Files """ - if check_mode is True: - return isamAppliance.create_return_object(changed=True) - else: - return isamAppliance.invoke_post_files( - "Replace all Runtime Template Files", - uri, - [ + warnings = [] + + if force is True or _check_import(isamAppliance, filename): + if delete_missing is True: + tempdir = get_random_temp_dir() + tempfilename = "template_files.zip" + tempfile = os.path.join(tempdir, tempfilename) + export_file(isamAppliance, tempfile) + + zServerFile = zipfile.ZipFile(tempfile) + zClientFile = zipfile.ZipFile(filename) + + files_on_server = []; + for info in zServerFile.infolist(): + files_on_server.append(info.filename) + files_on_client = []; + for info in zClientFile.infolist(): + files_on_client.append(info.filename) + missing_client_files = [x for x in files_on_server if x not in files_on_client] + + if missing_client_files != []: + logger.info("list all missing files in {}, which will be deleted on the server: {}.".format(filename, missing_client_files)) + + for x in missing_client_files: + if x.endswith('/'): + search_dir= os.path.dirname(x[:-1]) + '/' + if search_dir not in missing_client_files: + logger.debug("delete directory on the server: {0}.".format(x)) + delete(isamAppliance, x, "directory", check_mode=check_mode) + else: + search_dir= os.path.dirname(x) + '/' + if search_dir not in missing_client_files: + logger.debug("delete file on the server: {0}.".format(x)) + delete(isamAppliance, x, "file", check_mode=check_mode) + shutil.rmtree(tempdir) + + if check_mode is True: + return isamAppliance.create_return_object(changed=True) + else: + return isamAppliance.invoke_post_files( + "Replace all Runtime Template Files", + uri, + [ + { + 'file_formfield': 'file', + 'filename': filename, + 'mimetype': 'application/octet-stream' + } + ], { - 'file_formfield': 'file', - 'filename': filename, - 'mimetype': 'application/octet-stream' - } - ], - { - "force": force - }, json_response=False) + "force": force + }, json_response=False) + + return isamAppliance.create_return_object(warnings=warnings) + + +def _check_import(isamAppliance, filename): + """ + Checks if runtime template zip from server and client differ + :param isamAppliance: + :param filename: + :return: + """ + + tempdir = get_random_temp_dir() + tempfilename = "template_files.zip" + tempfile = os.path.join(tempdir, tempfilename) + export_file(isamAppliance, tempfile) + + identical = files_same_zip_content(filename,tempfile) + + shutil.rmtree(tempdir) + if identical: + logger.info("runtime template files {} are identical with the server content. No update necessary.".format(filename)) + return False + else: + logger.info("runtime template files {} differ from the server content. Updating runtime template files necessary.".format(filename)) + return True def check(isamAppliance, id, type, check_mode=False, force=False): ret_obj = None From a1d457999733895673a577ce1f9e1e63c86e734f Mon Sep 17 00:00:00 2001 From: Sebastian-Vetter Date: Wed, 15 May 2019 20:59:02 +0200 Subject: [PATCH 06/20] +1 function: check management root on import_zip for idempotency adding verification check on management root files between server and zip in import_file function. (makes use of new function: files_same_zip_content in ibmsecurity.utilities.tools) --- .../web/reverse_proxy/management_root/all.py | 112 ++++++++++++++---- 1 file changed, 90 insertions(+), 22 deletions(-) diff --git a/ibmsecurity/isam/web/reverse_proxy/management_root/all.py b/ibmsecurity/isam/web/reverse_proxy/management_root/all.py index 2bef3d03..eb281034 100644 --- a/ibmsecurity/isam/web/reverse_proxy/management_root/all.py +++ b/ibmsecurity/isam/web/reverse_proxy/management_root/all.py @@ -1,8 +1,12 @@ import logging import ibmsecurity.utilities.tools import os.path +import shutil +import zipfile from ibmsecurity.isam.web.reverse_proxy.management_root import directory from ibmsecurity.isam.web.reverse_proxy.management_root import file +from ibmsecurity.isam.web.reverse_proxy import instance +from ibmsecurity.utilities.tools import get_random_temp_dir, files_same_zip_content logger = logging.getLogger(__name__) @@ -22,37 +26,101 @@ def export_zip(isamAppliance, instance_id, filename, check_mode=False, force=Fal if check_mode is False: return isamAppliance.invoke_get_file( "Exporting the contents of the administration pages root as a .zip file", - "/wga/reverseproxy/{0}/management_root?index=&name=&enc_name=&type=&browser=".format(instance_id), - filename) + "/wga/reverseproxy/{0}/management_root/?export=true".format(instance_id), + filename, no_headers=True) return isamAppliance.create_return_object() -def import_zip(isamAppliance, instance_id, filename, check_mode=False, force=False): +def import_zip(isamAppliance, instance_id, filename, delete_missing=False, check_mode=False, force=False): """ Importing the contents of a .zip file to the administration pages root """ - if check_mode is True: - return isamAppliance.create_return_object(changed=True) - else: - return isamAppliance.invoke_post_files( - "Importing the contents of a .zip file to the administration pages root", - "/wga/reverseproxy/{0}/management_root".format(instance_id), filename, - [ + warnings = [] + + if force is True or _check_import(isamAppliance, instance_id, filename): + if delete_missing is True: + tempdir = get_random_temp_dir() + tempfilename = "management_root.zip" + tempfile = os.path.join(tempdir, tempfilename) + export_zip(isamAppliance, instance_id, tempfile) + + zServerFile = zipfile.ZipFile(tempfile) + zClientFile = zipfile.ZipFile(filename) + + files_on_server = []; + for info in zServerFile.infolist(): + files_on_server.append(info.filename) + files_on_client = []; + for info in zClientFile.infolist(): + files_on_client.append(info.filename) + missing_client_files = [x for x in files_on_server if x not in files_on_client] + + if missing_client_files != []: + logger.info("list all missing files in {}, which will be deleted on the server: {}.".format(filename, missing_client_files)) + + for x in missing_client_files: + if x.endswith('/'): + search_dir= os.path.dirname(x[:-1]) + '/' + if search_dir not in missing_client_files: + logger.debug("delete directory on the server: {0}.".format(x)) + directory.delete(isamAppliance, instance_id, x, check_mode=check_mode) + else: + search_dir= os.path.dirname(x) + '/' + if search_dir not in missing_client_files: + logger.debug("delete file on the server: {0}.".format(x)) + file.delete(isamAppliance, instance_id, x, check_mode=check_mode) + shutil.rmtree(tempdir) + + if check_mode is True: + return isamAppliance.create_return_object(changed=True) + else: + return isamAppliance.invoke_post_files( + "Importing the contents of a .zip file to the administration pages root", + "/wga/reverseproxy/{0}/management_root".format(instance_id), + [ + { + 'file_formfield': 'file', + 'filename': filename, + 'mimetype': 'application/octet-stream' + } + ], { - 'file_formfield': 'file', - 'filename': filename, - 'mimetype': 'application/octet-stream' - } - ], - { - 'type': 'file', - 'force': force - }) - + "force": force + }, json_response=False) + + return isamAppliance.create_return_object(warnings=warnings) + +def _check_import(isamAppliance, instance_id, filename): + """ + Checks if runtime template zip from server and client differ + :param isamAppliance: + :param filename: + :return: + """ + + if not instance._check(isamAppliance, instance_id): + logger.info("instance {} does not exist on this server. Skip import".format(instance_id)) + return False + + tempdir = get_random_temp_dir() + tempfilename = "management_root.zip" + tempfile = os.path.join(tempdir, tempfilename) + export_zip(isamAppliance, instance_id, tempfile) + + identical = files_same_zip_content(filename,tempfile) + + shutil.rmtree(tempdir) + if identical: + logger.info("management_root files {} are identical with the server content. No update necessary.".format(filename)) + return False + else: + logger.info("management_root files {} differ from the server content. Updating management_root files necessary.".format(filename)) + return True + def check(isamAppliance, instance_id, id, name, type, check_mode=False, force=False): ret_obj = None - + if(type.lower() == 'directory'): ret_obj = directory._check(isamAppliance, instance_id, id, name) elif(type.lower() == 'file'): @@ -66,5 +134,5 @@ def check(isamAppliance, instance_id, id, name, type, check_mode=False, force=Fa 'name': name, 'type': type } - + return isamAppliance.create_return_object(data=data) \ No newline at end of file From 32abfc44c1eef0099a14d4b5e4c469b1f64a653a Mon Sep 17 00:00:00 2001 From: Sebastian-Vetter Date: Mon, 1 Jul 2019 10:09:37 +0200 Subject: [PATCH 07/20] BuFix: aac runtime templates parameter correction Upload of aac runtime template files needs path+name instead of id for check and delete function --- ibmsecurity/isam/aac/runtime_template/root.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/ibmsecurity/isam/aac/runtime_template/root.py b/ibmsecurity/isam/aac/runtime_template/root.py index 78fa135e..ecd71edb 100644 --- a/ibmsecurity/isam/aac/runtime_template/root.py +++ b/ibmsecurity/isam/aac/runtime_template/root.py @@ -121,16 +121,16 @@ def _check_import(isamAppliance, filename): def check(isamAppliance, id, type, check_mode=False, force=False): ret_obj = None + name = os.path.basename(id) + path = os.path.dirname(id) + if (type.lower() == 'directory'): ret_obj = directory._check(isamAppliance, id) elif (type.lower() == 'file'): - ret_obj = file._check(isamAppliance, id) + ret_obj = file._check(isamAppliance, path, name) else: type = 'unknown' - name = os.path.basename(id) - path = os.path.dirname(id) - data = { 'id': ret_obj, 'path': path, @@ -152,7 +152,11 @@ def delete(isamAppliance, id, type, check_mode=False, force=False): :param force: :return: """ + + name = os.path.basename(id) + path = os.path.dirname(id) + if(type.lower() == 'directory'): return directory.delete(isamAppliance, id, check_mode, force) elif(type.lower() == 'file'): - return file.delete(isamAppliance, id, check_mode, force) \ No newline at end of file + return file.delete(isamAppliance, path, name, check_mode, force) \ No newline at end of file From 9942ea8240aec261cd3b4f2e764ef5cef313db1b Mon Sep 17 00:00:00 2001 From: svetterIO <> Date: Mon, 1 Jul 2019 16:28:58 +0200 Subject: [PATCH 08/20] BugFix: Skip compare for missing zip files If zip files for runtime template comparison is missing, skip it. This indicates aac or federation module is maybe not activated --- ibmsecurity/isam/aac/runtime_template/root.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/ibmsecurity/isam/aac/runtime_template/root.py b/ibmsecurity/isam/aac/runtime_template/root.py index ecd71edb..70e8c7fa 100644 --- a/ibmsecurity/isam/aac/runtime_template/root.py +++ b/ibmsecurity/isam/aac/runtime_template/root.py @@ -106,15 +106,19 @@ def _check_import(isamAppliance, filename): tempfile = os.path.join(tempdir, tempfilename) export_file(isamAppliance, tempfile) - identical = files_same_zip_content(filename,tempfile) + if os.path.exists(tempfile): + identical = files_same_zip_content(filename,tempfile) - shutil.rmtree(tempdir) - if identical: - logger.info("runtime template files {} are identical with the server content. No update necessary.".format(filename)) - return False + shutil.rmtree(tempdir) + if identical: + logger.info("runtime template files {} are identical with the server content. No update necessary.".format(filename)) + return False + else: + logger.info("runtime template files {} differ from the server content. Updating runtime template files necessary.".format(filename)) + return True else: - logger.info("runtime template files {} differ from the server content. Updating runtime template files necessary.".format(filename)) - return True + logger.info("missing zip file from server. Comparison skipped.") + return False From 78e2c85e434ea88aac59e92487e2e133d35fd00a Mon Sep 17 00:00:00 2001 From: svetterIO <> Date: Mon, 1 Jul 2019 16:39:14 +0200 Subject: [PATCH 09/20] +1 function: set_all Configure entire scim with idempotency checking --- ibmsecurity/isam/aac/scim.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/ibmsecurity/isam/aac/scim.py b/ibmsecurity/isam/aac/scim.py index ed3b510e..be1b68ea 100755 --- a/ibmsecurity/isam/aac/scim.py +++ b/ibmsecurity/isam/aac/scim.py @@ -1,4 +1,5 @@ import logging +from ibmsecurity.utilities import tools logger = logging.getLogger(__name__) @@ -63,3 +64,36 @@ def update_isam_user(isamAppliance, isam_domain, update_native_users, ldap_conne "Update SCIM ISAM user settings", "/mga/scim/configuration/urn:ietf:params:scim:schemas:extension:isam:1.0:User", ret_obj) + +def set_all(isamAppliance, scim_configuration, check_mode=False, force=False): + """ + Update entire SCIM settings + """ + if scim_configuration is None or scim_configuration == '': + return isamAppliance.create_return_object( + warnings="Need to pass content for scim configuration") + else: + if force is True or _check(isamAppliance, scim_configuration) is False : + if check_mode is True: + return isamAppliance.create_return_object(changed=True) + else: + return isamAppliance.invoke_put( + "Update SCIM settings", + "/mga/scim/configuration", + scim_configuration ) + + return isamAppliance.create_return_object() + +def _check(isamAppliance, scim_configuration): + """ + Check if scim configuration is identical with server + """ + ret_obj = get_all(isamAppliance) + logger.debug("Comparing server scim configuration with desired configuration.") + logger.debug("Server JSON: {0}".format(tools.json_sort(ret_obj['data']))) + logger.debug("Desired JSON: {0}".format(tools.json_sort(scim_configuration))) + if tools.json_sort(scim_configuration) != tools.json_sort(ret_obj['data']): + return False + + logger.debug("Server configuration is identical with desired configuration. No change necessary.") + return True \ No newline at end of file From aed1a08530bdcf4af64112e0599cca2b6d9f64df Mon Sep 17 00:00:00 2001 From: svetterIO <> Date: Mon, 1 Jul 2019 16:48:10 +0200 Subject: [PATCH 10/20] BugFix: server_connections jdbc id parameter missing --- ibmsecurity/isam/aac/server_connections/jdbc.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ibmsecurity/isam/aac/server_connections/jdbc.py b/ibmsecurity/isam/aac/server_connections/jdbc.py index fbd6d38a..47fc9ee0 100644 --- a/ibmsecurity/isam/aac/server_connections/jdbc.py +++ b/ibmsecurity/isam/aac/server_connections/jdbc.py @@ -67,6 +67,8 @@ def delete(isamAppliance, name=None, check_mode=False, force=False): if check_mode is True: return isamAppliance.create_return_object(changed=True) else: + ret_obj = search(isamAppliance, name) + id = ret_obj['data'] return isamAppliance.invoke_delete( "Deleting a JDBC server connection", "/mga/server_connections/jdbc/{0}/v1".format(id)) From 34155b2ce7a1f1c924f20cac6bd58c0b2e64b7df Mon Sep 17 00:00:00 2001 From: svetterIO <> Date: Mon, 1 Jul 2019 16:48:47 +0200 Subject: [PATCH 11/20] BugFix: server_connections ldap id parameter missing --- ibmsecurity/isam/aac/server_connections/ldap.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ibmsecurity/isam/aac/server_connections/ldap.py b/ibmsecurity/isam/aac/server_connections/ldap.py index 4e3d2eb8..241e9abc 100644 --- a/ibmsecurity/isam/aac/server_connections/ldap.py +++ b/ibmsecurity/isam/aac/server_connections/ldap.py @@ -89,6 +89,8 @@ def update(isamAppliance, connection, description='', locked=False, connectionMa connection=connection, connectionManager=connectionManager) if new_name is not None: # Rename condition json_data['name'] = new_name + ret_obj = _get_id(isamAppliance, name=name) + id = ret_obj['data'] return isamAppliance.invoke_put( "Modifying an LDAP server connection", "/mga/server_connections/ldap/{0}/v1".format(id), json_data) From 1950bc46ad0b47ab2fbf3110d6130a6f78015258 Mon Sep 17 00:00:00 2001 From: svetterIO <> Date: Mon, 1 Jul 2019 16:49:27 +0200 Subject: [PATCH 12/20] BugFix: server_connections smtp id parameter missing --- ibmsecurity/isam/aac/server_connections/smtp.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ibmsecurity/isam/aac/server_connections/smtp.py b/ibmsecurity/isam/aac/server_connections/smtp.py index a224a338..5f06a1d0 100644 --- a/ibmsecurity/isam/aac/server_connections/smtp.py +++ b/ibmsecurity/isam/aac/server_connections/smtp.py @@ -66,6 +66,8 @@ def delete(isamAppliance, name=None, check_mode=False, force=False): if check_mode is True: return isamAppliance.create_return_object(changed=True) else: + ret_obj = _get_id(isamAppliance, name=name) + id = ret_obj['data'] return isamAppliance.invoke_delete( "Deleting a SMTP server connection", "/mga/server_connections/smtp/{0}/v1".format(id)) @@ -87,6 +89,8 @@ def update(isamAppliance, name, connection, description='', locked=False, connec connectionManager=connectionManager) if new_name is not None: # Rename condition json_data['name'] = new_name + ret_obj = _get_id(isamAppliance, name=name) + id = ret_obj['data'] return isamAppliance.invoke_put( "Modifying a SMTP server connection", "/mga/server_connections/smtp/{0}/v1".format(id), json_data) From b0a40ea2cd5fd7979beed68ecce7c76efab23576 Mon Sep 17 00:00:00 2001 From: svetterIO <> Date: Mon, 1 Jul 2019 16:55:12 +0200 Subject: [PATCH 13/20] +1 Feature: add local_ip as possible junction parameter --- ibmsecurity/isam/web/reverse_proxy/junctions_server.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ibmsecurity/isam/web/reverse_proxy/junctions_server.py b/ibmsecurity/isam/web/reverse_proxy/junctions_server.py index 86631515..f61d8876 100644 --- a/ibmsecurity/isam/web/reverse_proxy/junctions_server.py +++ b/ibmsecurity/isam/web/reverse_proxy/junctions_server.py @@ -22,7 +22,7 @@ def search(isamAppliance, reverseproxy_id, junction_point, server_hostname, serv def add(isamAppliance, reverseproxy_id, junction_point, server_hostname, junction_type, server_port, server_dn=None, stateful_junction='no', case_sensitive_url='no', windows_style_url='no', virtual_hostname=None, virtual_https_hostname=None, query_contents=None, https_port=None, http_port=None, proxy_hostname=None, - proxy_port=None, sms_environment=None, vhost_label=None, server_uuid=None, check_mode=False, force=False): + proxy_port=None, sms_environment=None, vhost_label=None, server_uuid=None, local_ip=None, check_mode=False, force=False): """ Adding a back-end server to an existing standard or virtual junctions @@ -46,6 +46,7 @@ def add(isamAppliance, reverseproxy_id, junction_point, server_hostname, junctio :param sms_environment: :param vhost_label: :param server_uuid: + :param local_ip: :param check_mode: :param force: :return: @@ -89,6 +90,8 @@ def add(isamAppliance, reverseproxy_id, junction_point, server_hostname, junctio jct_srv_json["query_contents"] = query_contents if server_uuid is not None and server_uuid != '': jct_srv_json["server_uuid"] = server_uuid + if local_ip is not None and local_ip != '': + jct_srv_json['local_ip'] = local_ip return isamAppliance.invoke_put( "Adding a back-end server to an existing standard or virtual junctions", From a481576024d0edf24d4d265b39e28b9f72458269 Mon Sep 17 00:00:00 2001 From: svetterIO <> Date: Mon, 1 Jul 2019 17:27:07 +0200 Subject: [PATCH 14/20] BigFix: load of signer certificates not possible (network or firewall restrictions) load of signer certificates had following issues: - loading of local runtime certificates not possible (e.g. ansible can not reach local aac runtime certificate for checking) - ansible is required to be able to load the certificate for comparison (firewall restrictions can prevent this to work properly) fix: additional parameter "check_remote" introduces. description: parameter "check_remote" controls whether to load remote certificate into ansible for comparison or to simply compare the kdb for existance of the label. For backward compatibility and default behavior (LMI) reasons "check_remote" parameter is set to False by default. --- .../ssl_certificates/signer_certificate.py | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/ibmsecurity/isam/base/ssl_certificates/signer_certificate.py b/ibmsecurity/isam/base/ssl_certificates/signer_certificate.py index be4bd466..254de82d 100644 --- a/ibmsecurity/isam/base/ssl_certificates/signer_certificate.py +++ b/ibmsecurity/isam/base/ssl_certificates/signer_certificate.py @@ -22,11 +22,19 @@ def get(isamAppliance, kdb_id, cert_id, check_mode=False, force=False): "/isam/ssl_certificates/{0}/signer_cert/{1}".format(kdb_id, cert_id)) -def load(isamAppliance, kdb_id, label, server, port, check_mode=False, force=False): +def load(isamAppliance, kdb_id, label, server, port, check_remote=False, check_mode=False, force=False): """ Load a certificate from a server + + check_remote controls if ansible should check remote certificate by retrieving it or simply by + checking for existence of the label in the kdb """ - if force is True or _check_load(isamAppliance, kdb_id, label, server, port) is False: + if check_remote: + tmp_check = _check_load(isamAppliance, kdb_id, label, server, port) + else: + tmp_check = _check(isamAppliance, kdb_id, label) + + if force is True or tmp_check is False: if check_mode is True: return isamAppliance.create_return_object(changed=True) else: @@ -105,18 +113,9 @@ def delete(isamAppliance, kdb_id, cert_id, check_mode=False, force=False): if check_mode is True: return isamAppliance.create_return_object(changed=True) else: - try: - # Assume Python3 and import package - from urllib.parse import quote - except ImportError: - # Now try to import Python2 package - from urllib import quote - - # URL being encoded primarily to handle spaces and other special characers in them - f_uri = "/isam/ssl_certificates/{0}/signer_cert/{1}".format(kdb_id, cert_id) - full_uri = quote(f_uri) return isamAppliance.invoke_delete( - "Deleting a signer certificate from a certificate database", full_uri) + "Deleting a signer certificate from a certificate database", + "/isam/ssl_certificates/{0}/signer_cert/{1}".format(kdb_id, cert_id)) return isamAppliance.create_return_object() From 9e1fd07a94e42e09412fac94809e7047e263e859 Mon Sep 17 00:00:00 2001 From: Sebastian-Vetter Date: Mon, 8 Jul 2019 11:43:06 +0200 Subject: [PATCH 15/20] Typo: entry.py comment for set entries --- ibmsecurity/isam/web/reverse_proxy/configuration/entry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ibmsecurity/isam/web/reverse_proxy/configuration/entry.py b/ibmsecurity/isam/web/reverse_proxy/configuration/entry.py index e8c4431f..dea31b1d 100644 --- a/ibmsecurity/isam/web/reverse_proxy/configuration/entry.py +++ b/ibmsecurity/isam/web/reverse_proxy/configuration/entry.py @@ -77,7 +77,7 @@ def set(isamAppliance, reverseproxy_id, stanza_id, entries, check_mode=False, fo """ Set a configuration entry or entries by stanza - Reverse Proxy - Note: entries has to be [['key', 'value1'], ['key', 'value2]], cannot provide [['key', ['value1', 'value2']]] + Note: entries has to be [['key', 'value1'], ['key', 'value2']], cannot provide [['key', ['value1', 'value2']]] get() returns the second format - thus lots of logic to handle this discrepancy. Smart enough to update only that which is needed. From 664d2c0caf4ac8fd3b061ef572b7f3bc3d0052ac Mon Sep 17 00:00:00 2001 From: svetterIO <2234705+svetterIO@users.noreply.github.com> Date: Tue, 9 Jul 2019 11:47:37 +0200 Subject: [PATCH 16/20] +1 feature: set EmailMessage, ScimConfig, CI server connections properties by name This feature attempts to find the uuids from the properties "EmailMessage.serverConnection", "ScimConfig.serverConnection" and "CI.serverConnection". If a UUID is found, the value for this property is replaced by this UUID, otherwise the value remains unchanged. This allows you to set ServerConnections by name, while still allowing you to create the ServerConnection directly from the UUID. --- .../isam/aac/authentication/mechanisms.py | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/ibmsecurity/isam/aac/authentication/mechanisms.py b/ibmsecurity/isam/aac/authentication/mechanisms.py index eaa1b74a..95219c23 100644 --- a/ibmsecurity/isam/aac/authentication/mechanisms.py +++ b/ibmsecurity/isam/aac/authentication/mechanisms.py @@ -1,6 +1,9 @@ import logging from ibmsecurity.utilities import tools from ibmsecurity.isam.aac.authentication import mechanism_types +from ibmsecurity.isam.aac.server_connections import smtp +from ibmsecurity.isam.aac.server_connections import ci +from ibmsecurity.isam.aac.server_connections import ws logger = logging.getLogger(__name__) @@ -99,6 +102,20 @@ def add(isamAppliance, name, uri, description="", attributes=None, properties=No if attributes is not None: json_data['attributes'] = attributes if properties is not None: + logger.info("Searching for keys to substitute value with uuids") + id = {} + for property in properties: + if property['key'] == "EmailMessage.serverConnection": + id = smtp._get_id(isamAppliance, property['value'])['data'] + logger.info("Found EmailMessage.serverConnection by name[{}] with uuid[{}]".format(property['value'], id)) + elif property['key'] == "ScimConfig.serverConnection": + id = ws.search(isamAppliance, property['value'])['data'] + logger.info("Found ScimConfig.serverConnection by name[{0}] with uuid[{}]".format(property['value'], id)) + elif property['key'] == "CI.serverConnection": + id = ci._get_id(isamAppliance, property['value'])['data'] + logger.info("Found CI.serverConnection by name[{0}] with uuid[{}]".format(property['value'], id)) + if id != {}: + property['value'] = id json_data['properties'] = properties return isamAppliance.invoke_post( "Create a new federation", module_uri, json_data, @@ -197,6 +214,20 @@ def _check(isamAppliance, name, description, attributes, properties, predefined, except: pass if properties is not None: + logger.info("Searching for keys to substitute value with uuids") + id = {} + for property in properties: + if property['key'] == "EmailMessage.serverConnection": + id = smtp._get_id(isamAppliance, property['value'])['data'] + logger.info("Found EmailMessage.serverConnection by name[{}] with uuid[{}]".format(property['value'], id)) + elif property['key'] == "ScimConfig.serverConnection": + id = ws.search(isamAppliance, property['value'])['data'] + logger.info("Found ScimConfig.serverConnection by name[{0}] with uuid[{}]".format(property['value'], id)) + elif property['key'] == "CI.serverConnection": + id = ci._get_id(isamAppliance, property['value'])['data'] + logger.info("Found CI.serverConnection by name[{0}] with uuid[{}]".format(property['value'], id)) + if id != {}: + property['value'] = id json_data['properties'] = properties else: # May not exist so skip any exceptions when deleting From dcfa6f5fcd75b72b2b5e147abc14d56020fda2e0 Mon Sep 17 00:00:00 2001 From: svetterIO <2234705+svetterIO@users.noreply.github.com> Date: Tue, 9 Jul 2019 12:09:00 +0200 Subject: [PATCH 17/20] BugFix: logger info with manual field specification and automatic field numbering ValueError: cannot switch from manual field specification to automatic field numbering --- ibmsecurity/isam/aac/authentication/mechanisms.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ibmsecurity/isam/aac/authentication/mechanisms.py b/ibmsecurity/isam/aac/authentication/mechanisms.py index 95219c23..d5961055 100644 --- a/ibmsecurity/isam/aac/authentication/mechanisms.py +++ b/ibmsecurity/isam/aac/authentication/mechanisms.py @@ -110,10 +110,10 @@ def add(isamAppliance, name, uri, description="", attributes=None, properties=No logger.info("Found EmailMessage.serverConnection by name[{}] with uuid[{}]".format(property['value'], id)) elif property['key'] == "ScimConfig.serverConnection": id = ws.search(isamAppliance, property['value'])['data'] - logger.info("Found ScimConfig.serverConnection by name[{0}] with uuid[{}]".format(property['value'], id)) + logger.info("Found ScimConfig.serverConnection by name[{}] with uuid[{}]".format(property['value'], id)) elif property['key'] == "CI.serverConnection": id = ci._get_id(isamAppliance, property['value'])['data'] - logger.info("Found CI.serverConnection by name[{0}] with uuid[{}]".format(property['value'], id)) + logger.info("Found CI.serverConnection by name[{}] with uuid[{}]".format(property['value'], id)) if id != {}: property['value'] = id json_data['properties'] = properties @@ -222,10 +222,10 @@ def _check(isamAppliance, name, description, attributes, properties, predefined, logger.info("Found EmailMessage.serverConnection by name[{}] with uuid[{}]".format(property['value'], id)) elif property['key'] == "ScimConfig.serverConnection": id = ws.search(isamAppliance, property['value'])['data'] - logger.info("Found ScimConfig.serverConnection by name[{0}] with uuid[{}]".format(property['value'], id)) + logger.info("Found ScimConfig.serverConnection by name[{}] with uuid[{}]".format(property['value'], id)) elif property['key'] == "CI.serverConnection": id = ci._get_id(isamAppliance, property['value'])['data'] - logger.info("Found CI.serverConnection by name[{0}] with uuid[{}]".format(property['value'], id)) + logger.info("Found CI.serverConnection by name[{}] with uuid[{}]".format(property['value'], id)) if id != {}: property['value'] = id json_data['properties'] = properties From a58204b5840f93a371b88b779382675f0b9be23d Mon Sep 17 00:00:00 2001 From: svetterIO <2234705+svetterIO@users.noreply.github.com> Date: Fri, 12 Jul 2019 12:49:46 +0200 Subject: [PATCH 18/20] +1 feature: Compare server connections content + BugFix on CI server connection parameters Adding compare functionality to all server connection types: ci, ldap, smtp, ws, jdbc 1. Attention: ci server connection parameter changed. Following functions set update delete have been updated to work with the name parameter instead of id parameter. 2. Attention: jdbc type parameter removed and made static = 'db2' All these changes are adjustments to harmonize the functions signature accross all server connections (ci, ldap, ws, smtp, jdbc) --- ibmsecurity/isam/aac/server_connections/ci.py | 102 ++++++++++-------- .../isam/aac/server_connections/jdbc.py | 72 +++++++++---- .../isam/aac/server_connections/ldap.py | 56 +++++++--- .../isam/aac/server_connections/smtp.py | 56 +++++++--- ibmsecurity/isam/aac/server_connections/ws.py | 9 +- 5 files changed, 199 insertions(+), 96 deletions(-) diff --git a/ibmsecurity/isam/aac/server_connections/ci.py b/ibmsecurity/isam/aac/server_connections/ci.py index 4b093dd3..850ef5b5 100644 --- a/ibmsecurity/isam/aac/server_connections/ci.py +++ b/ibmsecurity/isam/aac/server_connections/ci.py @@ -1,10 +1,10 @@ import logging -import ibmsecurity.utilities.tools +from ibmsecurity.utilities import tools from ibmsecurity.appliance.ibmappliance import IBMError logger = logging.getLogger(__name__) -requires_modules = ["mga"] +requires_modules = ["mga", "federation"] requires_version = "9.0.5.0" @@ -38,11 +38,10 @@ def set(isamAppliance, name, connection, description='', locked=False, check_mod """ if _check_exists(isamAppliance, name=name) is False: # Force the add - we already know connection does not exist - return add(isamAppliance, name, connection, description, locked, check_mode, True) + return add(isamAppliance, name=name, connection=connection, description=description, locked=locked, check_mode=check_mode, force=True) else: # Update request - return update(isamAppliance, connection, description, locked, name, None, - check_mode, force) + return update(isamAppliance, connection=connection, description=description, locked=locked, name=name, check_mode=check_mode, force=force) def add(isamAppliance, name, connection, description='', locked=False, check_mode=False, force=False): @@ -75,32 +74,17 @@ def add(isamAppliance, name, connection, description='', locked=False, check_mod return response; -def deleteByName(isamAppliance, name=None, check_mode=False, force=False): +def delete(isamAppliance, name=None, id=None, check_mode=False, force=False): """ Deleting a CI server connection """ - if force is True or _check_exists(isamAppliance, name=name) is True: - if check_mode is True: - return isamAppliance.create_return_object(changed=True) - else: - ret_obj = _get_id(isamAppliance, name=name) - id = ret_obj['data'] - return isamAppliance.invoke_delete( - "Deleting a CI server connection", - "/mga/server_connections/ci/{0}/v1".format(id), requires_modules=requires_modules, - requires_version=requires_version) - - return isamAppliance.create_return_object() - - -def delete(isamAppliance, id, check_mode=False, force=False): - """ - Deleting a CI server connection - """ - if force is True or _check_exists(isamAppliance, id=id) is True: + if force is True or _check_exists(isamAppliance, name=name, id=id) is True: if check_mode is True: return isamAppliance.create_return_object(changed=True) else: + if id is None: + ret_obj = _get_id(isamAppliance, name=name) + id = ret_obj['data'] try: response = isamAppliance.invoke_delete( "Deleting a CI server connection", @@ -115,31 +99,59 @@ def delete(isamAppliance, id, check_mode=False, force=False): return response; -def update(isamAppliance, id, connection, description='', locked=False, name=None, - new_name=None, check_mode=False, force=False): +def update(isamAppliance, name, connection, description='', locked=False, new_name=None, check_mode=False, force=False, ): """ Modifying a CI server connection - Use new_name to rename the connection, cannot compare password so update will take place everytime + Use new_name to rename the connection. """ - if check_mode is True: - return isamAppliance.create_return_object(changed=True) + ret_obj = get(isamAppliance, name) + warnings = ret_obj["warnings"] + + if ret_obj["data"] == {}: + warnings.append("CI Service connection {0} not found, skipping update.".format(name)) + return isamAppliance.create_return_object(warnings=warnings) else: - json_data = _create_json(name=name, description=description, locked=locked, connection=connection) - if new_name is not None: # Rename condition - json_data['name'] = new_name - try: - response = isamAppliance.invoke_put( - "Modifying a CI server connection", - "/mga/server_connections/ci/{0}/v1".format(id), json_data, requires_modules=requires_modules, - requires_version=requires_version) - except IBMError as e: - if "400" in e[0]: - response = isamAppliance.create_return_object(rc=400, data=e[1]); - else: - raise; - - return response; + id = ret_obj["data"]["uuid"] + + needs_update = False + + json_data = _create_json(name=name, description=description, locked=locked, connection=connection) + if new_name is not None: # Rename condition + json_data['name'] = new_name + + if force is not True: + if 'uuid' in ret_obj['data']: + del ret_obj['data']['uuid'] + if 'clientSecret' in connection: + warnings.append("Since existing clientSecret cannot be read - this parameter will be ignored for idempotency. Add 'force' parameter to update the connection with a new clientSecret.") + connection.pop('clientSecret', None) + + sorted_ret_obj = tools.json_sort(ret_obj['data']) + sorted_json_data = tools.json_sort(json_data) + logger.debug("Sorted Existing Data:{0}".format(sorted_ret_obj)) + logger.debug("Sorted Desired Data:{0}".format(sorted_json_data)) + + if sorted_ret_obj != sorted_json_data: + needs_update = True + + if force is True or needs_update is True: + if check_mode is True: + return isamAppliance.create_return_object(changed=True, warnings=warnings) + else: + try: + response = isamAppliance.invoke_put( + "Modifying a CI server connection", + "/mga/server_connections/ci/{0}/v1".format(id), json_data, requires_modules=requires_modules, + requires_version=requires_version) + except IBMError as e: + if "400" in e[0]: + response = isamAppliance.create_return_object(rc=400, data=e[1]); + else: + raise; + + return response; + return isamAppliance.create_return_object(warnings=warnings) def _create_json(name, description, locked, connection): """ diff --git a/ibmsecurity/isam/aac/server_connections/jdbc.py b/ibmsecurity/isam/aac/server_connections/jdbc.py index 47fc9ee0..3bdc2b14 100644 --- a/ibmsecurity/isam/aac/server_connections/jdbc.py +++ b/ibmsecurity/isam/aac/server_connections/jdbc.py @@ -1,7 +1,9 @@ import logging -import ibmsecurity.utilities.tools +from ibmsecurity.utilities import tools logger = logging.getLogger(__name__) +requires_modules = ["mga", "federation"] +requires_version = "9.0.2.1" # Will change if introduced in an earlier version. def get_all(isamAppliance, check_mode=False, force=False): @@ -26,22 +28,22 @@ def get(isamAppliance, name, check_mode=False, force=False): "/mga/server_connections/jdbc/{0}/v1".format(id)) -def set(isamAppliance, name, connection, type, jndiId, description='', locked=False, connectionManager=None, +def set(isamAppliance, name, connection, jndiId, description='', locked=False, connectionManager=None, check_mode=False, force=False): """ Creating or Modifying an JDBC server connection """ if _check_exists(isamAppliance, name=name) is False: # Force the add - we already know connection does not exist - return add(isamAppliance, name, connection, type, jndiId, description, locked, connectionManager, check_mode, + return add(isamAppliance, name, connection, jndiId, description, locked, connectionManager, check_mode, True) else: # Update request - return update(isamAppliance, name, connection, type, jndiId, description, locked, connectionManager, None, + return update(isamAppliance, name, connection, jndiId, description, locked, connectionManager, None, check_mode, force) -def add(isamAppliance, name, connection, type, jndiId, description='', locked=False, connectionManager=None, +def add(isamAppliance, name, connection, jndiId, description='', locked=False, connectionManager=None, check_mode=False, force=False): """ Creating a JDBC server connection @@ -53,7 +55,7 @@ def add(isamAppliance, name, connection, type, jndiId, description='', locked=Fa return isamAppliance.invoke_post( "Creating a JDBC server connection", "/mga/server_connections/jdbc/v1", - _create_json(name=name, description=description, locked=locked, type=type, connection=connection, + _create_json(name=name, description=description, locked=locked, connection=connection, connectionManager=connectionManager, jndiId=jndiId)) return isamAppliance.create_return_object() @@ -76,34 +78,62 @@ def delete(isamAppliance, name=None, check_mode=False, force=False): return isamAppliance.create_return_object() -def update(isamAppliance, name, connection, type, jndiId, description='', locked=False, connectionManager=None, +def update(isamAppliance, name, connection, jndiId, description='', locked=False, connectionManager=None, new_name=None, check_mode=False, force=False): """ Modifying a JDBC server connection - Use new_name to rename the connection, cannot compare password so update will take place everytime + Use new_name to rename the connection. """ - if check_mode is True: - return isamAppliance.create_return_object(changed=True) + ret_obj = get(isamAppliance, name) + warnings = ret_obj["warnings"] + + if ret_obj["data"] == {}: + warnings.append("JDBC Service connection {0} not found, skipping update.".format(name)) + return isamAppliance.create_return_object(warnings=warnings) else: - json_data = _create_json(name=name, description=description, locked=locked, type=type, - connection=connection, connectionManager=connectionManager, jndiId=jndiId) - ret_obj = search(isamAppliance, name) - id = ret_obj['data'] - if new_name is not None: # Rename condition - json_data['name'] = new_name - return isamAppliance.invoke_put( - "Modifying a JDBC server connection", - "/mga/server_connections/jdbc/{0}/v1".format(id), json_data) + id = ret_obj["data"]["uuid"] + + needs_update = False + + json_data = _create_json(name=name, description=description, locked=locked, connection=connection, connectionManager=connectionManager, jndiId=jndiId) + if new_name is not None: # Rename condition + json_data['name'] = new_name + + if force is not True: + if 'uuid' in ret_obj['data']: + del ret_obj['data']['uuid'] + if 'password' in connection: + warnings.append("Since existing password cannot be read - this parameter will be ignored for idempotency. Add 'force' parameter to update the connection with a new password.") + connection.pop('password', None) + + sorted_ret_obj = tools.json_sort(ret_obj['data']) + sorted_json_data = tools.json_sort(json_data) + logger.debug("Sorted Existing Data:{0}".format(sorted_ret_obj)) + logger.debug("Sorted Desired Data:{0}".format(sorted_json_data)) + + if sorted_ret_obj != sorted_json_data: + needs_update = True + + if force is True or needs_update is True: + if check_mode is True: + return isamAppliance.create_return_object(changed=True, warnings=warnings) + else: + return isamAppliance.invoke_put( + "Modifying a JDBC server connection", + "/mga/server_connections/jdbc/{0}/v1".format(id), json_data, requires_modules=requires_modules, + requires_version=requires_version, warnings=warnings) + + return isamAppliance.create_return_object(warnings=warnings) -def _create_json(name, description, locked, type, jndiId, connection, connectionManager): +def _create_json(name, description, locked, jndiId, connection, connectionManager): """ Create a JSON to be used for the REST API call """ json = { "connection": connection, - "type": type, + "type": "db2", "jndiId": jndiId, "name": name, "description": description, diff --git a/ibmsecurity/isam/aac/server_connections/ldap.py b/ibmsecurity/isam/aac/server_connections/ldap.py index 241e9abc..ffbc7da1 100644 --- a/ibmsecurity/isam/aac/server_connections/ldap.py +++ b/ibmsecurity/isam/aac/server_connections/ldap.py @@ -1,7 +1,9 @@ import logging -import ibmsecurity.utilities.tools +from ibmsecurity.utilities import tools logger = logging.getLogger(__name__) +requires_modules = ["mga", "federation"] +requires_version = "9.0.2.1" # Will change if introduced in an earlier version. def get_all(isamAppliance, check_mode=False, force=False): @@ -80,21 +82,49 @@ def update(isamAppliance, connection, description='', locked=False, connectionMa """ Modifying an LDAP server connection - Use new_name to rename the connection, cannot compare password so update will take place everytime + Use new_name to rename the connection. """ - if check_mode is True: - return isamAppliance.create_return_object(changed=True) + + ret_obj = get(isamAppliance, name) + warnings = ret_obj["warnings"] + + if ret_obj["data"] == {}: + warnings.append("LDAP Service connection {0} not found, skipping update.".format(name)) + return isamAppliance.create_return_object(warnings=warnings) else: - json_data = _create_json(name=name, description=description, locked=locked, servers=servers, - connection=connection, connectionManager=connectionManager) - if new_name is not None: # Rename condition - json_data['name'] = new_name - ret_obj = _get_id(isamAppliance, name=name) - id = ret_obj['data'] - return isamAppliance.invoke_put( - "Modifying an LDAP server connection", - "/mga/server_connections/ldap/{0}/v1".format(id), json_data) + id = ret_obj["data"]["uuid"] + + needs_update = False + + json_data = _create_json(name=name, description=description, locked=locked, servers=servers, connection=connection, connectionManager=connectionManager) + if new_name is not None: # Rename condition + json_data['name'] = new_name + + if force is not True: + if 'uuid' in ret_obj['data']: + del ret_obj['data']['uuid'] + if 'bindPwd' in connection: + warnings.append("Since existing bindPwd cannot be read - this parameter will be ignored for idempotency. Add 'force' parameter to update the connection with a new bindPwd.") + connection.pop('bindPwd', None) + + sorted_ret_obj = tools.json_sort(ret_obj['data']) + sorted_json_data = tools.json_sort(json_data) + logger.debug("Sorted Existing Data:{0}".format(sorted_ret_obj)) + logger.debug("Sorted Desired Data:{0}".format(sorted_json_data)) + + if sorted_ret_obj != sorted_json_data: + needs_update = True + + if force is True or needs_update is True: + if check_mode is True: + return isamAppliance.create_return_object(changed=True, warnings=warnings) + else: + return isamAppliance.invoke_put( + "Modifying an LDAP server connection", + "/mga/server_connections/ldap/{0}/v1".format(id), json_data, requires_modules=requires_modules, + requires_version=requires_version, warnings=warnings) + return isamAppliance.create_return_object(warnings=warnings) def _create_json(name, description, locked, servers, connection, connectionManager): """ diff --git a/ibmsecurity/isam/aac/server_connections/smtp.py b/ibmsecurity/isam/aac/server_connections/smtp.py index 5f06a1d0..422e0131 100644 --- a/ibmsecurity/isam/aac/server_connections/smtp.py +++ b/ibmsecurity/isam/aac/server_connections/smtp.py @@ -1,7 +1,9 @@ import logging -import ibmsecurity.utilities.tools +from ibmsecurity.utilities import tools logger = logging.getLogger(__name__) +requires_modules = ["mga", "federation"] +requires_version = "9.0.2.1" # Will change if introduced in an earlier version. def get_all(isamAppliance, check_mode=False, force=False): @@ -80,20 +82,48 @@ def update(isamAppliance, name, connection, description='', locked=False, connec """ Modifying a SMTP server connection - Use new_name to rename the connection, cannot compare password so update will take place everytime + Use new_name to rename the connection. """ - if check_mode is True: - return isamAppliance.create_return_object(changed=True) + ret_obj = get(isamAppliance, name) + warnings = ret_obj["warnings"] + + if ret_obj["data"] == {}: + warnings.append("SMTP Service connection {0} not found, skipping update.".format(name)) + return isamAppliance.create_return_object(warnings=warnings) else: - json_data = _create_json(name=name, description=description, locked=locked, connection=connection, - connectionManager=connectionManager) - if new_name is not None: # Rename condition - json_data['name'] = new_name - ret_obj = _get_id(isamAppliance, name=name) - id = ret_obj['data'] - return isamAppliance.invoke_put( - "Modifying a SMTP server connection", - "/mga/server_connections/smtp/{0}/v1".format(id), json_data) + id = ret_obj["data"]["uuid"] + + needs_update = False + + json_data = _create_json(name=name, description=description, locked=locked, connection=connection, connectionManager=connectionManager) + if new_name is not None: # Rename condition + json_data['name'] = new_name + + if force is not True: + if 'uuid' in ret_obj['data']: + del ret_obj['data']['uuid'] + if 'password' in connection: + warnings.append("Since existing password cannot be read - this parameter will be ignored for idempotency. Add 'force' parameter to update the connection with a new password.") + connection.pop('password', None) + + sorted_ret_obj = tools.json_sort(ret_obj['data']) + sorted_json_data = tools.json_sort(json_data) + logger.debug("Sorted Existing Data:{0}".format(sorted_ret_obj)) + logger.debug("Sorted Desired Data:{0}".format(sorted_json_data)) + + if sorted_ret_obj != sorted_json_data: + needs_update = True + + if force is True or needs_update is True: + if check_mode is True: + return isamAppliance.create_return_object(changed=True, warnings=warnings) + else: + return isamAppliance.invoke_put( + "Modifying a SMTP server connection", + "/mga/server_connections/smtp/{0}/v1".format(id), json_data, requires_modules=requires_modules, + requires_version=requires_version, warnings=warnings) + + return isamAppliance.create_return_object(warnings=warnings) def _create_json(name, description, locked, connection, connectionManager): diff --git a/ibmsecurity/isam/aac/server_connections/ws.py b/ibmsecurity/isam/aac/server_connections/ws.py index eee678d0..d856edd0 100644 --- a/ibmsecurity/isam/aac/server_connections/ws.py +++ b/ibmsecurity/isam/aac/server_connections/ws.py @@ -117,15 +117,16 @@ def update(isamAppliance, connection, description='', locked=False, name=None, n if 'uuid' in ret_obj['data']: del ret_obj['data']['uuid'] + if 'password' in connection: + warnings.append("Since existing password cannot be read - this parameter will be ignored for idempotency. Add 'force' parameter to update the connection with a new password.") + connection.pop('password', None) + sorted_ret_obj = tools.json_sort(ret_obj['data']) sorted_json_data = tools.json_sort(json_data) logger.debug("Sorted Existing Data:{0}".format(sorted_ret_obj)) logger.debug("Sorted Desired Data:{0}".format(sorted_json_data)) - if sorted_ret_obj != sorted_json_data: - needs_update = True - if 'password' in connection: - warnings.append("Since existing password cannot be read - this call will not be idempotent.") + if sorted_ret_obj != sorted_json_data: needs_update = True if force is True or needs_update is True: From 0da0a6cfdda48d264a1c266ddaedac3420f1c74c Mon Sep 17 00:00:00 2001 From: svetterIO <2234705+svetterIO@users.noreply.github.com> Date: Thu, 15 Aug 2019 10:39:01 +0200 Subject: [PATCH 19/20] BugFix: mappging_rule filename check added REST API does two checks: 1. name already exist 2. filename (under which the file will be stored to the appliance) already exist The checks are indepentent from each other. If one of the checks fails an error will be raised by API call. _check function now reflects this behaviour by checking name and filename parameter for conflicts. --- ibmsecurity/isam/aac/mapping_rules.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/ibmsecurity/isam/aac/mapping_rules.py b/ibmsecurity/isam/aac/mapping_rules.py index 09280578..89eea7f8 100644 --- a/ibmsecurity/isam/aac/mapping_rules.py +++ b/ibmsecurity/isam/aac/mapping_rules.py @@ -78,9 +78,9 @@ def set(isamAppliance, name, category, filename=None, content=None, upload_filen else: filename = _extract_filename(upload_filename) if _check(isamAppliance, name=name) is False: - # Force the add - we already know connection does not exist + # need to check for duplicate filename constraint violations on add return add(isamAppliance, name=name, filename=filename, content=content, category=category, - check_mode=check_mode, force=True) + check_mode=check_mode, force=force) else: # Update request return update(isamAppliance, name=name, content=content, check_mode=check_mode, force=force) @@ -90,7 +90,7 @@ def add(isamAppliance, name, filename=None, content=None, category="OAUTH", chec """ Add a mapping rule """ - if force is True or _check(isamAppliance, name) is False: + if force is True or _check(isamAppliance, name=name, filename=filename) is False: if check_mode is True: return isamAppliance.create_return_object(changed=True) else: @@ -106,7 +106,7 @@ def add(isamAppliance, name, filename=None, content=None, category="OAUTH", chec "category": category }) - return isamAppliance.create_return_object() + return isamAppliance.create_return_object(rc=1, warnings=["mapping rule {} could not be added.".format(name)]) def delete(isamAppliance, name, check_mode=False, force=False): @@ -245,15 +245,18 @@ def import_file(isamAppliance, name, filename, check_mode=False, force=False): return isamAppliance.create_return_object() -def _check(isamAppliance, name): +def _check(isamAppliance, name, filename=None): """ - Check if Mapping Rules already exists + Check if Mapping Rules already exists based on the name and filename """ ret_obj = get_all(isamAppliance) for obj in ret_obj['data']: if obj['name'] == name: return True + if filename is not None and obj['fileName'] == filename: + logger.warning("Found mapping rule [{}] with same filename[{}]. This filename violates duplicate key value unique constraint. Choose another filename for [{}] or delete corresponding mapping rule [{}] to solve this".format(obj['name'],filename,filename,obj['name'])) + return True return False From 967f7dea62d9ef091231276943da6a56e2e355f0 Mon Sep 17 00:00:00 2001 From: svetterIO <2234705+svetterIO@users.noreply.github.com> Date: Thu, 15 Aug 2019 11:01:33 +0200 Subject: [PATCH 20/20] Merge conflict pre-work for server_connections manual re-engineering of server_connections ci, jdbc, smtp and ws from previous commit (https://github.com/IBM-Security/ibmsecurity/commit/1d1bb76c09d494c5d3e1e640a3a93d568fa29c79) to resolve merge conflict easily. --- ibmsecurity/isam/aac/server_connections/ci.py | 75 ++++++------------- .../isam/aac/server_connections/jdbc.py | 14 ++-- .../isam/aac/server_connections/ldap.py | 21 +++--- .../isam/aac/server_connections/smtp.py | 20 +++-- ibmsecurity/isam/aac/server_connections/ws.py | 11 ++- 5 files changed, 54 insertions(+), 87 deletions(-) diff --git a/ibmsecurity/isam/aac/server_connections/ci.py b/ibmsecurity/isam/aac/server_connections/ci.py index 850ef5b5..f1dc1553 100644 --- a/ibmsecurity/isam/aac/server_connections/ci.py +++ b/ibmsecurity/isam/aac/server_connections/ci.py @@ -1,6 +1,5 @@ import logging from ibmsecurity.utilities import tools -from ibmsecurity.appliance.ibmappliance import IBMError logger = logging.getLogger(__name__) @@ -21,7 +20,7 @@ def get(isamAppliance, name, check_mode=False, force=False): """ Retrieving a CI server connection """ - ret_obj = _get_id(isamAppliance, name=name) + ret_obj = search(isamAppliance, name=name) id = ret_obj['data'] if id == {}: @@ -32,16 +31,16 @@ def get(isamAppliance, name, check_mode=False, force=False): requires_modules=requires_modules, requires_version=requires_version) -def set(isamAppliance, name, connection, description='', locked=False, check_mode=False, force=False): +def set(isamAppliance, name, connection, description='', locked=False, new_name=None, check_mode=False, force=False): """ Creating or Modifying a CI server connection """ if _check_exists(isamAppliance, name=name) is False: # Force the add - we already know connection does not exist - return add(isamAppliance, name=name, connection=connection, description=description, locked=locked, check_mode=check_mode, force=True) + return add(isamAppliance=isamAppliance, name=name, connection=connection, description=description, locked=locked, check_mode=check_mode, force=True) else: # Update request - return update(isamAppliance, connection=connection, description=description, locked=locked, name=name, check_mode=check_mode, force=force) + return update(isamAppliance=isamAppliance, name=name, connection=connection, description=description, locked=locked, new_name=new_name, check_mode=check_mode, force=force) def add(isamAppliance, name, connection, description='', locked=False, check_mode=False, force=False): @@ -49,30 +48,17 @@ def add(isamAppliance, name, connection, description='', locked=False, check_mod Creating a CI server connection """ - response = isamAppliance.create_return_object(); - if force is True or _check_exists(isamAppliance, name=name) is False: if check_mode is True: return isamAppliance.create_return_object(changed=True) else: - try: - response = isamAppliance.invoke_post( - "Creating a CI server connection", - "/mga/server_connections/ci/v1", - _create_json(name=name, description=description, locked=locked, connection=connection), - requires_modules=requires_modules, requires_version=requires_version) - - - except IBMError as e: - if "400" in e[0]: - response = isamAppliance.create_return_object(rc=400, data=e[1]); - elif "409" in e[0]: - response = isamAppliance.create_return_object(rc=409, data=e[1]); - else: - raise; - - return response; + return isamAppliance.invoke_post( + "Creating a CI server connection", + "/mga/server_connections/ci/v1", + _create_json(name=name, description=description, locked=locked, connection=connection), + requires_modules=requires_modules, requires_version=requires_version) + return isamAppliance.create_return_object() def delete(isamAppliance, name=None, id=None, check_mode=False, force=False): """ @@ -83,23 +69,18 @@ def delete(isamAppliance, name=None, id=None, check_mode=False, force=False): return isamAppliance.create_return_object(changed=True) else: if id is None: - ret_obj = _get_id(isamAppliance, name=name) + ret_obj = search(isamAppliance, name=name) id = ret_obj['data'] - try: - response = isamAppliance.invoke_delete( - "Deleting a CI server connection", - "/mga/server_connections/ci/{0}/v1".format(id), requires_modules=requires_modules, - requires_version=requires_version) - except IBMError as e: - if "404" in e[0]: - response = isamAppliance.create_return_object(rc=404, data=e[1]); - else: - raise; - return response; + return isamAppliance.invoke_delete( + "Deleting a CI server connection", + "/mga/server_connections/ci/{0}/v1".format(id), requires_modules=requires_modules, + requires_version=requires_version) + + return isamAppliance.create_return_object() -def update(isamAppliance, name, connection, description='', locked=False, new_name=None, check_mode=False, force=False, ): +def update(isamAppliance, name, connection, description='', locked=False, new_name=None, check_mode=False, force=False): """ Modifying a CI server connection Use new_name to rename the connection. @@ -123,7 +104,7 @@ def update(isamAppliance, name, connection, description='', locked=False, new_na if 'uuid' in ret_obj['data']: del ret_obj['data']['uuid'] if 'clientSecret' in connection: - warnings.append("Since existing clientSecret cannot be read - this parameter will be ignored for idempotency. Add 'force' parameter to update the connection with a new clientSecret.") + warnings.append("Since existing clientSecret cannot be read for ci connections - this parameter will be ignored for idempotency. Add 'force' parameter to update the connection with a new clientSecret.") connection.pop('clientSecret', None) sorted_ret_obj = tools.json_sort(ret_obj['data']) @@ -138,18 +119,10 @@ def update(isamAppliance, name, connection, description='', locked=False, new_na if check_mode is True: return isamAppliance.create_return_object(changed=True, warnings=warnings) else: - try: - response = isamAppliance.invoke_put( - "Modifying a CI server connection", - "/mga/server_connections/ci/{0}/v1".format(id), json_data, requires_modules=requires_modules, - requires_version=requires_version) - except IBMError as e: - if "400" in e[0]: - response = isamAppliance.create_return_object(rc=400, data=e[1]); - else: - raise; - - return response; + return isamAppliance.invoke_put( + "Modifying a CI server connection", + "/mga/server_connections/ci/{0}/v1".format(id), json_data, requires_modules=requires_modules, + requires_version=requires_version) return isamAppliance.create_return_object(warnings=warnings) @@ -168,7 +141,7 @@ def _create_json(name, description, locked, connection): return json -def _get_id(isamAppliance, name): +def search(isamAppliance, name): """ Retrieve UUID for named CI connection """ diff --git a/ibmsecurity/isam/aac/server_connections/jdbc.py b/ibmsecurity/isam/aac/server_connections/jdbc.py index 3bdc2b14..ea224569 100644 --- a/ibmsecurity/isam/aac/server_connections/jdbc.py +++ b/ibmsecurity/isam/aac/server_connections/jdbc.py @@ -29,18 +29,16 @@ def get(isamAppliance, name, check_mode=False, force=False): def set(isamAppliance, name, connection, jndiId, description='', locked=False, connectionManager=None, - check_mode=False, force=False): + new_name=None, check_mode=False, force=False): """ Creating or Modifying an JDBC server connection """ if _check_exists(isamAppliance, name=name) is False: # Force the add - we already know connection does not exist - return add(isamAppliance, name, connection, jndiId, description, locked, connectionManager, check_mode, - True) + return add(isamAppliance=isamAppliance, name=name, connection=connection, jndiId=jndiId, description=description, locked=locked, connectionManager=connectionManager, check_mode=check_mode, force=True) else: # Update request - return update(isamAppliance, name, connection, jndiId, description, locked, connectionManager, None, - check_mode, force) + return update(isamAppliance=isamAppliance, name=name, connection=connection, jndiId=jndiId, description=description, locked=locked, connectionManager=connectionManager, new_name=new_name, check_mode=check_mode, force=force) def add(isamAppliance, name, connection, jndiId, description='', locked=False, connectionManager=None, @@ -61,7 +59,7 @@ def add(isamAppliance, name, connection, jndiId, description='', locked=False, c return isamAppliance.create_return_object() -def delete(isamAppliance, name=None, check_mode=False, force=False): +def delete(isamAppliance, name, check_mode=False, force=False): """ Deleting a JDBC server connection """ @@ -69,7 +67,7 @@ def delete(isamAppliance, name=None, check_mode=False, force=False): if check_mode is True: return isamAppliance.create_return_object(changed=True) else: - ret_obj = search(isamAppliance, name) + ret_obj = search(isamAppliance, name=name) id = ret_obj['data'] return isamAppliance.invoke_delete( "Deleting a JDBC server connection", @@ -104,7 +102,7 @@ def update(isamAppliance, name, connection, jndiId, description='', locked=False if 'uuid' in ret_obj['data']: del ret_obj['data']['uuid'] if 'password' in connection: - warnings.append("Since existing password cannot be read - this parameter will be ignored for idempotency. Add 'force' parameter to update the connection with a new password.") + warnings.append("Since existing password cannot be read for jdbc connections - this parameter will be ignored for idempotency. Add 'force' parameter to update the connection with a new password.") connection.pop('password', None) sorted_ret_obj = tools.json_sort(ret_obj['data']) diff --git a/ibmsecurity/isam/aac/server_connections/ldap.py b/ibmsecurity/isam/aac/server_connections/ldap.py index ffbc7da1..0d8fd5a4 100644 --- a/ibmsecurity/isam/aac/server_connections/ldap.py +++ b/ibmsecurity/isam/aac/server_connections/ldap.py @@ -14,11 +14,11 @@ def get_all(isamAppliance, check_mode=False, force=False): "/mga/server_connections/ldap/v1") -def get(isamAppliance, name=None, check_mode=False, force=False): +def get(isamAppliance, name, check_mode=False, force=False): """ Retrieving an LDAP server connection """ - ret_obj = _get_id(isamAppliance, name=name) + ret_obj = search(isamAppliance, name=name) id = ret_obj['data'] if id == {}: @@ -29,17 +29,16 @@ def get(isamAppliance, name=None, check_mode=False, force=False): def set(isamAppliance, name, connection, description='', locked=False, connectionManager=None, servers=None, - check_mode=False, force=False): + new_name=None, check_mode=False, force=False): """ Creating or Modifying an LDAP server connection """ if _check_exists(isamAppliance, name=name) is False: # Force the add - we already know connection does not exist - return add(isamAppliance, name, connection, description, locked, connectionManager, servers, check_mode, True) + return add(isamAppliance=isamAppliance, name=name, connection=connection, description=description, locked=locked, connectionManager=connectionManager, servers=servers, check_mode=check_mode, force=True) else: # Update request - return update(isamAppliance, connection, description, locked, connectionManager, servers, name, None, - check_mode, force) + return update(isamAppliance=isamAppliance, name=name, connection=connection, description=description, locked=locked, connectionManager=connectionManager, servers=servers, new_name=new_name, check_mode=check_mode, force=force) def add(isamAppliance, name, connection, description='', locked=False, connectionManager=None, servers=None, @@ -60,7 +59,7 @@ def add(isamAppliance, name, connection, description='', locked=False, connectio return isamAppliance.create_return_object() -def delete(isamAppliance, name=None, check_mode=False, force=False): +def delete(isamAppliance, name, check_mode=False, force=False): """ Deleting an LDAP server connection """ @@ -68,7 +67,7 @@ def delete(isamAppliance, name=None, check_mode=False, force=False): if check_mode is True: return isamAppliance.create_return_object(changed=True) else: - ret_obj = _get_id(isamAppliance, name=name) + ret_obj = search(isamAppliance, name=name) id = ret_obj['data'] return isamAppliance.invoke_delete( "Deleting an LDAP server connection", @@ -77,7 +76,7 @@ def delete(isamAppliance, name=None, check_mode=False, force=False): return isamAppliance.create_return_object() -def update(isamAppliance, connection, description='', locked=False, connectionManager=None, servers=None, name=None, +def update(isamAppliance, name, connection, description='', locked=False, connectionManager=None, servers=None, new_name=None, check_mode=False, force=False): """ Modifying an LDAP server connection @@ -104,7 +103,7 @@ def update(isamAppliance, connection, description='', locked=False, connectionMa if 'uuid' in ret_obj['data']: del ret_obj['data']['uuid'] if 'bindPwd' in connection: - warnings.append("Since existing bindPwd cannot be read - this parameter will be ignored for idempotency. Add 'force' parameter to update the connection with a new bindPwd.") + warnings.append("Since existing bindPwd cannot be read for ldap connections - this parameter will be ignored for idempotency. Add 'force' parameter to update the connection with a new bindPwd.") connection.pop('bindPwd', None) sorted_ret_obj = tools.json_sort(ret_obj['data']) @@ -146,7 +145,7 @@ def _create_json(name, description, locked, servers, connection, connectionManag return json -def _get_id(isamAppliance, name): +def search(isamAppliance, name): """ Retrieve UUID for named LDAP connection """ diff --git a/ibmsecurity/isam/aac/server_connections/smtp.py b/ibmsecurity/isam/aac/server_connections/smtp.py index 422e0131..1eb7455e 100644 --- a/ibmsecurity/isam/aac/server_connections/smtp.py +++ b/ibmsecurity/isam/aac/server_connections/smtp.py @@ -14,11 +14,11 @@ def get_all(isamAppliance, check_mode=False, force=False): "/mga/server_connections/smtp/v1") -def get(isamAppliance, name=None, check_mode=False, force=False): +def get(isamAppliance, name, check_mode=False, force=False): """ Retrieving a SMTP server connection """ - ret_obj = _get_id(isamAppliance, name=name) + ret_obj = search(isamAppliance, name=name) id = ret_obj['data'] if id == {}: @@ -28,18 +28,16 @@ def get(isamAppliance, name=None, check_mode=False, force=False): "/mga/server_connections/smtp/{0}/v1".format(id)) -def set(isamAppliance, name, connection, description='', locked=False, connectionManager=None, check_mode=False, - force=False): +def set(isamAppliance, name, connection, description='', locked=False, connectionManager=None, new_name=None, check_mode=False, force=False): """ Creating or Modifying an SMTP server connection """ if _check_exists(isamAppliance, name=name) is False: # Force the add - we already know connection does not exist - return add(isamAppliance, name, connection, description, locked, connectionManager, check_mode, True) + return add(isamAppliance=isamAppliance, name=name, connection=connection, description=description, locked=locked, connectionManager=connectionManager, check_mode=check_mode, force=True) else: # Update request - return update(isamAppliance, name, connection, description, locked, connectionManager, None, - check_mode, force) + return update(isamAppliance=isamAppliance, name=name, connection=connection, description=description, locked=locked, connectionManager=connectionManager, new_name=new_name, check_mode=check_mode, force=force) def add(isamAppliance, name, connection, description='', locked=False, connectionManager=None, check_mode=False, @@ -60,7 +58,7 @@ def add(isamAppliance, name, connection, description='', locked=False, connectio return isamAppliance.create_return_object() -def delete(isamAppliance, name=None, check_mode=False, force=False): +def delete(isamAppliance, name, check_mode=False, force=False): """ Deleting a SMTP server connection """ @@ -68,7 +66,7 @@ def delete(isamAppliance, name=None, check_mode=False, force=False): if check_mode is True: return isamAppliance.create_return_object(changed=True) else: - ret_obj = _get_id(isamAppliance, name=name) + ret_obj = search(isamAppliance, name=name) id = ret_obj['data'] return isamAppliance.invoke_delete( "Deleting a SMTP server connection", @@ -103,7 +101,7 @@ def update(isamAppliance, name, connection, description='', locked=False, connec if 'uuid' in ret_obj['data']: del ret_obj['data']['uuid'] if 'password' in connection: - warnings.append("Since existing password cannot be read - this parameter will be ignored for idempotency. Add 'force' parameter to update the connection with a new password.") + warnings.append("Since existing password cannot be read for smtp connections - this parameter will be ignored for idempotency. Add 'force' parameter to update the connection with a new password.") connection.pop('password', None) sorted_ret_obj = tools.json_sort(ret_obj['data']) @@ -144,7 +142,7 @@ def _create_json(name, description, locked, connection, connectionManager): return json -def _get_id(isamAppliance, name): +def search(isamAppliance, name): """ Retrieve UUID for named SMTP connection """ diff --git a/ibmsecurity/isam/aac/server_connections/ws.py b/ibmsecurity/isam/aac/server_connections/ws.py index d856edd0..527e8395 100644 --- a/ibmsecurity/isam/aac/server_connections/ws.py +++ b/ibmsecurity/isam/aac/server_connections/ws.py @@ -69,7 +69,7 @@ def add(isamAppliance, name, connection, description='', locked=False, check_mod return isamAppliance.create_return_object() -def delete(isamAppliance, name=None, check_mode=False, force=False): +def delete(isamAppliance, name, check_mode=False, force=False): """ Deleting a Web Service connection """ @@ -118,7 +118,7 @@ def update(isamAppliance, connection, description='', locked=False, name=None, n del ret_obj['data']['uuid'] if 'password' in connection: - warnings.append("Since existing password cannot be read - this parameter will be ignored for idempotency. Add 'force' parameter to update the connection with a new password.") + warnings.append("Since existing password cannot be read for ws connections - this parameter will be ignored for idempotency. Add 'force' parameter to update the connection with a new password.") connection.pop('password', None) sorted_ret_obj = tools.json_sort(ret_obj['data']) @@ -141,17 +141,16 @@ def update(isamAppliance, connection, description='', locked=False, name=None, n return isamAppliance.create_return_object(warnings=warnings) -def set(isamAppliance, name, connection, description='', locked=False, check_mode=False, force=False): +def set(isamAppliance, name, connection, description='', locked=False, new_name=None, check_mode=False, force=False): """ Creating or Modifying a Web Service connection """ if (search(isamAppliance, name=name))['data'] == {}: # Force the add - we already know connection does not exist - return add(isamAppliance, name, connection, description, locked, check_mode, True) + return add(isamAppliance=isamAppliance, name=name, connection=connection, description=description, locked=locked, check_mode=check_mode, force=True) else: # Update request - return update(isamAppliance, connection, description, locked, name, None, - check_mode, force) + return update(isamAppliance=isamAppliance, name=name, connection=connection, description=description, locked=locked, new_name=new_name, check_mode=check_mode, force=force) def _create_json(name, description, locked, connection):