From ca4f4e7712bd7960970f1b10fc859c8bc4679c18 Mon Sep 17 00:00:00 2001 From: tbordaz Date: Tue, 9 May 2023 15:06:55 +0200 Subject: [PATCH 1/5] Issue 5722 - RFE When a filter contains 'nsrole', improve response time by rewriting the filter (#5723) Bug description: 'nsrole' is a virtual attribute and is not indexed. With a poorly selective filter like below the search may be not indexed "(&(nsrole=cn=managed_role,cn=suffix)(objectclass=posixAccount)))" The RFE is to rewrite the filter component contains 'nsrole' attribute type. Rewritten component can then been indexed Fix description: For managed role, it replaces 'nsrole' with 'nsroleDN' attribute type For filtered roled, it replace the 'nsrole' component with the nsRoleFilter value relates: #5722 Reviewed by: Pierre Rogier (Thanks) --- dirsrvtests/tests/suites/roles/basic_test.py | 257 ++++++++++++++++++- ldap/servers/plugins/roles/roles_cache.c | 204 +++++++++++++++ ldap/servers/slapd/back-ldbm/ldbm_attr.c | 67 +---- ldap/servers/slapd/filter.c | 66 +++++ ldap/servers/slapd/slapi-plugin.h | 3 + 5 files changed, 535 insertions(+), 62 deletions(-) diff --git a/dirsrvtests/tests/suites/roles/basic_test.py b/dirsrvtests/tests/suites/roles/basic_test.py index bc227a2d7..13d188d68 100644 --- a/dirsrvtests/tests/suites/roles/basic_test.py +++ b/dirsrvtests/tests/suites/roles/basic_test.py @@ -13,16 +13,22 @@ Importing necessary Modules. import logging import time +import ldap import os import pytest -from lib389._constants import PW_DM, DEFAULT_SUFFIX +from lib389._constants import ErrorLog, PW_DM, DEFAULT_SUFFIX, DEFAULT_BENAME from lib389.idm.user import UserAccount, UserAccounts from lib389.idm.organization import Organization from lib389.idm.organizationalunit import OrganizationalUnit from lib389.topologies import topology_st as topo from lib389.idm.role import FilteredRoles, ManagedRoles, NestedRoles from lib389.idm.domain import Domain +from lib389.dbgen import dbgen_users +from lib389.tasks import ImportTask +from lib389.utils import get_default_db_lib +from lib389.rewriters import * +from lib389.backend import Backends logging.getLogger(__name__).setLevel(logging.INFO) log = logging.getLogger(__name__) @@ -504,6 +510,255 @@ def test_vattr_on_managed_role(topo, request): request.addfinalizer(fin) +def test_managed_and_filtered_role_rewrite(topo, request): + """Test that filter components containing 'nsrole=xxx' + are reworked if xxx is either a filtered role or a managed + role. + + :id: e30ff5ed-4f8b-48db-bb88-66f150fca31f + :setup: server + :steps: + 1. Setup nsrole rewriter + 2. Add a 'nsroleDN' indexes for managed roles + 3. Create an 90K ldif files + This is large so that unindex search will last long + 4. import/restart the instance + 5. Create a managed role and add 4 entries in that role + 6. Check that a search 'nsrole=managed_role' is fast + 7. Create a filtered role that use an indexed attribute (givenName) + 8. Check that a search 'nsrole=filtered_role' is fast + :expectedresults: + 1. Operation should succeed + 2. Operation should succeed + 3. Operation should succeed + 4. Operation should succeed + 5. Operation should succeed + 6. Operation should succeed + 7. Operation should succeed + 8. Operation should succeed + """ + # Setup nsrole rewriter + rewriters = Rewriters(topo.standalone) + rewriter = rewriters.ensure_state(properties={"cn": "nsrole", "nsslapd-libpath": 'libroles-plugin'}) + try: + rewriter.add('nsslapd-filterrewriter', "role_nsRole_filter_rewriter") + except: + pass + + # Create an index for nsRoleDN that is used by managed role + attrname = 'nsRoleDN' + backends = Backends(topo.standalone) + backend = backends.get(DEFAULT_BENAME) + indexes = backend.get_indexes() + try: + index = indexes.create(properties={ + 'cn': attrname, + 'nsSystemIndex': 'false', + 'nsIndexType': ['eq', 'pres'] + }) + except: + pass + + # Build LDIF file + bdb_values = { + 'wait30': 30 + } + + # Note: I still sometime get failure with a 60s timeout so lets use 90s + mdb_values = { + 'wait30': 90 + } + + if get_default_db_lib() == 'bdb': + values = bdb_values + else: + values = mdb_values + + ldif_dir = topo.standalone.get_ldif_dir() + import_ldif = ldif_dir + '/perf_import.ldif' + + RDN="userNew" + PARENT="ou=people,%s" % DEFAULT_SUFFIX + dbgen_users(topo.standalone, 90000, import_ldif, DEFAULT_SUFFIX, entry_name=RDN, generic=True, parent=PARENT) + + # online import + import_task = ImportTask(topo.standalone) + import_task.import_suffix_from_ldif(ldiffile=import_ldif, suffix=DEFAULT_SUFFIX) + import_task.wait(values['wait30']) # If things go wrong import takes a lot longer than this + assert import_task.is_complete() + + # Restart server + topo.standalone.restart() + + # Create Managed role entry + managed_roles = ManagedRoles(topo.standalone, DEFAULT_SUFFIX) + role = managed_roles.create(properties={"cn": 'MANAGED_ROLE'}) + + # Assign managed role to 4 entries out of the 90K + for i in range(1, 5): + dn = "uid=%s0000%d,%s" % (RDN, i, PARENT) + topo.standalone.modify_s(dn, [(ldap.MOD_REPLACE, 'nsRoleDN', [role.dn.encode()])]) + + + # Now check that search is fast, evaluating only 4 entries + search_start = time.time() + entries = topo.standalone.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE, "(nsrole=%s)" % role.dn) + duration = time.time() - search_start + log.info("Duration of the search was %f", duration) + assert(len(entries) == 4) + assert (duration < 1) + + # Restart server to refresh entrycache + topo.standalone.restart() + + # Create Filtered Role entry + # it uses 'givenName' attribute that is indexed (eq) by default + filtered_roles = FilteredRoles(topo.standalone, DEFAULT_SUFFIX) + role = filtered_roles.create(properties={'cn': 'FILTERED_ROLE', 'nsRoleFilter': 'givenName=Technical'}) + + # Now check that search is fast + search_start = time.time() + entries = topo.standalone.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE, "(nsrole=%s)" % role.dn) + duration = time.time() - search_start + log.info("Duration of the search was %f", duration) + assert (duration < 1) + + def fin(): + topo.standalone.restart() + try: + managed_roles = ManagedRoles(topo.standalone, DEFAULT_SUFFIX) + for i in managed_roles.list(): + i.delete() + filtered_roles = FilteredRoles(topo.standalone, DEFAULT_SUFFIX) + for i in filtered_roles.list(): + i.delete() + except: + pass + os.remove(import_ldif) + + request.addfinalizer(fin) + +def test_not_such_entry_role_rewrite(topo, request): + """Test that filter components containing 'nsrole=xxx' + ,where xxx does not refer to any role definition, + replace the component by 'nsuniqueid=-1' + + :id: b098dda5-fc77-46c4-84a7-5d0c7035bb77 + :setup: server + :steps: + 1. Setup nsrole rewriter + 2. Add a 'nsroleDN' indexes for managed roles + 3. Create an 90K ldif files + This is large so that unindex search will last long + 4. import/restart the instance + 5. Create a managed role and add 4 entries in that role + 6. Enable plugin log level to capture role plugin message + 7. Check that a search is fast "(OR(nsrole=managed_role)(nsrole=not_existing_role))" + 8. Stop the instance + 9. Check that a message like this was logged: replace (nsrole=not_existing_role) by (nsuniqueid=-1) + :expectedresults: + 1. Operation should succeed + 2. Operation should succeed + 3. Operation should succeed + 4. Operation should succeed + 5. Operation should succeed + 6. Operation should succeed + 7. Operation should succeed + 8. Operation should succeed + 9. Operation should succeed + """ + # Setup nsrole rewriter + rewriters = Rewriters(topo.standalone) + rewriter = rewriters.ensure_state(properties={"cn": "nsrole", "nsslapd-libpath": 'libroles-plugin'}) + try: + rewriter.add('nsslapd-filterrewriter', "role_nsRole_filter_rewriter") + except: + pass + + # Create an index for nsRoleDN that is used by managed role + attrname = 'nsRoleDN' + backends = Backends(topo.standalone) + backend = backends.get(DEFAULT_BENAME) + indexes = backend.get_indexes() + try: + index = indexes.create(properties={ + 'cn': attrname, + 'nsSystemIndex': 'false', + 'nsIndexType': ['eq', 'pres'] + }) + except: + pass + + # Build LDIF file + bdb_values = { + 'wait60': 60 + } + + # Note: I still sometime get failure with a 60s timeout so lets use 90s + mdb_values = { + 'wait60': 90 + } + + if get_default_db_lib() == 'bdb': + values = bdb_values + else: + values = mdb_values + + ldif_dir = topo.standalone.get_ldif_dir() + import_ldif = ldif_dir + '/perf_import.ldif' + + RDN="userNew" + PARENT="ou=people,%s" % DEFAULT_SUFFIX + dbgen_users(topo.standalone, 90000, import_ldif, DEFAULT_SUFFIX, entry_name=RDN, generic=True, parent=PARENT) + + # online import + import_task = ImportTask(topo.standalone) + import_task.import_suffix_from_ldif(ldiffile=import_ldif, suffix=DEFAULT_SUFFIX) + import_task.wait(values['wait60']) # If things go wrong import takes a lot longer than this + assert import_task.is_complete() + + # Restart server + topo.standalone.restart() + + # Create Managed role entry + managed_roles = ManagedRoles(topo.standalone, DEFAULT_SUFFIX) + role = managed_roles.create(properties={"cn": 'MANAGED_ROLE'}) + + # Assign managed role to 4 entries out of the 90K + for i in range(1, 5): + dn = "uid=%s0000%d,%s" % (RDN, i, PARENT) + topo.standalone.modify_s(dn, [(ldap.MOD_REPLACE, 'nsRoleDN', [role.dn.encode()])]) + + # Enable plugin level to check message + topo.standalone.config.loglevel(vals=(ErrorLog.DEFAULT,ErrorLog.PLUGIN)) + + # Now check that search is fast, evaluating only 4 entries + search_start = time.time() + entries = topo.standalone.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE, "(|(nsrole=%s)(nsrole=cn=not_such_entry_role,%s))" % (role.dn, DEFAULT_SUFFIX)) + duration = time.time() - search_start + log.info("Duration of the search was %f", duration) + assert(len(entries) == 4) + assert (duration < 1) + + # Restart server to refresh entrycache + topo.standalone.stop() + + # Check that when the role does not exist it is translated into 'nsuniqueid=-1' + pattern = ".*replace \(nsRole=cn=not_such_entry_role,dc=example,dc=com\) by \(nsuniqueid=-1\).*" + assert topo.standalone.ds_error_log.match(pattern) + + def fin(): + topo.standalone.restart() + try: + managed_roles = ManagedRoles(topo.standalone, DEFAULT_SUFFIX) + for i in managed_roles.list(): + i.delete() + except: + pass + os.remove(import_ldif) + + request.addfinalizer(fin) + if __name__ == "__main__": CURRENT_FILE = os.path.realpath(__file__) pytest.main("-s -v %s" % CURRENT_FILE) diff --git a/ldap/servers/plugins/roles/roles_cache.c b/ldap/servers/plugins/roles/roles_cache.c index 5fd52e7b9..bbed11802 100644 --- a/ldap/servers/plugins/roles/roles_cache.c +++ b/ldap/servers/plugins/roles/roles_cache.c @@ -2131,3 +2131,207 @@ roles_cache_dump(caddr_t data, caddr_t arg __attribute__((unused))) return 0; } + + +/* This is an example callback to substitute + * attribute type 'from' with 'to' in all + * the filter components + * example_substitute_type is a callback (FILTER_APPLY_FN) used by slapi_filter_apply + * typedef int (FILTER_APPLY_FN)(Slapi_Filter f, void *arg) + * To stick to the definition, the callback is defined using 'int' rather 'int32_t' + */ +typedef struct { + char *attrtype_from; + char *attrtype_to; +} role_substitute_type_arg_t; + + +static void +_rewrite_nsrole_component(Slapi_Filter *f, role_substitute_type_arg_t *substitute_arg) +{ + char *type; + struct berval *bval; + char *attrs[3] = {SLAPI_ATTR_OBJECTCLASS, ROLE_FILTER_ATTR_NAME, NULL}; + Slapi_Entry *nsrole_entry = NULL; + Slapi_DN *sdn = NULL; + char *rolefilter = NULL; + int rc; + char **oc_values = NULL; + + /* Substitution is only valid if original attribute is NSROLEATTR */ + if ((substitute_arg == NULL) || + (substitute_arg->attrtype_from == NULL) || + (substitute_arg->attrtype_to == NULL)) { + return; + } + if (strcasecmp(substitute_arg->attrtype_from, NSROLEATTR)) { + return; + } + if (slapi_filter_get_choice(f) != LDAP_FILTER_EQUALITY) { + /* only equality filter are handled + * else it is not possible to retrieve the role + * via its DN. + * Safety checking, at this point filter component has + * already been tested as equality match. + */ + return; + } + + /* Check that assertion does not refer to a filter/nested role */ + if (slapi_filter_get_ava(f, &type, &bval)) { + return; + } + sdn = slapi_sdn_new_dn_byref(bval->bv_val); + rc = slapi_search_internal_get_entry(sdn, attrs, &nsrole_entry, roles_get_plugin_identity()); + if (rc != LDAP_SUCCESS) { + if (rc == LDAP_NO_SUCH_OBJECT) { + /* the role does not exist (nsrole=) + * that means no entry match this component. To speed up + * the built of candidate list we may replace this component + * with an indexed component that return empty IDL. + * nsuniqueid is indexed and -1 is an invalid value. + */ + char *empty_IDL_filter; + empty_IDL_filter = slapi_ch_smprintf("(%s=-1)", SLAPI_ATTR_UNIQUEID); + slapi_filter_replace_strfilter(f, empty_IDL_filter); + slapi_log_err(SLAPI_LOG_PLUGIN, ROLES_PLUGIN_SUBSYSTEM, "_rewrite_nsrole_component: replace (%s=%s) by %s\n", + substitute_arg->attrtype_from, (char *)slapi_sdn_get_ndn(sdn), empty_IDL_filter); + slapi_ch_free_string(&empty_IDL_filter); + + } + goto bail; + } + oc_values = slapi_entry_attr_get_charray(nsrole_entry, SLAPI_ATTR_OBJECTCLASS); + rolefilter = slapi_entry_attr_get_charptr(nsrole_entry, ROLE_FILTER_ATTR_NAME); + for (size_t i = 0; oc_values && oc_values[i]; ++i) { + if (!strcasecmp(oc_values[i], (char *)"nsSimpleRoleDefinition") || + !strcasecmp(oc_values[i], (char *)"nsManagedRoleDefinition")) { + /* This is a managed role, okay to rewrite the attribute type */ + slapi_filter_changetype(f, substitute_arg->attrtype_to); + goto bail; + } else if (!strcasecmp(oc_values[i], (char *)"nsFilteredRoleDefinition") && rolefilter) { + /* filtered role, okay to rewrite the filter with + * the value of the nsRoleFilter + */ + slapi_filter_replace_strfilter(f, rolefilter); + goto bail; + } else if (!strcasecmp(oc_values[i], (char *)"nsNestedRoleDefinition")) { + /* nested roles can not be rewritten */ + goto bail; + } + } + +bail: + slapi_ch_free_string(&rolefilter); + slapi_ch_array_free(oc_values); + slapi_entry_free(nsrole_entry); + slapi_sdn_free(&sdn); + return; +} + +/* It calls _rewrite_nsrole_component for each filter component with + * - filter choice LDAP_FILTER_EQUALITY + * - filter attribute type nsRole + */ +static int +role_substitute_type(Slapi_Filter *f, void *arg) +{ + role_substitute_type_arg_t *substitute_arg = (role_substitute_type_arg_t *) arg; + char *filter_type; + + if ((substitute_arg == NULL) || + (substitute_arg->attrtype_from == NULL) || + (substitute_arg->attrtype_to == NULL)) { + return SLAPI_FILTER_SCAN_STOP; + } + + if (slapi_filter_get_choice(f) != LDAP_FILTER_EQUALITY) { + /* only equality filter are handled + * else it is not possible to retrieve the role + * via its DN + */ + return SLAPI_FILTER_SCAN_CONTINUE; + } + + /* In case this is expected attribute type and assertion + * Substitute 'from' by 'to' attribute type in the filter + */ + if (slapi_filter_get_attribute_type(f, &filter_type) == 0) { + if ((strcasecmp(filter_type, substitute_arg->attrtype_from) == 0)) { + _rewrite_nsrole_component(f, substitute_arg); + } + } + + /* Return continue because we should + * substitute 'from' in all filter components + */ + return SLAPI_FILTER_SCAN_CONTINUE; +} + + +/* + * During PRE_SEARCH, rewriters (a.k.a computed attributes) are called + * to rewrite some search filter components. + * Rewriters callbacks are registered by main and are retrieved from + * config entries under cn=rewriters,cn=config. + * + * In order to register the role rewriter, the following record + * is added to the config. + * + * dn: cn=role,cn=rewriters,cn=config + * objectClass: top + * objectClass: extensibleObject + * cn: role + * nsslapd-libpath: /lib/dirsrv/libslapd.so + * nsslapd-filterrewriter: role_nsRole_filter_rewriter + * + * The role rewriter supports: + * - 'nsrole' attribute type + * - LDAP_FILTER_EQUALITY filter choice + * - assertion being a managed/filtered role DN + * + * - Input '(nsrole=cn=admin1,dc=example,dc=com)' + * Output '(nsroleDN=cn=admin1,dc=example,dc=com)' + * - Input '(nsrole=cn=SalesManagerFilter,ou=people,dc=example,dc=com)' + * Output '(manager=user008762)' + * + * dn: cn=admin1,dc=example,dc=com + * ... + * objectClass: nsRoleDefinition + * objectClass: nsManagedRoleDefinition + * ... + * + * dn: cn=SalesManagerFilter,ou=people,dc=example,dc=com + * ... + * objectclass: nsRoleDefinition + * objectclass: nsFilteredRoleDefinition + * ... + * nsRoleFilter: manager=user008762 + * + * return code (from computed.c:compute_rewrite_search_filter): + * -1 : keep looking + * 0 : rewrote OK + * 1 : refuse to do this search + * 2 : operations error + */ +int32_t +role_nsRole_filter_rewriter(Slapi_PBlock *pb) +{ + Slapi_Filter *clientFilter = NULL; + int error_code = 0; + int rc; + role_substitute_type_arg_t arg; + arg.attrtype_from = NSROLEATTR; + arg.attrtype_to = ROLE_MANAGED_ATTR_NAME; + + slapi_pblock_get(pb, SLAPI_SEARCH_FILTER, &clientFilter); + rc = slapi_filter_apply(clientFilter, role_substitute_type, &arg, &error_code); + if (rc == SLAPI_FILTER_SCAN_NOMORE) { + return SEARCH_REWRITE_CALLBACK_CONTINUE; /* Let's others rewriter play */ + } else { + slapi_log_err(SLAPI_LOG_ERR, + "example_foo2cn_filter_rewriter", "Could not update the search filter - error %d (%d)\n", + rc, error_code); + return SEARCH_REWRITE_CALLBACK_ERROR; /* operation error */ + } +} diff --git a/ldap/servers/slapd/back-ldbm/ldbm_attr.c b/ldap/servers/slapd/back-ldbm/ldbm_attr.c index 1d86c2db4..07f3058a3 100644 --- a/ldap/servers/slapd/back-ldbm/ldbm_attr.c +++ b/ldap/servers/slapd/back-ldbm/ldbm_attr.c @@ -1039,33 +1039,6 @@ ldbm_compute_evaluator(computed_attr_context *c, char *type, Slapi_Entry *e, sla */ -/* Before calling this function, you must free all the parts - which will be overwritten, this function dosn't know - how to do that */ -static int -replace_filter(Slapi_Filter *f, char *s) -{ - Slapi_Filter *newf = NULL; - Slapi_Filter *temp = NULL; - char *buf = slapi_ch_strdup(s); - - newf = slapi_str2filter(buf); - slapi_ch_free((void **)&buf); - - if (NULL == newf) { - return -1; - } - - /* Now take the parts of newf and put them in f */ - /* An easy way to do this is to preserve the "next" ptr */ - temp = f->f_next; - *f = *newf; - f->f_next = temp; - /* Free the new filter husk */ - slapi_ch_free((void **)&newf); - return 0; -} - static void find_our_friends(char *s, int *has, int *num) { @@ -1075,30 +1048,6 @@ find_our_friends(char *s, int *has, int *num) } } -/* Free the parts of a filter we're about to overwrite */ -void -free_the_filter_bits(Slapi_Filter *f) -{ - /* We need to free: */ - switch (f->f_choice) { - case LDAP_FILTER_EQUALITY: - case LDAP_FILTER_GE: - case LDAP_FILTER_LE: - case LDAP_FILTER_APPROX: - ava_done(&f->f_ava); - break; - - case LDAP_FILTER_PRESENT: - if (f->f_type != NULL) { - slapi_ch_free((void **)&(f->f_type)); - } - break; - - default: - break; - } -} - static int grok_and_rewrite_filter(Slapi_Filter *f) { @@ -1116,11 +1065,9 @@ grok_and_rewrite_filter(Slapi_Filter *f) rhs = f->f_ava.ava_value.bv_val; if (has) { if (0 == strcasecmp(rhs, "TRUE")) { - free_the_filter_bits(f); - replace_filter(f, "(&(numsubordinates=*)(numsubordinates>=1))"); + slapi_filter_replace_strfilter(f, "(&(numsubordinates=*)(numsubordinates>=1))"); } else if (0 == strcasecmp(rhs, "FALSE")) { - free_the_filter_bits(f); - replace_filter(f, "(&(objectclass=*)(!(numsubordinates=*)))"); + slapi_filter_replace_strfilter(f, "(&(objectclass=*)(!(numsubordinates=*)))"); } else { return 1; /* Filter we can't rewrite */ } @@ -1133,7 +1080,7 @@ grok_and_rewrite_filter(Slapi_Filter *f) char *theType = f->f_ava.ava_type; rhs_berval = f->f_ava.ava_value; - replace_filter(f, "(&(numsubordinates=*)(numsubordinates=x))"); + slapi_filter_replace_ex(f, "(&(numsubordinates=*)(numsubordinates=x))"); /* Now fixup the resulting filter so that x = rhs */ slapi_ch_free((void **)&(f->f_and->f_next->f_ava.ava_value.bv_val)); /*free type also */ @@ -1143,8 +1090,7 @@ grok_and_rewrite_filter(Slapi_Filter *f) } else { if (rhs_number == 0) { /* This is the same as hassubordinates=FALSE */ - free_the_filter_bits(f); - replace_filter(f, "(&(objectclass=*)(!(numsubordinates=*)))"); + slapi_filter_replace_strfilter(f, "(&(objectclass=*)(!(numsubordinates=*)))"); } else { return 1; } @@ -1166,14 +1112,13 @@ grok_and_rewrite_filter(Slapi_Filter *f) rhs_num = atoi(rhs); if (0 == rhs_num) { /* If so, rewrite to same as numsubordinates=* */ - free_the_filter_bits(f); - replace_filter(f, "(objectclass=*)"); + slapi_filter_replace_strfilter(f, "(objectclass=*)"); } else { /* Rewrite to present and GE the rhs */ char *theType = f->f_ava.ava_type; rhs_berval = f->f_ava.ava_value; - replace_filter(f, "(&(numsubordinates=*)(numsubordinates>=x))"); + slapi_filter_replace_ex(f, "(&(numsubordinates=*)(numsubordinates>=x))"); /* Now fixup the resulting filter so that x = rhs */ slapi_ch_free((void **)&(f->f_and->f_next->f_ava.ava_value.bv_val)); /*free type also */ diff --git a/ldap/servers/slapd/filter.c b/ldap/servers/slapd/filter.c index 1f0873c34..44b726a34 100644 --- a/ldap/servers/slapd/filter.c +++ b/ldap/servers/slapd/filter.c @@ -1032,6 +1032,72 @@ slapi_filter_get_subfilt( return (0); } +/* + * Before calling this function, you must free all the parts + * which will be overwritten (i.e. slapi_free_the_filter_bits), + * this function dosn't know how to do that + */ +int +slapi_filter_replace_ex(Slapi_Filter *f, char *s) +{ + Slapi_Filter *newf = NULL; + Slapi_Filter *temp = NULL; + char *buf = slapi_ch_strdup(s); + + newf = slapi_str2filter(buf); + slapi_ch_free((void **)&buf); + + if (NULL == newf) { + return -1; + } + + /* Now take the parts of newf and put them in f */ + /* An easy way to do this is to preserve the "next" ptr */ + temp = f->f_next; + *f = *newf; + f->f_next = temp; + /* Free the new filter husk */ + slapi_ch_free((void **)&newf); + return 0; +} + +/* + * Free the parts of a filter we're about to overwrite + * moved from ldbm_attr.c + */ +void +slapi_filter_free_bits(Slapi_Filter *f) +{ + /* We need to free: */ + switch (f->f_choice) { + case LDAP_FILTER_EQUALITY: + case LDAP_FILTER_GE: + case LDAP_FILTER_LE: + case LDAP_FILTER_APPROX: + ava_done(&f->f_ava); + break; + + case LDAP_FILTER_PRESENT: + if (f->f_type != NULL) { + slapi_ch_free((void **)&(f->f_type)); + } + break; + + default: + break; + } +} + +/* + * it replaces the bits of the Slapi_Filter with the ones taken from strfilter + */ +int +slapi_filter_replace_strfilter(Slapi_Filter *f, char *strfilter) +{ + slapi_filter_free_bits(f); + return (slapi_filter_replace_ex(f, strfilter)); +} + static void filter_normalize_ava(struct slapi_filter *f, PRBool norm_values) { diff --git a/ldap/servers/slapd/slapi-plugin.h b/ldap/servers/slapd/slapi-plugin.h index e1f7beba5..79d04e418 100644 --- a/ldap/servers/slapd/slapi-plugin.h +++ b/ldap/servers/slapd/slapi-plugin.h @@ -5240,6 +5240,9 @@ int slapi_filter_get_choice(Slapi_Filter *f); int slapi_filter_get_ava(Slapi_Filter *f, char **type, struct berval **bval); int slapi_filter_get_attribute_type(Slapi_Filter *f, char **type); int slapi_filter_get_subfilt(Slapi_Filter *f, char **type, char **initial, char ***any, char ** final); +int slapi_filter_replace_ex(Slapi_Filter *f, char *s); +void slapi_filter_free_bits(Slapi_Filter *f); +int slapi_filter_replace_strfilter(Slapi_Filter *f, char *strfilter); Slapi_Filter *slapi_filter_list_first(Slapi_Filter *f); Slapi_Filter *slapi_filter_list_next(Slapi_Filter *f, Slapi_Filter *fprev); Slapi_Filter *slapi_str2filter(char *str); -- 2.41.0