743 lines
26 KiB
Diff
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
|
|
|