389-ds-base/0001-Issue-5722-RFE-When-a-...

743 lines
26 KiB
Diff

From ca4f4e7712bd7960970f1b10fc859c8bc4679c18 Mon Sep 17 00:00:00 2001
From: tbordaz <tbordaz@redhat.com>
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=<unknown role>)
+ * 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