Skip to content

Commit 4e3b51c

Browse files
Fix/test containers (#446)
* fix: add test for containers import zip * fix: add more values for .env * feature: test for policy attachments * fix: indentation * fix: small correction * fix: update tests * fix: reorder code * fix: policy attachents * documentation: update version * documentation: update version 2025.7.14.0 * documentation: update python versions * documentation: update python versions * documentation: update python versions
1 parent 1afa889 commit 4e3b51c

File tree

12 files changed

+573
-63
lines changed

12 files changed

+573
-63
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Code for ISVG appliance is brand new (tested with 10.0.1.0 and higher only).
99

1010
## Requirements
1111

12-
Python v3.7 and above is required for this package.
12+
Python v3.9 and above is required for this package.
1313

1414
The following Python Packages are required:
1515
1. requests - for making REST API calls

docs/changelog.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
## Latest
44

5+
## 2025.7.14.0
6+
7+
- fix: certificate_databases : python syntax (indentation)
8+
- fix: policy_attachments.py : idempotency and handle applications correctly
9+
- build: update python version support (drop 3.7; add 3.11, 3.13)
10+
511
## 2025.6.3.0
612

713
- feature: base/management_authentication.py - type federation

docs/contributing.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ IVIA_ADMIN=admin@local
3939
IVIA_PW=admin
4040
IVIA_HOST=<ip address of lmi>
4141
# IVIA_PORT = 80
42+
IVIA_SECMASTER=sec_master
43+
IVIA_SECMASTER_PW=<password for sec_master>
4244
````
4345

4446
## Standards

ibmsecurity/isam/aac/access_control/policy_attachments.py

Lines changed: 59 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,11 @@ def get(isamAppliance, server, resourceUri, check_mode=False, force=False):
2828
check_mode=check_mode,
2929
force=force,
3030
)
31-
resource_id = ret_obj["data"]
31+
resource_id = ret_obj.get("data", {})
3232

3333
if resource_id == {}:
3434
logger.info(
35-
f"Resource {server}/{resourceUri} had no match, skipping retrieval."
35+
f"Resource {server}{resourceUri} had no match, skipping retrieval."
3636
)
3737
return isamAppliance.create_return_object()
3838
else:
@@ -57,7 +57,7 @@ def get_attachments(isamAppliance, server, resourceUri, check_mode=False, force=
5757

5858
if resource_id == {}:
5959
logger.info(
60-
f"Resource {server}/{resourceUri} had no match, skipping retrieval."
60+
f"Resource {server}{resourceUri} had no match, skipping retrieval."
6161
)
6262
return isamAppliance.create_return_object()
6363
else:
@@ -77,10 +77,11 @@ def search(isamAppliance, server, resourceUri, force=False, check_mode=False):
7777
for obj in ret_obj["data"]:
7878
if obj["resourceUri"] == resourceUri and obj["server"] == server:
7979
logger.info(
80-
f"Found server/resourceUri {server}/{resourceUri} id: {obj['id']}"
80+
f"Found server/resourceUri {server}{resourceUri} id: {obj['id']}"
8181
)
8282
return_obj["data"] = obj["id"]
8383
return_obj["rc"] = 0
84+
return return_obj
8485

8586
return return_obj
8687

@@ -144,38 +145,55 @@ def config(
144145
{'name': '<definition name>', 'type': 'definition'}]
145146
"""
146147
warnings = []
147-
if force is False:
148-
ret_obj = search(isamAppliance, server, resourceUri)
148+
ret_obj = search(isamAppliance, server, resourceUri)
149149

150-
if force is True or ret_obj["data"] == {}:
151-
json_data = {"server": server, "resourceUri": resourceUri}
152-
if policyType is not None:
153-
if tools.version_compare(isamAppliance.facts["version"], "9.0.6.0") < 0:
154-
warnings.append(
155-
f"Appliance at version: {isamAppliance.facts['version']}, policyType: {cache} is not supported. Needs 9.0.6.0 or higher. Ignoring policyType for this call."
156-
)
157-
else:
158-
json_data["type"] = policyType
150+
json_data = {"server": server, "resourceUri": resourceUri}
151+
if policyType is not None:
152+
if tools.version_compare(isamAppliance.facts["version"], "9.0.6.0") < 0:
153+
warnings.append(
154+
f"Appliance at version: {isamAppliance.facts['version']}, policyType: {policyType} is not supported. Needs 9.0.6.0 or higher. Ignoring policyType for this call."
155+
)
156+
else:
157+
json_data["type"] = policyType
158+
159+
json_data["policies"] = _convert_policy_name_to_id(isamAppliance, policies)
160+
if policyCombiningAlgorithm is not None:
161+
json_data["policyCombiningAlgorithm"] = policyCombiningAlgorithm
162+
if cache is not None:
163+
if tools.version_compare(isamAppliance.facts["version"], "9.0.3.0") < 0:
164+
warnings.append(
165+
f"Appliance at version: {isamAppliance.facts['version']}, cache: {cache} is not supported. Needs 9.0.3.0 or higher. Ignoring cache for this call."
166+
)
167+
else:
168+
json_data["cache"] = int(cache)
159169

160-
json_data["policies"] = _convert_policy_name_to_id(isamAppliance, policies)
161-
if policyCombiningAlgorithm is not None:
162-
json_data["policyCombiningAlgorithm"] = policyCombiningAlgorithm
163-
if cache is not None:
164-
if tools.version_compare(isamAppliance.facts["version"], "9.0.3.0") < 0:
165-
warnings.append(
166-
f"Appliance at version: {isamAppliance.facts['version']}, cache: {cache} is not supported. Needs 9.0.3.0 or higher. Ignoring cache for this call."
167-
)
168-
else:
169-
json_data["cache"] = int(cache)
170-
if check_mode is True:
170+
if force or ret_obj.get("data", {}) == {}:
171+
if check_mode:
171172
return isamAppliance.create_return_object(changed=True, warnings=warnings)
172173
else:
173174
return isamAppliance.invoke_post(
174175
"Configure a resource", uri, json_data, warnings=warnings
175176
)
176-
177-
return isamAppliance.create_return_object()
178-
177+
else:
178+
# Idempotency check
179+
logger.debug(f"Idempotency check for {server}{resourceUri}")
180+
curConfig = get(isamAppliance, server, resourceUri)
181+
curConfig = curConfig.get("data", {})
182+
# Need to get rid of lastmodified and name in each policy
183+
_policies = list(map(lambda item: item.pop('lastmodified', None), curConfig.get("policies", [])))
184+
_policies = list(map(lambda item: item.pop('name', None), curConfig.get("policies", [])))
185+
if tools.json_equals(curConfig, json_data):
186+
# No updates needed
187+
return isamAppliance.create_return_object()
188+
else:
189+
if check_mode:
190+
return isamAppliance.create_return_object(changed=True, warnings=warnings)
191+
else:
192+
# delete and add again
193+
delete(isamAppliance, server, resourceUri)
194+
return isamAppliance.invoke_post(
195+
"Reconfigure a resource", uri, json_data, warnings=warnings
196+
)
179197

180198
def update(
181199
isamAppliance,
@@ -306,17 +324,22 @@ def publish(isamAppliance, server, resourceUri, check_mode=False, force=False):
306324
"""
307325
ret_obj = get(isamAppliance, server, resourceUri)
308326

309-
if force or (
310-
ret_obj["data"] != {} and (
311-
ret_obj["data"]["deployrequired"] or not ret_obj["data"]["deployed"]
312-
) and ret_obj["data"]["policies"] != []
313-
):
327+
deploy_required = False
328+
if ret_obj.get("data", {}).get("deployrequired", False) or not ret_obj.get("data", {}).get("deployed", False):
329+
deploy_required = True
330+
331+
logger.debug(f"\n\nDeploy required: {deploy_required}\n\n")
332+
333+
resource_id = ret_obj.get('data', {}).get('id', None)
334+
logger.debug(f"\n\nID: {resource_id}\n\n")
335+
# if (force or deploy_required) and ret_obj.get("data", {}).get("policies", []) != []:
336+
if (force or deploy_required) and resource_id is not None:
314337
if check_mode:
315338
return isamAppliance.create_return_object(changed=True)
316339
else:
317340
return isamAppliance.invoke_put(
318341
"Publish the policy attachments for a resource",
319-
f"{uri}/deployment/{ret_obj['data']['id']}",
342+
f"{uri}/deployment/{resource_id}",
320343
{},
321344
)
322345

@@ -333,7 +356,7 @@ def publish_list(isamAppliance, attachments, check_mode=False, force=False):
333356
id_list = []
334357
for attach in attachments:
335358
ret_obj = get(isamAppliance, attach["server"], attach["resourceUri"])
336-
if force is True or ret_obj["data"]["deployrequired"] is True:
359+
if force or ret_obj["data"]["deployrequired"]:
337360
id_list.append(ret_obj["data"]["id"])
338361
logger.debug(f"Attachments: {id_list}")
339362

ibmsecurity/isam/base/container_ext/volume.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,13 @@ def import_zip(isamAppliance, filename, volume_name=None, volume_id=None, check_
6666
import os.path
6767
logger.debug(f"\n\nTrying to import {filename}\n\n")
6868

69+
if volume_id is None and volume_name is None:
70+
return isamAppliance.create_return_object(warnings=["Volume name or volume id need to be specified"])
71+
6972
if volume_id is None:
7073
volume_id = search(isamAppliance, volume_name)
7174
volume_id = volume_id.get('data', None)
72-
73-
if volume_id is None and volume_name is None:
74-
return isamAppliance.create_return_object(warnings=["Volume name or volume id need to be specified"])
75+
logger.debug(f"\n\nVolume id is now set to {volume_id}\n\n")
7576

7677
if force or os.path.exists(filename):
7778
return isamAppliance.invoke_put_files(
@@ -109,6 +110,7 @@ def search(isamAppliance, volume_name, check_mode=False, force=False):
109110
if obj["name"] == volume_name:
110111
return_obj["data"] = obj["id"]
111112
return_obj["rc"] = 0
113+
logger.debug(f"\n\nFound {obj['id']} for {volume_name}\n\n")
112114
break
113115

114116
return return_obj

ibmsecurity/isam/base/ssl_certificates/certificate_databases.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ def create(isamAppliance, kdb_name, type='kdb',
8383
"""
8484
warnings = []
8585
if force or not _check(isamAppliance, kdb_name):
86+
logger.debug("Creating new keystore")
8687
if check_mode:
8788
return isamAppliance.create_return_object(changed=True)
8889
else:
@@ -126,16 +127,22 @@ def create(isamAppliance, kdb_name, type='kdb',
126127
json_data.pop("rfs_port", None)
127128
json_data.pop("rfs_auth", None)
128129

129-
retObj = isamAppliance.invoke_post( f"Creating certificate database {kdb_name}",
130+
retObj = isamAppliance.invoke_post( f"Creating certificate database {kdb_name}",
130131
"/isam/ssl_certificates",
131132
json_data,
132133
warnings=warnings,
133134
ignore_error=True
134135
)
135-
if retObj.get("rc", 0) == 400:
136+
if retObj.get("rc", 0) == 400:
136137
warnings.append(f"Invalid type (you need to install an extension to support network hsm {type})")
137138
return isamAppliance.create_return_object(warnings=warnings)
138-
else:
139+
elif retObj.get("rc", 0) > 400 and retObj.get("rc", 0) < 500:
140+
warnings.append(f"HTTP error !")
141+
return isamAppliance.create_return_object(warnings=warnings)
142+
elif retObj.get("rc", 0) >= 500 and retObj.get("rc", 0) < 600:
143+
warnings.append(f"Server error !")
144+
return isamAppliance.create_return_object(warnings=warnings)
145+
else:
139146
return retObj
140147

141148
return isamAppliance.create_return_object()
@@ -243,7 +250,7 @@ def set(isamAppliance, cert_id, description=None, type="kdb", check_mode=False,
243250
warnings = []
244251
desc_match = True # This will remain True even when cert db is not found!
245252

246-
if type == "kdb":
253+
if type in ("p12", "kdb"):
247254
if not force:
248255
if description is None:
249256
desc_match = True
@@ -302,11 +309,12 @@ def _check(isamAppliance, id):
302309
Check if certificate database already exists
303310
"""
304311
ret_obj = get_all(isamAppliance)
305-
312+
logger.debug(f"\nCHECKING IF {id} EXISTS")
306313
for certdb in ret_obj['data']:
314+
logger.info("Cert databases id " + certdb['id'])
307315
if certdb['id'] == id:
308316
return True
309-
317+
logger.debug(f"\n{id} DOES NOT EXIST\n")
310318
return False
311319

312320

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@ build-backend = "setuptools.build_meta"
77

88
[project]
99
name = "ibmsecurity"
10-
version = "2025.6.3.0"
10+
version = "2025.7.14.0"
1111
authors = [
1212
{ name="IBM", email="[email protected]" },
1313
]
1414
description = "Idempotent functions for IBM Security Appliance REST APIs"
1515
readme = "README.md"
16-
requires-python = ">=3.7"
16+
requires-python = ">=3.9"
1717
license = "Apache-2.0"
1818
classifiers = [
1919
"Programming Language :: Python :: 3",

setup.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,19 @@
55
packages=find_packages(),
66
# 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
77
# seq# will be zero unless there are multiple release on a given day - then increment by one for additional release for that date
8-
version="2025.6.3.0",
8+
version="2025.7.14.0",
99
description="Idempotent functions for IBM Security Appliance REST APIs",
1010
author="IBM",
1111
author_email="[email protected]",
1212
url="",
1313
classifiers=[
1414
"Programming Language :: Python",
15-
"Programming Language :: Python :: 3.7",
1615
"Programming Language :: Python :: 3.8",
1716
"Programming Language :: Python :: 3.9",
17+
"Programming Language :: Python :: 3.10",
18+
"Programming Language :: Python :: 3.11",
19+
"Programming Language :: Python :: 3.12",
20+
"Programming Language :: Python :: 3.13",
1821
"License :: OSI Approved :: Apache Software License",
1922
"Operating System :: OS Independent",
2023
"Development Status :: 5 - Production/Stable",

test/files/oidcop.zip

2.25 KB
Binary file not shown.

0 commit comments

Comments
 (0)