|
|
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 |
|