LDAP-263: Added LdapQuery method to OdmManager. Added methods for hardcoded filters in LdapQueryBuilder.
This commit is contained in:
@@ -17,9 +17,12 @@
|
||||
package org.springframework.ldap.query;
|
||||
|
||||
import org.springframework.ldap.filter.Filter;
|
||||
import org.springframework.ldap.filter.HardcodedFilter;
|
||||
import org.springframework.ldap.support.LdapEncoder;
|
||||
import org.springframework.ldap.support.LdapUtils;
|
||||
|
||||
import javax.naming.Name;
|
||||
import java.text.MessageFormat;
|
||||
|
||||
/**
|
||||
* Builder of LdapQueries. Start with a call to {@link #query()}, proceed with specifying the
|
||||
@@ -150,6 +153,7 @@ public class LdapQueryBuilder implements LdapQuery {
|
||||
*
|
||||
* @param attribute The attribute that the first part of the filter should test against.
|
||||
* @return A ConditionCriteria instance for specifying the compare operation.
|
||||
* @throws IllegalStateException if a filter has already been specified.
|
||||
*/
|
||||
public ConditionCriteria where(String attribute) {
|
||||
assertFilterNotStarted();
|
||||
@@ -157,6 +161,44 @@ public class LdapQueryBuilder implements LdapQuery {
|
||||
return new DefaultConditionCriteria(rootContainer, attribute);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a hardcoded filter. Please note that using this method, the filter string will not be
|
||||
* validated or escaped in any way. <b>Never</b> use direct user input and use it concatenating strings
|
||||
* to use as LDAP filters. Doing so opens up for "LDAP injection", where malicious user
|
||||
* may inject specifically constructed data to form filters at their convenience. When user input is used
|
||||
* consider using {@link #where(String)} or {@link #filter(String, String...)} instead.
|
||||
*
|
||||
* @param hardcodedFilter The hardcoded filter string to use in the search.
|
||||
* @return this instance.
|
||||
* @throws IllegalStateException if a filter has already been specified.
|
||||
*/
|
||||
public LdapQuery filter(String hardcodedFilter) {
|
||||
assertFilterNotStarted();
|
||||
rootContainer = new DefaultContainerCriteria(this);
|
||||
rootContainer.append(new HardcodedFilter(hardcodedFilter));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a hardcoded filter using the specified parameters. The parameters will be properly encoded using
|
||||
* {@link LdapEncoder#filterEncode(String)} to make sure no malicious data gets through. The <code>filterFormat</code>
|
||||
* String should be formatted for input to {@link MessageFormat#format(String, Object...)}.
|
||||
*
|
||||
* @param filterFormat the filter format string, formatted for input to {@link MessageFormat#format(String, Object...)}.
|
||||
* @param params the parameters that will be used for building the final filter. All parameters will be properly encoded.
|
||||
* @return this instance.
|
||||
* @throws IllegalStateException if a filter has already been specified.
|
||||
*/
|
||||
public LdapQuery filter(String filterFormat, String... params) {
|
||||
Object[] encodedParams = new String[params.length];
|
||||
|
||||
for (int i=0; i < params.length; i++) {
|
||||
encodedParams[i] = LdapEncoder.filterEncode(params[i]);
|
||||
}
|
||||
|
||||
return filter(MessageFormat.format(filterFormat, encodedParams));
|
||||
}
|
||||
|
||||
private void assertFilterNotStarted() {
|
||||
if(rootContainer != null) {
|
||||
throw new IllegalStateException("Invalid operation - filter condition specification already started");
|
||||
|
||||
@@ -59,6 +59,34 @@ public class LdapQueryBuilderTest {
|
||||
assertEquals("(cn=*)", result.filter().encode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildHardcodedFilter() {
|
||||
LdapQuery result = query().filter("(cn=Person*)");
|
||||
|
||||
assertEquals("(cn=Person*)", result.filter().encode());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void verifyThatHardcodedFilterFailsIfFilterAlreadySpecified() {
|
||||
LdapQueryBuilder query = query();
|
||||
query.where("sn").is("Doe");
|
||||
query.filter("(cn=Person*)");
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void verifyThatFilterFormatFailsIfFilterAlreadySpecified() {
|
||||
LdapQueryBuilder query = query();
|
||||
query.where("sn").is("Doe");
|
||||
query.filter("(|(cn={0})(cn={1}))", "Person*", "Parson*");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildFilterFormat() {
|
||||
LdapQuery result = query().filter("(|(cn={0})(cn={1}))", "Person*", "Parson*");
|
||||
|
||||
assertEquals("(|(cn=Person\\2a)(cn=Parson\\2a))", result.filter().encode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuildSimpleAnd() {
|
||||
LdapQuery query = query()
|
||||
|
||||
@@ -1,9 +1,26 @@
|
||||
/*
|
||||
* Copyright 2005-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.ldap.odm.core;
|
||||
|
||||
import java.util.List;
|
||||
import org.springframework.ldap.query.LdapQuery;
|
||||
|
||||
import javax.naming.Name;
|
||||
import javax.naming.directory.SearchControls;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The OdmManager interface provides generic CRUD (create/read/update/delete)
|
||||
@@ -13,6 +30,7 @@ import javax.naming.directory.SearchControls;
|
||||
* {@link org.springframework.ldap.odm.annotations}.
|
||||
*
|
||||
* @author Paul Harvey <paul.at.pauls-place.me.uk>
|
||||
* @author Mattias Hellborg Arthursson
|
||||
*
|
||||
* @see org.springframework.ldap.odm.annotations.Entry
|
||||
* @see org.springframework.ldap.odm.annotations.Attribute
|
||||
@@ -94,4 +112,19 @@ public interface OdmManager {
|
||||
* @see <a href="http://www.rfc-editor.org/rfc/rfc4515.txt">LDAP: String Representation of Search Filters RFC.</a>
|
||||
*/
|
||||
<T> List<T> search(Class<T> clazz, Name base, String filter, SearchControls searchControls);
|
||||
|
||||
/**
|
||||
* Search for entries in the LDAP directory.
|
||||
* <p>
|
||||
* Only those entries that both match the query search filter and
|
||||
* are represented by the given Java class are returned.
|
||||
*
|
||||
* @param <T> The Java type to return
|
||||
* @param clazz The Java type to return
|
||||
* @param query the LDAP query specification
|
||||
* @return All matching entries.
|
||||
*
|
||||
* @exception org.springframework.ldap.NamingException on error.
|
||||
*/
|
||||
<T> List<T> search(Class<T> clazz, LdapQuery query);
|
||||
}
|
||||
|
||||
@@ -29,6 +29,8 @@ import org.springframework.ldap.filter.EqualsFilter;
|
||||
import org.springframework.ldap.odm.core.OdmException;
|
||||
import org.springframework.ldap.odm.core.OdmManager;
|
||||
import org.springframework.ldap.odm.typeconversion.ConverterManager;
|
||||
import org.springframework.ldap.query.LdapQuery;
|
||||
import org.springframework.ldap.query.SearchScope;
|
||||
import org.springframework.ldap.support.LdapUtils;
|
||||
|
||||
import javax.naming.Name;
|
||||
@@ -52,6 +54,7 @@ import java.util.Set;
|
||||
* convert between Java and LDAP representations of attribute values.
|
||||
*
|
||||
* @author Paul Harvey <paul.at.pauls-place.me.uk>
|
||||
* @author Mattias Hellborg Arthursson
|
||||
*
|
||||
*/
|
||||
public final class OdmManagerImpl implements OdmManager {
|
||||
@@ -272,11 +275,32 @@ public final class OdmManagerImpl implements OdmManager {
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.ldap.odm.core.OdmManager#findAll(javax.naming.Name, javax.naming.directory.SearchControls)
|
||||
*/
|
||||
@Override
|
||||
public <T> List<T> search(Class<T> clazz, LdapQuery query) {
|
||||
SearchControls searchControls = new SearchControls();
|
||||
SearchScope searchScope = query.searchScope();
|
||||
if(searchScope == null) {
|
||||
searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
|
||||
} else {
|
||||
searchControls.setSearchScope(searchScope.getId());
|
||||
}
|
||||
|
||||
Integer countLimit = query.countLimit();
|
||||
if(countLimit != null) {
|
||||
searchControls.setCountLimit(countLimit);
|
||||
}
|
||||
|
||||
Integer timeLimit = query.timeLimit();
|
||||
if(timeLimit != null) {
|
||||
searchControls.setCountLimit(timeLimit);
|
||||
}
|
||||
|
||||
// Defaults to null which means 'all', so if it's not set we're still good.
|
||||
searchControls.setReturningAttributes(query.attributes());
|
||||
|
||||
return search(clazz, query.base(), query.filter().encode(), searchControls);
|
||||
}
|
||||
|
||||
public <T> List<T> findAll(Class<T> managedClass, Name base, SearchControls scope) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug(String.format("Searching for all Entries with objectClass=%1$s, with base=%2$s, scope=%3$s",
|
||||
|
||||
@@ -67,6 +67,7 @@ import java.util.Set;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.springframework.ldap.query.LdapQueryBuilder.query;
|
||||
|
||||
// Tests all OdmManager functions
|
||||
public final class TestLdap {
|
||||
@@ -361,6 +362,16 @@ public final class TestLdap {
|
||||
assertEquals("Hartnell", foundPerson.getSurname());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifySearchWithLdapQuery() {
|
||||
List<Person> result = odmManager.search(Person.class, query().base(baseName).where("cn").is("William Hartnell"));
|
||||
assertEquals(1, result.size());
|
||||
|
||||
Person foundPerson = result.get(0);
|
||||
assertEquals("William Hartnell", foundPerson.getCn());
|
||||
assertEquals("Hartnell", foundPerson.getSurname());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updatePlainPerson() {
|
||||
List<PlainPerson> result = odmManager.search(PlainPerson.class, baseName, "(cn=William Hartnell)", searchControls);
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2005-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.ldap.odm.sample;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
@@ -5,17 +21,14 @@ import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.ldap.odm.core.OdmManager;
|
||||
import org.springframework.ldap.support.LdapUtils;
|
||||
|
||||
import javax.naming.directory.SearchControls;
|
||||
import javax.naming.ldap.LdapName;
|
||||
import java.util.List;
|
||||
|
||||
import static org.springframework.ldap.query.LdapQueryBuilder.query;
|
||||
|
||||
// A very simple example - just showing how little code you actually need to write
|
||||
// when using Spring LDAP ODM
|
||||
public class SearchForPeople {
|
||||
private static final SearchControls searchControls =
|
||||
new SearchControls(SearchControls.SUBTREE_SCOPE, 100, 10000,
|
||||
null, true, false);
|
||||
|
||||
private static final LdapName baseDn = LdapUtils.newLdapName("o=Whoniverse");
|
||||
|
||||
private static void print(List<SimplePerson> personList) {
|
||||
@@ -31,7 +44,7 @@ public class SearchForPeople {
|
||||
OdmManager odmManager = (OdmManager)context.getBean("odmManager");
|
||||
|
||||
// Find people with a surname of Harvey
|
||||
List<SimplePerson> searchResults = odmManager.search(SimplePerson.class, baseDn, "sn=Harvey", searchControls);
|
||||
List<SimplePerson> searchResults = odmManager.search(SimplePerson.class, query().base(baseDn).where("sn").is("Harvey"));
|
||||
|
||||
// Print the results
|
||||
print(searchResults);
|
||||
|
||||
@@ -1,19 +1,34 @@
|
||||
/*
|
||||
* Copyright 2005-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.ldap.odm.sample;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.HashSet;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import javax.naming.Name;
|
||||
|
||||
import static org.springframework.ldap.odm.annotations.Attribute.*;
|
||||
|
||||
import org.springframework.ldap.odm.annotations.Attribute;
|
||||
import org.springframework.ldap.odm.annotations.Entry;
|
||||
import org.springframework.ldap.odm.annotations.Id;
|
||||
|
||||
import javax.naming.Name;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import static org.springframework.ldap.odm.annotations.Attribute.Type;
|
||||
|
||||
/**
|
||||
* Automatically generated to represent the LDAP object classes
|
||||
* "person", "top".
|
||||
|
||||
@@ -108,4 +108,28 @@ List<Person> persons = ldapTemplate.search(
|
||||
</itemizedlist>
|
||||
</para>
|
||||
</sect1>
|
||||
<sect1>
|
||||
<title>Hardcoded Filters</title>
|
||||
<para>
|
||||
There are occasions when you will want to specify a hardcoded filter as input to an <literal>LdapQuery</literal>.
|
||||
<literal>LdapQueryBuilder</literal> has two methods for this purpose:
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<literal>filter(String hardcodedFilter)</literal> - uses the specified string as filter.
|
||||
Note that the specified input string will not be touched in any way, meaning that this method is not
|
||||
particularly well suited if you are building filters from user input.
|
||||
</listitem>
|
||||
<listitem>
|
||||
<literal>filter(String filterFormat, String... params)</literal> - uses the specified string as input
|
||||
to <literal>MessageFormat</literal>, properly encoding the parameters and inserting them at the
|
||||
specified places in the filter string.
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
<para>
|
||||
You cannot mix the hardcoded filter methods with the <literal>where</literal> approach described above; it's
|
||||
either one or the other. What this means is that if you specified a filter using <literal>filter()</literal>
|
||||
you will get an exception if you try to call <literal>where</literal> afterwards.
|
||||
</para>
|
||||
</sect1>
|
||||
</chapter>
|
||||
@@ -50,6 +50,9 @@
|
||||
<para><code><T> List<T> search(Class<T> clazz, Name
|
||||
base, String filter, SearchControls searchControls)</code></para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para><code><T> List<T> search(Class<T> clazz, LdapQuery query)</code></para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>A reference to an implementation of this interface can be obtained
|
||||
|
||||
Reference in New Issue
Block a user