def _check_id(isamAppliance, id, trace_id, level, flush_interval, rollover_size, max_rollover_files, compress): """ Check to see if the file_id exists or not """ ret_obj = get_all(isamAppliance, id) id_exists, warnings = False, ret_obj['warnings'] new_obj = { 'id': trace_id, 'level': level, 'flush_interval': flush_interval, 'rollover_size': rollover_size, 'max_rollover_files': max_rollover_files, 'compress': compress } sorted_new_obj = json_sort(new_obj) if not warnings: for obj in ret_obj['data']: del obj['file_size'] sorted_obj = json_sort(obj) if sorted_obj == sorted_new_obj: id_exists = True return id_exists, warnings
def update(isamAppliance, aliasDBType, properties=None, check_mode=False, force=False): """ Update the alias settings """ ret_obj = get(isamAppliance) update_required = False if ret_obj['data']['aliasDBType'] == aliasDBType: if 'properties' in ret_obj['data']: props = ret_obj['data']['properties'] sorted_props = json_sort(props) sorted_properties = json_sort(properties) if sorted_props != sorted_properties: update_required = True else: update_required = True if force is True or update_required is True: if check_mode is True: return isamAppliance.create_return_object(changed=True) else: return isamAppliance.invoke_put( "Update the alias settings", "{0}".format(uri), { 'aliasDBType': aliasDBType, 'properties': properties }, requires_modules=requires_modules, requires_version=requires_version) return isamAppliance.create_return_object()
def _check_update(isamAppliance, instance_id, component_id, status, rollover_size, max_rollover_files, compress): """ Check to see if the file_id exists or not """ ret_obj = get(isamAppliance, instance_id) new_obj = { 'id': component_id, 'status': status, 'rollover_size': rollover_size, 'max_rollover_files': max_rollover_files, 'compress': compress } sorted_new_obj = json_sort(new_obj) for obj in ret_obj['data']: if obj['id'] == component_id: if obj['status'] == "Off" and status == "Off": return True else: del obj['file_size'] sorted_obj = json_sort(obj) if sorted_obj == sorted_new_obj: return True return False
def _check(isamAppliance, cors_policy_name, allowed_origins, allow_credentials, exposed_headers, handle_preflight, allowed_methods, allowed_headers, max_age): ret_obj = get_all(isamAppliance) exist = False json_data = {} for obj in ret_obj['data']: if obj['name'] == cors_policy_name: current_data = obj exist = True if exist is False: return False, ret_obj['warnings'], json_data json_data = { "name": cors_policy_name, "allowed_origins": allowed_origins, "allow_credentials": allow_credentials, "exposed_headers": exposed_headers, "handle_preflight": handle_preflight, "allowed_methods": allowed_methods, "allowed_headers": allowed_headers, "max_age": max_age } sorted_obj1 = tools.json_sort(json_data) logger.debug("Sorted input: {0}".format(sorted_obj1)) sorted_obj2 = tools.json_sort(current_data) logger.debug("Sorted existing data: {0}".format(sorted_obj2)) if sorted_obj1 != sorted_obj2: logger.info("Changes detected, update needed.") return True, ret_obj['warnings'], json_data else: return False, ret_obj['warnings'], json_data
def _check(isamAppliance, cluster_json): """ Check if provided json values match the configuration on appliance :param isamAppliance: :param cluster_json: :return: """ ret_obj = get(isamAppliance) sorted_ret_obj = tools.json_sort(ret_obj['data']) sorted_json_data = tools.json_sort(cluster_json) logger.debug("Sorted Existing Data:{0}".format(sorted_ret_obj)) logger.debug("Sorted Desired Data:{0}".format(sorted_json_data)) for key, value in cluster_json.iteritems(): try: if isinstance(value, list): if ibmsecurity.utilities.tools.json_sort( ret_obj['data'][key] != ibmsecurity.utilities.tools.json_sort(value)): logger.debug( "For key: {0}, values: {1} and {2} do not match.".format(key, value, ret_obj['data'][key])) return False else: if ret_obj['data'][key] != value: logger.debug( "For key: {0}, values: {1} and {2} do not match.".format(key, value, ret_obj['data'][key])) return False except: # In case there is an error looking up the key in existing configuration (missing) logger.debug("Exception processing Key: {0} Value: {1} - missing key in current config?".format(key, value)) return False logger.debug("JSON provided already is contained in current appliance configuration.") return True
def _check(isamAppliance, cluster_json): """ Check if provided json values match the configuration on appliance :param isamAppliance: :param cluster_json: :return: """ obj = {"value": False, "warnings": ""} ret_obj = get(isamAppliance) sorted_ret_obj = tools.json_sort(ret_obj['data']) sorted_json_data = tools.json_sort(cluster_json) logger.debug("Sorted Existing Data:{0}".format(sorted_ret_obj)) logger.debug("Sorted Desired Data:{0}".format(sorted_json_data)) if sorted_ret_obj != sorted_json_data: logger.info("Existing and input data do not match - updated needed.") obj['value'] = False obj['warnings'] = ret_obj['warnings'] return obj else: obj['value'] = True obj['warnings'] = ret_obj['warnings'] return obj
def _check_contents(isamAppliance, name, max_pooled_connections, idle_timeout, connect_timeout, io_timeout, health_check_interval, servers, cross_domain_support, matching_hosts): ret_obj = get(isamAppliance, name) current_content = ret_obj['data'] warnings = ret_obj['warnings'] json_data = { 'name': name, 'max-pooled-connections': max_pooled_connections, 'idle-timeout': idle_timeout, 'connect-timeout': connect_timeout, 'io-timeout': io_timeout, 'health-check-interval': health_check_interval } if servers is not None: json_data['servers'] = servers if cross_domain_support is not None: json_data['cross-domain-support'] = cross_domain_support if matching_hosts is not None: json_data['matching-hosts'] = matching_hosts sorted_obj1 = tools.json_sort(json_data) logger.debug("Sorted sorted_obj1: {0}".format(sorted_obj1)) sorted_obj2 = tools.json_sort(current_content) logger.debug("Sorted sorted_obj2: {0}".format(sorted_obj2)) if sorted_obj1 != sorted_obj2: logger.info("Changes detected, update needed.") return False, warnings, json_data else: return True, warnings, json_data
def set(isamAppliance, server, port, protocol, name, tag, facility, severity, check_mode=False, force=False): """ Set a remote syslog forwarding source """ ret_obj = forwarder.get_all(isamAppliance=isamAppliance, check_mode=check_mode, force=force) existing_forwarder, i, existing_forwarder_source, j = _find_forwarder_source( ret_obj, server, port, protocol, name) json_data = { 'name': name, 'tag': tag, 'facility': facility, 'severity': severity } warnings = [] update_required = False if existing_forwarder is None: warnings.append("Unable to find forwarder for {} {} {}".format( server, port, protocol)) else: if existing_forwarder_source is None: json_to_post = ret_obj['data'] json_to_post[i]['sources'].append(json_data) update_required = True else: sorted_json_data = tools.json_sort(json_data) logger.debug("Sorted input: {0}".format(sorted_json_data)) sorted_ret_obj = tools.json_sort(existing_forwarder_source) logger.debug("Sorted existing data: {0}".format(sorted_ret_obj)) if sorted_ret_obj != sorted_json_data: logger.info("Changes detected, update needed.") ret_obj['data'][i]['sources'][j] = json_data json_to_post = ret_obj['data'] update_required = True if update_required: if check_mode: return isamAppliance.create_return_object(changed=True, warnings=warnings) else: return forwarder._update_forwarder_policy(isamAppliance, json_to_post, warnings=warnings) return isamAppliance.create_return_object(warnings=warnings)
def _check(isamAppliance, json_data): ret_obj = get(isamAppliance) sorted_json1 = tools.json_sort(ret_obj['data']) sorted_json2 = tools.json_sort(json_data) if sorted_json1 == sorted_json2: return False, ret_obj['warnings'] else: return True, ret_obj['warnings']
def update(isamAppliance, name, properties, attributes=None, description=None, type="JavaScript", new_name=None, check_mode=False, force=False): """ Update a specific JavaScript policy information point """ ret_obj = search(isamAppliance, name=name) id = ret_obj['data'] update_required = False if id != {}: ret_obj = get(isamAppliance, name=name) if new_name != None: json_data = _create_json(name=new_name, properties=properties, attributes=attributes, description=description, type=type) else: json_data = _create_json(name=name, properties=properties, attributes=attributes, description=description, type=type) sorted_json_data = json_sort(json_data) logger.debug("Sorted input: {0}".format(sorted_json_data)) del ret_obj['data']['id'] del ret_obj['data']['predefined'] sorted_ret_obj = json_sort(ret_obj['data']) logger.debug("Sorted existing data: {0}".format(sorted_ret_obj)) if sorted_json_data != sorted_ret_obj: update_required = True else: logger.info("PIP '{0}' does not exists. Skipping update.".format(name)) warnings = ["PIP '{0}' does not exists. Skipping update.".format(name)] return isamAppliance.create_return_object(warnings=warnings) if force is True or update_required is True: if check_mode is True: return isamAppliance.create_return_object(changed=True) else: return isamAppliance.invoke_put( "Update a specific JavaScript policy information point", "{0}/{1}".format(uri, id), json_data, requires_modules=requires_modules, requires_version=requires_version ) if update_required is False: logger.info("Input is the same as current PIP '{0}'. Skipping update.".format(name)) return isamAppliance.create_return_object()
def set(isamAppliance, server, port=514, protocol='udp', debug=False, keyfile=None, ca_certificate=None, client_certificate=None, permitted_peers=None, sources=[], check_mode=False, force=False): ret_obj = get_all(isamAppliance, check_mode, force) existing_forwarder, i = _find_forwarder(ret_obj, server, port, protocol) json_data = { 'server': server, 'port': port, 'protocol': protocol, 'debug': debug, 'sources': sources } if keyfile is not None and keyfile != '': json_data['keyfile'] = keyfile if ca_certificate is not None and ca_certificate != '': json_data['ca_certificate'] = ca_certificate if client_certificate is not None and client_certificate != '': json_data['client_certificate'] = client_certificate if permitted_peers is not None and permitted_peers != '': json_data['permitted_peers'] = permitted_peers update_required = False # Check of the given server/port/protocol exist - if not then we add it if existing_forwarder is None or force is True: json_to_post = ret_obj['data'] json_to_post.append(json_data) update_required = True else: sorted_json_data = tools.json_sort(json_data) logger.debug("Sorted input: {0}".format(sorted_json_data)) sorted_ret_obj = tools.json_sort(existing_forwarder) logger.debug("Sorted existing data: {0}".format(sorted_ret_obj)) if sorted_ret_obj != sorted_json_data: logger.info("Changes detected, update needed.") ret_obj['data'][i] = json_data json_to_post = ret_obj['data'] update_required = True if update_required: if check_mode: return isamAppliance.create_return_object(changed=True) else: return _update_forwarder_policy(isamAppliance, json_to_post) return isamAppliance.create_return_object()
def update(isamAppliance, name, connection, description='', locked=False, new_name=None, check_mode=False, force=False): """ Modifying a Web Service connection Use new_name to rename the connection, cannot compare password so update will take place everytime """ ret_obj = get(isamAppliance, name) warnings = ret_obj["warnings"] if ret_obj["data"] == {}: warnings.append("Web Service connection {0} not found, skipping update.".format(name)) return isamAppliance.create_return_object(warnings=warnings) else: id = ret_obj["data"]["uuid"] needs_update = False json_data = _create_json(name=name, description=description, locked=locked, connection=connection) if new_name is not None: # Rename condition json_data['name'] = new_name if force is not True: if 'uuid' in ret_obj['data']: del ret_obj['data']['uuid'] if 'password' in connection: warnings.append("Since existing password cannot be read for ws connections - this parameter will be ignored for idempotency. Add 'force' parameter to update the connection with a new password.") connection.pop('password', None) sorted_ret_obj = tools.json_sort(ret_obj['data']) sorted_json_data = tools.json_sort(json_data) logger.debug("Sorted Existing Data:{0}".format(sorted_ret_obj)) logger.debug("Sorted Desired Data:{0}".format(sorted_json_data)) if sorted_ret_obj != sorted_json_data: needs_update = True if force is True or _check_exists(isamAppliance, name): if check_mode is True: return isamAppliance.create_return_object(changed=True) else: json_data = _create_json(name=name, description=description, locked=locked, connection=connection) if new_name is not None: # Rename condition json_data['name'] = new_name ret_obj = search(isamAppliance, name=name) id = ret_obj['data'] return isamAppliance.invoke_put( "Modifying a Web Service connection", "{0}/{1}/v1".format(uri, id), json_data, requires_modules=requires_modules, requires_version=requires_version) return isamAppliance.create_return_object()
def _check(isamAppliance, cluster_json, ignore_password_for_idempotency): """ Check if provided json values match the configuration on appliance :param isamAppliance: :param cluster_json: :return: """ ret_obj = get(isamAppliance) sorted_ret_obj = tools.json_sort(ret_obj['data']) sorted_json_data = tools.json_sort(cluster_json) logger.debug("Sorted Existing Data:{0}".format(sorted_ret_obj)) logger.debug("Sorted Desired Data:{0}".format(sorted_json_data)) if ignore_password_for_idempotency: temp = copy.deepcopy( cluster_json ) # deep copy neccessary: otherwise password parameter would be removed from desired config dict 'cluster_json'. Comparison is done with temp<>ret_obj object for idx, x in enumerate(cluster_json): if "password" in x: logger.debug( "Ignoring JSON password entry: '{0}' to satisfy idempotency." .format(x)) del temp[x] logger.debug("Passwordless JSON to Apply: {0}".format(temp)) else: temp = cluster_json for key, value in temp.iteritems(): try: if isinstance(value, list): if ibmsecurity.utilities.tools.json_sort( ret_obj['data'][key] != ibmsecurity.utilities.tools.json_sort(value)): logger.debug( "For key: {0}, values: {1} and {2} do not match.". format(key, value, ret_obj['data'][key])) return False else: if ret_obj['data'][key] != value: logger.debug( "For key: {0}, values: {1} and {2} do not match.". format(key, value, ret_obj['data'][key])) return False except: # In case there is an error looking up the key in existing configuration (missing) logger.debug( "Exception processing Key: {0} Value: {1} - missing key in current config?" .format(key, value)) return False logger.debug( "JSON provided already is contained in current appliance configuration." ) return True
def update(isamAppliance, app_id, platform, provider, new_app_id=None, check_mode=False, force=False): """ Update a push notification registration """ ret_obj = search(isamAppliance, app_id=app_id) id = ret_obj['data'] update_required = False if id != {}: ret_obj = _get(isamAppliance, id=id) json_data = { 'platform': platform, 'provider': provider } if new_app_id is not None: json_data['app_id'] = new_app_id, else: json_data['app_id'] = app_id sorted_json_data = json_sort(json_data) logger.debug("Sorted input: {0}".format(sorted_json_data)) del ret_obj['data']['push_id'] sorted_ret_obj = json_sort(ret_obj['data']) logger.debug("Sorted existing data: {0}".format(sorted_ret_obj)) if sorted_json_data != sorted_ret_obj: update_required = True else: logger.info( "Input is the same as current Push notification registration '{0}'. Skipping update.".format(app_id)) else: logger.info("Push notification registration '{0}' does not exists. Skipping update.".format(app_id)) warnings = ["Push notification registration '{0}' does not exists. Skipping update.".format(app_id)] return isamAppliance.create_return_object(warnings=warnings) if force is True or update_required is True: if check_mode is True: return isamAppliance.create_return_object(changed=True) else: return isamAppliance.invoke_put( "Update a push notification registration", "{0}/{1}".format(uri, id), json_data, requires_modules=requires_modules, requires_version=requires_version ) return isamAppliance.create_return_object()
def _check(isamAppliance, scim_configuration): """ Check if scim configuration is identical with server """ ret_obj = get_all(isamAppliance) logger.debug("Comparing server scim configuration with desired configuration.") logger.debug("Server JSON: {0}".format(tools.json_sort(ret_obj['data']))) logger.debug("Desired JSON: {0}".format(tools.json_sort(scim_configuration))) if tools.json_sort(scim_configuration) != tools.json_sort(ret_obj['data']): return False logger.debug("Server configuration is identical with desired configuration. No change necessary.") return True
def update(isamAppliance, name, connection, locked=False, description='', new_name=None, ignore_password_for_idempotency=False, check_mode=False, force=False): """ Modifying an ISAM Runtime server connection Use new_name to rename the connection. """ ret_obj = get(isamAppliance, name) warnings = ret_obj["warnings"] if ret_obj["data"] == {}: warnings.append("ISAM Runtime server connection {0} not found, skipping update.".format(name)) return isamAppliance.create_return_object(warnings=warnings) else: id = ret_obj["data"]["uuid"] needs_update = False json_data = _create_json(name=name, description=description, locked=locked, connection=connection) if new_name is not None: # Rename condition json_data['name'] = new_name if force is not True: if 'uuid' in ret_obj['data']: del ret_obj['data']['uuid'] if ignore_password_for_idempotency: if 'bindPwd' in connection: warnings.append("Request made to ignore bindPwd for idempotency check.") connection.pop('bindPwd', None) sorted_ret_obj = tools.json_sort(ret_obj['data']) sorted_json_data = tools.json_sort(json_data) logger.debug("Sorted Existing Data:{0}".format(sorted_ret_obj)) logger.debug("Sorted Desired Data:{0}".format(sorted_json_data)) if sorted_ret_obj != sorted_json_data: needs_update = True if force is True or needs_update is True: if check_mode is True: return isamAppliance.create_return_object(changed=True, warnings=warnings) else: return isamAppliance.invoke_put( "Modifying an ISAM Runtime server connection", "{0}/{1}/v1".format(uri, id), json_data, requires_modules=requires_modules, requires_version=requires_version ) return isamAppliance.create_return_object(warnings=warnings)
def _check(isamAppliance, scim_configuration): """ Check if scim configuration is identical with server """ ret_obj = get_all(isamAppliance) logger.debug("Comparing server scim configuration with desired configuration.") cur_sorted_json = tools.json_sort(ret_obj['data']) logger.debug("Server JSON : {0}".format(cur_sorted_json)) given_sorted_json = tools.json_sort(scim_configuration) logger.debug("Desired JSON: {0}".format(given_sorted_json)) if cur_sorted_json != given_sorted_json: return False logger.debug("Changes detected!") else: logger.debug("Server configuration is identical with desired configuration. No change necessary.") return True
def _check(isamAppliance, policy_name, groups, attributes): json_data = {} ret_obj = get_all(isamAppliance) for obj in ret_obj['data']: if obj['name'] == policy_name: obj2 = { 'name': policy_name, 'groups': groups, 'attributes': attributes } sorted_obj1 = tools.json_sort(obj) logger.debug("Sorted existing data: {0}".format(sorted_obj1)) sorted_obj2 = tools.json_sort(obj2) logger.debug("Sorted input data: {0}".format(sorted_obj2)) if sorted_obj1 != sorted_obj2: logger.info("Changes detected, update needed.") return True, ret_obj['warnings'], obj2 return False, ret_obj['warnings'], json_data
def _check(isamAppliance, fido2_metadata): """ Check if FIDO2 metadata configuration is identical with server """ ret_obj = get_all(isamAppliance) logger.debug("Comparing server FIDO2 metadata configuration with desired configuration.") # Converting python ret_obj['data'] dict to valid JSON (RFC 8259) # e.g. converts python boolean 'True' -> to JSON literal lowercase value 'true' cur_json_string = json.dumps(ret_obj['data']) cur_sorted_json = tools.json_sort(cur_json_string) logger.debug("Server JSON : {0}".format(cur_sorted_json)) given_json_string = json.dumps(fido2_metadata) given_sorted_json = tools.json_sort(given_json_string) logger.debug("Desired JSON: {0}".format(given_sorted_json)) if cur_sorted_json != given_sorted_json: return False logger.debug("Changes detected!") else: logger.debug("Server configuration is identical with desired configuration. No change necessary.") return True
def _check(isamAppliance, service_name, json_data): """ Checks update for idempotency """ ret_obj = get(isamAppliance, service_name) warnings = ret_obj['warnings'] if ret_obj['data'] == {}: return False, warnings sorted_ret_obj = tools.json_sort(ret_obj['data']) sorted_json_data = tools.json_sort(json_data) logger.debug("Sorted Existing Data:{0}".format(sorted_ret_obj)) logger.debug("Sorted Desired Data:{0}".format(sorted_json_data)) if sorted_ret_obj != sorted_json_data: return True, warnings else: return False, warnings
def _check_resource_content(isamAppliance, instance_name, resource_server_name, method, path, policy, cors_policy, static_response_headers, url_aliases, name, rate_limiting_policy, documentation): ret_obj = get(isamAppliance, instance_name, resource_server_name, path, method) current_data = ret_obj['data'] del current_data['id'] json_data = { 'method': method, 'policy': policy, 'cors_policy': cors_policy, 'static_response_headers': static_response_headers, 'url_aliases': url_aliases } if tools.version_compare(isamAppliance.facts["version"], "10.0.0") < 0: json_data['path'] = path else: json_data['path'] = "{0}{1}".format(resource_server_name, path) if name is not None: json_data['name'] = name if rate_limiting_policy is not None: json_data['rate_limiting_policy'] = rate_limiting_policy if documentation is not None: json_data['documentation'] = documentation sorted_obj1 = tools.json_sort(json_data) logger.debug("Sorted sorted_obj1: {0}".format(sorted_obj1)) sorted_obj2 = tools.json_sort(current_data) logger.debug("Sorted sorted_obj2: {0}".format(sorted_obj2)) if sorted_obj1 != sorted_obj2: logger.info("Changes detected, update needed.") return True, ret_obj['warnings'], json_data return False, ret_obj['warnings'], json_data
def _check_update(isamAppliance, json_data): """ idempotency test for each parameter """ ret_obj = get(isamAppliance) warnings = ret_obj['warnings'] if 'enabled' in ret_obj['data']: if ret_obj['data']['enabled'] is False: return False, warnings else: del ret_obj['data']['enabled'] sorted_ret_obj = tools.json_sort(ret_obj['data']) sorted_json_data = tools.json_sort(json_data) logger.debug("Sorted Existing Data:{0}".format(sorted_ret_obj)) logger.debug("Sorted Desired Data:{0}".format(sorted_json_data)) if sorted_ret_obj != sorted_json_data: return True, warnings else: return False, warnings
def _check(isamAppliance, db_json, ignore_password_for_idempotency=False): """ Check if provided json values match the configuration on appliance :param isamAppliance: :param db_json: :return: """ obj = {'value': True, 'warnings': ""} ret_obj = get(isamAppliance) obj['warnings'] = ret_obj['warnings'] del_password = False if ignore_password_for_idempotency is True: if 'hvdb_password' in ret_obj['data']: del ret_obj['data']['hvdb_password'] if 'hvdb_password' in db_json: password = db_json['hvdb_password'] del_password = True del db_json['hvdb_password'] sorted_ret_obj = tools.json_sort(ret_obj['data']) sorted_json_data = tools.json_sort(db_json) logger.debug("Sorted Existing Data:{0}".format(sorted_ret_obj)) logger.debug("Sorted Desired Data:{0}".format(sorted_json_data)) if del_password is True: db_json['hvdb_password'] = password if sorted_ret_obj != sorted_json_data: obj['value'] = False return obj else: obj['value'] = True return obj
def _check(isamAppliance, json_data): """ Check for idempotency """ ret_obj = get(isamAppliance) warnings = ret_obj['warnings'] change_required = False if json_data['local'] is True: if 'local' in ret_obj['data']: if json_data['local'] != ret_obj['data']['local']: change_required = True else: sorted_ret_obj = tools.json_sort(ret_obj['data']) sorted_json_data = tools.json_sort(json_data) logger.debug("Sorted Existing Data:{0}".format(sorted_ret_obj)) logger.debug("Sorted Desired Data:{0}".format(sorted_json_data)) if sorted_ret_obj != sorted_json_data: change_required = True return change_required, warnings
def _check_update(isamAppliance, service_name, address, port, json_data): """ idempontency test """ ret_obj = get(isamAppliance, service_name, address, port) warnings = ret_obj['warnings'] ret_data = ret_obj['data'] if 'id' in ret_data: del ret_data['id'] else: return False, warnings sorted_ret_data = tools.json_sort(ret_data) sorted_json_data = tools.json_sort(json_data) logger.debug("Sorted Existing Data:{0}".format(sorted_ret_data)) logger.debug("Sorted Desired Data:{0}".format(sorted_json_data)) if sorted_ret_data != sorted_json_data: return True, warnings else: return False, warnings
def set(isamAppliance, addr, hostnames, check_mode=False, force=False): """ Setting a host record (IP address and host name) Sample JSON: {"addr":"127.0.0.2","hostnames":[{"name":"test1.ibm.com"}, {"name":"test2.ibm.com"}]} """ add_required = False delete_required = False if force is False: # Check if record exists if _check(isamAppliance, addr) is True: ret_obj = get(isamAppliance, host_address=addr) # Check if hostnames match (case sensitive check) if json_sort(ret_obj['data']) != json_sort(hostnames): delete_required = True add_required = True else: add_required = True if force is True or delete_required is True: # No need to check if record exists - force delete delete(isamAppliance, host_address=addr, check_mode=check_mode, force=True) if force is True or add_required is True: # No need to check for add - force add return add(isamAppliance, addr=addr, hostnames=hostnames, check_mode=check_mode, force=True) return isamAppliance.create_return_object()
def set(isamAppliance, reverseproxy_id, junction_point, server_hostname, server_port, junction_type="tcp", virtual_hostname=None, server_dn=None, query_contents=None, stateful_junction=None, case_sensitive_url=None, windows_style_url=None, https_port=None, http_port=None, proxy_hostname=None, proxy_port=None, sms_environment=None, vhost_label=None, junction_hard_limit=None, junction_soft_limit=None, basic_auth_mode=None, tfim_sso=None, remote_http_header=None, preserve_cookie=None, cookie_include_path=None, transparent_path_junction=None, mutual_auth=None, insert_session_cookies=None, request_encoding=None, enable_basic_auth=None, key_label=None, gso_resource_group=None, junction_cookie_javascript_block=None, client_ip_http=None, version_two_cookies=None, ltpa_keyfile=None, authz_rules=None, fsso_config_file=None, username=None, password=None, server_uuid=None, local_ip=None, ltpa_keyfile_password=None, delegation_support=None, scripting_support=None, insert_ltpa_cookies=None, check_mode=False, force=False): """ Setting a standard or virtual junction - compares with existing junction and replaces if changes are detected TODO: Compare all the parameters in the function - LTPA, BA are some that are not being compared """ warnings = [] add_required = False if force is False: # Check if record exists logger.debug("Check if the junction exists.") if _check(isamAppliance, reverseproxy_id, junction_point) is True: logger.debug("Junction exists. Compare junction details.") ret_obj = get(isamAppliance, reverseproxy_id=reverseproxy_id, junctionname=junction_point) server_found = False logger.debug("See if the backend junction server matches any on the junction. Look for just one match.") srvs = ret_obj['data']['servers'] srvs_len = len(srvs) 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)) server_found = True server_json = { 'server_hostname': server_hostname, 'server_port': str(server_port) } if case_sensitive_url is None: server_json['case_sensitive_url'] = 'no' else: server_json['case_sensitive_url'] = case_sensitive_url if http_port is None: server_json['http_port'] = str(server_port) else: server_json['http_port'] = str(http_port) if local_ip is None: server_json['local_ip'] = '' else: server_json['local_ip'] = local_ip if query_contents is None: server_json['query_content_url'] = '/cgi-bin/query_contents' else: server_json['query_content_url'] = query_contents if server_dn is None: server_json['server_dn'] = '' else: server_json['server_dn'] = server_dn if server_uuid is not None: server_json['server_uuid'] = server_uuid else: # Server UUID gets generated if not specified if 'server_uuid' in srv: del srv['server_uuid'] if virtual_hostname is None: if 'virtual_junction_hostname' in srv: del srv['virtual_junction_hostname'] else: server_json['virtual_junction_hostname'] = virtual_hostname if windows_style_url is None: server_json['windows_style_url'] = 'no' else: server_json['windows_style_url'] = windows_style_url # Delete dynamic data shown when we get junctions details if 'current_requests' in srv: del srv['current_requests'] if 'total_requests' in srv: del srv['total_requests'] if 'operation_state' in srv: del srv['operation_state'] if 'server_state' in srv: del srv['server_state'] # Not sure what this attribute is supposed to contain? if 'query_contents' in srv: del srv['query_contents'] if tools.json_sort(server_json) != tools.json_sort(srv): logger.debug("Servers are found to be different. See following JSON for difference.") logger.debug("New Server JSON: {0}".format(tools.json_sort(server_json))) logger.debug("Old Server JSON: {0}".format(tools.json_sort(srv))) add_required = True break if server_found is False: add_required = True elif add_required is False: exist_jct = ret_obj['data'] jct_json = { 'junction_point': junction_point, 'junction_type': junction_type.upper() } if authz_rules is None: jct_json['authz_rules'] = 'no' else: jct_json['authz_rules'] = authz_rules if basic_auth_mode is None: jct_json['basic_auth_mode'] = 'filter' else: jct_json['basic_auth_mode'] = basic_auth_mode if client_ip_http is None or client_ip_http.lower() == 'no': jct_json['client_ip_http'] = 'do not insert' elif client_ip_http.lower() == 'yes': jct_json['client_ip_http'] = 'insert' else: jct_json['client_ip_http'] = client_ip_http if cookie_include_path is None: jct_json['cookie_include_path'] = 'no' else: jct_json['cookie_include_path'] = cookie_include_path if delegation_support is None: jct_json['delegation_support'] = 'no' else: jct_json['delegation_support'] = delegation_support if fsso_config_file is None: jct_json['fsso_config_file'] = 'disabled' else: jct_json['fsso_config_file'] = fsso_config_file if insert_session_cookies is None: jct_json['insert_session_cookies'] = 'no' else: jct_json['insert_session_cookies'] = insert_session_cookies if junction_hard_limit is None: jct_json['junction_hard_limit'] = '0 - using global value' else: jct_json['junction_hard_limit'] = str(junction_hard_limit) if junction_soft_limit is None: jct_json['junction_soft_limit'] = '0 - using global value' else: jct_json['junction_soft_limit'] = str(junction_soft_limit) if junction_cookie_javascript_block is not None: jct_json['junction_cookie_javascript_block'] = junction_cookie_javascript_block if mutual_auth is None: jct_json['mutual_auth'] = 'no' else: jct_json['mutual_auth'] = mutual_auth if preserve_cookie is None: jct_json['preserve_cookie'] = 'no' else: jct_json['preserve_cookie'] = preserve_cookie if remote_http_header is None: jct_json['remote_http_header'] = 'do not insert' else: jct_json['remote_http_header'] = remote_http_header # To allow for multiple header values to be sorted during compare convert retrieved data into array if exist_jct['remote_http_header'].startswith('insert - '): exist_jct['remote_http_header'] = (exist_jct['remote_http_header'][9:]).split(' ') if request_encoding is None: jct_json['request_encoding'] = 'UTF-8, URI Encoded' else: jct_json['request_encoding'] = request_encoding if scripting_support is None: jct_json['scripting_support'] = 'no' else: jct_json['scripting_support'] = scripting_support if stateful_junction is None: jct_json['stateful_junction'] = 'no' else: jct_json['stateful_junction'] = stateful_junction if tfim_sso is None: jct_json['tfim_sso'] = 'no' else: jct_json['tfim_sso'] = tfim_sso if transparent_path_junction is None: jct_json['transparent_path_junction'] = 'no' else: jct_json['transparent_path_junction'] = transparent_path_junction # TODO: Not sure of how to match following attributes! Need to revisit. # TODO: Not all function parameters are being checked - need to add! del exist_jct['boolean_rule_header'] del exist_jct['forms_based_sso'] del exist_jct['http_header_ident'] del exist_jct['session_cookie_backend_portal'] # We are already comparing server details - so remove this from this compare del exist_jct['servers'] # Delete dynamic data shown when we get junctions details del exist_jct['active_worker_threads'] if tools.json_sort(jct_json) != tools.json_sort(exist_jct): logger.debug("Junctions are found to be different. See following JSON for difference.") logger.debug("New Junction JSON: {0}".format(tools.json_sort(jct_json))) logger.debug("Old Junction JSON: {0}".format(tools.json_sort(exist_jct))) add_required = True if add_required is True and srvs_len > 1: warnings.append( "Junction will replaced. Existing multiple servers #{0} will be overwritten. Please re-add as needed.".format( srvs_len)) else: add_required = True if force is True or add_required is True: # Junction force add will replace the junction, no need for delete (force set to True as a result) return add(isamAppliance=isamAppliance, reverseproxy_id=reverseproxy_id, junction_point=junction_point, server_hostname=server_hostname, server_port=server_port, junction_type=junction_type, virtual_hostname=virtual_hostname, server_dn=server_dn, query_contents=query_contents, stateful_junction=stateful_junction, case_sensitive_url=case_sensitive_url, windows_style_url=windows_style_url, https_port=https_port, http_port=http_port, proxy_hostname=proxy_hostname, proxy_port=proxy_port, sms_environment=sms_environment, vhost_label=vhost_label, junction_hard_limit=junction_hard_limit, junction_soft_limit=junction_soft_limit, basic_auth_mode=basic_auth_mode, tfim_sso=tfim_sso, remote_http_header=remote_http_header, preserve_cookie=preserve_cookie, cookie_include_path=cookie_include_path, transparent_path_junction=transparent_path_junction, mutual_auth=mutual_auth, insert_session_cookies=insert_session_cookies, request_encoding=request_encoding, enable_basic_auth=enable_basic_auth, key_label=key_label, gso_resource_group=gso_resource_group, junction_cookie_javascript_block=junction_cookie_javascript_block, client_ip_http=client_ip_http, version_two_cookies=version_two_cookies, ltpa_keyfile=ltpa_keyfile, authz_rules=authz_rules, fsso_config_file=fsso_config_file, username=username, password=password, server_uuid=server_uuid, local_ip=local_ip, ltpa_keyfile_password=ltpa_keyfile_password, delegation_support=delegation_support, scripting_support=scripting_support, insert_ltpa_cookies=insert_ltpa_cookies, check_mode=check_mode, force=True, warnings=warnings) return isamAppliance.create_return_object()
def _check(isamAppliance, name, chainName, requestType, description, tokenType, xPath, signResponses, signatureKey, validateRequests, validationKey, sendValidationConfirmation, issuer, appliesTo, properties, new_name=None): """ Check and return True if update needed """ update_required = False json_data = {"name": name, "requestType": requestType} ret_obj = templates.search(isamAppliance, name=chainName) if ret_obj['data'] == {}: logger.info("Unable to find a valid STS Chain Template for {0}".format( chainName)) return None, update_required, json_data else: json_data['chainId'] = ret_obj['data'] ret_obj = get(isamAppliance, name) if ret_obj['data'] == {}: logger.info("STS Chain not found, returning no update required.") return None, update_required, json_data else: chain_id = ret_obj['data']['id'] del ret_obj['data']['id'] if new_name is not None: json_data['name'] = new_name if description is not None: json_data['description'] = description elif 'description' in ret_obj['data']: del ret_obj['data']['description'] if tokenType is not None: json_data['tokenType'] = tokenType if xPath is not None: json_data['xPath'] = xPath if signResponses is not None: json_data['signResponses'] = signResponses if signatureKey is not None: json_data['signatureKey'] = signatureKey if validateRequests is not None: json_data['validateRequests'] = validateRequests if validationKey is not None: json_data['validationKey'] = validationKey if sendValidationConfirmation is not None: json_data[ 'sendValidationConfirmation'] = sendValidationConfirmation if issuer is not None: json_data['issuer'] = issuer if appliesTo is not None: json_data['appliesTo'] = appliesTo if properties is not None: json_data['properties'] = properties sorted_json_data = tools.json_sort(json_data) logger.debug("Sorted input: {0}".format(sorted_json_data)) sorted_ret_obj = tools.json_sort(ret_obj['data']) logger.debug("Sorted existing data: {0}".format(sorted_ret_obj)) if sorted_ret_obj != sorted_json_data: logger.info("Changes detected, update needed.") update_required = True return chain_id, update_required, json_data
def update(isamAppliance, name, description="", accessPolicyName=None, grantTypes=["AUTHORIZATION_CODE"], tcmBehavior="NEVER_PROMPT", accessTokenLifetime=3600, accessTokenLength=20, enforceSingleUseAuthorizationGrant=False, authorizationCodeLifetime=300, authorizationCodeLength=30, issueRefreshToken=True, refreshTokenLength=40, maxAuthorizationGrantLifetime=604800, enforceSingleAccessTokenPerGrant=False, enableMultipleRefreshTokensForFaultTolerance=False, pinPolicyEnabled=False, pinLength=4, tokenCharSet="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", oidc=None, check_mode=False, force=False): """ Update a specified API protection definition """ ret_obj = get(isamAppliance, name) warnings = ret_obj["warnings"] if ret_obj["data"] == {}: warnings.append("Definiton {0} not found, skipping update.".format(name)) return isamAppliance.create_return_object(warnings=warnings) else: defn_id = ret_obj["data"]["id"] needs_update = False json_data = { "name": name, "description": description, "grantTypes": grantTypes, "tcmBehavior": tcmBehavior, "accessTokenLifetime": int(accessTokenLifetime), "accessTokenLength": int(accessTokenLength), "enforceSingleUseAuthorizationGrant": enforceSingleUseAuthorizationGrant, "authorizationCodeLifetime": int(authorizationCodeLifetime), "authorizationCodeLength": int(authorizationCodeLength), "issueRefreshToken": issueRefreshToken, "refreshTokenLength": int(refreshTokenLength), "maxAuthorizationGrantLifetime": int(maxAuthorizationGrantLifetime), "enforceSingleAccessTokenPerGrant": enforceSingleAccessTokenPerGrant, "enableMultipleRefreshTokensForFaultTolerance": enableMultipleRefreshTokensForFaultTolerance, "pinPolicyEnabled": pinPolicyEnabled, "pinLength": int(pinLength), "tokenCharSet": tokenCharSet } if accessPolicyName is not None: if tools.version_compare(isamAppliance.facts["version"], "9.0.4.0") < 0: warnings.append( "Appliance at version: {0}, access policy: {1} is not supported. Needs 9.0.4.0 or higher. Ignoring access policy for this call.".format( isamAppliance.facts["version"], oidc)) accessPolicyName = None else: ret_obj = access_policy.search(isamAppliance, accessPolicyName, check_mode=check_mode, force=force) if ret_obj['data'] == {}: warnings = ret_obj["warnings"] warnings.append( "Access Policy {0} is not found. Cannot update definition.".format(accessPolicyName)) return isamAppliance.create_return_object(warnings=warnings) else: json_data["accessPolicyId"] = int(ret_obj['data']) if oidc is not None: if tools.version_compare(isamAppliance.facts["version"], "9.0.4.0") < 0: warnings.append( "Appliance at version: {0}, oidc: {1} is not supported. Needs 9.0.4.0 or higher. Ignoring oidc for this call.".format( isamAppliance.facts["version"], oidc)) oidc = None else: if 'attributeSources' in oidc: oidc['attributeSources'] = _map_oidc_attributeSources(isamAppliance, oidc['attributeSources'], check_mode, force) json_data["oidc"] = oidc if force is not True: if 'datecreated' in ret_obj['data']: del ret_obj['data']['datecreated'] if 'id' in ret_obj['data']: del ret_obj['data']['id'] if 'lastmodified' in ret_obj['data']: del ret_obj['data']['lastmodified'] if 'mappingRules' in ret_obj['data']: del ret_obj['data']['mappingRules'] # Inspecting oidcConfig and remove missing or None attributes in returned object if oidc is not None and 'oidc' in ret_obj['data']: if 'enabled' in ret_obj['data']['oidc'] and ret_obj['data']['oidc']['enabled'] is None: del ret_obj['data']['oidc']['enabled'] if 'iss' in ret_obj['data']['oidc'] and ret_obj['data']['oidc']['iss'] is None: del ret_obj['data']['oidc']['iss'] if 'poc' in ret_obj['data']['oidc'] and ret_obj['data']['oidc']['poc'] is None: del ret_obj['data']['oidc']['poc'] if 'lifetime' in ret_obj['data']['oidc'] and ret_obj['data']['oidc']['lifetime'] is None: del ret_obj['data']['oidc']['lifetime'] if 'alg' in ret_obj['data']['oidc'] and ret_obj['data']['oidc']['alg'] is None: del ret_obj['data']['oidc']['alg'] if 'db' in ret_obj['data']['oidc'] and ret_obj['data']['oidc']['db'] is None: del ret_obj['data']['oidc']['db'] if 'cert' in ret_obj['data']['oidc'] and ret_obj['data']['oidc']['cert'] is None: del ret_obj['data']['oidc']['cert'] if 'attributeSources' in ret_obj['data']['oidc'] and ret_obj['data']['oidc']['attributeSources'] is None: del ret_obj['data']['oidc']['attributeSources'] # Inspecting oidcEncConfig and remove missing or None attributes in returned object if 'enc' in ret_obj['data']['oidc'] and ret_obj['data']['oidc']['enc'] is not None: if 'enabled' in ret_obj['data']['oidc']['enc'] and ret_obj['data']['oidc']['enc']['enabled'] is None: del ret_obj['data']['oidc']['enc']['enabled'] if 'alg' in ret_obj['data']['oidc']['enc'] and ret_obj['data']['oidc']['enc']['alg'] is None: del ret_obj['data']['oidc']['enc']['alg'] if 'enc' in ret_obj['data']['oidc']['enc'] and ret_obj['data']['oidc']['enc']['enc'] is None: del ret_obj['data']['oidc']['enc']['enc'] # For dynamicClients & issueSecret parameters # # If the values for dynamicClients or issueSecret are missing, then they are # considered to be of the value "false" by the appliance, this allows for old # configuration to be forward compatible, without the function of the # definition being changed by the same payload. if 'dynamicClients' in json_data['oidc']: if tools.version_compare(isamAppliance.facts["version"], "9.0.5.0") < 0: warnings.append( "Appliance at version: {0}, dynamicClients: {1} is not supported. Needs 9.0.5.0 or higher. Ignoring dynamicClients for this call.".format( isamAppliance.facts["version"], json_data['oidc']['dynamicClients'])) del json_data['oidc']['dynamicClients'] else: if tools.version_compare(isamAppliance.facts["version"], "9.0.5.0") >= 0: if 'dynamicClients' in ret_obj['data']['oidc'] and ret_obj['data']['oidc'][ 'dynamicClients'] is False: del ret_obj['data']['oidc']['dynamicClients'] if 'issueSecret' in json_data['oidc']: if tools.version_compare(isamAppliance.facts["version"], "9.0.5.0") < 0: warnings.append( "Appliance at version: {0}, issueSecret: {1} is not supported. Needs 9.0.5.0 or higher. Ignoring issueSecret for this call.".format( isamAppliance.facts["version"], json_data['oidc']['issueSecret'])) del json_data['oidc']['issueSecret'] else: if tools.version_compare(isamAppliance.facts["version"], "9.0.5.0") >= 0: if 'issueSecret' in ret_obj['data']['oidc'] and ret_obj['data']['oidc']['issueSecret'] is False: del ret_obj['data']['oidc']['issueSecret'] sorted_ret_obj = tools.json_sort(ret_obj['data']) sorted_json_data = tools.json_sort(json_data) logger.debug("Sorted Existing Data:{0}".format(sorted_ret_obj)) logger.debug("Sorted Desired Data:{0}".format(sorted_json_data)) if sorted_ret_obj != sorted_json_data: needs_update = True if force is True or needs_update is True: if check_mode is True: return isamAppliance.create_return_object(changed=True, warnings=warnings) else: return isamAppliance.invoke_put( "Update a specified API protection definition", "{0}/{1}".format(uri, defn_id), json_data, requires_modules=requires_modules, requires_version=requires_version, warnings=warnings) return isamAppliance.create_return_object(warnings=warnings)
def update(isamAppliance, name, definitionName, companyName, redirectUri=None, companyUrl=None, contactPerson=None, contactType=None, email=None, phone=None, otherInfo=None, clientId=None, clientSecret=None, requirePkce=None, encryptionDb=None, encryptionCert=None, jwksUri=None, extProperties=None, check_mode=False, force=False, new_name=None): """ Update a specified mapping rule """ ret_obj = definitions.search(isamAppliance, definitionName, check_mode=check_mode, force=force) if ret_obj['data'] == {}: warnings = ret_obj["warnings"] warnings.append( "API Protection Definition {0} is not found. Cannot process client request." .format(definitionName)) return isamAppliance.create_return_object(warnings=warnings) else: definition = ret_obj['data'] ret_obj = get(isamAppliance, name) warnings = ret_obj["warnings"] if ret_obj["data"] == {}: warnings.append("Client {0} not found, skipping update.".format(name)) return isamAppliance.create_return_object(warnings=warnings) else: id = ret_obj["data"]["id"] needs_update = False # Create a simple json with just the main client attributes json_data = {"definition": definition, "companyName": companyName} if new_name is not None: json_data['name'] = new_name else: json_data['name'] = name if force is not True: del ret_obj['data']['id'] # Add attributes that have been supplied... otherwise skip them. if redirectUri is not None: json_data["redirectUri"] = redirectUri elif 'redirectUri' in ret_obj['data']: del ret_obj['data']['redirectUri'] if companyUrl is not None: json_data["companyUrl"] = companyUrl elif 'companyUrl' in ret_obj['data']: del ret_obj['data']['companyUrl'] if contactPerson is not None: json_data["contactPerson"] = contactPerson elif 'contactPerson' in ret_obj['data']: del ret_obj['data']['contactPerson'] if contactType is not None: json_data["contactType"] = contactType elif 'contactType' in ret_obj['data']: del ret_obj['data']['contactType'] if email is not None: json_data["email"] = email elif 'email' in ret_obj['data']: del ret_obj['data']['email'] if phone is not None: json_data["phone"] = phone elif 'phone' in ret_obj['data']: del ret_obj['data']['phone'] if otherInfo is not None: json_data["otherInfo"] = otherInfo elif 'otherInfo' in ret_obj['data']: del ret_obj['data']['otherInfo'] if clientId is not None: json_data["clientId"] = clientId elif 'clientId' in ret_obj['data']: del ret_obj['data']['clientId'] if clientSecret is not None: json_data["clientSecret"] = clientSecret elif 'clientSecret' in ret_obj['data']: del ret_obj['data']['clientSecret'] if requirePkce is not None: if tools.version_compare(isamAppliance.facts["version"], "9.0.4.0") < 0: warnings.append( "Appliance at version: {0}, requirePkce: {1} is not supported. Needs 9.0.4.0 or higher. Ignoring requirePkce for this call." .format(isamAppliance.facts["version"], requirePkce)) else: json_data["requirePkce"] = requirePkce elif 'requirePkce' in ret_obj['data']: del ret_obj['data']['requirePkce'] if encryptionDb is not None: if tools.version_compare(isamAppliance.facts["version"], "9.0.4.0") < 0: warnings.append( "Appliance at version: {0}, encryptionDb: {1} is not supported. Needs 9.0.4.0 or higher. Ignoring encryptionDb for this call." .format(isamAppliance.facts["version"], encryptionDb)) else: json_data["encryptionDb"] = encryptionDb elif 'encryptionDb' in ret_obj['data']: del ret_obj['data']['encryptionDb'] if encryptionCert is not None: if tools.version_compare(isamAppliance.facts["version"], "9.0.4.0") < 0: warnings.append( "Appliance at version: {0}, encryptionCert: {1} is not supported. Needs 9.0.4.0 or higher. Ignoring encryptionCert for this call." .format(isamAppliance.facts["version"], encryptionCert)) else: json_data["encryptionCert"] = encryptionCert elif 'encryptionCert' in ret_obj['data']: del ret_obj['data']['encryptionCert'] if jwksUri is not None: if tools.version_compare(isamAppliance.facts["version"], "9.0.4.0") < 0: warnings.append( "Appliance at version: {0}, jwksUri: {1} is not supported. Needs 9.0.4.0 or higher. Ignoring jwksUri for this call." .format(isamAppliance.facts["version"], jwksUri)) else: json_data["jwksUri"] = jwksUri elif 'jwksUri' in ret_obj['data']: del ret_obj['data']['jwksUri'] if extProperties is not None: if tools.version_compare(isamAppliance.facts["version"], "9.0.5.0") < 0: warnings.append( "Appliance at version: {0}, extProperties: {1} is not supported. Needs 9.0.5.0 or higher. Ignoring extProperties for this call." .format(isamAppliance.facts["version"], extProperties)) else: json_data["extProperties"] = extProperties elif 'extProperties' in ret_obj['data']: del ret_obj['data']['extProperties'] sorted_ret_obj = tools.json_sort(ret_obj['data']) sorted_json_data = tools.json_sort(json_data) logger.debug("Sorted Existing Data:{0}".format(sorted_ret_obj)) logger.debug("Sorted Desired Data:{0}".format(sorted_json_data)) if sorted_ret_obj != sorted_json_data: needs_update = True if force is True or needs_update is True: if check_mode is True: return isamAppliance.create_return_object(changed=True, warnings=warnings) else: return isamAppliance.invoke_put("Update a specified mapping rule", "{0}/{1}".format(uri, id), json_data, requires_modules=requires_modules, requires_version=requires_version, warnings=warnings) return isamAppliance.create_return_object(warnings=warnings)