From 6873636fe65e8c7f32bed327988ab2f2918325f4 Mon Sep 17 00:00:00 2001 From: Tom Bosmans Date: Thu, 22 Jan 2026 14:24:08 +0100 Subject: [PATCH 01/23] build: update .gitignore Signed-off-by: Tom Bosmans --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index c796bfcb..8c16a6cc 100644 --- a/.gitignore +++ b/.gitignore @@ -85,6 +85,7 @@ celerybeat-schedule venv/ ENV/ *.venv/ +.venv*/ # Spyder project settings .spyderproject From 267c524d13ee5b31bd1c58fa8b15c3023f5b3eee Mon Sep 17 00:00:00 2001 From: Tom Bosmans Date: Thu, 22 Jan 2026 14:24:31 +0100 Subject: [PATCH 02/23] build: update python versions test Signed-off-by: Tom Bosmans --- .github/workflows/pylint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml index bdc851f2..ab9f89e0 100644 --- a/.github/workflows/pylint.yml +++ b/.github/workflows/pylint.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.11"] + python-version: ["3.11", "3.13"] steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} From a1178fad2cecd666eebfe3832b22cfd095eac2e1 Mon Sep 17 00:00:00 2001 From: Tom Bosmans Date: Thu, 22 Jan 2026 14:39:24 +0100 Subject: [PATCH 03/23] fix: format to f-strings Signed-off-by: Tom Bosmans --- .../web/reverse_proxy/management_root/all.py | 16 ++++++++-------- .../reverse_proxy/management_root/directory.py | 6 +++--- .../web/reverse_proxy/management_root/file.py | 18 +++++++++--------- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/ibmsecurity/isam/web/reverse_proxy/management_root/all.py b/ibmsecurity/isam/web/reverse_proxy/management_root/all.py index b604a94e..4ab43090 100644 --- a/ibmsecurity/isam/web/reverse_proxy/management_root/all.py +++ b/ibmsecurity/isam/web/reverse_proxy/management_root/all.py @@ -26,7 +26,7 @@ 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), + f"/wga/reverseproxy/{instance_id}/management_root?index=&name=&enc_name=&type=&browser=", filename=filename, no_headers=True) return isamAppliance.create_return_object() @@ -58,18 +58,18 @@ def import_zip(isamAppliance, instance_id, filename, delete_missing=False, check 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)) + logger.info(f"list all missing files in {filename}, which will be deleted on the server: {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)) + logger.debug(f"delete directory on the server: {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)) + logger.debug(f"delete file on the server: {x}.") file.delete(isamAppliance, instance_id, x, check_mode=check_mode) shutil.rmtree(tempdir) @@ -78,7 +78,7 @@ def import_zip(isamAppliance, instance_id, filename, delete_missing=False, check 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), + f"/wga/reverseproxy/{instance_id}/management_root", [ { 'file_formfield': 'file', @@ -101,7 +101,7 @@ def _check_import(isamAppliance, instance_id, filename): """ if not instance._check(isamAppliance, instance_id): - logger.info("instance {} does not exist on this server. Skip import".format(instance_id)) + logger.info(f"instance {instance_id} does not exist on this server. Skip import") return False tempdir = get_random_temp_dir() @@ -113,10 +113,10 @@ def _check_import(isamAppliance, instance_id, filename): shutil.rmtree(tempdir) if identical: - logger.info("management_root files {} are identical with the server content. No update necessary.".format(filename)) + logger.info(f"management_root files {filename} are identical with the server content. No update necessary.") return False else: - logger.info("management_root files {} differ from the server content. Updating management_root files necessary.".format(filename)) + logger.info(f"management_root files {filename} differ from the server content. Updating management_root files necessary.") return True def check(isamAppliance, instance_id, id, name, type, check_mode=False, force=False): diff --git a/ibmsecurity/isam/web/reverse_proxy/management_root/directory.py b/ibmsecurity/isam/web/reverse_proxy/management_root/directory.py index 34ee995d..e57ba171 100644 --- a/ibmsecurity/isam/web/reverse_proxy/management_root/directory.py +++ b/ibmsecurity/isam/web/reverse_proxy/management_root/directory.py @@ -72,7 +72,7 @@ def create(isamAppliance, instance_id, id, name, check_mode=False, force=False): else: return isamAppliance.invoke_post( "Creating a directory in the administration pages root", - "/wga/reverseproxy/{0}/management_root/{1}".format(instance_id, id), + f"/wga/reverseproxy/{instance_id}/management_root/{id}", { 'dir_name': name, 'type': 'dir' @@ -99,7 +99,7 @@ def delete(isamAppliance, instance_id, id, check_mode=False, force=False): else: return isamAppliance.invoke_delete( "Deleting a directory in the administration pages root", - "/wga/reverseproxy/{0}/management_root/{1}".format(instance_id, id)) + f"/wga/reverseproxy/{instance_id}/management_root/{id}") return isamAppliance.create_return_object() @@ -126,7 +126,7 @@ def rename(isamAppliance, instance_id, id, new_name, check_mode=False, force=Fal else: return isamAppliance.invoke_put( "Renaming a directory in the administration pages root", - "/wga/reverseproxy/{0}/management_root/{1}".format(instance_id, id), + f"/wga/reverseproxy/{instance_id}/management_root/{id}", { 'id': dir_id, 'new_name': new_name, diff --git a/ibmsecurity/isam/web/reverse_proxy/management_root/file.py b/ibmsecurity/isam/web/reverse_proxy/management_root/file.py index bd0b0d1b..9aef4207 100644 --- a/ibmsecurity/isam/web/reverse_proxy/management_root/file.py +++ b/ibmsecurity/isam/web/reverse_proxy/management_root/file.py @@ -12,7 +12,7 @@ def get_all(isamAppliance, instance_id, check_mode=False, force=False): Retrieving the current administration pages root contents """ return isamAppliance.invoke_get("Retrieving the current administration pages root contents", - "/wga/reverseproxy/{0}/management_root?recursive=yes".format(instance_id)) + f"/wga/reverseproxy/{instance_id}/management_root?recursive=yes") def get(isamAppliance, instance_id, id, check_mode=False, force=False): @@ -20,7 +20,7 @@ def get(isamAppliance, instance_id, id, check_mode=False, force=False): Retrieving the contents of a file in the administration pages root """ return isamAppliance.invoke_get("Retrieving the contents of a file in the administration pages root", - "/wga/reverseproxy/{0}/management_root/{1}".format(instance_id, id)) + f"/wga/reverseproxy/{instance_id}/management_root/{id}") def _check(isamAppliance, instance_id, id, name): @@ -98,7 +98,7 @@ def create(isamAppliance, instance_id, id, name, contents=None, check_mode=False else: return isamAppliance.invoke_post( "Creating a file in the administration pages root", - "/wga/reverseproxy/{0}/management_root/{1}".format(instance_id, id), + f"/wga/reverseproxy/{instance_id}/management_root/{id}", { 'file_name': name, 'type': 'file', @@ -128,7 +128,7 @@ def update(isamAppliance, instance_id, id, filename=None, contents=None, check_m if filename is not None: return isamAppliance.invoke_put_files( "Update a file in the administration page root", - "/wga/reverseproxy/{0}/management_root/{1}".format(instance_id, id), + f"/wga/reverseproxy/{instance_id}/management_root/{id}", [ { 'file_formfield': 'file', @@ -143,7 +143,7 @@ def update(isamAppliance, instance_id, id, filename=None, contents=None, check_m elif contents is not None: return isamAppliance.invoke_put_files( "Update a file in the administration page root", - "/wga/reverseproxy/{0}/management_root/{1}".format(instance_id, id), + f"/wga/reverseproxy/{instance_id}/management_root/{id}", { 'contents': contents, 'type': 'file' @@ -170,7 +170,7 @@ def delete(isamAppliance, instance_id, id, check_mode=False, force=False): else: return isamAppliance.invoke_delete( "Deleting a file in the administration pages root", - "/wga/reverseproxy/{0}/management_root/{1}".format(instance_id, id)) + f"/wga/reverseproxy/{instance_id}/management_root/{id}") return isamAppliance.create_return_object() @@ -197,7 +197,7 @@ def rename(isamAppliance, instance_id, id, new_name, check_mode=False, force=Fal else: return isamAppliance.invoke_put( "Renaming a file in the administration pages root", - "/wga/reverseproxy/{0}/management_root/{1}".format(instance_id, id), + f"/wga/reverseproxy/{instance_id}/management_root/{id}", { 'id': file_id, 'new_name': new_name, @@ -223,7 +223,7 @@ def export_file(isamAppliance, instance_id, id, filename, check_mode=False, forc if check_mode is False: return isamAppliance.invoke_get_file( "Exporting a file in the administration pages root", - "/wga/reverseproxy/{0}/management_root/{1}?export=true".format(instance_id, id), filename) + f"/wga/reverseproxy/{instance_id}/management_root/{id}?export=true", filename) return isamAppliance.create_return_object() @@ -269,7 +269,7 @@ def import_file(isamAppliance, instance_id, id, filename, check_mode=False, forc else: return isamAppliance.invoke_post_files( "Importing a file in the administration pages root", - "/wga/reverseproxy/{0}/management_root/{1}".format(instance_id, id), + f"/wga/reverseproxy/{instance_id}/management_root/{id}", [ { 'file_formfield': 'file', From 1641ec2c967065e492c41fc400186509e5e44050 Mon Sep 17 00:00:00 2001 From: Tom Bosmans Date: Thu, 22 Jan 2026 14:40:26 +0100 Subject: [PATCH 04/23] fix: format to f-strings Signed-off-by: Tom Bosmans --- .../isam/web/reverse_proxy/common_configurations.py | 6 +++--- ibmsecurity/isam/web/reverse_proxy/common_logs.py | 4 ++-- .../isam/web/reverse_proxy/federation_configuration.py | 10 +++++----- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/ibmsecurity/isam/web/reverse_proxy/common_configurations.py b/ibmsecurity/isam/web/reverse_proxy/common_configurations.py index 7500226c..10b9ae81 100644 --- a/ibmsecurity/isam/web/reverse_proxy/common_configurations.py +++ b/ibmsecurity/isam/web/reverse_proxy/common_configurations.py @@ -12,7 +12,7 @@ def get_all(isamAppliance, reverseproxy_id, check_mode=False, force=False): Retrieving all common configuration for reverse proxy """ return isamAppliance.invoke_get("Retrieving the all common configuration for reverse proxy", - "{0}/{1}".format(uri, reverseproxy_id), + f"{uri}/{reverseproxy_id}", requires_modules=requires_modules, requires_version=requires_version) @@ -23,13 +23,13 @@ def get(isamAppliance, reverseproxy_id, configuration_id, check_mode=False, forc """ ret_object = get_all(isamAppliance, reverseproxy_id, check_mode, force) - logger.debug("Looking for {0} value in reverse proxy {1}".format(configuration_id, reverseproxy_id)) + logger.debug(f"Looking for {configuration_id} value in reverse proxy {reverseproxy_id}") ret_obj = isamAppliance.create_return_object() try: ret_obj['data'] = ret_object['data'][configuration_id] except: - logger.error("Invalid configuration_id: {0}".format(configuration_id)) + logger.error(f"Invalid configuration_id: {configuration_id}") return ret_obj diff --git a/ibmsecurity/isam/web/reverse_proxy/common_logs.py b/ibmsecurity/isam/web/reverse_proxy/common_logs.py index e0817b05..d9016ca7 100644 --- a/ibmsecurity/isam/web/reverse_proxy/common_logs.py +++ b/ibmsecurity/isam/web/reverse_proxy/common_logs.py @@ -48,7 +48,7 @@ def delete(isamAppliance, file_id, check_mode=False, force=False): else: return isamAppliance.invoke_delete( "Clearing a common log file", - "{0}/{1}".format(uri, file_id), requires_model=requires_model) + f"{uri}/{file_id}", requires_model=requires_model) return isamAppliance.create_return_object(warnings=ret_obj['warnings']) @@ -64,7 +64,7 @@ def export_file(isamAppliance, file_id, filename, check_mode=False, force=False) if check_mode is False: # No point downloading a file if in check_mode return isamAppliance.invoke_get_file( "Exporting a common log file", - "{0}/{1}?export".format(uri, file_id), + f"{uri}/{file_id}?export", filename, requires_model=requires_model) return isamAppliance.create_return_object(warnings=ret_obj['warnings']) diff --git a/ibmsecurity/isam/web/reverse_proxy/federation_configuration.py b/ibmsecurity/isam/web/reverse_proxy/federation_configuration.py index 0e671791..82b6513f 100644 --- a/ibmsecurity/isam/web/reverse_proxy/federation_configuration.py +++ b/ibmsecurity/isam/web/reverse_proxy/federation_configuration.py @@ -37,7 +37,7 @@ def config(isamAppliance, instance_id, federation_id=None, federation_name=None, federation_id = ret_obj['data'] if federation_id == {}: - logger.info("Federation {0}, not found. Skipping config.".format(federation_name)) + logger.info(f"Federation {federation_name}, not found. Skipping config.") return isamAppliance.create_return_object() if federation_id is None: @@ -50,7 +50,7 @@ def config(isamAppliance, instance_id, federation_id=None, federation_name=None, else: return isamAppliance.invoke_post( "Federation configuration for a reverse proxy instance", - "/wga/reverseproxy/{0}/fed_config".format(instance_id), + f"/wga/reverseproxy/{instance_id}/fed_config", { "runtime": { "hostname": hostname, @@ -85,7 +85,7 @@ def unconfig(isamAppliance, instance_id, federation_id=None, federation_name=Non federation_id = ret_obj['data'] if federation_id == {}: - logger.info("Federation {0}, not found. Skipping config.".format(federation_name)) + logger.info(f"Federation {federation_name}, not found. Skipping config.") return isamAppliance.create_return_object() if federation_id is None: @@ -98,7 +98,7 @@ def unconfig(isamAppliance, instance_id, federation_id=None, federation_name=Non else: return isamAppliance.invoke_delete( "Federation unconfiguration for a reverse proxy instance", - "/wga/reverseproxy/{0}/fed_config/{1}".format(instance_id, federation_id)) + f"/wga/reverseproxy/{instance_id}/fed_config/{federation_id}") return isamAppliance.create_return_object() @@ -111,7 +111,7 @@ def _check(isamappliance, instance_id, federation_id): # IF there is any exception - i.e. stanza not found return False try: if federation_id in ret_obj['data']: - logger.info("federation_id {0} found in reverse_proxy stanza isam-fed-autocfg.".format(federation_id)) + logger.info(f"federation_id {federation_id} found in reverse_proxy stanza isam-fed-autocfg.") return True except: pass From 39e14f971596e98ed12a9085eddded05bb3431b6 Mon Sep 17 00:00:00 2001 From: Tom Bosmans Date: Thu, 22 Jan 2026 14:41:01 +0100 Subject: [PATCH 05/23] fix: format to f-strings Signed-off-by: Tom Bosmans --- ibmsecurity/isam/web/reverse_proxy/instance.py | 18 +++++++++--------- .../isam/web/reverse_proxy/interfaces.py | 8 ++++---- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/ibmsecurity/isam/web/reverse_proxy/instance.py b/ibmsecurity/isam/web/reverse_proxy/instance.py index c90155d0..814ee8be 100644 --- a/ibmsecurity/isam/web/reverse_proxy/instance.py +++ b/ibmsecurity/isam/web/reverse_proxy/instance.py @@ -25,11 +25,11 @@ def _check(isamAppliance, id): """ ret_obj = get(isamAppliance) - logger.debug("Looking for existing reverse proxies in: {0}".format(ret_obj['data'])) + logger.debug(f"Looking for existing reverse proxies in: {ret_obj['data']}") if ret_obj['data']: for rp in ret_obj['data']: if rp['id'] == id: - logger.debug("Found reverse proxy: {0}".format(id)) + logger.debug(f"Found reverse proxy: {id}") return True return False @@ -81,7 +81,7 @@ def delete(isamAppliance, id, admin_pwd, admin_id='sec_master', domain='Default' return isamAppliance.create_return_object(changed=True) else: return isamAppliance.invoke_put(description="Remove a reverse proxy", - uri="{0}/{1}".format(uri, id), + uri=f"{uri}/{id}", data={ "operation": "unconfigure", "admin_id": admin_id, @@ -102,7 +102,7 @@ def import_config(isamAppliance, id, file, overwrite=True, check_mode=False, for return isamAppliance.create_return_object(changed=True) else: return isamAppliance.invoke_post_files(description="Import or Migrate reverse proxy", - uri="{0}/{1}/migrate".format(uri, id), + uri=f"{uri}/{id}/migrate", fileinfo=[{ 'file_formfield': 'file', 'filename': file, @@ -127,7 +127,7 @@ def export_config(isamAppliance, id, filename, check_mode=False, force=False): else: return isamAppliance.invoke_get_file( description="Export a Reverse Proxy Configuration", - uri="{0}/{1}?action=export".format(uri, id), + uri=f"{uri}/{id}?action=export", filename=filename) return isamAppliance.create_return_object() @@ -153,7 +153,7 @@ def execute(isamAppliance, id, operation="restart", check_mode=False, force=Fals return isamAppliance.create_return_object(changed=True) else: return isamAppliance.invoke_put(description="Execute an operation on reverse proxy", - uri="{0}/{1}".format(uri, id), + uri=f"{uri}/{id}", data={ "operation": operation }, @@ -189,7 +189,7 @@ def execute_multiples(isamAppliance, instances, operation, check_mode=False, for return isamAppliance.create_return_object(changed=True) elif len(new_instances) > 1: return isamAppliance.invoke_put("Stopping, starting, or restarting multiple instances", - "{0}".format(uri), + uri, { "operation": operation, "instances": new_instances @@ -210,7 +210,7 @@ def obfuscating(isamAppliance, id, pwd, check_mode=False, force=False): https://www.ibm.com/support/knowledgecenter/SSPREK_9.0.6/com.ibm.isam.doc/wrp_stza_ref/reference/ref_gso_obfuscation_key.html """ return isamAppliance.invoke_post("Obfuscating a GSO password", - "{0}/{1}?action=obfuscate_gso_pwd".format(uri, id), + f"{uri}/{id}?action=obfuscate_gso_pwd", { "pwd": pwd }, @@ -222,7 +222,7 @@ def renew_cert(isamAppliance, id, isamUser, check_mode=False, force=False): Renew a reverse proxy instance management certificate """ return isamAppliance.invoke_put("Renew a reverse proxy instance management certificate", - "{0}/{1}".format(uri, id), + f"{uri}/{id}", { "admin_id": isamUser.username, "admin_pwd": isamUser.password, diff --git a/ibmsecurity/isam/web/reverse_proxy/interfaces.py b/ibmsecurity/isam/web/reverse_proxy/interfaces.py index 06ab2272..0f1fa7b2 100644 --- a/ibmsecurity/isam/web/reverse_proxy/interfaces.py +++ b/ibmsecurity/isam/web/reverse_proxy/interfaces.py @@ -25,9 +25,9 @@ def get_next_http_port(isamAppliance, ip_address, check_mode=False, force=False) Find the next available HTTP port for an interface """ if ibmsecurity.utilities.tools.version_compare(isamAppliance.facts["version"], "8.0.0.0") < 0: - get_uri = "/isam/wga_templates/httpport/{0}".format(ip_address) + get_uri = f"/isam/wga_templates/httpport/{ip_address}" else: - get_uri = "/isam/wga_templates/httpport?ip_addr={0}".format(ip_address) + get_uri = f"/isam/wga_templates/httpport?ip_addr={ip_address}" return isamAppliance.invoke_get("Find the next available HTTP port for an interface", get_uri,requires_model=requires_model) @@ -37,9 +37,9 @@ def get_next_https_port(isamAppliance, ip_address, check_mode=False, force=False Find the next available HTTPS port for an interface """ if ibmsecurity.utilities.tools.version_compare(isamAppliance.facts["version"], "8.0.0.0") < 0: - get_uri = "/isam/wga_templates/httpsport/{0}".format(ip_address) + get_uri = f"/isam/wga_templates/httpsport/{ip_address}" else: - get_uri = "/isam/wga_templates/httpsport?ip_addr={0}".format(ip_address) + get_uri = f"/isam/wga_templates/httpsport?ip_addr={ip_address}" return isamAppliance.invoke_get("Find the next available HTTPS port for an interface", get_uri,requires_model=requires_model) From 5974bd009dc62ba1b9434fa9d4f3e5326f69a5ce Mon Sep 17 00:00:00 2001 From: Tom Bosmans Date: Thu, 22 Jan 2026 14:43:07 +0100 Subject: [PATCH 06/23] fix: format to f-strings Signed-off-by: Tom Bosmans --- .../web/reverse_proxy/ivg_configuration.py | 2 +- .../isam/web/reverse_proxy/junctions.py | 56 +++++++++---------- .../web/reverse_proxy/junctions_server.py | 11 ++-- 3 files changed, 31 insertions(+), 38 deletions(-) diff --git a/ibmsecurity/isam/web/reverse_proxy/ivg_configuration.py b/ibmsecurity/isam/web/reverse_proxy/ivg_configuration.py index 721462b9..557276d9 100644 --- a/ibmsecurity/isam/web/reverse_proxy/ivg_configuration.py +++ b/ibmsecurity/isam/web/reverse_proxy/ivg_configuration.py @@ -28,7 +28,7 @@ def config(isamAppliance, instance_id, junction="/ivg", mmfa=True, check_mode=Fa else: return isamAppliance.invoke_post( "IVG configuration for a reverse proxy instance", - "/wga/reverseproxy/{0}/verify_gateway_config".format(instance_id), json_data, warnings=warnings, + f"/wga/reverseproxy/{instance_id}/verify_gateway_config", json_data, warnings=warnings, requires_modules=requires_modules, requires_version=requires_version) return isamAppliance.create_return_object(warnings=warnings) diff --git a/ibmsecurity/isam/web/reverse_proxy/junctions.py b/ibmsecurity/isam/web/reverse_proxy/junctions.py index 2c220f4a..48ba891a 100644 --- a/ibmsecurity/isam/web/reverse_proxy/junctions.py +++ b/ibmsecurity/isam/web/reverse_proxy/junctions.py @@ -35,13 +35,13 @@ def get_all(isamAppliance, reverseproxy_id, detailed=None, check_mode=False, for if detailed and tools.version_compare(isamAppliance.facts["version"], "10.0.4") >= 0: try: returnValue = isamAppliance.invoke_get("Retrieving a list of standard and virtual junctions", - "{0}/{1}/junctions?detailed=true".format(uri, reverseproxy_id), + f"{uri}/{reverseproxy_id}/junctions?detailed=true", requires_modules=requires_modules, requires_version=requires_version) except: warnings.append("Detailed retrieval of junctions failed unexpectedly. Falling back to simple version.") returnValue = isamAppliance.invoke_get("Retrieving a list of standard and virtual junctions (fallback)", - "{0}/{1}/junctions".format(uri, reverseproxy_id), + f"{uri}/{reverseproxy_id}/junctions", requires_modules=requires_modules, requires_version=requires_version, warnings=warnings @@ -51,7 +51,7 @@ def get_all(isamAppliance, reverseproxy_id, detailed=None, check_mode=False, for else: #ignore detailed for older versions return isamAppliance.invoke_get("Retrieving a list of standard and virtual junctions", - "{0}/{1}/junctions".format(uri, reverseproxy_id), + f"{uri}/{reverseproxy_id}/junctions", requires_modules=requires_modules, requires_version=requires_version) @@ -70,8 +70,7 @@ def get(isamAppliance, reverseproxy_id, junctionname, check_mode=False, force=Fa """ logger = isamAppliance.logger ret_obj = isamAppliance.invoke_get("Retrieving the parameters for a single standard or virtual junction", - "{0}/{1}/junctions?junctions_id={2}".format(uri, reverseproxy_id, - junctionname), + "{0}/{1}/junctions?junctions_id={2}".format(uri, reverseproxy_id, junctionname), requires_modules=requires_modules, requires_version=requires_version, warnings=warnings) @@ -82,10 +81,10 @@ def get(isamAppliance, reverseproxy_id, junctionname, check_mode=False, force=Fa else: srv_separator = '&' srvs = ret_obj['data']['servers'].split(srv_separator) - logger.debug("Servers in raw string: {0}".format(ret_obj['data']['servers'])) - logger.debug("Number of servers in junction: {0}".format(len(srvs))) + logger.debug(f"Servers in raw string: {ret_obj['data']['servers']}") + logger.debug(f"Number of servers in junction: {len(srvs)}") for srv in srvs: - logger.debug("Parsing Server: {0}".format(srv)) + logger.debug(f"Parsing Server: {srv}") server = {} for s in srv.split(';'): if s != '': @@ -309,29 +308,25 @@ def add(isamAppliance, reverseproxy_id, junction_point, server_hostname, server_ if http2_proxy is not None: if tools.version_compare(isamAppliance.facts["version"], "9.0.4.0") < 0: warnings.append( - "Appliance at version: {0}, http2_proxy: {1} is not supported. Needs 9.0.4.0 or higher. Ignoring http2_proxy for this call.".format( - isamAppliance.facts["version"], http2_proxy)) + "Appliance at version: {0}, http2_proxy: {1} is not supported. Needs 9.0.4.0 or higher. Ignoring http2_proxy for this call.".format(isamAppliance.facts["version"], http2_proxy)) else: jct_json['http2_proxy'] = http2_proxy if sni_name is not None: if tools.version_compare(isamAppliance.facts["version"], "9.0.4.0") < 0: warnings.append( - "Appliance at version: {0}, sni_name: {1} is not supported. Needs 9.0.4.0 or higher. Ignoring sni_name for this call.".format( - isamAppliance.facts["version"], sni_name)) + "Appliance at version: {0}, sni_name: {1} is not supported. Needs 9.0.4.0 or higher. Ignoring sni_name for this call.".format(isamAppliance.facts["version"], sni_name)) else: jct_json['sni_name'] = sni_name if description is not None: if tools.version_compare(isamAppliance.facts["version"], "9.0.7.0") < 0: warnings.append( - "Appliance at version: {0}, description: {1} is not supported. Needs 9.0.7.0 or higher. Ignoring description for this call.".format( - isamAppliance.facts["version"], description)) + "Appliance at version: {0}, description: {1} is not supported. Needs 9.0.7.0 or higher. Ignoring description for this call.".format(isamAppliance.facts["version"], description)) else: jct_json['description'] = description if priority is not None: if tools.version_compare(isamAppliance.facts["version"], "10.0.2.0") < 0: warnings.append( - "Appliance at version: {0}, priority: {1} is not supported. Needs 10.0.2.0 or higher. Ignoring priority for this call.".format( - isamAppliance.facts["version"], priority)) + "Appliance at version: {0}, priority: {1} is not supported. Needs 10.0.2.0 or higher. Ignoring priority for this call.".format(isamAppliance.facts["version"], priority)) else: jct_json['priority'] = priority else: @@ -345,15 +340,14 @@ def add(isamAppliance, reverseproxy_id, junction_point, server_hostname, server_ if server_cn is not None: if tools.version_compare(isamAppliance.facts["version"], "10.0.2.0") < 0: warnings.append( - "Appliance at version: {0}, server_cn: {1} is not supported. Needs 10.0.2.0 or higher. Ignoring server_cn for this call.".format( - isamAppliance.facts["version"], server_cn)) + "Appliance at version: {0}, server_cn: {1} is not supported. Needs 10.0.2.0 or higher. Ignoring server_cn for this call.".format(isamAppliance.facts["version"], server_cn)) else: jct_json['server_cn'] = server_cn if isVirtualJunction and silent: jct_json['silent'] = silent return isamAppliance.invoke_post( "Creating a standard or virtual junction", - "{0}/{1}/junctions".format(uri, reverseproxy_id), jct_json, + f"{uri}/{reverseproxy_id}/junctions", jct_json, requires_modules=requires_modules, requires_version=requires_version, warnings=warnings) @@ -377,7 +371,7 @@ def delete(isamAppliance, reverseproxy_id, junctionname, check_mode=False, force else: return isamAppliance.invoke_delete( "Deleting a standard or virtual junction", - "{0}/{1}/junctions?junctions_id={2}".format(uri, reverseproxy_id, junctionname), + f"{uri}/{reverseproxy_id}/junctions?junctions_id={junctionname}", requires_modules=requires_modules, requires_version=requires_version) @@ -405,7 +399,7 @@ def set(isamAppliance, reverseproxy_id, junction_point, server_hostname, server_ isVirtualJunction = True if junction_point[:1] == '/': isVirtualJunction = False - logger.debug("Junction: {0} is a standard junction".format(junction_point)) + logger.debug(f"Junction: {junction_point} is a standard junction") if force is False: # Check if record exists logger.debug("Check if the junction exists.") @@ -468,7 +462,7 @@ def set(isamAppliance, reverseproxy_id, junction_point, server_hostname, server_ else: jct_json['client_ip_http'] = client_ip_http if not isVirtualJunction: - logger.debug("Only for standard junctions - {0}.".format(virtual_hostname)) + logger.debug(f"Only for standard junctions - {virtual_hostname}.") if cookie_include_path is None: jct_json['cookie_include_path'] = 'no' else: @@ -535,7 +529,7 @@ def set(isamAppliance, reverseproxy_id, junction_point, server_hostname, server_ else: jct_json['transparent_path_junction'] = transparent_path_junction if isVirtualJunction: - logger.debug("Only for virtual junctions - virtual hostname {0}.".format(virtual_hostname)) + logger.debug(f"Only for virtual junctions - virtual hostname {virtual_hostname}.") if virtual_hostname: jct_json['virtual_junction_hostname'] = virtual_hostname @@ -761,10 +755,10 @@ def set_all(isamAppliance, reverseproxy_id: str, junctions: list=[], check_mode= if c.get('servers', None) is not None: if type(c.get('servers', [])) is str: srvs = c['servers'].split(srv_separator) - logger.debug("Servers in raw string: {0}".format(c['servers'])) - logger.debug("Number of servers in junction: {0}".format(len(srvs))) + logger.debug(f"Servers in raw string: {c['servers']}") + logger.debug(f"Number of servers in junction: {len(srvs)}") for srv in srvs: - logger.debug("Parsing Server: {0}".format(srv)) + logger.debug(f"Parsing Server: {srv}") server = {} for s in srv.split(';'): if s != '': @@ -786,10 +780,10 @@ def set_all(isamAppliance, reverseproxy_id: str, junctions: list=[], check_mode= if _checkUpdate.get('servers', None) is not None: if type(_checkUpdate.get('servers', [])) is str: srvs = _checkUpdate['servers'].split(srv_separator) - logger.debug("Servers in raw string: {0}".format(_checkUpdate['servers'])) - logger.debug("Number of servers in junction: {0}".format(len(srvs))) + logger.debug(f"Servers in raw string: {_checkUpdate['servers']}") + logger.debug(f"Number of servers in junction: {len(srvs)}") for srv in srvs: - logger.debug("Parsing Server: {0}".format(srv)) + logger.debug(f"Parsing Server: {srv}") server = {} for s in srv.split(';'): if s != '': @@ -866,7 +860,7 @@ def junction_server_exists(isamAppliance, srvs, server_hostname: str, server_por server_found = False for srv in srvs: if srv['server_hostname'] == server_hostname and str(srv['server_port']) == str(server_port): - logger.debug("Matched a server - {0}.".format(srv)) + logger.debug(f"Matched a server - {srv}.") server_found = True server_json = { 'server_hostname': server_hostname, @@ -911,7 +905,7 @@ def junction_server_exists(isamAppliance, srvs, server_hostname: str, server_por srv.pop('server_uuid', None) if not isVirtualJunction: if virtual_hostname is not None: - logger.debug("Only for standard junctions - {0}.".format(virtual_hostname)) + logger.debug(f"Only for standard junctions - {virtual_hostname}.") server_json['virtual_junction_hostname'] = virtual_hostname else: if server_json['server_port'] in ['80', 80]: diff --git a/ibmsecurity/isam/web/reverse_proxy/junctions_server.py b/ibmsecurity/isam/web/reverse_proxy/junctions_server.py index 673132cc..90555519 100644 --- a/ibmsecurity/isam/web/reverse_proxy/junctions_server.py +++ b/ibmsecurity/isam/web/reverse_proxy/junctions_server.py @@ -15,7 +15,7 @@ def search(isamAppliance, reverseproxy_id, junction_point, server_hostname, serv ret_obj_new = isamAppliance.create_return_object() ret_obj = ibmsecurity.isam.web.reverse_proxy.junctions.get(isamAppliance, reverseproxy_id, junction_point) for s in ret_obj['data']['servers']: - logger.debug("Servers in Junction server: {0} port: {1}".format(s['server_hostname'], s['server_port'])) + logger.debug(f"Servers in Junction server: {s['server_hostname']} port: {s['server_port']}") if str(server_hostname) == str(s['server_hostname']) and str(server_port) == str(s['server_port']): ret_obj_new['data'] = s['server_uuid'] break @@ -26,7 +26,7 @@ def get(isamAppliance, reverseproxy_id, junction_point, server_hostname, server_ ret_obj_new = isamAppliance.create_return_object() ret_obj = ibmsecurity.isam.web.reverse_proxy.junctions.get(isamAppliance, reverseproxy_id, junction_point) for s in ret_obj['data']['servers']: - logger.debug("Servers in Junction server: {0} port: {1}".format(s['server_hostname'], s['server_port'])) + logger.debug(f"Servers in Junction server: {s['server_hostname']} port: {s['server_port']}") if str(server_hostname) == str(s['server_hostname']) and str(server_port) == str(s['server_port']): ret_obj_new['data'] = s break @@ -121,7 +121,7 @@ def add(isamAppliance, reverseproxy_id, junction_point, server_hostname, server_ jct_srv_json['priority'] = "9" return isamAppliance.invoke_put( "Adding a back-end server to an existing standard or virtual junctions", - "{0}/{1}/junctions".format(uri, reverseproxy_id), jct_srv_json) + f"{uri}/{reverseproxy_id}/junctions", jct_srv_json) return isamAppliance.create_return_object() @@ -222,7 +222,7 @@ def set(isamAppliance, reverseproxy_id, junction_point, server_hostname, server_ else: return isamAppliance.invoke_put( "Adding a back-end server to an existing standard or virtual junctions", - "{0}/{1}/junctions".format(uri, reverseproxy_id), data=jct_srv_json, warnings=warnings) + f"{uri}/{reverseproxy_id}/junctions", data=jct_srv_json, warnings=warnings) else: logger.debug("Servers are the same") return isamAppliance.create_return_object(warnings=warnings) @@ -249,7 +249,6 @@ def delete(isamAppliance, reverseproxy_id, junction_point, server_hostname, serv else: return isamAppliance.invoke_delete( "Deleting a standard or virtual junction's server", - "{0}/{1}/junctions?junctions_id={2}&servers_id={3}".format(uri, reverseproxy_id, junction_point, - ret_obj['data'])) + "{0}/{1}/junctions?junctions_id={2}&servers_id={3}".format(uri, reverseproxy_id, junction_point, ret_obj['data'])) return isamAppliance.create_return_object() From c9d90d764e69c7865f89691339cc2d823e8900f6 Mon Sep 17 00:00:00 2001 From: Tom Bosmans Date: Thu, 22 Jan 2026 14:43:57 +0100 Subject: [PATCH 07/23] fix: format to f-strings (2nd run) Signed-off-by: Tom Bosmans --- ibmsecurity/isam/web/reverse_proxy/junctions.py | 12 ++++++------ .../isam/web/reverse_proxy/junctions_server.py | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ibmsecurity/isam/web/reverse_proxy/junctions.py b/ibmsecurity/isam/web/reverse_proxy/junctions.py index 48ba891a..bf73841c 100644 --- a/ibmsecurity/isam/web/reverse_proxy/junctions.py +++ b/ibmsecurity/isam/web/reverse_proxy/junctions.py @@ -70,7 +70,7 @@ def get(isamAppliance, reverseproxy_id, junctionname, check_mode=False, force=Fa """ logger = isamAppliance.logger ret_obj = isamAppliance.invoke_get("Retrieving the parameters for a single standard or virtual junction", - "{0}/{1}/junctions?junctions_id={2}".format(uri, reverseproxy_id, junctionname), + f"{uri}/{reverseproxy_id}/junctions?junctions_id={junctionname}", requires_modules=requires_modules, requires_version=requires_version, warnings=warnings) @@ -308,25 +308,25 @@ def add(isamAppliance, reverseproxy_id, junction_point, server_hostname, server_ if http2_proxy is not None: if tools.version_compare(isamAppliance.facts["version"], "9.0.4.0") < 0: warnings.append( - "Appliance at version: {0}, http2_proxy: {1} is not supported. Needs 9.0.4.0 or higher. Ignoring http2_proxy for this call.".format(isamAppliance.facts["version"], http2_proxy)) + f"Appliance at version: {isamAppliance.facts['version']}, http2_proxy: {http2_proxy} is not supported. Needs 9.0.4.0 or higher. Ignoring http2_proxy for this call.") else: jct_json['http2_proxy'] = http2_proxy if sni_name is not None: if tools.version_compare(isamAppliance.facts["version"], "9.0.4.0") < 0: warnings.append( - "Appliance at version: {0}, sni_name: {1} is not supported. Needs 9.0.4.0 or higher. Ignoring sni_name for this call.".format(isamAppliance.facts["version"], sni_name)) + f"Appliance at version: {isamAppliance.facts['version']}, sni_name: {sni_name} is not supported. Needs 9.0.4.0 or higher. Ignoring sni_name for this call.") else: jct_json['sni_name'] = sni_name if description is not None: if tools.version_compare(isamAppliance.facts["version"], "9.0.7.0") < 0: warnings.append( - "Appliance at version: {0}, description: {1} is not supported. Needs 9.0.7.0 or higher. Ignoring description for this call.".format(isamAppliance.facts["version"], description)) + f"Appliance at version: {isamAppliance.facts['version']}, description: {description} is not supported. Needs 9.0.7.0 or higher. Ignoring description for this call.") else: jct_json['description'] = description if priority is not None: if tools.version_compare(isamAppliance.facts["version"], "10.0.2.0") < 0: warnings.append( - "Appliance at version: {0}, priority: {1} is not supported. Needs 10.0.2.0 or higher. Ignoring priority for this call.".format(isamAppliance.facts["version"], priority)) + f"Appliance at version: {isamAppliance.facts['version']}, priority: {priority} is not supported. Needs 10.0.2.0 or higher. Ignoring priority for this call.") else: jct_json['priority'] = priority else: @@ -340,7 +340,7 @@ def add(isamAppliance, reverseproxy_id, junction_point, server_hostname, server_ if server_cn is not None: if tools.version_compare(isamAppliance.facts["version"], "10.0.2.0") < 0: warnings.append( - "Appliance at version: {0}, server_cn: {1} is not supported. Needs 10.0.2.0 or higher. Ignoring server_cn for this call.".format(isamAppliance.facts["version"], server_cn)) + f"Appliance at version: {isamAppliance.facts['version']}, server_cn: {server_cn} is not supported. Needs 10.0.2.0 or higher. Ignoring server_cn for this call.") else: jct_json['server_cn'] = server_cn if isVirtualJunction and silent: diff --git a/ibmsecurity/isam/web/reverse_proxy/junctions_server.py b/ibmsecurity/isam/web/reverse_proxy/junctions_server.py index 90555519..c3802b21 100644 --- a/ibmsecurity/isam/web/reverse_proxy/junctions_server.py +++ b/ibmsecurity/isam/web/reverse_proxy/junctions_server.py @@ -249,6 +249,6 @@ def delete(isamAppliance, reverseproxy_id, junction_point, server_hostname, serv else: return isamAppliance.invoke_delete( "Deleting a standard or virtual junction's server", - "{0}/{1}/junctions?junctions_id={2}&servers_id={3}".format(uri, reverseproxy_id, junction_point, ret_obj['data'])) + f"{uri}/{reverseproxy_id}/junctions?junctions_id={junction_point}&servers_id={ret_obj['data']}") return isamAppliance.create_return_object() From 2f14ff01a57f047348b823e75e6be176cfdf08f1 Mon Sep 17 00:00:00 2001 From: Tom Bosmans Date: Thu, 22 Jan 2026 14:46:31 +0100 Subject: [PATCH 08/23] fix: format to f-strings Signed-off-by: Tom Bosmans --- ibmsecurity/isam/web/reverse_proxy/trace.py | 39 +++++++------------ .../web/reverse_proxy/transaction_logging.py | 21 +++++----- 2 files changed, 23 insertions(+), 37 deletions(-) diff --git a/ibmsecurity/isam/web/reverse_proxy/trace.py b/ibmsecurity/isam/web/reverse_proxy/trace.py index 3ac87ed8..9ae8449a 100644 --- a/ibmsecurity/isam/web/reverse_proxy/trace.py +++ b/ibmsecurity/isam/web/reverse_proxy/trace.py @@ -14,7 +14,7 @@ def get_all(isamAppliance, instance_id, check_mode=False, force=False): """ try: return isamAppliance.invoke_get("Retrieving all trace components - Reverse Proxy", - "{0}/{1}/tracing".format(uri, instance_id), requires_model=requires_model) + f"{uri}/{instance_id}/tracing", requires_model=requires_model) except: # Return empty array - exception thrown if list has no entries or does not exist ret_obj = isamAppliance.create_return_object() @@ -27,9 +27,8 @@ def get_all_logs(isamAppliance, instance_id, component_id, check_mode=False, for Retrieving all trace log files for a component - Reverse Proxy """ return isamAppliance.invoke_get("Retrieving all trace log files for a component - Reverse Proxy", - "{0}/{1}/tracing/{2}/trace_files".format(uri, - instance_id, - component_id),requires_model=requires_model) + "{0}/{1}/tracing/{2}/trace_files".format(uri, instance_id, component_id), + requires_model=requires_model) def get(isamAppliance, instance_id, component_id, file_id, options=None, size=None, start=None, check_mode=False, @@ -38,13 +37,8 @@ def get(isamAppliance, instance_id, component_id, file_id, options=None, size=No Retrieving snippet of a trace log file for a component - Reverse Proxy """ return isamAppliance.invoke_get("Retrieving snippet of a trace log file - Reverse Proxy", - "{0}/{1}/tracing/{2}/trace_files/{3}".format(uri, - instance_id, - component_id, - file_id, - tools.create_query_string( - options=options, start=start, - size=size)),requires_model=requires_model) + "{0}/{1}/tracing/{2}/trace_files/{3}".format(uri,instance_id,component_id,file_id,tools.create_query_string(options=options, start=start, size=size)), + requires_model=requires_model) def export_file(isamAppliance, instance_id, component_id, file_id, filename, check_mode=False, force=False): @@ -56,10 +50,8 @@ def export_file(isamAppliance, instance_id, component_id, file_id, filename, che if force is True or (os.path.exists(filename) is False): if check_mode is False: # No point downloading a file if in check_mode return isamAppliance.invoke_get_file("Exporting a Reverse Proxy trace log file.", - "{0}/{1}/tracing/{2}/trace_files/{3}?export".format(uri, - instance_id, - component_id, - file_id), filename,requires_model=requires_model) + "{0}/{1}/tracing/{2}/trace_files/{3}?export".format(uri,instance_id,component_id,file_id), filename, + requires_model=requires_model) return isamAppliance.create_return_object() @@ -76,9 +68,7 @@ def set(isamAppliance, instance_id, component_id, level, flush_interval, else: return isamAppliance.invoke_put( "Modify trace settings for a component", - "{0}/{1}/tracing/{2}".format(uri, - instance_id, - component_id), + f"{uri}/{instance_id}/tracing/{component_id}", { 'level': level, 'flush_interval': flush_interval, @@ -107,10 +97,8 @@ def delete(isamAppliance, instance_id, component_id, file_id, check_mode=False, else: return isamAppliance.invoke_delete( "Deleting a trace log file", - "{0}/{1}/tracing/{2}/trace_files/{3}".format(uri, - instance_id, - component_id, - file_id),requires_model=requires_model) + "{0}/{1}/tracing/{2}/trace_files/{3}".format(uri,instance_id,component_id,file_id), + requires_model=requires_model) return isamAppliance.create_return_object(warnings=warnings) @@ -134,9 +122,8 @@ def delete_all(isamAppliance, instance_id, component_id, check_mode=False, force else: return isamAppliance.invoke_delete( "Deleting all trace log files", - "{0}/{1}/tracing/{2}/trace_files".format(uri, - instance_id, - component_id),requires_model=requires_model) + "{0}/{1}/tracing/{2}/trace_files".format(uri,instance_id,component_id), + requires_model=requires_model) return isamAppliance.create_return_object(warnings=warnings) @@ -167,7 +154,7 @@ def delete_multiple_files(isamAppliance, instance_id, component_id, files, check else: return isamAppliance.invoke_put( "Deleting multiple trace files for a component", - "{0}/{1}/tracing/{2}/trace_files/?action=delete".format(uri, instance_id, component_id), + f"{uri}/{instance_id}/tracing/{component_id}/trace_files/?action=delete", { 'files': files_to_delete } diff --git a/ibmsecurity/isam/web/reverse_proxy/transaction_logging.py b/ibmsecurity/isam/web/reverse_proxy/transaction_logging.py index faa92b5d..739703c0 100644 --- a/ibmsecurity/isam/web/reverse_proxy/transaction_logging.py +++ b/ibmsecurity/isam/web/reverse_proxy/transaction_logging.py @@ -15,7 +15,7 @@ def get(isamAppliance, instance_id, check_mode=False, force=False): """ return isamAppliance.invoke_get("Retrieving all transaction logging components and their details", - "{0}/{1}/transaction_logging".format(uri, instance_id), + f"{uri}/{instance_id}/transaction_logging", requires_modules=requires_modules, requires_version=requires_version,requires_model=requires_model) @@ -25,8 +25,7 @@ def get_files(isamAppliance, instance_id, component_id, check_mode=False, force= """ return isamAppliance.invoke_get("Retrieving all transaction log files for a component", - "{0}/{1}/transaction_logging/{2}/translog_files".format(uri, instance_id, - component_id), + "{0}/{1}/transaction_logging/{2}/translog_files".format(uri, instance_id, component_id), requires_modules=requires_modules, requires_version=requires_version,requires_model=requires_model) @@ -36,8 +35,8 @@ def export_file(isamAppliance, instance_id, component_id, file_id, filepath, che """ if os.path.exists(filepath) is True: - logger.info("File '{0}' already exists. Skipping export.".format(filepath)) - warnings = ["File '{0}' already exists. Skipping export.".format(filepath)] + logger.info(f"File '{filepath}' already exists. Skipping export.") + warnings = [f"File '{filepath}' already exists. Skipping export."] return isamAppliance.create_return_object(warnings=warnings) warnings = _check(isamAppliance,instance_id,component_id) @@ -47,7 +46,7 @@ def export_file(isamAppliance, instance_id, component_id, file_id, filepath, che else: return isamAppliance.invoke_get_file( "Exporting the transaction logging data file or rollover transaction logging data file for a component", - "{0}/{1}/transaction_logging/{2}/translog_files/{3}?export".format(uri, instance_id, component_id, file_id), + f"{uri}/{instance_id}/transaction_logging/{component_id}/translog_files/{file_id}?export", filepath ,requires_model=requires_model) @@ -72,7 +71,7 @@ def update(isamAppliance, instance_id, component_id, status, rollover_size, max_ return isamAppliance.invoke_put( "Modifying the status and rollover size for a component", - "{0}/{1}/transaction_logging/{2}".format(uri, instance_id, component_id), + f"{uri}/{instance_id}/transaction_logging/{component_id}", { 'id': component_id, 'status': status, @@ -99,7 +98,7 @@ def rollover(isamAppliance, instance_id, component_id, check_mode=False, force=F else: return isamAppliance.invoke_put( "Rolling over the transaction logging data file for a component", - "{0}/{1}/transaction_logging/{2}".format(uri, instance_id, component_id), + f"{uri}/{instance_id}/transaction_logging/{component_id}", { 'rollover': "yes" }, @@ -122,7 +121,7 @@ def delete_unused(isamAppliance, instance_id, component_id, check_mode=False, fo else: return isamAppliance.invoke_delete( "Deleting the unused transaction logging data file and rollover files for a component", - "{0}/{1}/transaction_logging/{2}/translog_files".format(uri, instance_id, component_id),requires_model=requires_model) + f"{uri}/{instance_id}/transaction_logging/{component_id}/translog_files",requires_model=requires_model) return isamAppliance.create_return_object() @@ -139,7 +138,7 @@ def delete(isamAppliance, instance_id, component_id, file_id, check_mode=False, else: return isamAppliance.invoke_delete( "Deleting the transaction logging data file or rollover file for a component", - "{0}/{1}/transaction_logging/{2}/translog_files/{3}".format(uri, instance_id, component_id, file_id),requires_model=requires_model) + f"{uri}/{instance_id}/transaction_logging/{component_id}/translog_files/{file_id}",requires_model=requires_model) return isamAppliance.create_return_object(warnings=warnings) @@ -171,7 +170,7 @@ def delete_multiple_files(isamAppliance, instance_id, component_id, files, check else: return isamAppliance.invoke_put( "Deleting multiple transaction logging data file and rollover files for a component", - "{0}/{1}/transaction_logging/{2}/translog_files?action=delete".format(uri, instance_id, component_id), + f"{uri}/{instance_id}/transaction_logging/{component_id}/translog_files?action=delete", { 'files': files },requires_model=requires_model From f165bd431fed31e69d1a4ba8f0f76d995eace91f Mon Sep 17 00:00:00 2001 From: Tom Bosmans Date: Thu, 22 Jan 2026 14:49:02 +0100 Subject: [PATCH 09/23] fix: format to f-strings Signed-off-by: Tom Bosmans --- .../web/reverse_proxy/aac_configuration.py | 6 +-- ibmsecurity/isam/web/reverse_proxy/logs.py | 10 ++--- .../web/reverse_proxy/mmfa_configuration.py | 7 ++-- .../isam/web/reverse_proxy/statistics.py | 38 ++++++------------- 4 files changed, 22 insertions(+), 39 deletions(-) diff --git a/ibmsecurity/isam/web/reverse_proxy/aac_configuration.py b/ibmsecurity/isam/web/reverse_proxy/aac_configuration.py index a81062ef..a81ad04e 100644 --- a/ibmsecurity/isam/web/reverse_proxy/aac_configuration.py +++ b/ibmsecurity/isam/web/reverse_proxy/aac_configuration.py @@ -66,7 +66,7 @@ def config( else: return isamAppliance.invoke_post( " Authentication and Context based access configuration for a reverse proxy instance", - "/wga/reverseproxy/{}/authsvc_config".format(instance_id), + f"/wga/reverseproxy/{instance_id}/authsvc_config", json_data, warnings=warnings, requires_modules=requires_modules, @@ -90,9 +90,7 @@ def _check_config(isamAppliance, instance_id, junction): for j in ret_obj["data"]: if j["id"] == junction: logger.info( - "Junction {} was found - hence aac config must have already executed.".format( - junction - ) + f"Junction {junction} was found - hence aac config must have already executed." ) return True diff --git a/ibmsecurity/isam/web/reverse_proxy/logs.py b/ibmsecurity/isam/web/reverse_proxy/logs.py index 9b3fc2e9..ce35fa8e 100644 --- a/ibmsecurity/isam/web/reverse_proxy/logs.py +++ b/ibmsecurity/isam/web/reverse_proxy/logs.py @@ -13,7 +13,7 @@ def get_all(isamAppliance, instance_id, check_mode=False, force=False): Retrieving the names of all common log files and file sizes """ return isamAppliance.invoke_get("Retrieving the names of all common log files and file sizes", - "{0}/{1}".format(uri, instance_id), requires_model=requires_model) + f"{uri}/{instance_id}", requires_model=requires_model) def get(isamAppliance, instance_id, file_id, options=None, size=None, start=None, check_mode=False, force=False): @@ -21,8 +21,8 @@ def get(isamAppliance, instance_id, file_id, options=None, size=None, start=None Retrieving a snippet of a common log file """ return isamAppliance.invoke_get("Retrieving a snippet of a common log file", - "{0}/{1}/{2}{3}".format(uri, instance_id, file_id, tools.create_query_string( - options=options, start=start, size=size)), requires_model=requires_model) + "{0}/{1}/{2}{3}".format(uri, instance_id, file_id, tools.create_query_string(options=options, start=start, size=size)), + requires_model=requires_model) def delete(isamAppliance, instance_id, file_id, check_mode=False, force=False): @@ -48,7 +48,7 @@ def delete(isamAppliance, instance_id, file_id, check_mode=False, force=False): else: return isamAppliance.invoke_delete( "Clearing a common log file", - "{0}/{1}/{2}".format(uri, instance_id, file_id), requires_model=requires_model) + f"{uri}/{instance_id}/{file_id}", requires_model=requires_model) return isamAppliance.create_return_object(warnings=ret_obj['warnings']) @@ -64,7 +64,7 @@ def export_file(isamAppliance, instance_id, file_id, filename, check_mode=False, if check_mode is False: # No point downloading a file if in check_mode return isamAppliance.invoke_get_file( "Exporting a common log file", - "{0}/{1}/{2}?export=true".format(uri, instance_id, file_id), + f"{uri}/{instance_id}/{file_id}?export=true", filename, requires_model=requires_model) return isamAppliance.create_return_object(warnings=ret_obj['warnings']) diff --git a/ibmsecurity/isam/web/reverse_proxy/mmfa_configuration.py b/ibmsecurity/isam/web/reverse_proxy/mmfa_configuration.py index 453693f5..741ad628 100644 --- a/ibmsecurity/isam/web/reverse_proxy/mmfa_configuration.py +++ b/ibmsecurity/isam/web/reverse_proxy/mmfa_configuration.py @@ -28,7 +28,7 @@ def config(isamAppliance, instance_id, lmi, runtime, reuse_acls=None, reuse_cert json_data['channel'] = channel return isamAppliance.invoke_post( "MMFA configuration for a reverse proxy instance", - "/wga/reverseproxy/{0}/mmfa_config".format(instance_id), json_data, + f"/wga/reverseproxy/{instance_id}/mmfa_config", json_data, requires_modules=requires_modules, requires_version=requires_version) return isamAppliance.create_return_object() @@ -44,7 +44,7 @@ def unconfig(isamAppliance, instance_id, check_mode=False, force=False): else: return isamAppliance.invoke_delete( "MMFA unconfiguration for a reverse proxy instance", - "/wga/reverseproxy/{0}/mmfa_config".format(instance_id), + f"/wga/reverseproxy/{instance_id}/mmfa_config", requires_modules=requires_modules, requires_version=requires_version) return isamAppliance.create_return_object() @@ -62,8 +62,7 @@ def _check_config(isamAppliance, instance_id): ret_obj = entry.get(isamAppliance, instance_id, "mmfa-config-info", "autoconfig") if ret_obj['data']['autoconfig'] == ["mmfa"]: logger.info( - "WebSEAL entry mmfa-config-info:autoconfig has value {}. 'mmfa' means config already done.".format( - ret_obj['data'])) + "WebSEAL entry mmfa-config-info:autoconfig has value {}. 'mmfa' means config already done.".format(ret_obj['data'])) return True except: pass diff --git a/ibmsecurity/isam/web/reverse_proxy/statistics.py b/ibmsecurity/isam/web/reverse_proxy/statistics.py index 51a4ab34..d547eb07 100644 --- a/ibmsecurity/isam/web/reverse_proxy/statistics.py +++ b/ibmsecurity/isam/web/reverse_proxy/statistics.py @@ -15,7 +15,7 @@ def get_all(isamAppliance, instance_id, check_mode=False, force=False): """ try: return isamAppliance.invoke_get("Retrieving all statistics components - Reverse Proxy", - "{0}/{1}/statistics".format(uri, instance_id),requires_model=requires_model) + f"{uri}/{instance_id}/statistics",requires_model=requires_model) except: # Return empty array - exception thrown if list has no entries or does not exist ret_obj = isamAppliance.create_return_object() @@ -28,9 +28,7 @@ def get_all_logs(isamAppliance, instance_id, component_id, check_mode=False, for Retrieving all log files for a component - Reverse Proxy """ return isamAppliance.invoke_get("Retrieving all statistics log files for a component - Reverse Proxy", - "{0}/{1}/statistics/{2}/stats_files".format(uri, - instance_id, - component_id),requires_model=requires_model) + "{0}/{1}/statistics/{2}/stats_files".format(uri,instance_id,component_id),requires_model=requires_model) def get(isamAppliance, instance_id, component_id, file_id, options=None, @@ -39,13 +37,8 @@ def get(isamAppliance, instance_id, component_id, file_id, options=None, Retrieving snippets of a statistics log file for a component - Reverse Proxy """ return isamAppliance.invoke_get("Retrieving snippet of a statistics log file - Reverse Proxy", - "{0}/{1}/statistics/{2}/stats_files/{3}".format(uri, - instance_id, - component_id, - file_id, - tools.create_query_string( - options=options, start=start, - size=size)),requires_model=requires_model) + "{0}/{1}/statistics/{2}/stats_files/{3}".format(uri,instance_id,component_id,file_id,tools.create_query_string(options=options, start=start,size=size)), + requires_model=requires_model) def export_file(isamAppliance, instance_id, component_id, file_id, filename, check_mode=False, force=False): @@ -57,11 +50,9 @@ def export_file(isamAppliance, instance_id, component_id, file_id, filename, che if force is True or (os.path.exists(filename) is False): if check_mode is False: # No point downloading a file if in check_mode return isamAppliance.invoke_get_file("Exporting a Reverse Proxy statistics log file.", - "{0}/{1}/statistics/{2}/stats_files/{3}?export".format(uri, - instance_id, - component_id, - file_id), - filename,requires_model=requires_model) + "{0}/{1}/statistics/{2}/stats_files/{3}?export".format(uri,instance_id,component_id,file_id), + filename, + requires_model=requires_model) return isamAppliance.create_return_object() @@ -78,9 +69,7 @@ def set(isamAppliance, instance_id, component_id, status, hours, mins, secs, else: return isamAppliance.invoke_put( "Modify statistics settings for a component", - "{0}/{1}/statistics/{2}".format(uri, - instance_id, - component_id), + f"{uri}/{instance_id}/statistics/{component_id}", { 'status': status, 'interval_hours': hours, @@ -115,10 +104,8 @@ def delete(isamAppliance, instance_id, component_id, file_id, check_mode=False, else: return isamAppliance.invoke_delete( "Deleting a statistics log file", - "{0}/{1}/statistics/{2}/stats_files/{3}".format(uri, - instance_id, - component_id, - file_id),requires_model=requires_model) + "{0}/{1}/statistics/{2}/stats_files/{3}".format(uri,instance_id,component_id, file_id), + requires_model=requires_model) return isamAppliance.create_return_object(warnings=warnings) @@ -142,9 +129,8 @@ def delete_all(isamAppliance, instance_id, component_id, check_mode=False, force else: return isamAppliance.invoke_delete( "Deleting all statistics log files", - "{0}/{1}/statistics/{2}/stats_files".format(uri, - instance_id, - component_id),requires_model=requires_model) + "{0}/{1}/statistics/{2}/stats_files".format(uri,instance_id,component_id), + requires_model=requires_model) return isamAppliance.create_return_object(warnings=warnings) From 5ebeb5e2f8bb5770751a793652de46e8396b1cc1 Mon Sep 17 00:00:00 2001 From: Tom Bosmans Date: Thu, 22 Jan 2026 14:51:07 +0100 Subject: [PATCH 10/23] fix: format to f-strings Signed-off-by: Tom Bosmans --- .../web/reverse_proxy/configuration/entry.py | 24 +++++++------------ .../web/reverse_proxy/configuration/stanza.py | 16 +++++++++---- .../web/reverse_proxy/configuration/trace.py | 12 +++++----- .../reverse_proxy/configuration/waf_config.py | 8 +++---- ibmsecurity/isam/web/reverse_proxy/logs.py | 2 +- .../web/reverse_proxy/mmfa_configuration.py | 2 +- .../isam/web/reverse_proxy/statistics.py | 8 +++---- ibmsecurity/isam/web/reverse_proxy/trace.py | 8 +++---- .../web/reverse_proxy/transaction_logging.py | 2 +- 9 files changed, 41 insertions(+), 41 deletions(-) diff --git a/ibmsecurity/isam/web/reverse_proxy/configuration/entry.py b/ibmsecurity/isam/web/reverse_proxy/configuration/entry.py index bfcdfc88..55c88722 100644 --- a/ibmsecurity/isam/web/reverse_proxy/configuration/entry.py +++ b/ibmsecurity/isam/web/reverse_proxy/configuration/entry.py @@ -20,9 +20,7 @@ def get_all(isamAppliance, reverseproxy_id, stanza_id, check_mode=False, force=F """ try: ret_obj = isamAppliance.invoke_get("Retrieving all configuration entries for a stanza - Reverse Proxy", - "{0}/{1}/configuration/stanza/{2}".format(uri, - reverseproxy_id, - stanza_id)) + f"{uri}/{reverseproxy_id}/configuration/stanza/{stanza_id}") logger.debug(f"Get All {stanza_id}:\n {ret_obj}") except: # Return empty array - exception thrown if stanza has no entries or does not exist @@ -37,8 +35,7 @@ def get(isamAppliance, reverseproxy_id, stanza_id, entry_id, check_mode=False, f Retrieving a specific configuration entry - Reverse Proxy """ # URL being encoded primarily to handle request-log-format that has "%" values in them - f_uri = "{0}/{1}/configuration/stanza/{2}/entry_name/{3}".format(uri, reverseproxy_id, - stanza_id, entry_id) + f_uri = f"{uri}/{reverseproxy_id}/configuration/stanza/{stanza_id}/entry_name/{entry_id}" # Replace % with %25 if it is not encoded already import re ruri = re.sub("%(?![0-9a-fA-F]{2})", "%25", f_uri) @@ -71,8 +68,7 @@ def add(isamAppliance, reverseproxy_id, stanza_id, entries, check_mode=False, fo exists, update_required, value = _check(isamAppliance, reverseproxy_id, stanza_id, entry[0], entry[1]) if exists is True: logger.debug( - 'Entries exists {0}/{1}/{2}/{3}! Will be ignored.'.format(reverseproxy_id, stanza_id, entry[0], - entry[1])) + f'Entries exists {reverseproxy_id}/{stanza_id}/{entry[0]}/{entry[1]}! Will be ignored.') else: add_entries.append(entry) add_required = True @@ -90,7 +86,7 @@ def add(isamAppliance, reverseproxy_id, stanza_id, entries, check_mode=False, fo def _add(isamAppliance, reverseproxy_id, stanza_id, entries): return isamAppliance.invoke_post( "Adding a configuration entry or entries by stanza - Reverse Proxy", - "{0}/{1}/configuration/stanza/{2}/entry_name".format(uri, reverseproxy_id, stanza_id), + f"{uri}/{reverseproxy_id}/configuration/stanza/{stanza_id}/entry_name", {"entries": entries}) @@ -179,8 +175,7 @@ def delete(isamAppliance, reverseproxy_id, stanza_id, entry_id, value_id='', che return isamAppliance.create_return_object(changed=True) else: # URL being encoded primarily to handle request-log-format that has "%" values in them - f_uri = "{0}/{1}/configuration/stanza/{2}/entry_name/{3}/value/{4}".format(uri, reverseproxy_id, stanza_id, - entry_id, value_id) + f_uri = f"{uri}/{reverseproxy_id}/configuration/stanza/{stanza_id}/entry_name/{entry_id}/value/{value_id}" # Replace % with %25 if it is not encoded already import re ruri = re.sub("%(?![0-9a-fA-F]{2})", "%25", f_uri) @@ -222,7 +217,7 @@ def delete_all(isamAppliance, reverseproxy_id, stanza_id, entry_id, check_mode=F return isamAppliance.create_return_object(changed=True) else: # URL being encoded primarily to handle request-log-format that has "%" values in them - f_uri = "{0}/{1}/configuration/stanza/{2}/entry_name/{3}".format(uri, reverseproxy_id, stanza_id, entry_id) + f_uri = f"{uri}/{reverseproxy_id}/configuration/stanza/{stanza_id}/entry_name/{entry_id}" # Replace % with %25 if it is not encoded already import re @@ -255,8 +250,7 @@ def update(isamAppliance, reverseproxy_id, stanza_id, entry_id, value_id, check_ return isamAppliance.create_return_object(changed=True) else: # URL being encoded primarily to handle request-log-format that has "%" values in them - f_uri = "{0}/{1}/configuration/stanza/{2}/entry_name/{3}".format(uri, reverseproxy_id, - stanza_id, entry_id) + f_uri = f"{uri}/{reverseproxy_id}/configuration/stanza/{stanza_id}/entry_name/{entry_id}" # Replace % with %25 if it is not encoded already import re ruri = re.sub("%(?![0-9a-fA-F]{2})", "%25", f_uri) @@ -295,8 +289,8 @@ def _check(isamAppliance, reverseproxy_id, stanza_id, entry_id, value_id): stanza_id, entry_id, value)) - logger.debug("Existing Value(s): {0}".format(value)) - logger.debug("Value to update : {0}".format(value_id)) + logger.debug(f"Existing Value(s): {value}") + logger.debug(f"Value to update : {value_id}") if isinstance(value_id, list): if value != value_id: # Comparing list with no sorting... sequence of values is of importance diff --git a/ibmsecurity/isam/web/reverse_proxy/configuration/stanza.py b/ibmsecurity/isam/web/reverse_proxy/configuration/stanza.py index 4c627654..238e1ac4 100644 --- a/ibmsecurity/isam/web/reverse_proxy/configuration/stanza.py +++ b/ibmsecurity/isam/web/reverse_proxy/configuration/stanza.py @@ -1,3 +1,5 @@ +from urllib.parse import urlencode + import logging import ibmsecurity.utilities.tools @@ -9,7 +11,7 @@ def get(isamAppliance, reverseproxy_id, check_mode=False, force=False): Retrieving a list of stanzas - Reverse Proxy """ return isamAppliance.invoke_get("Retrieving a list of stanzas - Reverse Proxy", - "/wga/reverseproxy/{0}/configuration/stanza".format(reverseproxy_id)) + f"/wga/reverseproxy/{reverseproxy_id}/configuration/stanza") def add(isamAppliance, reverseproxy_id, stanza_id, check_mode=False, force=False): @@ -22,7 +24,7 @@ def add(isamAppliance, reverseproxy_id, stanza_id, check_mode=False, force=False else: return isamAppliance.invoke_post( "Adding a configuration stanza name - Reverse Proxy", - "/wga/reverseproxy/{0}/configuration/stanza/{1}".format(reverseproxy_id, stanza_id), + f"/wga/reverseproxy/{reverseproxy_id}/configuration/stanza/{stanza_id}", {}) return isamAppliance.create_return_object() @@ -32,13 +34,17 @@ def delete(isamAppliance, reverseproxy_id, stanza_id, check_mode=False, force=Fa """ Deleting a stanza - Reverse Proxy """ - if force is True or _check(isamAppliance, reverseproxy_id, stanza_id) is True: - if check_mode is True: + if force or _check(isamAppliance, reverseproxy_id, stanza_id): + if check_mode: return isamAppliance.create_return_object(changed=True) else: + try: + stanza_id = urlencode(stanza_id) + except Exception: + pass return isamAppliance.invoke_delete( "Deleting a stanza - Reverse Proxy", - "/wga/reverseproxy/{0}/configuration/stanza/{1}".format(reverseproxy_id, stanza_id)) + f"/wga/reverseproxy/{reverseproxy_id}/configuration/stanza/{stanza_id}") return isamAppliance.create_return_object() diff --git a/ibmsecurity/isam/web/reverse_proxy/configuration/trace.py b/ibmsecurity/isam/web/reverse_proxy/configuration/trace.py index 0ad1cbb6..7f5b43fb 100644 --- a/ibmsecurity/isam/web/reverse_proxy/configuration/trace.py +++ b/ibmsecurity/isam/web/reverse_proxy/configuration/trace.py @@ -16,7 +16,7 @@ def get(isamAppliance, instance_id, check_mode=False, force=False): """ return isamAppliance.invoke_get("Retrieving a tracing configuration file", - "{0}/{1}/tracing_configuration".format(uri, instance_id), + f"{uri}/{instance_id}/tracing_configuration", requires_modules=requires_modules, requires_version=requires_version) @@ -26,8 +26,8 @@ def export_file(isamAppliance, instance_id, filepath, check_mode=False, force=Fa """ if os.path.exists(filepath) is True: - logger.info("File '{0}' already exists. Skipping export.".format(filepath)) - warnings = ["File '{0}' already exists. Skipping export.".format(filepath)] + logger.info(f"File '{filepath}' already exists. Skipping export.") + warnings = [f"File '{filepath}' already exists. Skipping export."] return isamAppliance.create_return_object(warnings=warnings) if check_mode is True: @@ -35,7 +35,7 @@ def export_file(isamAppliance, instance_id, filepath, check_mode=False, force=Fa else: return isamAppliance.invoke_get_file( "Exporting a tracing configuration file", - "{0}/{1}/tracing_configuration?export".format(uri, instance_id), filepath + f"{uri}/{instance_id}/tracing_configuration?export", filepath ) return isamAppliance.create_return_object() @@ -61,7 +61,7 @@ def update(isamAppliance, instance_id, contents, check_mode=False, force=False): return isamAppliance.invoke_put( "Updating tracing configuration file data - using contents string", - "{0}/{1}/tracing_configuration".format(uri, instance_id), + f"{uri}/{instance_id}/tracing_configuration", { 'file_contents': contents }, @@ -84,7 +84,7 @@ def import_file(isamAppliance, instance_id, filepath, check_mode=False, force=Fa return isamAppliance.invoke_put_files( "Updating tracing configuration file data - using a file", - "{0}/{1}/tracing_configuration".format(uri, instance_id), + f"{uri}/{instance_id}/tracing_configuration", [ { 'file_formfield': 'file', diff --git a/ibmsecurity/isam/web/reverse_proxy/configuration/waf_config.py b/ibmsecurity/isam/web/reverse_proxy/configuration/waf_config.py index 0fff3278..d8e86934 100644 --- a/ibmsecurity/isam/web/reverse_proxy/configuration/waf_config.py +++ b/ibmsecurity/isam/web/reverse_proxy/configuration/waf_config.py @@ -13,7 +13,7 @@ def get(isamAppliance, instance_name, check_mode=False, force=False): Retrieving the WAF configuration file (modsecurity.conf) """ return isamAppliance.invoke_get("Retrieving the WAF configuration file", - "/isam/advanced_configuration/{0}?component=waf".format(instance_name)) + f"/isam/advanced_configuration/{instance_name}?component=waf") def export_file(isamAppliance, instance_name, filename, check_mode=False, force=False): @@ -24,7 +24,7 @@ def export_file(isamAppliance, instance_name, filename, check_mode=False, force= if check_mode is False: return isamAppliance.invoke_get_file( "Exporting the WAF configuration file", - "/isam/advanced_configuration/{0}?export&component=waf".format(instance_name), + f"/isam/advanced_configuration/{instance_name}?export&component=waf", filename) return isamAppliance.create_return_object() @@ -39,7 +39,7 @@ def revert(isamAppliance, instance_name, check_mode=False, force=False): else: return isamAppliance.invoke_put( "Reverting a previously updated WAF configuration file", - "/isam/advanced_configuration/{0}&component=waf".format(instance_name), + f"/isam/advanced_configuration/{instance_name}&component=waf", { 'operation': 'revert' }) @@ -54,7 +54,7 @@ def update(isamAppliance, instance_name, file_contents, check_mode=False, force= else: return isamAppliance.invoke_put( "Updating the WAF configuration file", - "/isam/advanced_configuration/{0}?component=waf".format(instance_name), + f"/isam/advanced_configuration/{instance_name}?component=waf", { 'file_contents': file_contents }) diff --git a/ibmsecurity/isam/web/reverse_proxy/logs.py b/ibmsecurity/isam/web/reverse_proxy/logs.py index ce35fa8e..8113af93 100644 --- a/ibmsecurity/isam/web/reverse_proxy/logs.py +++ b/ibmsecurity/isam/web/reverse_proxy/logs.py @@ -21,7 +21,7 @@ def get(isamAppliance, instance_id, file_id, options=None, size=None, start=None Retrieving a snippet of a common log file """ return isamAppliance.invoke_get("Retrieving a snippet of a common log file", - "{0}/{1}/{2}{3}".format(uri, instance_id, file_id, tools.create_query_string(options=options, start=start, size=size)), + f"{uri}/{instance_id}/{file_id}{tools.create_query_string(options=options, start=start, size=size)}", requires_model=requires_model) diff --git a/ibmsecurity/isam/web/reverse_proxy/mmfa_configuration.py b/ibmsecurity/isam/web/reverse_proxy/mmfa_configuration.py index 741ad628..023b4bac 100644 --- a/ibmsecurity/isam/web/reverse_proxy/mmfa_configuration.py +++ b/ibmsecurity/isam/web/reverse_proxy/mmfa_configuration.py @@ -62,7 +62,7 @@ def _check_config(isamAppliance, instance_id): ret_obj = entry.get(isamAppliance, instance_id, "mmfa-config-info", "autoconfig") if ret_obj['data']['autoconfig'] == ["mmfa"]: logger.info( - "WebSEAL entry mmfa-config-info:autoconfig has value {}. 'mmfa' means config already done.".format(ret_obj['data'])) + f"WebSEAL entry mmfa-config-info:autoconfig has value {ret_obj['data']}. 'mmfa' means config already done.") return True except: pass diff --git a/ibmsecurity/isam/web/reverse_proxy/statistics.py b/ibmsecurity/isam/web/reverse_proxy/statistics.py index d547eb07..fb9fafdc 100644 --- a/ibmsecurity/isam/web/reverse_proxy/statistics.py +++ b/ibmsecurity/isam/web/reverse_proxy/statistics.py @@ -28,7 +28,7 @@ def get_all_logs(isamAppliance, instance_id, component_id, check_mode=False, for Retrieving all log files for a component - Reverse Proxy """ return isamAppliance.invoke_get("Retrieving all statistics log files for a component - Reverse Proxy", - "{0}/{1}/statistics/{2}/stats_files".format(uri,instance_id,component_id),requires_model=requires_model) + f"{uri}/{instance_id}/statistics/{component_id}/stats_files",requires_model=requires_model) def get(isamAppliance, instance_id, component_id, file_id, options=None, @@ -50,7 +50,7 @@ def export_file(isamAppliance, instance_id, component_id, file_id, filename, che if force is True or (os.path.exists(filename) is False): if check_mode is False: # No point downloading a file if in check_mode return isamAppliance.invoke_get_file("Exporting a Reverse Proxy statistics log file.", - "{0}/{1}/statistics/{2}/stats_files/{3}?export".format(uri,instance_id,component_id,file_id), + f"{uri}/{instance_id}/statistics/{component_id}/stats_files/{file_id}?export", filename, requires_model=requires_model) return isamAppliance.create_return_object() @@ -104,7 +104,7 @@ def delete(isamAppliance, instance_id, component_id, file_id, check_mode=False, else: return isamAppliance.invoke_delete( "Deleting a statistics log file", - "{0}/{1}/statistics/{2}/stats_files/{3}".format(uri,instance_id,component_id, file_id), + f"{uri}/{instance_id}/statistics/{component_id}/stats_files/{file_id}", requires_model=requires_model) return isamAppliance.create_return_object(warnings=warnings) @@ -129,7 +129,7 @@ def delete_all(isamAppliance, instance_id, component_id, check_mode=False, force else: return isamAppliance.invoke_delete( "Deleting all statistics log files", - "{0}/{1}/statistics/{2}/stats_files".format(uri,instance_id,component_id), + f"{uri}/{instance_id}/statistics/{component_id}/stats_files", requires_model=requires_model) return isamAppliance.create_return_object(warnings=warnings) diff --git a/ibmsecurity/isam/web/reverse_proxy/trace.py b/ibmsecurity/isam/web/reverse_proxy/trace.py index 9ae8449a..5d932f0e 100644 --- a/ibmsecurity/isam/web/reverse_proxy/trace.py +++ b/ibmsecurity/isam/web/reverse_proxy/trace.py @@ -27,7 +27,7 @@ def get_all_logs(isamAppliance, instance_id, component_id, check_mode=False, for Retrieving all trace log files for a component - Reverse Proxy """ return isamAppliance.invoke_get("Retrieving all trace log files for a component - Reverse Proxy", - "{0}/{1}/tracing/{2}/trace_files".format(uri, instance_id, component_id), + f"{uri}/{instance_id}/tracing/{component_id}/trace_files", requires_model=requires_model) @@ -50,7 +50,7 @@ def export_file(isamAppliance, instance_id, component_id, file_id, filename, che if force is True or (os.path.exists(filename) is False): if check_mode is False: # No point downloading a file if in check_mode return isamAppliance.invoke_get_file("Exporting a Reverse Proxy trace log file.", - "{0}/{1}/tracing/{2}/trace_files/{3}?export".format(uri,instance_id,component_id,file_id), filename, + f"{uri}/{instance_id}/tracing/{component_id}/trace_files/{file_id}?export", filename, requires_model=requires_model) return isamAppliance.create_return_object() @@ -97,7 +97,7 @@ def delete(isamAppliance, instance_id, component_id, file_id, check_mode=False, else: return isamAppliance.invoke_delete( "Deleting a trace log file", - "{0}/{1}/tracing/{2}/trace_files/{3}".format(uri,instance_id,component_id,file_id), + f"{uri}/{instance_id}/tracing/{component_id}/trace_files/{file_id}", requires_model=requires_model) return isamAppliance.create_return_object(warnings=warnings) @@ -122,7 +122,7 @@ def delete_all(isamAppliance, instance_id, component_id, check_mode=False, force else: return isamAppliance.invoke_delete( "Deleting all trace log files", - "{0}/{1}/tracing/{2}/trace_files".format(uri,instance_id,component_id), + f"{uri}/{instance_id}/tracing/{component_id}/trace_files", requires_model=requires_model) return isamAppliance.create_return_object(warnings=warnings) diff --git a/ibmsecurity/isam/web/reverse_proxy/transaction_logging.py b/ibmsecurity/isam/web/reverse_proxy/transaction_logging.py index 739703c0..b1bb0749 100644 --- a/ibmsecurity/isam/web/reverse_proxy/transaction_logging.py +++ b/ibmsecurity/isam/web/reverse_proxy/transaction_logging.py @@ -25,7 +25,7 @@ def get_files(isamAppliance, instance_id, component_id, check_mode=False, force= """ return isamAppliance.invoke_get("Retrieving all transaction log files for a component", - "{0}/{1}/transaction_logging/{2}/translog_files".format(uri, instance_id, component_id), + f"{uri}/{instance_id}/transaction_logging/{component_id}/translog_files", requires_modules=requires_modules, requires_version=requires_version,requires_model=requires_model) From ea3e02cbaa678c4d54a116592676bfb506346034 Mon Sep 17 00:00:00 2001 From: Tom Bosmans Date: Thu, 22 Jan 2026 15:27:54 +0100 Subject: [PATCH 11/23] fix: allow handling of stanza's that look like [stanza:/] (specifically for jwt:/) Signed-off-by: Tom Bosmans --- .../web/reverse_proxy/configuration/stanza.py | 28 ++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/ibmsecurity/isam/web/reverse_proxy/configuration/stanza.py b/ibmsecurity/isam/web/reverse_proxy/configuration/stanza.py index 238e1ac4..d634e439 100644 --- a/ibmsecurity/isam/web/reverse_proxy/configuration/stanza.py +++ b/ibmsecurity/isam/web/reverse_proxy/configuration/stanza.py @@ -1,4 +1,5 @@ -from urllib.parse import urlencode +from urllib.parse import quote_plus +from urllib.parse import unquote_plus import logging import ibmsecurity.utilities.tools @@ -18,8 +19,13 @@ def add(isamAppliance, reverseproxy_id, stanza_id, check_mode=False, force=False """ Adding a configuration stanza name - Reverse Proxy """ - if force is True or _check(isamAppliance, reverseproxy_id, stanza_id) is False: - if check_mode is True: + try: + stanza_id = quote_plus(stanza_id) + logger.debug(f"Add/Set Encoded stanza_id {stanza_id}") + except Exception: + pass + if force or not _check(isamAppliance, reverseproxy_id, stanza_id): + if check_mode: return isamAppliance.create_return_object(changed=True) else: return isamAppliance.invoke_post( @@ -30,18 +36,23 @@ def add(isamAppliance, reverseproxy_id, stanza_id, check_mode=False, force=False return isamAppliance.create_return_object() +# Alias to supply set function +set = add + + def delete(isamAppliance, reverseproxy_id, stanza_id, check_mode=False, force=False): """ Deleting a stanza - Reverse Proxy """ + try: + stanza_id = quote_plus(stanza_id) + logger.debug(f"Delete Encoded stanza_id {stanza_id}") + except Exception: + pass if force or _check(isamAppliance, reverseproxy_id, stanza_id): if check_mode: return isamAppliance.create_return_object(changed=True) else: - try: - stanza_id = urlencode(stanza_id) - except Exception: - pass return isamAppliance.invoke_delete( "Deleting a stanza - Reverse Proxy", f"/wga/reverseproxy/{reverseproxy_id}/configuration/stanza/{stanza_id}") @@ -59,6 +70,9 @@ def _check(isamAppliance, reverseproxy_id, stanza_id): if stanza == stanza_id: logger.info("Stanza found in resource: " + reverseproxy_id) return True + if stanza == unquote_plus(stanza_id): + logger.info("Encoded Stanza found in resource: " + reverseproxy_id) + return True logger.info("Stanza *not* found in resource: " + reverseproxy_id) return False From 2e3358bf698439d5823e5582f43d91c2c4a68858 Mon Sep 17 00:00:00 2001 From: Tom Bosmans Date: Fri, 23 Jan 2026 09:22:52 +0100 Subject: [PATCH 12/23] fix: allow handling of entries (specifically request-log-format and stanza's that are not url safe) Signed-off-by: Tom Bosmans --- .../web/reverse_proxy/configuration/entry.py | 118 +++++++----------- 1 file changed, 42 insertions(+), 76 deletions(-) diff --git a/ibmsecurity/isam/web/reverse_proxy/configuration/entry.py b/ibmsecurity/isam/web/reverse_proxy/configuration/entry.py index 55c88722..98abec5b 100644 --- a/ibmsecurity/isam/web/reverse_proxy/configuration/entry.py +++ b/ibmsecurity/isam/web/reverse_proxy/configuration/entry.py @@ -1,3 +1,6 @@ +from urllib.parse import quote_plus +from urllib.parse import unquote_plus + import logging import ibmsecurity.utilities.tools from ibmsecurity.utilities.tools import jsonSortedListEncoder @@ -18,6 +21,7 @@ def get_all(isamAppliance, reverseproxy_id, stanza_id, check_mode=False, force=F """ Retrieving all configuration entries for a stanza - Reverse Proxy """ + stanza_id = quote_plus(stanza_id) try: ret_obj = isamAppliance.invoke_get("Retrieving all configuration entries for a stanza - Reverse Proxy", f"{uri}/{reverseproxy_id}/configuration/stanza/{stanza_id}") @@ -35,19 +39,10 @@ def get(isamAppliance, reverseproxy_id, stanza_id, entry_id, check_mode=False, f Retrieving a specific configuration entry - Reverse Proxy """ # URL being encoded primarily to handle request-log-format that has "%" values in them - f_uri = f"{uri}/{reverseproxy_id}/configuration/stanza/{stanza_id}/entry_name/{entry_id}" - # Replace % with %25 if it is not encoded already - import re - ruri = re.sub("%(?![0-9a-fA-F]{2})", "%25", f_uri) - # URL encode - try: - # Assume Python3 and import package - from urllib.parse import quote - except ImportError: - # Now try to import Python2 package - from urllib import quote + stanza_id = quote_plus(stanza_id) + entry_id = quote_plus(entry_id) + full_uri = f"{uri}/{reverseproxy_id}/configuration/stanza/{stanza_id}/entry_name/{entry_id}" - full_uri = quote(ruri) return isamAppliance.invoke_get("Retrieving a specific configuration entry - Reverse Proxy", full_uri) @@ -62,11 +57,11 @@ def add(isamAppliance, reverseproxy_id, stanza_id, entries, check_mode=False, fo add_required = False - if force is False: + if not force: add_entries = [] for entry in entries: exists, update_required, value = _check(isamAppliance, reverseproxy_id, stanza_id, entry[0], entry[1]) - if exists is True: + if exists: logger.debug( f'Entries exists {reverseproxy_id}/{stanza_id}/{entry[0]}/{entry[1]}! Will be ignored.') else: @@ -74,8 +69,8 @@ def add(isamAppliance, reverseproxy_id, stanza_id, entries, check_mode=False, fo add_required = True entries = add_entries - if force is True or add_required is True: - if check_mode is True: + if force or add_required: + if check_mode: return isamAppliance.create_return_object(changed=True) else: return _add(isamAppliance, reverseproxy_id, stanza_id, entries) @@ -84,6 +79,7 @@ def add(isamAppliance, reverseproxy_id, stanza_id, entries, check_mode=False, fo def _add(isamAppliance, reverseproxy_id, stanza_id, entries): + stanza_id = quote_plus(stanza_id) return isamAppliance.invoke_post( "Adding a configuration entry or entries by stanza - Reverse Proxy", f"{uri}/{reverseproxy_id}/configuration/stanza/{stanza_id}/entry_name", @@ -120,8 +116,9 @@ def set(isamAppliance, reverseproxy_id, stanza_id, entries, check_mode=False, fo if force or (newEntriesJSON != currentEntriesJSON): for entry in entries: logger.info(f"Deleting entry, will be re-added: {reverseproxy_id}/{stanza_id}/{entry[0]}") - delete_all(isamAppliance, reverseproxy_id, stanza_id, entry[0], check_mode, True) - if check_mode is True: + if not check_mode: + delete_all(isamAppliance, reverseproxy_id, stanza_id, entry[0], check_mode, True) + if check_mode: return isamAppliance.create_return_object(changed=True) else: return _add(isamAppliance, reverseproxy_id, stanza_id, entries) @@ -171,28 +168,20 @@ def delete(isamAppliance, reverseproxy_id, stanza_id, entry_id, value_id='', che exists, update_required, value = _check(isamAppliance, reverseproxy_id, stanza_id, entry_id, value_id) if force or exists: - if check_mode is True: + if check_mode: return isamAppliance.create_return_object(changed=True) else: # URL being encoded primarily to handle request-log-format that has "%" values in them - f_uri = f"{uri}/{reverseproxy_id}/configuration/stanza/{stanza_id}/entry_name/{entry_id}/value/{value_id}" - # Replace % with %25 if it is not encoded already - import re - ruri = re.sub("%(?![0-9a-fA-F]{2})", "%25", f_uri) - # URL encode - try: - # Assume Python3 and import package - from urllib.parse import quote - except ImportError: - # Now try to import Python2 package - from urllib import quote - - full_uri = quote(ruri) + stanza_id = quote_plus(stanza_id) + entry_id = quote_plus(entry_id) + value_id = quote_plus(value_id) + full_uri = f"{uri}/{reverseproxy_id}/configuration/stanza/{stanza_id}/entry_name/{entry_id}/value/{value_id}" + # Workaround for value_id encoding in 9.0.7.1 - if ibmsecurity.utilities.tools.version_compare(isamAppliance.facts['version'], '9.0.7.1') >= 0: - uri_parts = full_uri.split('/value/') - uri_parts[1] = uri_parts[1].replace('/', '%2F') - full_uri = '/value/'.join(uri_parts) + #if ibmsecurity.utilities.tools.version_compare(isamAppliance.facts['version'], '9.0.7.1') >= 0: + # uri_parts = full_uri.split('/value/') + # uri_parts[1] = uri_parts[1].replace('/', '%2F') + # full_uri = '/value/'.join(uri_parts) return isamAppliance.invoke_delete( "Deleting a value from a configuration entry - Reverse Proxy", full_uri) @@ -204,34 +193,22 @@ def delete_all(isamAppliance, reverseproxy_id, stanza_id, entry_id, check_mode=F Deleting all values from a configuration entry - Reverse Proxy """ delete_required = False - if force is False: + if not force: try: ret_obj = get(isamAppliance, reverseproxy_id, stanza_id, entry_id) if ret_obj['data'] != {}: delete_required = True - except: + except Exception: pass - if force is True or delete_required is True: - if check_mode is True: + if force or delete_required: + if check_mode: return isamAppliance.create_return_object(changed=True) else: # URL being encoded primarily to handle request-log-format that has "%" values in them - f_uri = f"{uri}/{reverseproxy_id}/configuration/stanza/{stanza_id}/entry_name/{entry_id}" - - # Replace % with %25 if it is not encoded already - import re - ruri = re.sub("%(?![0-9a-fA-F]{2})", "%25", f_uri) - # URL encode - try: - # Assume Python3 and import package - from urllib.parse import quote - except ImportError: - # Now try to import Python2 package - from urllib import quote - - full_uri = quote(ruri) - + stanza_id = quote_plus(stanza_id) + entry_id = quote_plus(entry_id) + full_uri = f"{uri}/{reverseproxy_id}/configuration/stanza/{stanza_id}/entry_name/{entry_id}" return isamAppliance.invoke_delete( "Deleting all values from a configuration entry - Reverse Proxy", full_uri) @@ -242,27 +219,19 @@ def update(isamAppliance, reverseproxy_id, stanza_id, entry_id, value_id, check_ """ Updating a configuration entry or entries by stanza - Reverse Proxy """ - if force is False: + if not force: exists, update_required, cur_value = _check(isamAppliance, reverseproxy_id, stanza_id, entry_id, value_id) - if force is True or update_required is True: - if check_mode is True: + if force or update_required: + if check_mode: return isamAppliance.create_return_object(changed=True) else: # URL being encoded primarily to handle request-log-format that has "%" values in them - f_uri = f"{uri}/{reverseproxy_id}/configuration/stanza/{stanza_id}/entry_name/{entry_id}" - # Replace % with %25 if it is not encoded already - import re - ruri = re.sub("%(?![0-9a-fA-F]{2})", "%25", f_uri) - # URL encode - try: - # Assume Python3 and import package - from urllib.parse import quote - except ImportError: - # Now try to import Python2 package - from urllib import quote - - full_uri = quote(ruri) + # make sure stanza_id and entry_id are url safe + stanza_id = quote_plus(stanza_id) + entry_id = quote_plus(entry_id) + full_uri = f"{uri}/{reverseproxy_id}/configuration/stanza/{stanza_id}/entry_name/{entry_id}" + return isamAppliance.invoke_put( "Updating a configuration entry or entries by stanza - Reverse Proxy", full_uri, @@ -285,10 +254,7 @@ def _check(isamAppliance, reverseproxy_id, stanza_id, entry_id, value_id): except: return False, True, None # Exception means entry / stanza not found - logger.info("Entry found in rp:{0}, stanza:{1}, entryid:{2}, value:{3}".format(reverseproxy_id, - stanza_id, - entry_id, - value)) + logger.info(f"Entry found in rp:{reverseproxy_id}, stanza:{stanza_id}, entryid:{entry_id}, value:{value}") logger.debug(f"Existing Value(s): {value}") logger.debug(f"Value to update : {value_id}") @@ -299,7 +265,7 @@ def _check(isamAppliance, reverseproxy_id, stanza_id, entry_id, value_id): else: # assuming base string provided for value_id if len(value) == 1: if str(value_id) != str(value[0]): - logger.debug("Single value do not match!") + logger.debug("Single value does not match!") update_required = True exists = False # to satisfy delete call else: # base string will not match a zero length array or multiple values in it From 0733f80f6cb1c155a1f9f9dc77aff223f980272c Mon Sep 17 00:00:00 2001 From: Tom Bosmans Date: Fri, 23 Jan 2026 09:23:38 +0100 Subject: [PATCH 13/23] test: tests for reverse proxy config Signed-off-by: Tom Bosmans --- ...test_1_web_2_reverseproxy_config_stanza.py | 79 +++++++++++++++ .../test_1_web_3_reverseproxy_config_entry.py | 99 +++++++++++++++++++ 2 files changed, 178 insertions(+) create mode 100644 test/test_1_web_2_reverseproxy_config_stanza.py create mode 100644 test/test_1_web_3_reverseproxy_config_entry.py diff --git a/test/test_1_web_2_reverseproxy_config_stanza.py b/test/test_1_web_2_reverseproxy_config_stanza.py new file mode 100644 index 00000000..f48c9429 --- /dev/null +++ b/test/test_1_web_2_reverseproxy_config_stanza.py @@ -0,0 +1,79 @@ +import logging + +import ibmsecurity.isam.web.reverse_proxy.configuration.stanza +import ibmsecurity.isam.appliance + +import pytest + + +def getTestData(): + testdata = [ + { + "inst_name": "default", + "stanza_id": "custom" + }, + { + "inst_name": "default", + "stanza_id": "jwt:/" + }, + { + "inst_name": "test", + "stanza_id": "custom" + } + ] + return testdata + + +@pytest.mark.order(after="test_1_web_1_reverseproxy_setup.py::test_create_webseal_instance") +@pytest.mark.parametrize("items", getTestData()) +def test_set_stanza(iviaServer, caplog, items) -> None: + """ibmsecurity/isam/web/reverse_proxy/configuration/stanza.py""" + caplog.set_level(logging.DEBUG) + # items is a key-value pair + logging.log(logging.INFO, items) + arg = {} + inst_name, stanza_id = None, None + for k, v in items.items(): + if k == 'inst_name': + inst_name = v + continue + if k == 'stanza_id': + stanza_id = v + continue + #if k == 'key': + # key = v + # continue + arg[k] = v + + returnValue = ibmsecurity.isam.web.reverse_proxy.configuration.stanza.set(iviaServer, inst_name, stanza_id, **arg) + logging.log(logging.INFO, returnValue) + + if returnValue is not None: + assert not returnValue.failed() + + +@pytest.mark.parametrize("items", getTestData()) +def test_delete_stanza(iviaServer, caplog, items) -> None: + """ibmsecurity/isam/web/reverse_proxy/configuration/stanza.py""" + caplog.set_level(logging.DEBUG) + # items is a key-value pair + logging.log(logging.INFO, items) + arg = {} + inst_name, stanza_id = None, None + for k, v in items.items(): + if k == 'inst_name': + inst_name = v + continue + if k == 'stanza_id': + stanza_id = v + continue + #if k == 'key': + # key = v + # continue + arg[k] = v + + returnValue = ibmsecurity.isam.web.reverse_proxy.configuration.stanza.delete(iviaServer, inst_name, stanza_id, **arg) + logging.log(logging.INFO, returnValue) + + if returnValue is not None: + assert not returnValue.failed() diff --git a/test/test_1_web_3_reverseproxy_config_entry.py b/test/test_1_web_3_reverseproxy_config_entry.py new file mode 100644 index 00000000..5a1eb745 --- /dev/null +++ b/test/test_1_web_3_reverseproxy_config_entry.py @@ -0,0 +1,99 @@ +import logging + +import ibmsecurity.isam.web.reverse_proxy.configuration.entry +import ibmsecurity.isam.appliance + +import pytest + + +def getTestData(): + testdata = [ + { + "inst_name": "default", + "stanza_id": "ssl-qop-mgmt-default", + "entries": [ + ["default", 'TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384'], + ["default", 'TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384'], + ["default", 'TLS_RSA_WITH_AES_256_GCM_SHA384'], + ["default", 'TLS_CHACHA20_POLY1305_SHA256'], + ] + }, + { + "inst_name": "default", + "stanza_id": "jwt:/", + "entries": [ + ["key-label", "syslogng.tbosmans.ibm.com"], + ["claims", "text::ibm.com::iss"], + ["claims", "text::test::test"], + ] + }, + { + "inst_name": "default", + "stanza_id": "logging", + "entries": [ + ["request-log-format", 'client=%h, ident=%l, user=%u, time=%t, request="%r", status=%s, size=%b, http_user_agent="%{User-Agent}i", response_time=%F, junction=%j, junction_status=%c, junction_server=%S, junction_response_time=%J, incoming_isva_session_cookie=%{PD-S-SESSION-ID}e, outgoing_isva_session_cookie=%{PD-S-SESSION-ID}E, REFERER="%{referer}i"'], + ] + }, + ] + return testdata + + +@pytest.mark.order(after="test_1_web_1_reverseproxy_setup.py::test_create_webseal_instance") +@pytest.mark.parametrize("items", getTestData()) +def test_set_stanza_entries(iviaServer, caplog, items) -> None: + """ibmsecurity/isam/web/reverse_proxy/configuration/stanza.py""" + caplog.set_level(logging.DEBUG) + # items is a key-value pair + logging.log(logging.INFO, items) + arg = {} + inst_name, reverseproxy_id, stanza_id, entries = None, None, None, None + for k, v in items.items(): + if k == 'inst_name': + inst_name = v + continue + if k == 'reverseproxy_id': + inst_name = v + continue + if k == 'stanza_id': + stanza_id = v + continue + if k == 'entries': + entries = v + continue + #if k == 'key': + # key = v + # continue + arg[k] = v + + returnValue = ibmsecurity.isam.web.reverse_proxy.configuration.entry.set(iviaServer, inst_name, stanza_id, entries, **arg) + logging.log(logging.INFO, returnValue) + + if returnValue is not None: + assert not returnValue.failed() + + +#@pytest.mark.parametrize("items", getTestData()) +#def test_delete_stanza(iviaServer, caplog, items) -> None: +# """ibmsecurity/isam/web/reverse_proxy/configuration/stanza.py""" +# caplog.set_level(logging.DEBUG) +# # items is a key-value pair +# logging.log(logging.INFO, items) +# arg = {} +# inst_name, stanza_id = None, None +# for k, v in items.items(): +# if k == 'inst_name': +# inst_name = v +# continue +# if k == 'stanza_id': +# stanza_id = v +# continue +# #if k == 'key': +# # key = v +# # continue +# arg[k] = v +# +# returnValue = ibmsecurity.isam.web.reverse_proxy.configuration.stanza.delete(iviaServer, inst_name, stanza_id, **arg) +# logging.log(logging.INFO, returnValue) +# +# if returnValue is not None: +# assert not returnValue.failed() From 79b6b4cc9d80ac6512376ea56e08eacfae1e5feb Mon Sep 17 00:00:00 2001 From: Tom Bosmans Date: Fri, 23 Jan 2026 09:39:22 +0100 Subject: [PATCH 14/23] test: add restarts to the reverse proxies after changing stanza or entries Signed-off-by: Tom Bosmans --- ...test_1_web_2_reverseproxy_config_stanza.py | 29 +++++++++++++++++++ .../test_1_web_3_reverseproxy_config_entry.py | 28 ++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/test/test_1_web_2_reverseproxy_config_stanza.py b/test/test_1_web_2_reverseproxy_config_stanza.py index f48c9429..cd1b4ac9 100644 --- a/test/test_1_web_2_reverseproxy_config_stanza.py +++ b/test/test_1_web_2_reverseproxy_config_stanza.py @@ -1,6 +1,7 @@ import logging import ibmsecurity.isam.web.reverse_proxy.configuration.stanza +import ibmsecurity.isam.web.reverse_proxy.instance import ibmsecurity.isam.appliance import pytest @@ -77,3 +78,31 @@ def test_delete_stanza(iviaServer, caplog, items) -> None: if returnValue is not None: assert not returnValue.failed() + + +def test_reverseproxy_commit_before_restart(iviaServer, caplog) -> None: + caplog.set_level(logging.DEBUG) + returnValue = ibmsecurity.isam.appliance.commit(iviaServer, publish=True) + if returnValue is not None: + assert not returnValue.failed() + + +@pytest.mark.parametrize("items", getTestData()) +def test_reverseproxy_restart_stanza(iviaServer, caplog, items) -> None: + caplog.set_level(logging.DEBUG) + arg = {} + inst_name = None + for k, v in items.items(): + if k == 'inst_name': + inst_name = v + continue + if k == 'stanza_id': + stanza_id = v + continue + #if k == 'key': + # key = v + # continue + arg[k] = v + returnValue = ibmsecurity.isam.web.reverse_proxy.instance.execute(iviaServer, id=inst_name) + if returnValue is not None: + assert not returnValue.failed() diff --git a/test/test_1_web_3_reverseproxy_config_entry.py b/test/test_1_web_3_reverseproxy_config_entry.py index 5a1eb745..ee6eba24 100644 --- a/test/test_1_web_3_reverseproxy_config_entry.py +++ b/test/test_1_web_3_reverseproxy_config_entry.py @@ -97,3 +97,31 @@ def test_set_stanza_entries(iviaServer, caplog, items) -> None: # # if returnValue is not None: # assert not returnValue.failed() + + +def test_reverseproxy_entries_commit_before_restart(iviaServer, caplog) -> None: + caplog.set_level(logging.DEBUG) + returnValue = ibmsecurity.isam.appliance.commit(iviaServer, publish=True) + if returnValue is not None: + assert not returnValue.failed() + + +@pytest.mark.parametrize("items", getTestData()) +def test_reverseproxy_restart_entries(iviaServer, caplog, items) -> None: + caplog.set_level(logging.DEBUG) + arg = {} + inst_name = None + for k, v in items.items(): + if k == 'inst_name': + inst_name = v + continue + if k == 'stanza_id': + stanza_id = v + continue + #if k == 'key': + # key = v + # continue + arg[k] = v + returnValue = ibmsecurity.isam.web.reverse_proxy.instance.execute(iviaServer, id=inst_name) + if returnValue is not None: + assert not returnValue.failed() From 2a2055891f221fc8770933e8807db8f35309716e Mon Sep 17 00:00:00 2001 From: Tom Bosmans Date: Fri, 23 Jan 2026 09:43:17 +0100 Subject: [PATCH 15/23] docs: update changelog Signed-off-by: Tom Bosmans --- docs/changelog.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 6900f6cb..e8eb6dad 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -2,6 +2,12 @@ ## Latest +## 2026.1.23.0 + +- fix: web/reverse_proxy/configuration/stanza.py - urlencode stanza id names (specifically to handle junction stanzas, eg [jwt:/]) +- fix: web/reverse_proxy/configuration/entry.py - urlencode stanza ids, entry names and some values (specifically to handle junction stanzas, eg [jwt:/], + and values that contain url unsafe characters eg. request-log-format) + ## 2026.1.22.0 - fix: base/dsc.py - add missing parameters for Docker DSC config (#468) From 91617d3768943f9237c1661a4bd2548344bc2aa5 Mon Sep 17 00:00:00 2001 From: Tom Bosmans Date: Fri, 23 Jan 2026 09:43:28 +0100 Subject: [PATCH 16/23] test: trivial Signed-off-by: Tom Bosmans --- conftest.py | 1 + 1 file changed, 1 insertion(+) diff --git a/conftest.py b/conftest.py index d508cf43..8fd8eb34 100644 --- a/conftest.py +++ b/conftest.py @@ -30,6 +30,7 @@ def iviaServer(): print('\n') return returnValue + # ibmsecurity def pytest_runtest_setup(item): print("setting up function:", item.name) From 728e5154c1c6604eab5d3529740a678b8bcbe5fc Mon Sep 17 00:00:00 2001 From: Tom Bosmans Date: Fri, 23 Jan 2026 09:44:20 +0100 Subject: [PATCH 17/23] build: version update Signed-off-by: Tom Bosmans --- pyproject.toml | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 8ae74113..814277dd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta" [project] name = "ibmsecurity" -version = "2026.1.22.0" +version = "2026.1.23.0" authors = [ { name="IBM", email="secorch@wwpdl.vnet.ibm.com" }, ] diff --git a/setup.py b/setup.py index 154e21f0..ac76628d 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ packages=find_packages(exclude=["test.*","test"]), # 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="2026.1.22.0", + version="2026.1.23.0", description="Idempotent functions for IBM Verify Appliance REST APIs", author="IBM", author_email="secorch@wwpdl.vnet.ibm.com", From 6dc25fbd4312b039f13daf0d0b02de57401d15f9 Mon Sep 17 00:00:00 2001 From: Tom Bosmans Date: Fri, 23 Jan 2026 10:14:24 +0100 Subject: [PATCH 18/23] test: don't fail on pytest Signed-off-by: Tom Bosmans --- tox.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index 2540d390..e342a1e5 100644 --- a/tox.ini +++ b/tox.ini @@ -52,14 +52,14 @@ commands_pre = {env_python} -m pip check commands = sh -c "{env_python} -m pip freeze > {env_dir}/log/requirements.txt" - coverage run -m pytest {posargs: \ + - coverage run -m pytest {posargs: \ # -n auto \ # Using this breaks coverage (UNIQUE constraint failed: context.context) -ra \ --showlocals \ --durations=10 \ test } - {py,py310,py311,py312,py313}: sh -c "coverage combine -a -q --data-file={env_dir}/.coverage {work_dir}/*/.coverage.* && coverage xml --data-file={env_dir}/.coverage -o {env_dir}/coverage.xml --fail-under=0 && coverage report --data-file={env_dir}/.coverage" + {py,py311,py313,py314}: sh -c "coverage combine -a -q --data-file={env_dir}/.coverage {work_dir}/*/.coverage.* && coverage xml --data-file={env_dir}/.coverage -o {env_dir}/coverage.xml --fail-under=0 && coverage report --data-file={env_dir}/.coverage" allowlist_externals = bash find From e3040b233cb467dbd702b1c9b9d83fc38fa78ed8 Mon Sep 17 00:00:00 2001 From: Tom Bosmans Date: Fri, 23 Jan 2026 10:14:44 +0100 Subject: [PATCH 19/23] fix: remove requires_model Signed-off-by: Tom Bosmans --- ibmsecurity/isam/web/runtime/process.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/ibmsecurity/isam/web/runtime/process.py b/ibmsecurity/isam/web/runtime/process.py index 43368f66..34ac9e6b 100644 --- a/ibmsecurity/isam/web/runtime/process.py +++ b/ibmsecurity/isam/web/runtime/process.py @@ -2,15 +2,14 @@ import ibmsecurity.utilities.tools logger = logging.getLogger(__name__) -requires_model = "Appliance" + def get(isamAppliance, check_mode=False, force=False): """ Retrieve runtime component status """ - requires_model = None return isamAppliance.invoke_get("Retrieving web runtime component status", - "/isam/runtime_components/",requires_model=requires_model) + "/isam/runtime_components/") def _check(isamAppliance): @@ -32,6 +31,7 @@ def _check(isamAppliance): else: return check_value, warnings + def config(isamAppliance, admin_pwd, ps_mode="local", user_registry="local", ldap_host=None, ldap_port=None, ldap_dn=None, ldap_pwd=None, ldap_ssl_db=None, ldap_ssl_label=None, ldap_suffix=None, clean_ldap=False, domain="Default", admin_cert_lifetime="1460", ssl_compliance="none", isam_host=None, isam_port="7135", @@ -42,11 +42,10 @@ def config(isamAppliance, admin_pwd, ps_mode="local", user_registry="local", lda :param isamAppliance: :return: """ - requires_model = None check_value, warnings = _check(isamAppliance) - if (force is True or check_value is False): - if check_mode is True: + if (force or not check_value): + if check_mode: return isamAppliance.create_return_object(changed=True, warnings=warnings) else: json_data = { @@ -76,7 +75,8 @@ def config(isamAppliance, admin_pwd, ps_mode="local", user_registry="local", lda json_data["local_interface_only"] = local_interface_only return isamAppliance.invoke_post("Configure web runtime Component", "/isam/runtime_components/", - json_data, requires_model=requires_model) + json_data, + warnings=warnings) return isamAppliance.create_return_object(warnings=warnings) @@ -100,7 +100,7 @@ def unconfig(isamAppliance, clean=False, ldap_dn=None, ldap_pwd=None, check_mode "clean": clean, "ldap_dn": ldap_dn, "ldap_pwd": ldap_pwd - },requires_model=requires_model) + }) return isamAppliance.create_return_object(warnings=warnings) @@ -141,7 +141,7 @@ def import_config(isamAppliance, migrate_file, check_mode=False, force=False): 'filename': migrate_file, 'mimetype': 'application/octet-stream' }], - {},requires_model=requires_model) + {}) return isamAppliance.create_return_object(warnings=warnings) @@ -178,7 +178,7 @@ def execute(isamAppliance, operation='restart', check_mode=False, force=False): "/isam/runtime_components/", { "operation": operation - },requires_model=requires_model) + }) def compare(isamAppliance1, isamAppliance2): From 56db63c7dfafbf4045a104bc15af9e19822215b2 Mon Sep 17 00:00:00 2001 From: Tom Bosmans Date: Fri, 23 Jan 2026 10:14:59 +0100 Subject: [PATCH 20/23] test: tiny updates Signed-off-by: Tom Bosmans --- test/test_1_web_0_policyruntime.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test_1_web_0_policyruntime.py b/test/test_1_web_0_policyruntime.py index 5aca62ad..b899f906 100644 --- a/test/test_1_web_0_policyruntime.py +++ b/test/test_1_web_0_policyruntime.py @@ -22,6 +22,7 @@ def getTestData(): ] return testdata + @pytest.mark.order(after="test_0_base_2_ssl_personalcerts.py::test_import_personal_cert") @pytest.mark.parametrize("items", getTestData()) def test_configure_policy_runtime(iviaServer, caplog, items) -> None: From 523d1bb6c511edab50a388e1fad871479a630aa8 Mon Sep 17 00:00:00 2001 From: Tom Bosmans Date: Fri, 23 Jan 2026 10:56:19 +0100 Subject: [PATCH 21/23] fix: simpler check for existing runtime configuration Signed-off-by: Tom Bosmans --- ibmsecurity/isam/web/runtime/process.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/ibmsecurity/isam/web/runtime/process.py b/ibmsecurity/isam/web/runtime/process.py index 34ac9e6b..200e9d1e 100644 --- a/ibmsecurity/isam/web/runtime/process.py +++ b/ibmsecurity/isam/web/runtime/process.py @@ -19,15 +19,11 @@ def _check(isamAppliance): :return: """ ret_obj = get(isamAppliance) - check_value, warnings=False, ret_obj['warnings'] - - if warnings == []: - if ret_obj['data']['modecode'] == '-1': - check_value = False - return check_value, warnings - else: - check_value = True - return check_value, warnings + check_value = True + warnings = ret_obj.get("warnings", []) + if ret_obj['data'].get('modecode', '99') == '-1': + check_value = False + return check_value, warnings else: return check_value, warnings From 82c2c3d060283974aee9f614e7932746cc8ac608 Mon Sep 17 00:00:00 2001 From: Tom Bosmans Date: Fri, 23 Jan 2026 10:57:14 +0100 Subject: [PATCH 22/23] docs: update changelog Signed-off-by: Tom Bosmans --- docs/changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.md b/docs/changelog.md index e8eb6dad..0dfeb68c 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -7,6 +7,7 @@ - fix: web/reverse_proxy/configuration/stanza.py - urlencode stanza id names (specifically to handle junction stanzas, eg [jwt:/]) - fix: web/reverse_proxy/configuration/entry.py - urlencode stanza ids, entry names and some values (specifically to handle junction stanzas, eg [jwt:/], and values that contain url unsafe characters eg. request-log-format) +- fix: web/runtime/process.py - remove unnecessary `requires_model` (applicable to all current models) ## 2026.1.22.0 From b43026d7c5a7332b474c197ee26cfe4b47a35a6f Mon Sep 17 00:00:00 2001 From: Tom Bosmans Date: Fri, 23 Jan 2026 10:57:28 +0100 Subject: [PATCH 23/23] test: rollback change Signed-off-by: Tom Bosmans --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index e342a1e5..5e57a333 100644 --- a/tox.ini +++ b/tox.ini @@ -52,7 +52,7 @@ commands_pre = {env_python} -m pip check commands = sh -c "{env_python} -m pip freeze > {env_dir}/log/requirements.txt" - - coverage run -m pytest {posargs: \ + coverage run -m pytest {posargs: \ # -n auto \ # Using this breaks coverage (UNIQUE constraint failed: context.context) -ra \ --showlocals \