Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/all junctions #411

Merged
merged 14 commits into from
Feb 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion changelog.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
---
# Manual change log

## Unreleased

## 2024.2.23.0

- feature: set_all function for reverse proxy junctions
- fix: ignore_if_down cannot be set to False (#410)

## 2023.11.10.0

- fix: available_updates.py state is no longer available in 10.0.5 or higher, and too many variables in post request
Expand Down
2 changes: 2 additions & 0 deletions ibmsecurity/appliance/isamappliance.py
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,8 @@ def _invoke_request(self, func, description, uri, ignore_error, data={}, require
used directly. The invoke_get/invoke_put/etc functions should be used instead.
"""
self._log_desc(description=description)
self.session.cookies.pop('LtpaToken2', None)
self.session.cookies.pop('JSESSIONID', None)

warnings, return_call = self._process_warnings(uri=uri, requires_modules=requires_modules,
requires_version=requires_version, requires_model=requires_model,
Expand Down
625 changes: 460 additions & 165 deletions ibmsecurity/isam/web/reverse_proxy/junctions.py

Large diffs are not rendered by default.

19 changes: 19 additions & 0 deletions ibmsecurity/isam/web/reverse_proxy/junctions_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# some shared variables.
# Quick & dirty
server_fields = {'server_hostname': {'type': 'string'},
'server_port': {'type': 'number'},
'case_sensitive_url': {'type': 'yesno', 'max_version': "10.0.6.0"},
'case_insensitive_url': {'type': 'yesno', 'min_version': "10.0.6.0"},
'http_port': {'type': 'number'},
'local_ip': {'type': 'string'},
'query_contents': {'type': 'string', 'alt_name': 'query_content_url'},
'server_dn': {'type': 'string'},
'server_uuid': {'type': 'ignore'},
'virtual_hostname': {'type': 'string', 'alt_name': 'virtual_junction_hostname'},
'windows_style_url': {'type': 'yesno'},
'current_requests': {'type': 'ignore'},
'total_requests': {'type': 'ignore'},
'operation_state': {'type': 'ignore'},
'server_state': {'type': 'ignore'},
'priority': {'type': 'number', 'min_version': "10.0.2.0"},
'server_cn': {'type': 'string', 'min_version': "10.0.2.0"}}
199 changes: 148 additions & 51 deletions ibmsecurity/isam/web/reverse_proxy/junctions_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
import ibmsecurity.utilities.tools
from ibmsecurity.utilities import tools
import ibmsecurity.isam.web.reverse_proxy.junctions
from ibmsecurity.isam.web.reverse_proxy.junctions_config import server_fields
import json

logger = logging.getLogger(__name__)

uri = "/wga/reverseproxy"


def search(isamAppliance, reverseproxy_id, junction_point, server_hostname, server_port):
ret_obj_new = isamAppliance.create_return_object()
ret_obj = ibmsecurity.isam.web.reverse_proxy.junctions.get(isamAppliance, reverseproxy_id, junction_point)
Expand All @@ -19,12 +20,18 @@ def search(isamAppliance, reverseproxy_id, junction_point, server_hostname, serv

return ret_obj_new

def get(isamAppliance, reverseproxy_id, junction_point, server_hostname, server_port):
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']))
if str(server_hostname) == str(s['server_hostname']) and str(server_port) == str(s['server_port']):
ret_obj_new['data'] = s
break
return ret_obj_new

def add(isamAppliance, reverseproxy_id, junction_point, server_hostname, junction_type, server_port, server_dn=None,
stateful_junction='no', case_sensitive_url='no', windows_style_url='no', virtual_hostname=None,
virtual_https_hostname=None, query_contents=None, https_port=None, http_port=None, proxy_hostname=None,
proxy_port=None, sms_environment=None, vhost_label=None, server_uuid=None, priority=None, server_cn=None,
case_insensitive_url=None, check_mode=False, force=False):
def add(isamAppliance, reverseproxy_id, junction_point, server_hostname, server_port, junction_type="tcp", check_mode=False, force=False,
**optionargs):
"""
Adding a back-end server to an existing standard or virtual junctions

Expand Down Expand Up @@ -56,78 +63,168 @@ def add(isamAppliance, reverseproxy_id, junction_point, server_hostname, junctio
:return:
"""
# Search for the UUID of the junctioned server
if force is False:
if not force:
ret_obj = search(isamAppliance, reverseproxy_id, junction_point, server_hostname, server_port)

if force is True or ret_obj['data'] == {}:
if force or ret_obj['data'] == {}:
if check_mode is True:
return isamAppliance.create_return_object(changed=True)
else:
jct_srv_json = {
"junction_point": junction_point,
"junction_type": junction_type,
"server_hostname": server_hostname,
"server_port": server_port,
"stateful_junction": stateful_junction,
"windows_style_url": windows_style_url,
"server_port": server_port
}
for _k, _v in optionargs.items():
if _v is not None:
jct_srv_json[_k] = _v
if jct_srv_json.get('stateful_junction', None) is None:
jct_srv_json['stateful_junction'] = 'no'
if jct_srv_json.get('windows_style_url', None) is None:
jct_srv_json['windows_style_url'] = 'no'

# case_insensitive/case_sensitive
if tools.version_compare(isamAppliance.facts["version"], "10.0.6.0") >= 0:
# If no case_insensitive_url is passed, we take the old one and invert it.
# Who thinks it's a good idea to make changes in an API like this ?
if case_insensitive_url is None:
if case_sensitive_url.lower() == 'yes':
jct_srv_json["case_insensitive_url"] = 'no'
else:
if jct_srv_json.get('case_insensitive_url', None) is None:
case_sensitive_url = jct_srv_json.get('case_sensitive_url', None)
if case_sensitive_url is not None and case_sensitive_url.lower() == 'no':
jct_srv_json["case_insensitive_url"] = 'yes'
else:
jct_srv_json["case_insensitive_url"] = case_insensitive_url
else:
jct_srv_json["case_insensitive_url"] = 'no' # default
jct_srv_json.pop("case_sensitive_url", None)
else:
jct_srv_json["case_sensitive_url"] = case_sensitive_url
if https_port is not None:
jct_srv_json["https_port"] = https_port
if http_port is not None:
jct_srv_json["http_port"] = http_port
if proxy_hostname is not None:
jct_srv_json["proxy_hostname"] = proxy_hostname
if proxy_port is not None:
jct_srv_json["proxy_port"] = proxy_port
if sms_environment is not None:
jct_srv_json["sms_environment"] = sms_environment
if vhost_label is not None:
jct_srv_json["vhost_label"] = vhost_label
if server_dn is not None:
jct_srv_json["server_dn"] = server_dn
if virtual_hostname:
jct_srv_json["virtual_hostname"] = virtual_hostname
if virtual_https_hostname is not None:
jct_srv_json["virtual_https_hostname"] = virtual_https_hostname
if query_contents is not None:
jct_srv_json["query_contents"] = query_contents
if server_uuid is not None and server_uuid != '':
jct_srv_json["server_uuid"] = server_uuid
if server_cn is not None:
if tools.version_compare(isamAppliance.facts["version"], "10.0.2.0") < 0:
jct_srv_json.pop("case_insensitive_url", None)

if tools.version_compare(isamAppliance.facts["version"], "10.0.2.0") < 0:
if jct_srv_json.get('server_cn', None) is not None:
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))
server_cn = None
else:
jct_srv_json["server_cn"] = server_cn
if priority is not None:
if tools.version_compare(isamAppliance.facts["version"], "10.0.2.0") < 0:
jct_srv_json.pop("server_cn", None)

if tools.version_compare(isamAppliance.facts["version"], "10.0.2.0") < 0:
if jct_srv_json.get('priority', None) is not None:
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))
priority = None
else:
jct_srv_json["priority"] = priority

jct_srv_json.pop("priority", None)
else:
if jct_srv_json.get('priority', None) is None:
warnings.append(
"Appliance at version: {0}, priority is required on 10.0.2 or higher".format(
isamAppliance.facts["version"]))
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)

return isamAppliance.create_return_object()

def set(isamAppliance, reverseproxy_id, junction_point, server_hostname, server_port, junction_type="tcp", check_mode=False, force=False, warnings=[],
**optionargs):
"""
Adding a back-end server to an existing standard or virtual junctions

:param isamAppliance:
:param reverseproxy_id:
:param junction_point:
:param server_hostname:
:param junction_type:
:param server_port:
:param virtual_hostname:
:param virtual_https_hostname:
:param server_dn:
:param query_contents:
:param stateful_junction:
:param case_sensitive_url:
:param windows_style_url:
:param https_port:
:param http_port:
:param proxy_hostname:
:param proxy_port:
:param sms_environment:
:param vhost_label:
:param server_uuid:
:param server_cn:
:param priority:
:param check_mode:
:param force:
:return:
"""
# load option args
# server_dn=None,
# stateful_junction='no', case_sensitive_url='no', windows_style_url='no', virtual_hostname=None,
# virtual_https_hostname=None, query_contents=None, https_port=None, http_port=None, proxy_hostname=None,
# proxy_port=None, sms_environment=None, vhost_label=None, server_uuid=None,
# server_cn=None, priority='9'

# Search an existing server
ret_obj = get(isamAppliance, reverseproxy_id, junction_point, server_hostname, server_port)
exist_jct = {}
jct_srv_json = {}
if ret_obj:
exist_jct = ret_obj.get('data', {})
exist_jct.pop('current_requests', None)
exist_jct.pop('total_requests', None)
exist_jct.pop('operation_state', None)
exist_jct.pop('server_state', None)
exist_jct.pop('query_contents', None)

for _k, _v in optionargs.items():
# only keep valid arguments
if _k in list(server_fields.keys()):
jct_srv_json[_k] = _v
else:
logger.debug(f"Invalid input parameter used {_k}")
warnings.append(f"Invalid input parameter used in function junctions_server.set() : {_k}")
# add defaults
#defaults_no = ["stateful_junction", "case_sensitive_url", "windows_style_url"]
#for d in defaults_no:
# if jct_srv_json.get(d, None) is None:
# jct_srv_json[d] = 'no'
# 10.0.0.2
if jct_srv_json.get('priority', None) 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 description for this call.".format(
isamAppliance.facts["version"], priority))
jct_srv_json.pop('priority', None)
else:
if tools.version_compare(isamAppliance.facts["version"], "10.0.2.0") >= 0:
warnings.append(
"Appliance at version: {0}, priority is required".format(
isamAppliance.facts["version"]))
jct_srv_json['priority'] = "9"

# only compare values that are in the new request
exist_jct = {k: v for k, v in exist_jct.items() if k in jct_srv_json.keys()}

newJSON = json.dumps(jct_srv_json, skipkeys=True, sort_keys=True)
logger.debug(f"\nSorted Desired Junction {junction_point} - {server_hostname}:\n\n {newJSON}\n")

oldJSON = json.dumps(exist_jct, skipkeys=True, sort_keys=True)
logger.debug(f"\nSorted Current Junction {junction_point} - {server_hostname}:\n\n {oldJSON}\n")

jct_srv_json["junction_point"] = junction_point
jct_srv_json["junction_type"] = junction_type
jct_srv_json["server_hostname"] = server_hostname
jct_srv_json["server_port"] = server_port

if force or (newJSON != oldJSON):
logger.debug(f"The JSONs are different. We're going to add the servers.")
if check_mode is True:
return isamAppliance.create_return_object(changed=True, warnings=warnings)
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)
else:
logger.debug("Servers are the same")
return isamAppliance.create_return_object(warnings=warnings)


def delete(isamAppliance, reverseproxy_id, junction_point, server_hostname, server_port, check_mode=False, force=False):
"""
Expand Down
10 changes: 5 additions & 5 deletions ibmsecurity/isam/web/runtime/federated_directories/stanza.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ def add(isamAppliance, id, hostname, port, bind_dn, bind_pwd, suffix, use_ssl=Fa
'suffix': suffix
}

if ignore_if_down and tools.version_compare(isamAppliance.facts["version"], "10.0.4") >= 0:
if tools.version_compare(isamAppliance.facts["version"], "10.0.4") >= 0:
json_data['ignore_if_down'] = ignore_if_down

# Do not pass if there is no value - call fails otherwise
Expand All @@ -86,11 +86,11 @@ def update(isamAppliance, id, hostname, port, bind_dn, bind_pwd, suffix, use_ssl
"""
Update an existing federated directory
"""
if force is True or (
if force or (
_exists(isamAppliance, id) and _check(isamAppliance, id, hostname, port, bind_dn, bind_pwd,
use_ssl, client_cert_label, suffix, ignore_if_down) is False):

if check_mode is True:
if check_mode:
return isamAppliance.create_return_object(changed=True)
else:
json_data = {
Expand All @@ -101,7 +101,7 @@ def update(isamAppliance, id, hostname, port, bind_dn, bind_pwd, suffix, use_ssl
'use_ssl': use_ssl,
'suffix': suffix
}
if ignore_if_down and tools.version_compare(isamAppliance.facts["version"], "10.0.4") >= 0:
if tools.version_compare(isamAppliance.facts["version"], "10.0.4") >= 0:
json_data['ignore_if_down'] = ignore_if_down
# Do not pass if there is no value - call fails otherwise
if client_cert_label is not None:
Expand Down Expand Up @@ -170,7 +170,7 @@ def _check(isamAppliance, id, hostname, port, bind_dn, bind_pwd, use_ssl, client
}
if use_ssl is True:
set_value['client_cert_label'] = client_cert_label
if ignore_if_down and tools.version_compare(isamAppliance.facts["version"], "10.0.4") >= 0:
if tools.version_compare(isamAppliance.facts["version"], "10.0.4") >= 0:
set_value['ignore_if_down'] = ignore_if_down

newEntriesJSON = json.dumps(set_value, skipkeys=True, sort_keys=True)
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "ibmsecurity"
version = "2023.11.10.0"
version = "2024.2.23.0"
authors = [
{ name="IBM", email="secorch@us.ibm.com" },
]
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
packages = find_packages(),
# Date of release used for version - please be sure to use YYYY.MM.DD.seq#, MM and DD should be two digits e.g. 2017.02.05.0
# seq# will be zero unless there are multiple release on a given day - then increment by one for additional release for that date
version = '2023.11.10.0',
version = '2024.2.23.0',
description = 'Idempotent functions for IBM Security Appliance REST APIs',
author='IBM',
author_email='secorch@us.ibm.com',
Expand Down