diff --git a/code/zato-cli/src/zato/cli/enmasse.py b/code/zato-cli/src/zato/cli/enmasse.py index 5e59183921..e5557c24ac 100644 --- a/code/zato-cli/src/zato/cli/enmasse.py +++ b/code/zato-cli/src/zato/cli/enmasse.py @@ -17,7 +17,7 @@ # Zato from zato.cli import ManageCommand -from zato.common.api import All_Sec_Def_Types, DATA_FORMAT, GENERIC as COMMON_GENERIC, LDAP as COMMON_LDAP, \ +from zato.common.api import All_Sec_Def_Types, Data_Format, GENERIC as COMMON_GENERIC, LDAP as COMMON_LDAP, \ NotGiven, TLS as COMMON_TLS from zato.common.typing_ import cast_ @@ -66,6 +66,7 @@ # ################################################################################################################################ +outconn_wsx = COMMON_GENERIC.CONNECTION.TYPE.OUTCONN_WSX outconn_ldap = COMMON_GENERIC.CONNECTION.TYPE.OUTCONN_LDAP # We need to have our own version because type "bearer_token" exists in enmasse only. @@ -267,6 +268,7 @@ class Include_Type: 'channel_plain_http': 'channel_rest', 'outconn_plain_http': 'outgoing_rest', 'zato_generic_connection_outconn-ldap': 'outgoing_ldap', + 'zato_generic_connection_outconn-wsx': 'outgoing_wsx', } # ################################################################################################################################ @@ -1111,30 +1113,72 @@ def _needs_change_password(self, item_type, attrs, is_edit): def _import(self, item_type, attrs, is_edit): - # First, resolve values pointing to environment variables + # Zato + from zato.common.util.config import extract_param_placeholders + + # First, resolve values pointing to parameter placeholders and environment variables .. for key, orig_value in attrs.items(): + # .. add type hints .. key = cast_('str', key) orig_value = cast_('any_', orig_value) + # .. preprocess values only if they are strings .. if isinstance(orig_value, str): - if orig_value.startswith(zato_enmasse_env1): - _prefix = zato_enmasse_env1 - elif orig_value.startswith(zato_enmasse_env2): - _prefix = zato_enmasse_env2 - else: - _prefix = None + # .. assume there will be no placeholders for this value .. + has_params = False + + # .. extract any potential placeholders .. + params = extract_param_placeholders(orig_value) + + # .. go through each placeholder .. + for param in params: + + # .. indicate that we actually do have a placeholder .. + has_params = True + + # .. check if it points to an environment variable .. + if zato_enmasse_env2 in param: - if _prefix: + # .. we are here if we can find an environment variable .. + # .. based on a placeholder parameter, so we now need .. + # .. to extract the value of this variable or use a default one .. + # .. in case the value does not exist .. + env_variable_name = param.replace(zato_enmasse_env2, '') + env_variable_name = env_variable_name[1:-1] - value = orig_value.split(_prefix) - value = value[1] - if not value: - raise Exception('Could not build a value from `{}` in `{}`'.format(orig_value, item_type)) + # .. let's find this variable or use the default one .. + env_value = os.environ.get(env_variable_name, 'Missing_Value_' + env_variable_name) + + # .. now, we can insert this variable in the original value .. + orig_value = orig_value.replace(param, env_value) + + # .. if we have at least one placeholder, we can populate the new value already here .. + if has_params: + attrs[key] = orig_value + + # .. otherwise, we still need to check if the entire value is not an environment variable .. + else: + + if orig_value.startswith(zato_enmasse_env1): + _prefix = zato_enmasse_env1 + elif orig_value.startswith(zato_enmasse_env2): + _prefix = zato_enmasse_env2 else: - value = os.environ.get(value) - attrs[key] = value + _prefix = None + + if _prefix: + + value = orig_value.split(_prefix) + value = value[1] + + if not value: + raise Exception('Could not build a value from `{}` in `{}`'.format(orig_value, item_type)) + else: + value = os.environ.get(value) + + attrs[key] = value # # Preprocess the data to be imported @@ -1164,7 +1208,7 @@ def _import(self, item_type, attrs, is_edit): elif item_type == 'oauth': if not 'data_format' in attrs: - attrs['data_format'] = DATA_FORMAT.JSON + attrs['data_format'] = Data_Format.JSON if not 'client_id_field' in attrs: attrs['client_id_field'] = 'client_id' @@ -2005,7 +2049,30 @@ def _pre_process_input(self, data:'strdict') -> 'strdict': value['type_'] = wrapper_type # .. populate wrapper type-specific attributes .. - if wrapper_type == outconn_ldap: + if wrapper_type == outconn_wsx: + + if not 'is_outconn' in value: + value['is_outconn'] = True + + if not 'is_channel' in value: + value['is_channel'] = False + + if not 'is_internal' in value: + value['is_internal'] = False + + if not 'pool_size' in value: + value['pool_size'] = 1 + + if not 'sec_use_rbac' in value: + value['sec_use_rbac'] = False + + if not 'is_zato' in value: + value['is_zato'] = False + + if not 'data_format' in value: + value['data_format'] = Data_Format.JSON + + elif wrapper_type == outconn_ldap: # .. passwords are to be turned into secrets .. if password := value.pop('password', None): @@ -2575,7 +2642,7 @@ def _preprocess_item_attrs( # .. the data format of REST objects defaults to JSON which is why we do not return it, unless it is different .. if item_type in {'channel_plain_http', 'outconn_plain_http', 'zato_generic_rest_wrapper'}: - if item_copy.get('data_format') == DATA_FORMAT.JSON: + if item_copy.get('data_format') == Data_Format.JSON: _ = item.pop('data_format', None) return item @@ -2750,7 +2817,8 @@ def write_output( # .. now, replace generic connection types which are more involved .. new_names = { - 'outgoing_ldap': [] + 'outgoing_ldap': [], + 'outgoing_wsx': [], } for old_name, value_list in to_write.items(): value_list = cast_('anylist', value_list) @@ -3072,7 +3140,6 @@ def _run_import(self) -> 'anylist': args.path = sys.argv[1] args.input = sys.argv[2] if 'import' in args else '' - # args.path = '' enmasse = Enmasse(args) enmasse.run(args) diff --git a/code/zato-common/src/zato/common/util/config.py b/code/zato-common/src/zato/common/util/config.py index 72439abad7..bd526c95b3 100644 --- a/code/zato-common/src/zato/common/util/config.py +++ b/code/zato-common/src/zato/common/util/config.py @@ -13,6 +13,9 @@ # Bunch from bunch import Bunch +# parse +from parse import PARSE_RE as parse_re + # Zato from zato.common.api import Secret_Shadow from zato.common.const import SECRETS @@ -135,5 +138,21 @@ def replace_query_string_items(server:'ParallelServer', data:'any_') -> 'str': # .. and return it to our caller. return data +# ################################################################################################################################ + +def extract_param_placeholders(data:'str') -> 'any_': + + # Parse out groups for path parameters .. + groups = parse_re.split(data) + + # .. go through each group .. + for group in groups: + + # .. if it is a parameter placeholder .. + if group and group[0] == '{': + + # .. yield it to our caller. + yield group + # ################################################################################################################################ # ################################################################################################################################ diff --git a/code/zato-server/src/zato/server/base/worker/__init__.py b/code/zato-server/src/zato/server/base/worker/__init__.py index ebe1b31337..70087bb31c 100644 --- a/code/zato-server/src/zato/server/base/worker/__init__.py +++ b/code/zato-server/src/zato/server/base/worker/__init__.py @@ -38,7 +38,7 @@ from zato.common import broker_message from zato.common.api import CHANNEL, CONNECTION, DATA_FORMAT, FILE_TRANSFER, GENERIC as COMMON_GENERIC, \ HotDeploy, HTTP_SOAP_SERIALIZATION_TYPE, IPC, NOTIF, PUBSUB, RATE_LIMIT, SEC_DEF_TYPE, simple_types, \ - URL_TYPE, WEB_SOCKET, Wrapper_Name_Prefix_List, ZATO_NONE, ZATO_ODB_POOL_NAME, ZMQ + URL_TYPE, WEB_SOCKET, Wrapper_Name_Prefix_List, ZATO_DEFAULT, ZATO_NONE, ZATO_ODB_POOL_NAME, ZMQ from zato.common.broker_message import code_to_name, GENERIC as BROKER_MSG_GENERIC, SERVICE from zato.common.const import SECRETS from zato.common.dispatch import dispatcher @@ -473,6 +473,18 @@ def _update_aws_config(self, msg:'Bunch') -> 'None': msg.metadata = parse_extra_into_dict(msg.metadata_) +# ################################################################################################################################ + + def _get_tls_verify_from_config(self, config:'any_') -> 'bool': + + tls_config = self.worker_config.tls_ca_cert[config.sec_tls_ca_cert_name] + tls_config = tls_config.config + tls_config = tls_config.value + tls_from_payload = get_tls_from_payload(tls_config) + tls_verify = get_tls_ca_cert_full_path(self.server.tls_dir, tls_from_payload) + + return tls_verify + # ################################################################################################################################ def _http_soap_wrapper_from_config(self, config:'Bunch', has_sec_config:'bool'=True) -> 'BaseHTTPSOAPWrapper': @@ -578,14 +590,12 @@ def _http_soap_wrapper_from_config(self, config:'Bunch', has_sec_config:'bool'=T tls_verify = False else: - tls_verify = get_tls_ca_cert_full_path(self.server.tls_dir, get_tls_from_payload( - self.worker_config.tls_ca_cert[config.sec_tls_ca_cert_name].config.value)) + tls_verify = self._get_tls_verify_from_config(config) # < 3.2 else: - if config.get('sec_tls_ca_cert_id') and config.sec_tls_ca_cert_id != ZATO_NONE: - tls_verify = get_tls_ca_cert_full_path(self.server.tls_dir, get_tls_from_payload( - self.worker_config.tls_ca_cert[config.sec_tls_ca_cert_name].config.value)) + if not config.get('sec_tls_ca_cert_id') and config.sec_tls_ca_cert_id in {ZATO_DEFAULT, ZATO_NONE}: + tls_verify = self._get_tls_verify_from_config(config) else: tls_verify = False diff --git a/code/zato-server/src/zato/server/connection/http_soap/outgoing.py b/code/zato-server/src/zato/server/connection/http_soap/outgoing.py index 9db1b98cd6..b12edc1be2 100644 --- a/code/zato-server/src/zato/server/connection/http_soap/outgoing.py +++ b/code/zato-server/src/zato/server/connection/http_soap/outgoing.py @@ -19,9 +19,6 @@ # gevent from gevent.lock import RLock -# parse -from parse import PARSE_RE - # requests from requests import Response as _RequestsResponse from requests.adapters import HTTPAdapter @@ -41,6 +38,7 @@ from zato.common.marshal_.api import extract_model_class, is_list, Model from zato.common.typing_ import cast_ from zato.common.util.api import get_component_name +from zato.common.util.config import extract_param_placeholders from zato.common.util.open_ import open_rb from zato.server.connection.queue import ConnectionQueue @@ -446,16 +444,13 @@ def set_address_data(self) -> 'None': to extract any named parameters that will have to be passed in by users during actual calls to the resource. """ - self.address = '{}{}'.format(self.config['address_host'], self.config['address_url_path']) - groups = PARSE_RE.split(self.config['address_url_path']) - logger.debug('self.address:[%s], groups:[%s]', self.address, groups) - - for group in groups: - if group and group[0] == '{': - self.path_params.append(group[1:-1]) + # Set the full adddress .. + self.address = '{}{}'.format(self.config['address_host'], self.config['address_url_path']) - logger.debug('self.address:[%s], self.path_params:[%s]', self.address, self.path_params) + # .. and parse out placeholders for path parameters. + for param_name in extract_param_placeholders(self.config['address_url_path']): + self.path_params.append(param_name[1:-1]) # ################################################################################################################################ # ################################################################################################################################ diff --git a/code/zato-web-admin/src/zato/admin/static/js/http_soap/index.js b/code/zato-web-admin/src/zato/admin/static/js/http_soap/index.js index 290e4c61f2..fabc7eae04 100644 --- a/code/zato-web-admin/src/zato/admin/static/js/http_soap/index.js +++ b/code/zato-web-admin/src/zato/admin/static/js/http_soap/index.js @@ -217,11 +217,13 @@ $.fn.zato.http_soap.data_table.new_row = function(item, data, include_tr) { row += String.format('