Blame SOURCES/0012-Ticket-49958-extended-search-fail-to-match-entries.patch

bc0d6c
From 02de69d6987b059459980b50de285c2fd7bb3e2c Mon Sep 17 00:00:00 2001
bc0d6c
From: Thierry Bordaz <tbordaz@redhat.com>
bc0d6c
Date: Mon, 24 Sep 2018 14:14:16 +0200
bc0d6c
Subject: [PATCH] Ticket 49958: extended search fail to match entries
bc0d6c
bc0d6c
Bug Description:
bc0d6c
	During an extended search, a structure is created for each filter component.
bc0d6c
	The structure contains the keys generated from the assertion and using the given
bc0d6c
	matching rule indexer.
bc0d6c
	Later the keys will be compared (with the MR) with keys generated from the
bc0d6c
	attribute values of the candidate entries.
bc0d6c
	The bug is that parsing the assertion, instead of removing the heading spaces
bc0d6c
	the routine clear the assertion that is empty. So the generated keys is NULL.
bc0d6c
bc0d6c
Fix Description:
bc0d6c
	The fix consists to only remove heading spaces
bc0d6c
bc0d6c
https://pagure.io/389-ds-base/issue/49958
bc0d6c
bc0d6c
Reviewed by: Mark Reynolds
bc0d6c
bc0d6c
Platforms tested: F27
bc0d6c
bc0d6c
Flag Day: no
bc0d6c
bc0d6c
Doc impact: no
bc0d6c
---
bc0d6c
 .../tests/suites/filter/filter_test.py        | 203 ++++++++++++++++++
bc0d6c
 ldap/servers/plugins/collation/orfilter.c     |   4 +-
bc0d6c
 2 files changed, 204 insertions(+), 3 deletions(-)
bc0d6c
bc0d6c
diff --git a/dirsrvtests/tests/suites/filter/filter_test.py b/dirsrvtests/tests/suites/filter/filter_test.py
bc0d6c
index 280db68a3..61c449989 100644
bc0d6c
--- a/dirsrvtests/tests/suites/filter/filter_test.py
bc0d6c
+++ b/dirsrvtests/tests/suites/filter/filter_test.py
bc0d6c
@@ -83,6 +83,209 @@ def test_filter_search_original_attrs(topology_st):
bc0d6c
     log.info('test_filter_search_original_attrs: PASSED')
bc0d6c
 
bc0d6c
 
bc0d6c
+@pytest.mark.bz1511462
bc0d6c
+def test_filter_scope_one(topology_st):
bc0d6c
+    """Test ldapsearch with scope one gives only single entry
bc0d6c
+
bc0d6c
+    :id: cf5a6078-bbe6-4d43-ac71-553c45923f91
bc0d6c
+    :setup: Standalone instance
bc0d6c
+    :steps:
bc0d6c
+         1. Search cn=Directory Administrators,dc=example,dc=com using ldapsearch with
bc0d6c
+            scope one using base as dc=example,dc=com
bc0d6c
+         2. Check that search should return only one entry
bc0d6c
+    :expectedresults:
bc0d6c
+         1. This should pass
bc0d6c
+         2. This should pass
bc0d6c
+    """
bc0d6c
+
bc0d6c
+    parent_dn="dn: dc=example,dc=com"
bc0d6c
+    child_dn="dn: cn=Directory Administrators,dc=example,dc=com"
bc0d6c
+
bc0d6c
+    log.info('Search user using ldapsearch with scope one')
bc0d6c
+    results = topology_st.standalone.search_s(DEFAULT_SUFFIX, ldap.SCOPE_ONELEVEL,'cn=Directory Administrators',['cn'] )
bc0d6c
+    log.info(results)
bc0d6c
+
bc0d6c
+    log.info('Search should only have one entry')
bc0d6c
+    assert len(results) == 1
bc0d6c
+
bc0d6c
+@pytest.mark.ds47313
bc0d6c
+def test_filter_with_attribute_subtype(topology_st):
bc0d6c
+    """Adds 2 test entries and Search with
bc0d6c
+    filters including subtype and !
bc0d6c
+
bc0d6c
+    :id: 0e69f5f2-6a0a-480e-8282-fbcc50231908
bc0d6c
+    :setup: Standalone instance
bc0d6c
+    :steps:
bc0d6c
+        1. Add 2 entries and create 3 filters
bc0d6c
+        2. Search for entry with filter: (&(cn=test_entry en only)(!(cn=test_entry fr)))
bc0d6c
+        3. Search for entry with filter: (&(cn=test_entry en only)(!(cn;fr=test_entry fr)))
bc0d6c
+        4. Search for entry with filter: (&(cn=test_entry en only)(!(cn;en=test_entry en)))
bc0d6c
+        5. Delete the added entries
bc0d6c
+    :expectedresults:
bc0d6c
+        1. Operation should be successful
bc0d6c
+        2. Search should be successful
bc0d6c
+        3. Search should be successful
bc0d6c
+        4. Search should not be successful
bc0d6c
+        5. Delete the added entries
bc0d6c
+    """
bc0d6c
+
bc0d6c
+    # bind as directory manager
bc0d6c
+    topology_st.standalone.log.info("Bind as %s" % DN_DM)
bc0d6c
+    topology_st.standalone.simple_bind_s(DN_DM, PASSWORD)
bc0d6c
+
bc0d6c
+    # enable filter error logging
bc0d6c
+    # mod = [(ldap.MOD_REPLACE, 'nsslapd-errorlog-level', '32')]
bc0d6c
+    # topology_st.standalone.modify_s(DN_CONFIG, mod)
bc0d6c
+
bc0d6c
+    topology_st.standalone.log.info("\n\n######################### ADD ######################\n")
bc0d6c
+
bc0d6c
+    # Prepare the entry with cn;fr & cn;en
bc0d6c
+    entry_name_fr = '%s fr' % (ENTRY_NAME)
bc0d6c
+    entry_name_en = '%s en' % (ENTRY_NAME)
bc0d6c
+    entry_name_both = '%s both' % (ENTRY_NAME)
bc0d6c
+    entry_dn_both = 'cn=%s, %s' % (entry_name_both, SUFFIX)
bc0d6c
+    entry_both = Entry(entry_dn_both)
bc0d6c
+    entry_both.setValues('objectclass', 'top', 'person')
bc0d6c
+    entry_both.setValues('sn', entry_name_both)
bc0d6c
+    entry_both.setValues('cn', entry_name_both)
bc0d6c
+    entry_both.setValues('cn;fr', entry_name_fr)
bc0d6c
+    entry_both.setValues('cn;en', entry_name_en)
bc0d6c
+
bc0d6c
+    # Prepare the entry with one member
bc0d6c
+    entry_name_en_only = '%s en only' % (ENTRY_NAME)
bc0d6c
+    entry_dn_en_only = 'cn=%s, %s' % (entry_name_en_only, SUFFIX)
bc0d6c
+    entry_en_only = Entry(entry_dn_en_only)
bc0d6c
+    entry_en_only.setValues('objectclass', 'top', 'person')
bc0d6c
+    entry_en_only.setValues('sn', entry_name_en_only)
bc0d6c
+    entry_en_only.setValues('cn', entry_name_en_only)
bc0d6c
+    entry_en_only.setValues('cn;en', entry_name_en)
bc0d6c
+
bc0d6c
+    topology_st.standalone.log.info("Try to add Add %s: %r" % (entry_dn_both, entry_both))
bc0d6c
+    topology_st.standalone.add_s(entry_both)
bc0d6c
+
bc0d6c
+    topology_st.standalone.log.info("Try to add Add %s: %r" % (entry_dn_en_only, entry_en_only))
bc0d6c
+    topology_st.standalone.add_s(entry_en_only)
bc0d6c
+
bc0d6c
+    topology_st.standalone.log.info("\n\n######################### SEARCH ######################\n")
bc0d6c
+
bc0d6c
+    # filter: (&(cn=test_entry en only)(!(cn=test_entry fr)))
bc0d6c
+    myfilter = '(&(sn=%s)(!(cn=%s)))' % (entry_name_en_only, entry_name_fr)
bc0d6c
+    topology_st.standalone.log.info("Try to search with filter %s" % myfilter)
bc0d6c
+    ents = topology_st.standalone.search_s(SUFFIX, ldap.SCOPE_SUBTREE, myfilter)
bc0d6c
+    assert len(ents) == 1
bc0d6c
+    assert ensure_str(ents[0].sn) == entry_name_en_only
bc0d6c
+    topology_st.standalone.log.info("Found %s" % ents[0].dn)
bc0d6c
+
bc0d6c
+    # filter: (&(cn=test_entry en only)(!(cn;fr=test_entry fr)))
bc0d6c
+    myfilter = '(&(sn=%s)(!(cn;fr=%s)))' % (entry_name_en_only, entry_name_fr)
bc0d6c
+    topology_st.standalone.log.info("Try to search with filter %s" % myfilter)
bc0d6c
+    ents = topology_st.standalone.search_s(SUFFIX, ldap.SCOPE_SUBTREE, myfilter)
bc0d6c
+    assert len(ents) == 1
bc0d6c
+    assert ensure_str(ents[0].sn) == entry_name_en_only
bc0d6c
+    topology_st.standalone.log.info("Found %s" % ents[0].dn)
bc0d6c
+
bc0d6c
+    # filter: (&(cn=test_entry en only)(!(cn;en=test_entry en)))
bc0d6c
+    myfilter = '(&(sn=%s)(!(cn;en=%s)))' % (entry_name_en_only, entry_name_en)
bc0d6c
+    topology_st.standalone.log.info("Try to search with filter %s" % myfilter)
bc0d6c
+    ents = topology_st.standalone.search_s(SUFFIX, ldap.SCOPE_SUBTREE, myfilter)
bc0d6c
+    assert len(ents) == 0
bc0d6c
+    topology_st.standalone.log.info("Found none")
bc0d6c
+
bc0d6c
+    topology_st.standalone.log.info("\n\n######################### DELETE ######################\n")
bc0d6c
+
bc0d6c
+    topology_st.standalone.log.info("Try to delete  %s " % entry_dn_both)
bc0d6c
+    topology_st.standalone.delete_s(entry_dn_both)
bc0d6c
+
bc0d6c
+    topology_st.standalone.log.info("Try to delete  %s " % entry_dn_en_only)
bc0d6c
+    topology_st.standalone.delete_s(entry_dn_en_only)
bc0d6c
+
bc0d6c
+    log.info('Testcase PASSED')
bc0d6c
+
bc0d6c
+
bc0d6c
+@pytest.mark.bz1615155
bc0d6c
+def test_extended_search(topology_st):
bc0d6c
+    """Test we can search with equality extended matching rule
bc0d6c
+
bc0d6c
+    :id: 
bc0d6c
+    :setup: Standalone instance
bc0d6c
+    :steps:
bc0d6c
+         1. Add a test user with 'sn: ext-test-entry'
bc0d6c
+         2. Search '(cn:de:=ext-test-entry)'
bc0d6c
+         3. Search '(sn:caseIgnoreIA5Match:=EXT-TEST-ENTRY)'
bc0d6c
+         4. Search '(sn:caseIgnoreMatch:=EXT-TEST-ENTRY)'
bc0d6c
+         5. Search '(sn:caseExactMatch:=EXT-TEST-ENTRY)'
bc0d6c
+         6. Search '(sn:caseExactMatch:=ext-test-entry)'
bc0d6c
+         7. Search '(sn:caseExactIA5Match:=EXT-TEST-ENTRY)'
bc0d6c
+         8. Search '(sn:caseExactIA5Match:=ext-test-entry)'
bc0d6c
+    :expectedresults:
bc0d6c
+         1. This should pass
bc0d6c
+         2. This should return one entry
bc0d6c
+         3. This should return one entry
bc0d6c
+         4. This should return one entry
bc0d6c
+         5. This should return NO entry
bc0d6c
+         6. This should return one entry
bc0d6c
+         7. This should return NO entry
bc0d6c
+         8. This should return one entry
bc0d6c
+         3. return one entry
bc0d6c
+    """
bc0d6c
+    log.info('Running test_filter_escaped...')
bc0d6c
+    
bc0d6c
+    ATTR_VAL = 'ext-test-entry'
bc0d6c
+    USER1_DN = "uid=%s,%s" % (ATTR_VAL, DEFAULT_SUFFIX)
bc0d6c
+
bc0d6c
+    try:
bc0d6c
+        topology_st.standalone.add_s(Entry((USER1_DN, {'objectclass': "top extensibleObject".split(),
bc0d6c
+                                                       'sn': ATTR_VAL.encode(),
bc0d6c
+                                                       'cn': ATTR_VAL.encode(),
bc0d6c
+                                                       'uid': ATTR_VAL.encode()})))
bc0d6c
+    except ldap.LDAPError as e:
bc0d6c
+        log.fatal('test_extended_search: Failed to add test user ' + USER1_DN + ': error ' +
bc0d6c
+                  e.message['desc'])
bc0d6c
+        assert False
bc0d6c
+
bc0d6c
+    # filter: '(cn:de:=ext-test-entry)'
bc0d6c
+    myfilter = '(cn:de:=%s)' % ATTR_VAL
bc0d6c
+    topology_st.standalone.log.info("Try to search with filter %s" % myfilter)
bc0d6c
+    ents = topology_st.standalone.search_s(SUFFIX, ldap.SCOPE_SUBTREE, myfilter)
bc0d6c
+    assert len(ents) == 1
bc0d6c
+
bc0d6c
+    # filter: '(sn:caseIgnoreIA5Match:=EXT-TEST-ENTRY)'
bc0d6c
+    myfilter = '(cn:caseIgnoreIA5Match:=%s)' % ATTR_VAL.upper()
bc0d6c
+    topology_st.standalone.log.info("Try to search with filter %s" % myfilter)
bc0d6c
+    ents = topology_st.standalone.search_s(SUFFIX, ldap.SCOPE_SUBTREE, myfilter)
bc0d6c
+    assert len(ents) == 1
bc0d6c
+
bc0d6c
+    # filter: '(sn:caseIgnoreMatch:=EXT-TEST-ENTRY)'
bc0d6c
+    myfilter = '(cn:caseIgnoreMatch:=%s)' % ATTR_VAL.upper()
bc0d6c
+    topology_st.standalone.log.info("Try to search with filter %s" % myfilter)
bc0d6c
+    ents = topology_st.standalone.search_s(SUFFIX, ldap.SCOPE_SUBTREE, myfilter)
bc0d6c
+    assert len(ents) == 1
bc0d6c
+
bc0d6c
+    # filter: '(sn:caseExactMatch:=EXT-TEST-ENTRY)'
bc0d6c
+    myfilter = '(cn:caseExactMatch:=%s)' % ATTR_VAL.upper()
bc0d6c
+    topology_st.standalone.log.info("Try to search with filter %s" % myfilter)
bc0d6c
+    ents = topology_st.standalone.search_s(SUFFIX, ldap.SCOPE_SUBTREE, myfilter)
bc0d6c
+    assert len(ents) == 0
bc0d6c
+
bc0d6c
+    # filter: '(sn:caseExactMatch:=ext-test-entry)'
bc0d6c
+    myfilter = '(cn:caseExactMatch:=%s)' % ATTR_VAL
bc0d6c
+    topology_st.standalone.log.info("Try to search with filter %s" % myfilter)
bc0d6c
+    ents = topology_st.standalone.search_s(SUFFIX, ldap.SCOPE_SUBTREE, myfilter)
bc0d6c
+    assert len(ents) == 1
bc0d6c
+
bc0d6c
+    # filter: '(sn:caseExactIA5Match:=EXT-TEST-ENTRY)'
bc0d6c
+    myfilter = '(cn:caseExactIA5Match:=%s)' % ATTR_VAL.upper()
bc0d6c
+    topology_st.standalone.log.info("Try to search with filter %s" % myfilter)
bc0d6c
+    ents = topology_st.standalone.search_s(SUFFIX, ldap.SCOPE_SUBTREE, myfilter)
bc0d6c
+    assert len(ents) == 0
bc0d6c
+
bc0d6c
+    # filter: '(sn:caseExactIA5Match:=ext-test-entry)'
bc0d6c
+    myfilter = '(cn:caseExactIA5Match:=%s)' % ATTR_VAL
bc0d6c
+    topology_st.standalone.log.info("Try to search with filter %s" % myfilter)
bc0d6c
+    ents = topology_st.standalone.search_s(SUFFIX, ldap.SCOPE_SUBTREE, myfilter)
bc0d6c
+    assert len(ents) == 1
bc0d6c
+
bc0d6c
+
bc0d6c
 if __name__ == '__main__':
bc0d6c
     # Run isolated
bc0d6c
     # -s for DEBUG mode
bc0d6c
diff --git a/ldap/servers/plugins/collation/orfilter.c b/ldap/servers/plugins/collation/orfilter.c
bc0d6c
index 7705de9d6..c092d77ca 100644
bc0d6c
--- a/ldap/servers/plugins/collation/orfilter.c
bc0d6c
+++ b/ldap/servers/plugins/collation/orfilter.c
bc0d6c
@@ -531,10 +531,8 @@ or_filter_create(Slapi_PBlock *pb)
bc0d6c
             default:
bc0d6c
                 break;
bc0d6c
             }
bc0d6c
-            for (; len > 0 && *val != ' '; ++val, --len)
bc0d6c
+            for (; len > 0 && *val == ' '; ++val, --len)
bc0d6c
                 ;
bc0d6c
-            if (len > 0)
bc0d6c
-                ++val, --len; /* skip the space */
bc0d6c
             bv.bv_len = len;
bc0d6c
             bv.bv_val = (len > 0) ? val : NULL;
bc0d6c
         } else { /* mrOID does not identify an ordering rule. */
bc0d6c
-- 
bc0d6c
2.17.2
bc0d6c